1. 分支管理
git的分布式特性已经让我们欢呼不已,但git的魅力远不止这些,git还有一个大招,那就是神奇的分支。上面提到,每次commit都会将暂存区中的最新修改信息递交到版本库中,从而形成一系列不同时间点的版本节点,形成一个版本历史,这样的一个历史线条,称之为一个分支。git允许并且鼓励一个项目有多个分支,每创建一个分支,相当于将版本演进放到一个单独的世界,不受外界的干扰,独自演化,到恰当的时候可以融合。这非常适合于多人协作的软件开发情景,如下图所示。

git的分支
2. 分支的使用场景
继续探讨分支之前,先来总结分支的基本使用场景:
- 多人协作开发时,每个小分队拥有自己的分支,开发完一个阶段之后融合进主版本
- 为稳定的主版本加入新功能时,创建新分支进行Develop,测试成功后融入主版本
- 为已发布的主版本进行Debug时,创建新分支进行修复,成功之后融入主版本
- 将不同软件的版本库作为分支进行融合,这正是开源软件协作的基石

git的分支管理
回忆之前两个小节的内容,master是git在初始化的时候默认产生的分支,而HEAD是当前分支的最新版本,实际上master是一个指针,它指向主分支的历史线的最新版本节点,而HEAD是一个指向master的指针,容易想到,如果当前仓库有多个版本分支,比如Develop分支,那么HEAD可以随时切换到Develop。它们的关系如下图所示。

主分支与Develop分支
3. 分支的创建和切换
下面来用一个例子说明分支与融合的整个基本操作过程,假设现在我要写一篇文稿,写完使用git保存最新版本。首先初始化一个git仓库,创建一个git.txt并将其commit到当前默认的master分支中,然后使用branch命令查看当前所在的分支。

可以看到当前分支是master,这是git初始化时自动创建的分支,随着给文档继续添加内容,不断commit,就会形成一个历史版本时间线,假设每次commit的时候使用A、B、C、D来简单描述当前的节点,那么经过几次commit之后的结果大概会如下左图所示。

主分支的逐步演进 新建分支dev并且换分支
接着,我们打算新建一条分支dev,用来开发一个新的功能,当测试完成之后再融入主版本分支,如上右图所示。

上面命令(1)和命令(2)+(3)的效果是等价的,都意味着先创建一个分支dev,然后切换到分支dev,此时使用branch命令再来查看当前所在的分支,会发现已经切换到dev下了。

接着,在dev上对当前仓库做出修改,并将修改后的版本commint到版本库中

新的版本E是分支dev上的最新节点(而不是master)。此时各分支的关系如下图所示。

新分支dev上的版本迭代
4. 分支的融合
注意,此时我们的工作在dev分支上,这上面所做的一切均不会对原有的master分支代码版本有任何影响,因此master作为在工程开发中发布给用户使用的稳定版本,不会因为软件的更新迭代而受

- 命令(2):将分支dev融合到当前分支(即master分支)
- 命令(3):将分支dev删除
上述操作步骤,就是典型的开发流程——在一个对外不可见的分支上,做完了开发与测试,或者修复了Bug之后,将新分支融合进主版本,然后将分支删除。
当然,上述命令(2)的正常执行是有条件的,那就是当前的master分支的最新版内容,与指定的dev分支上最新版的内容没有冲突,可以融合。
5. 分支的冲突与解决
来考虑另一种很常见的情况,那就是张三在master分支(这只是举例,一般master是不允许做任何修改的!)上做了修改,而李四再次创建dev分支并在上面做了不同的修改,此时张三再想把这两条分支融合,就必然会产生冲突。首先是张三在master上的修改:

而与此同时,李四在dev上也在文档的末尾添加了一行:

然后,当张三和要把李四的dev分支融合的时候,Boom!爆炸了!两个不一样的修改导致git无所适从。以上情形读者可以使用同一个用户来模拟。

此时的冲突如下图所示,由两个不同的修改者沿着两条分支到达不同的版本节点,由此带来了冲突。当执行了merge命令之后,git将会将差异信息直接写入相关文件内,并且锁定当前分支,无法切换到别的分支,直到手动将差异统一为止。

两个冲突的分支
查看当前的git.txt的内容,将会看到两分支版本的差异:

接下来把内容修改好,然后再将最终修改版添加并commit到版本库:

从上述 git log 命令的输出看到,经过手工修改并commit之后,两个分支成功融合为一个版本了。可见,经过分支和融合就可以实现多路开发,这个功能十分强大,每个人都可以有自己的分支,团队合作的分支看起来差不多是下图的样子。

团队合作的分支管理