前言:
在前面第3、4 篇陆续学习了git的基础命令以及其流程原理等。
这一节我们学习git中很牛逼哄哄的分支管理,它是git的核心。掌握好git的分支,就可以走遍天下都不怕了。
同样也是参考了这本书:
http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
什么是分支
在学习分支之前,我们首先得明白什么是分支,分支是干嘛的?味道咋样。
分支,顾名思义,是从主干分出去的一条枝干,就好比一颗大树一样,有很多支节,git的分支和svn等其他版本库管理软件一样,是从主干分离出去的一条枝干线,用来处理另外的一个事情,而且是平行的,不影响主干的开发,等分支事情完成后,再合并回到主干线,完成分支的使命。
分支有什么用
我们画个图来举个例子,假如你正在学习git,又在学习svn,有两种可能,是你先学git,等学会再学svn,另一种是你白天学git,晚上学svn,两种情况都是可以学到知识,可想而知,是一起学来的更快,且互不影响:
这是一条很形象的分支的例子。
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
创建与合并与删除分支
原理
git 中的分支的创建和合并非常之快,快的超乎想象,为什么这么快呢?在学习创建和合并命令之前,我们先来搞清楚git的分支创建的原理。原理弄清楚了,学习命令就很简单了。
在之前我们的几节中,我们知道了,在用git创建版本库后,它会默认帮我们创建一个master分支,这个就是主干支,只要我们未创建和切换到其他的分支,默认的所有的提交修改都是在这个master主分支之上。前面还提到git reflog这个命令,是用来显示所有的提交,是一条时间线串起来的,安照时间,把我们的每次提交给用线给串起来:
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
随着你每次的commit提交,master分支就像前移动一步,这个线就会越来越长,就像这样:
好,这个时候,我们创建一个分支,创建命令不明白不要急,后面再说。假设你已经知道怎么创建了,现在我们创建一个名字叫做dev的分支,git是怎么实现的呢?它是创建了一个叫做dev的指针(看,C语言没学好吧!),然后改变HEAD的指向,从指向master变成指向dev,这样一个分支就创建好了。所以这就是git中创建分支如此之快的原因:
从现在开始呢,就已经切换的新的dev分支了。所有的操作将会再dev上面了,master主分支指针保持不变:
假如我们完成了dev分支的工作,代码需要上线了,上线一般会从master分支的代码发布到线上。所以,我们就要把dev的代码marge到master上去。git中的合并也是很快速的,原因也是一样,只是单纯的移动指针,就是直接把master指向dev的当前提交,就完成了合并:
你看,就是这么快速,所以git中鼓励大量使用分支来开发,因为,创建分支,合并分支,就是动动指针,不会改变内容。
好了。假如我们dev已经开发完成,完全可以将这个分支删除掉,只保留master主枝干,同样,删除也很快,因为仅仅是将dev分支的指针删除了,不涉及文件:
命令与实践
上面说完了整个创建分支,合并分支的原理,相信已经掌握了,现在是不是已经想跃跃欲试用git命令如何实现呢?
我们一步步来。
在git 中我们一般会用
git checkout -b 分支名
来创建分支,并切换到这个新创建的分支,以后的修改提交就在这个新分支了:
tonyyang@021ZJ1315 /d/learngit (master)
$ git checkout -b dev Switched to a new branch 'dev' tonyyang@021ZJ1315 /d/learngit (dev)
上面的显示很明显的看出了。创建好了dev分支,并且已经切换到dev分支了。
其实,git checkout -b 分支名命令是2个命令的简写:git branch dev 和 git checkout dev
tonyyang@021ZJ1315 /d/learngit (master) $ git branch dev2 tonyyang@021ZJ1315 /d/learngit (master) $ git checkout dev2 Switched to branch 'dev2' tonyyang@021ZJ1315 /d/learngit (dev2)
我们可以用命令
git branch
来显示所有的分支,前面的一个*号,表示当前正在这个分支上。
$ git branch
dev
* dev2
master
好。那我们就在dev上进行开发和提交。我们修改一下README.md 的一行内容:Creating a new branch is quick.
Git is a distributed version control system. Git is free software. it is a good tool. distributed under the GPL. Git has a mutable index called stage. Git tracks changes. Creating a new branch is quick.
好。我们add,commit一系列正常的提交:
$ git add README.md $ git commit -m "branch test" [dev 5647b32] branch test 1 file changed, 1 insertion(+) $ git status On branch dev nothing to commit, working directory clean
好,现在假设dev的工作已经开发完成了,要上线了。那么我们就切换到master主干。如何切换呢?用git checkout master来切换到master分支了:
$ git checkout master Switched to branch 'master'
好,提示我们已经切换到master分支了,那我们看一下README.md文件其变化没?
Git is a distributed version control system. Git is free software. it is a good tool. distributed under the GPL. Git has a mutable index called stage. Git tracks changes.
咦?居然没有Creating a new branch is quick.这一句,哦。恍然大悟,因为这一句是在dev分支加进来的,现在已经切换到master分支了,当然就没有了,那么如何加进来了,这个时候就要提到合并命令了。git 中有 git merge 分支名来把分支合并到当前分支,默认是快速合并 Fast forward 模式。
所以我们可以这样把dev合并进来:
$ git merge dev Updating 4b8661a..5647b32 Fast-forward README.md | 1 + 1 file changed, 1 insertion(+)
现在再看一下README.md就有内容了。既然dev分支已经完成了使命,我们就可以将它删掉,保持一个整洁的分支。
git中用命令git branches -d 分支名来删除这个分支。
$ git branch -d dev Deleted branch dev (was 5647b32).
现在问题来了,我们用 git merge 分支名来默认采用的是快速合并Fast forward 模式, 这个模式最大的有点是快速,见下图:
是直接尾部合并,指针指向,所以,当我们删除dev分支后,将会丢失这个分支的所有信息,其实还是蛮危险的,那么如何合并,删除分支后,log 中还能保持这个分支的信息呢?那就要用到禁用快速分支模式。用git merge --no-ff -m "merge with no-ff" dev来完成合并了。这种方式是这样子做的,它加了一个新的commit:
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
$ git log --graph --pretty=oneline --abbrev-commit
* 7825a50 merge with no-ff
|\
| * 6224937 add merge
|/
* 59bc1cb conflict fixed
...
小结
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch name
切换分支:git checkout name
创建+切换分支:git checkout -b name
合并某分支到当前分支:git merge name
删除分支:git branch -d name
解决冲突
在多人开发中,冲突出现的概率十有八九,几乎是家常便饭,冲突一般是怎么出现的呢?1是几个人同时修改了1处代码,合并的时候就会有冲突了。2是你提交的时候未先更新,导致你的老代码和新代码冲突了。所以,提交之前要先更新。
回到正题,不管是哪种原因,如果出现了冲突,咋么办。当然是解决冲突了。和svn中的一样。git中也是那么解决冲突的。我们先来模拟一个冲突。
准备新的feature1分支,继续我们的新分支开发:
$ git checkout -b feature1
Switched to a new branch 'feature1'
修改README.md最后一行,改为:
Creating a new branch is quick AND simple.
在feature1分支上提交:
$ git add README.md
$ git commit -m "AND simple"
[feature1 75a857c] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)
切换到master分支:
$ git checkout master
Switched to branch 'master'
在master分支上把readme.txt文件的最后一行改为:
Creating a new branch is quick & simple.
提交:
$ git add r
$ git commit -m "& simple"
[master 400b400] & simple
1 file changed, 1 insertion(+), 1 deletion(-)
现在,master分支和feature1分支各自都分别有新的提交,变成了这样:
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:
$ git merge feature1
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
果然冲突了!Git告诉我们,README.md 文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: README.md
#
no changes added to commit (use "git add" and/or "git commit -a")
我们可以直接查看README.md的内容:
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存:
Creating a new branch is quick and simple.
再提交:
$ git add README.md
$ git commit -m "conflict fixed"
[master 59bc1cb] conflict fixed
现在,master分支和feature1分支变成了下图所示:
用带参数的git log也可以看到分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
* 59bc1cb conflict fixed
|\
| * 75a857c AND simple
* | 400b400 & simple
|/
* fec145a branch test
...
现在,删除feature1分支:
$ git branch -d feature1
Deleted branch feature1 (was 75a857c).
小结
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
用git log --graph命令可以看到分支合并图。