把一个分支整合到另一个分支的办法有两种:merge(合并)
和 rebase(衍合)
。在本章我们会学习什么是衍合,如何使用衍合,为什么衍合操作如此富有魅力,以及我们应该在什么情况下使用衍合。
简单的说衍合就是把当前的分支的变化在另外的一个分支上重演一次。
在我们以前的项目的基础上,把 master分支向前推进两步。
因为我在自己练习的时候已经将featurex1分支在master分支上做了一次rebase,所以目前的featurex1分支和master分支在统一条线上。
为了更清楚的表示,我们这里在featurex1分支的基础上再拉出一个分支叫做featurex1_issue1,然后将分支推进两步。
这个时候,master 分支在前面的基础上前进了两步,featurex1_issue1分支也前进了两步,可以让featurex1_issue1在master分支上做rebase.
为什么需要对有些分支做rebase 呢?
最后整合得到的结果没有任何区别,但衍合能产生一个更为整洁的提交历史。
rebase 以后能够得到更加简洁的提交历史。
而且要特别强调,rebase以后,原来的分支就不再了,而是顺着在哪个分支做的rebase继续沿着那个分支顺延下去。。。。这点要特别明确,所以新生成的合并的分支会看起来比较简洁。而且,当一个分支在master分支上做了rebase,这个分支的版本要超过master指针指向的文件快照,还要合并mastter分支和这个新的衍合后的分支,master分支就可以指向最新的整合的分支了。因为两个分支存在一个分支的指针是另一个分支的祖先,所以可以实现fast-forward
watkins@watkins:~/watkins/finance$ git checkout featurex1_issue1 Switched to branch 'featurex1_issue1' watkins@watkins:~/watkins/finance$ gitk watkins@watkins:~/watkins/finance$ git rebase master First, rewinding head to replay your work on top of it... Applying: issue2_1 Applying: issue2_2 watkins@watkins:~/watkins/finance$ git checkout master Switched to branch 'master' watkins@watkins:~/watkins/finance$ gitk watkins@watkins:~/watkins/finance$ git merge featurex1_issue1 Updating 1139a2e..baaf2e4 Fast-forward funciton3 | 3 +++ 1 file changed, 3 insertions(+) watkins@watkins:~/watkins/finance$
请回顾之前有关合并的一节(见图 3-27),你会看到开发进程分叉到两个不同分支,又各自提交了更新。
之前介绍过,最容易的整合分支的方法是 merge
命令,它会把两个分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)进行三方合并。如图 3-28 所示:
其实,还有另外一个选择:你可以把在 C3 里产生的变化补丁重新在 C4 的基础上打一遍。在 Git 里,这种操作叫做_衍合(rebase)_。有了 rebase
命令,就可以把在一个分支里提交的改变在另一个分支里重放一遍。
在这个例子里,可以运行下面的命令:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
它的原理是回到两个分支(你所在的分支和你想要衍合进去的分支)的共同祖先,提取你所在分支每次提交时产生的差异(diff),把这些差异分别保存到临时文件里,然后从当前分支转换到你需要衍合入的分支,依序施用每一个差异补丁文件。图 3-29 演示了这一过程:
现在,你可以回到 master 分支然后进行一次快进合并(见图 3-30):
现在,合并后的 C3(即现在的 C3’)所指的快照,同三方合并例子中的 C5 所指的快照内容一模一样了。最后整合得到的结果没有任何区别,但衍合能产生一个更为整洁的提交历史。如果视察一个衍合过的分支的历史记录,看起来更清楚:仿佛所有修改都是先后进行的,尽管实际上它们原来是同时发生的。
你可以经常使用衍合,确保在远程分支里的提交历史更清晰。比方说,某些项目自己不是维护者,但想帮点忙,就应该尽可能使用衍合:先在一个分支里进行开发,当准备向主项目提交补丁的时候,再把它衍合到origin/master
里面。这样,维护者就不需要做任何整合工作,只需根据你提供的仓库地址作一次快进,或者采纳你提交的补丁。
请注意,合并结果中最后一次提交所指向的快照,无论是通过一次衍合还是一次三方合并,都是同样的快照内容,只是提交的历史不同罢了。衍合按照每行改变发生的次序重演发生的改变,而合并是把最终结果合在一起。
呃,奇妙的衍合也不是完美无缺的,一句话可以总结这点:
永远不要衍合那些已经推送到公共仓库的更新。