前言:

在前面第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命令可以看到分支合并图。


Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐