git详解
来源:互联网 发布:前景检测算法 matlab 编辑:程序博客网 时间:2024/06/08 08:29
git详解
本文为“廖雪峰git教程”的学习心得
1.安装与设置git
- 首先安装git,在ubuntu中使用
apt-get install git
即可。 - 安装完成后,还需要最后一步设置,在命令行输入:
git config --global user.name "xxxx"
git config --global user.email "xxx@xxxx.com" - 因为git是分布式的,所以,每个机器都必须自报家门:你的名字和Email地址。注意git config命令的–global参数,用了这个参数,表示此机器上所有的git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址
- 配置公司的服务器地址,这样git才能通过ssh等方式定位到具体的ip
sudo vi /etc/hosts在文件尾加入公司服务器地址:1xx.xx.xx.xx git.xxx.org1xx.xx.xx.xx Yocto.xxx.org
2.初始化一个空的版本库
- 创建版本库,所谓的版本库就是在这个目录下所有的文件都将被git管理,每个文件的修改、删除,Git都能跟踪。比如我们进入一个目录/xxxx,在控制台中输入
git init
随后控制台显示
Initialized empty Git repository in /xxxx/.git/
它告诉我们创建了一个空的仓库.git,我们可以用ls -a
查看/xxxx目录,发现确实多出来了一个.git目录,以后里面都会存放和此版本库有关的一些文件 - 于是乎,/xxxx就变成了版本库,里面的文件改动就可以被git管理了。有一点是要注意的,git只能管理文本文件比如txt、c文件等,像图片、可执行程序等二进制文件则无法被管理
3.add(stash)和commit的区别
- 首先对于简单应用,可以简单粗暴的认为,add 和 commit操作是绑在一起用的,要把文件提交到版本库,只需先add 再 commit即可
- add的功能是将文件添加入“暂存区域”,比如我们添加一个test.c
git add test.c
- commit的功能是把“暂存区域”内的所有文件提交到本地版本库中,比如输入:
git commit -m "wrote a test.c file"
其中的-m参数后面可以指定
随后控制台显示
[master (root-commit) 6c614cb] wrote a test.c file
1 file changed, 34 insertions(+)
create mode 100755 test.c
表示commit成功 - 为什么提交要分为add和commit两步呢?主要是为了实现分批提交,降低commit的颗粒度,比如我们修改了 a.c, b.c, c.c, d.c,其中 a.c 和 c.c 是一个功能相关修改,b.c,d.c属于另外一个功能相关修改。那么就可以采用:
- 很多时候,我们会修改一些代码或添加一些新文件,这些改动并不是针对项目的,仅仅是为了在本地方便自己开发。我们不想提交这些改动应该怎么做?这里不能使用gitignore来忽略这些文件,因为涉及到改动gitignore,这会污染远程仓库的gitignore。完全新添加的文件属于”Untracked files”,而已有文件中修改过的代码则属于”tracked files”。对于”Untracked files”,放在在工作区没有关系,我们可以不用管,而”tracked files”改过了,就不能随意放在工作区了,必须要放到暂存区。这里使用
git stash
来将它们放入暂存区,这条命令与git add最大的区别是,stash之后commit便会忽略暂存区中对应的文件,这相当于一种忽略机制
git add a.c c.cgit commit -m "function 1"git add b.c d.cgit commit -m "function 2"
- 这样,我们就能实现先改2个文件,再改2个文件的效果(其实我们是4个文件同时改了)。由此一来,版本之间区分就更加细致
4.查看状态
- 当我们在版本库内工作时,可以随时输入
git status
来查看距离上一次add、commit以来,哪些文件被改动过了 - 我们还可以输入
git diff xxx
来查看某个文件中具体被改动的内容,被改动的地方都会被打印出来 - 当需要查看提交日志(log)时,使用
git log
命令即可,我们就可以得知每一次commit的历史记录,这个操作一般用来确定要回退到过去的某个版本 - 我们还可以通过
git reflog
来查看每一次git操作,以及它们的“简略编号”commit_id,这个操作一般用来查看id以回到任意某个版本 - 我们可以输入
git branch
来查看一共有几条分支,以及他们的信息,当前分支前面会标一个*号 - 用
git log --graph --pretty=oneline --abbrev-commit
命令可以看到极为直观的分支图,使用git log --graph
则会看到详细但不直观的分支图
5.版本回退
所谓的版本回退,是将版本库、工作区的状态全部回到从前的某个状态。我们还是以test.c为例,使用git log查看当前commit的历史记录,可知当前版本是add printf的那一次commit
commit 774f8be8f6e711816ab5f84c5bba822cc9226d20Author: taurenking <xxx.com>Date: Sat Sep 24 21:08:06 2016 +0800 add printfcommit 6fc8d726ad6f4f58ad4654d126bf905427b53b17Author: taurenking <xxx.com>Date: Sat Sep 24 20:58:28 2016 +0800 add entercommit 6c614cb5a30dcbfd60c078d5871fd8c9a8d651e3Author: taurenking <xxx.com>Date: Sat Sep 24 07:13:12 2016 +0800 wrote a test.c file
- 在git中,用HEAD表示当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,往前的100个版本就是HEAD~100
- 版本回退可以使用
git reset --hard commit_id
(推荐)或者git reset --hard HEAD^
(不推荐) 此类命令,我们输入git reset --hard HEAD^
回到上一个版本,再git log
查看commit历史,发现最新的add printf那一次commit记录消失了!说明我们成功回到了add enter的那一次状态
commit 6fc8d726ad6f4f58ad4654d126bf905427b53b17Author: taurenking <xxx.com>Date: Sat Sep 24 20:58:28 2016 +0800 add entercommit 6c614cb5a30dcbfd60c078d5871fd8c9a8d651e3Author: taurenking <xxx.com>Date: Sat Sep 24 07:13:12 2016 +0800 wrote a test.c file
- 那我们如果反悔了怎么办?想回到add printf的那一次commit,可是git log中的那一次记录已经消失了,这时我们可以输入
git reflog
来查看每一次git操作,以及它们的“简略编号”commit_id,此操作一般用来查看id以回到“未来”的某个版本,打印信息如下:
6fc8d72 HEAD@{0}: reset: moving to HEAD^
774f8be HEAD@{1}: commit: add printf
6fc8d72 HEAD@{2}: commit: add enter
6c614cb HEAD@{3}: commit (initial): wrote a test.c file
我们输入git reset --hard 774f8be
,就回到了add printf的那一次commit状态了 - 总结:
git reset --hard commit_id
或者git reset --hard HEAD^
都可以回退版本,第一个更值得推荐,因为可以回到任意的某个状态,极为灵活
6.重置工作区与删除文件
- 当我们对工作区的文件进行了误操作时,我们可以将文件重置为当前版本库中的状态,使用
git reset --hard
即可。注意,这和版本回退是两个概念,这里只是重置工作区到最近一次的commit状态罢了,版本库并未发生任何改动 - 当我们要删除一个文件该怎么做呢?首先肯定是先把工作区内的实际文件删掉,直接
rm -rf xxx
即可;然后输入git rm xxx
从版本库中删除该文件,最后git commit
- 如果我们的文件删错了并且还未add、commit,那么直接重置工作区即可找回。但是如果删除后已经add、commit了,那就只能版本回退了
7.远程仓库
- GitHub提供很有名的服务,但是很多公司也自己搭建服务器来实现远程仓库。一般远程仓库使用ssh或http等协议。比如GitHub,如果上面有一个远程版本库(也可以理解为项目),那么网页上会提供一个ssh或http的地址来对应该版本库,通过这个地址就能访问该版本库
- 使用http的优点是比较简单,缺点是速度比较慢,并且每次推送都必须输入口令
- 如果要使用ssh,必须先要拥有SSH Key,用户目录下(root用户就是/root)输入
ssh-keygen -t rsa -C "xxxx@xxx.com"
,它会询问是否在当前目录创建,直接按回车确认,然后它会让我们设置一个密码 - 这样一来用户目录下就有了一个.ssh目录,里面有两个文件id_rsa和id_rsa.pub,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人,一般使用ssh来访问GitHub这种远程仓库时,就需要事先把自己id_rsa.pub公钥登在网上登记一下
- 当远程仓库已经存在,而我们本地没有任何东西,需要把远程仓库原模原样的复制一份到本地时,就可以使用clone命令,首先进入一个空目录,clone之前目录无须事先进行init!!!!输入
git clone xxxxxxxxxxx(远程仓库的ssh或http地址)
,即可 - 当远程仓库不存在,而我们想把本地已经存在的版本库传上去时:
- 第一步,需要现在GitHub或其他服务器创建一个空的远程版本库(也可以理解为项目)
- 第二步,登记ssh公钥(可选),然后获得远程仓库的ssh或http地址
- 第三部,在本地版本库的目录内,输入
git remote add origin xxxxxxxxxxx(远程仓库的ssh或http地址)
令本地版本库和远程版本库关联起来。命令中的origin是git对远程版本库的默认命名,也可以改成别的(一般别改。。) - 第四步,本地库的内容推送到远程,输入
git push -u origin master
,实际上是把当前分支master推送到远程。由于远程库是空的,我们第一次推送master分支时,加上了-u参数,git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
- 从现在起,只要本地作了add、commit,就可以通过命令:
git push origin master
来推送到远程了 - 注意第一次用ssh连接远程仓库时可能会警告:
The authenticity of host ‘github.com (xx.xx.xx.xx)’ can’t be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?
这是正常的,按照它说的输入yes即可
8.利用git来生成patch
有时,我们会需要对项目做补丁,利用git可以生成patch
- 一种老旧的方法是使用diff实现,首先在工作区做修改,修改完成并测试可靠后,使用
git diff>xxx.patch
,将工作区状态和上一次commit之间的区别做成patch - 一种推荐的方法是使用format来实现,先把做的改动add、commit一下,然后
git format-patch HEAD^
即可将当前的commit和再上一次commit之间的区别做成patch
9.添加代码比较工具
虽然git diff可以方便的比较工作区和commit之间的差异,但是却不能编辑
- 我们可以将vim中自带的vimdiff添加到git的difftool中,这样今后就能用
git difftool
来比较和编辑代码了
git config --global diff.tool vimdiffgit config --global difftool.prompt falsegit config --global alias.d difftool
9.最基本的分支管理
这里阐述的是最简单的分支管理,不涉及多人合作、以及代码更新,实际工程中的分支管理详见下一章
- 比如我们一开始有一条主线master,如下图,用HEAD指向的节点表示当前版本
- 输入
git checkout -b dev
创建了一个分支dev,git checkout命令加上-b参数表示创建并切换到该分支,相当于以下两条命令:git branch dev
、git checkout dev
- 然后,我们就可以在dev分支上正常工作了,add、commit后:
- 我们想把分支dev合并进主线master,而
git merge xxx
命令用于合并指定分支xxx到当前分支,所以首先要git checkout master
回到主干,再git merge dev
合并支线到主干 - 那么如果多个人同时维护主线master呢?比如我创建了一条分支feature1,工作完后
git checkout master
回到主干,再git merge feature1
合并支线到主干,突然git告诉我test.c有冲突,原来master被另一个人更新过了,并且那个人写的test.c内容和我有冲突
这时,我们处于“解决冲突状态”,我们需要去把冲突的文件修改一下,git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改之。修改完后输入:git add test.c
git commit -m "conflict fixed"
自动退出“解决冲突状态”,完成合并,提交到了本地版本库 - 如何删除分支?
git branch -d xxx
即可删除xxx分支
10.Pull Request工作流实例
整个project涉及到3个仓库
- public: 在Git服务器上,是项目的正式发布仓库,由项目管理员修改,用户只读
- private: 在Git服务器上,是用户从public仓库fork(又称clone reponsitory)到自己名下的私有仓库,用户可以读写
- local: 在用户本地机,是用户从private仓库clone下来的,用户可以直接修改
开发逻辑:用户在自己的本地仓库做开发,开发完了上传到私有仓库,然后申请合并私有仓库的内容到公共仓库
下面以一个项目实例来阐述细节。假设我们的project是一个GUI程序,该程序将被用在三个产品上,分别是mini、normal、super,该程序的代码在三个产品上有细微的不同,那么本project就适合分出三条branch,名字分别就是mini、normal、super。后面的讲解以super这个产品的开发为例
fork正式项目
- 首先,在git服务器上,公司public仓库里静静的躺着三条branch:mini、normal、super(其中,默认的branch是normal)。我们若想开发产品super,就必须把fork一份public仓库,从而得到自己的private仓库。这一步通常是网页上登陆git服务器,浏览到public仓库,点fork(又称clone reponsitory)按钮。
clone私有仓库
- fork完之后就得到了和公司public仓库完全相同的private仓库,里面同样有三条分支。然后我们就要创建一个本地仓库,建一个新的空目录,最好把名字起成project的名字,然后进去直接
git clone privateURL localdir
(privateURL可以是private仓库的ssh地址),这样local仓库就建成了,可以进行开发了
整顿branch
- 利用
git branch
查看local仓库的branch,发现clone下来的只有一条默认的branch,即normal,其实super和mini只不过被隐藏起来了罢了,这是git clone的特性,即只显示默认的branch。我们使用git checkout super
,再次git branch
,发现我们成功切换到了super - 在开始改代码前,我们必须新建一条专为开发使用的branch。
git checkout -b super_dev
,一般用作开发的分支都以_dev结尾
开发local代码
- 在super_dev分支上,我们可以进行多次小修改,并且多次add、commit
git add a.c c.cgit commit -m "function 1"
- 很多时候,我们会修改一些代码或添加一些新文件,这些改动并不是针对项目的,仅仅是为了在本地方便自己开发。我们不想提交这些改动应该怎么做?这里不能使用gitignore来忽略这些文件,因为涉及到改动gitignore,这会污染远程仓库的gitignore。完全新添加的文件属于”Untracked files”,而已有文件中修改过的代码则属于”tracked files”。对于”Untracked files”,放在工作区没有关系,我们可以不用管,而”tracked files”改过了,就不能随意放在工作区了,必须要放到暂存区。这里使用
git stash
来将它们放入暂存区,这条命令与git add最大的区别是,stash之后commit便会忽略暂存区中对应的文件,这相当于一种忽略机制。但是当我们提交完后要让它们从暂存区中弹出来git stash pop
同步更新
- 当我们完成工作了,想发布代码时,先要将public仓库当前状态同步到local。这一步是必须的,因为我们要保证发布的代码含有public仓库最新的状态
git checkout supergit pull publicURL supergit checkout super_devgit rebase super
首先切换到super,将public仓库中的super同步下来。然后切换回super_dev,用rebase将super的更新作用到当前分支super_dev
- rebase时,若public仓库中的更新和我们super_dev中的改动有冲突,则需要我们解决,rebase产生冲突时,会自动创建并切换到一个临时branch,可以用git branch查看。当我们解决完冲突后,使用
git rebase --continue
返回
提交工作
- 提交工作时,最好使用专用的merge_request分支,如果没有的话就创建一个。之所以要使用专用的merge_request分支,是因为开发(dev)可能和merge_request同时进行,并且代码可能不一样,要保证两者的独立性和纯洁性
git checkout super_devgit checkout -b super_merge_request
- 如果已经存在super_merge_request分支,则将super_dev更新作用到super_merge_request即可
git checkout super_merge_requestgit rebase super_dev
- 然后最重要的是精简commit,将杂乱无章的commit合并精简。假设我们曾有过三次commit,号码为111111、222222、333333,先要将其合并,则输入
git rebase -i 000000
其中00000是服务器上最新的状态,即我们改动之前的状态。输入完会弹出窗口,里面#开头的都是注释,不用care
pick 33333 xxxxxxxpick 22222 xxxxxxxpick 11111 xxxxxxx
我们只需将222222、1111111前面的pick改成s,即可将这两个commit记录删掉(合并到33333),保存退出后又会弹出窗口,我们将非注释部分的那几行 commit message, 修改合并成一行新的 commit message,保存然后退出,再次输入git log查看 commit 历史信息,会发现这三个 commit 已经合并了
- 将super_merge_request推送到远端的私有仓库的super中
git push private_URL super_merge_request:super#或者下面这句也可以git push origin super_merge_request:super
- 最后我们可以登录服务器查看private仓库里的提交,做最后的检查
merge request
- 我们在private仓库里的提交边上可以找到merge request(有时也称之为Pull Request)按钮,发起一个merge request,弹出窗口将询问以指定代码审核者、源分支、目标仓库和目标分支。
- 管理员若接受此merge request,则我们的代码改动将会被合并到public仓库的super分支,至此整个开发过程就完成了
回炉重造
- 如果管理员认为还有问题,需要修改,那么不用关闭网页上的merge request,直接更新即可。
- 先在本地super_merge_request上修改,然后commit,再按照前文所说的将commit合并精简。再将super_merge_request推送到远端的私有仓库的super中
git push private_URL super_merge_request:super#或者下面这句也可以git push origin super_merge_request:super
- 由于之前的那次merge request已和private仓库中的super关联起来了,当我们改动了private仓库中的super后,merge request便会自动更新,相当智能
- 当管理员认可了merge request,最后在本地进行
git checkout super_devgit rebase super_merge_request
来将super_merge_request的特性合并入dev
- 重返工作时别忘了用下面命令恢复在暂存区的改动(比如用来增加工作效率的脚本)
git stash listgit stash pop stash@{num}
- Git详解
- Git 详解
- Git详解
- Git详解
- GIT详解
- git详解
- Git详解
- Git详解
- Git详解
- git详解
- git 详解
- GIT 详解
- git 详解
- git 详解
- git 详解
- Git 详解
- git详解
- git详解
- 阶梯乘法——阶乘与梯乘
- Spark2.0.0集群搭建部署
- android开发自定义View(三)仿芝麻信用积分
- Flume采集日志进入HDFS以及Hadoop环境搭建
- 分布式缓存架构基础
- git详解
- 5.20
- 内存泄漏与内存溢出的区别
- 巧妙利用“宏”来编辑QT中的信号和槽
- 最常用的Notepad++的快捷键
- ASP.NET开发必备51种代码(非常实用)
- mac osx wine 1.7.5 源码编译方法及中文乱码的解决
- Oracle 数据库备份与恢复的理论基础
- perl 线程概述