关于Git协作,除了知道merge,你可能还需要知道rebase【二】

2016-11-24 原创

在上篇《关于Git协作,除了知道merge,你可能还需要知道rebase【一】》中,我们谈到了一般git的协作流程,以及merge和rebase的简述,本篇将对二者的区别做个小结,并深入细节讨论。

小结

从结果来看,我们对比一下两个种方式产生的结果:
1

(左边的是merge操作,右边的是rebase操作)

一目了然,使用rebase衍合操作更能产生干净的、线性的、可读性高的文件版本树。这对于文件的管理是非常有帮助的。

所以,当协作人员过多的时候,使用rebase来替换掉merge其实是有好处的。
如果你只是想弄懂merge和rebase有什么区别的话,那么看到这里,其实能够解答你的这个问题了。

如果你跟笔者一样,对这里面的原理有一些好奇心的话,那么不妨继续看下去,笔者将深入对里面的实现一一分析,引入自己的问题和看法。

深入细节

假设这是我们的一个版本树:

2

merge命令,它会把两个分支最新的快照(C3和C4)以及二者共同的祖先(C2)进行三方合并,达到整合分支的目的。像这样:
3

如果C4想要整合C3的分支,那么通过rebase的方式,其实就是C3里产生的变化的补丁重新在C4的基础上打一遍,相当于C3中提交的变动在C4中重新重新执行了一遍。

rebase会回到两个分支(当前所在的分支和你想要rebase的分支)的共同祖先,提取当前所在分支每次提交时产生的差异(diff),把这些差异分别保存到临时文件里,然后从当前分支转到你需要rebase的分支,依次序的使用每一个差异补丁文件。像这样 :

4
(在C3所在的分支中执行)

git rebase master // master 这里是C4

这个时候C3的分支处整个版本树的最前面,master落后一个版本,这个时候如果想让master保持最新的版本库内容,你需要切换回master分支并且执行
git merge [C3 branch name]

但是这个时候的merge其实只是把当前的[HEAD]指针往前移了一个单位而已,并没有做任何合并的动作,这种合并,一般称之为快进合并。

另外一种方式就是,当前分支是master,并且在master上执行了rebase其他分支的操作,这个时候如果rebase成功,则master分支就会领先目标分支一个版本。

但是在实际开发流程中,master一般不被允许这样直接去整合另外的分支。所以实际操作中,还是使用第一种方式。

还要merge做什么?

我们知道merge和rebase的区别,也知道使用rebase的种种好处,但是我们的还是要思考一个问题:
“rebase好处这么多,那是不是可以替代merge了?以后一直用rebase就好了。
如果你这样想,那就真的是too young too naive了。”
5

存在必定有它的道理,不然人家干吗弄两个命令出来。

事实上,在使用rebase的时候我们的遵循这样一个原则:
永远不要rebase那些已经推送到公共仓库的更新。

在rebase的时候,实际上抛弃了一些现存的commit而创造了一些类似但是不同的新的commit。如果这些commit推送到服务器上,你的小伙伴pull下来并在这个基础上进行开发并提交了新的commit,然后你用rebase重写这些commit再推送一次,你的小伙伴就不得不重新合并他们的工作,这样会对别人的理解造成困扰。

举个栗子:
新加入一个项目组,老大给了你项目的git地址。毫无疑问,第一步就是把项目clone到本地,然后你对这个本地仓库做了一些新的功能并且暂时提交到了本地:

6

总共提交了两个commit到你的本地(C2和C3)。

那么在你开发的时候,团队其他的小伙伴也在同时做着另外的开发,并且将这些改动合并提交到了服务器上。然后通知你,服务器上有些改动,你更新一下:
7

服务器上被其他小伙伴更新到了C6。
这个时候你本地还是基于C1更新到了C3。
这个时候你合并服务器的版本产生了C7。

看上去一切正常。但是如果这个时候,你的小伙伴觉得他之前的提交用的是merge,觉得稍微有些不妥,准备用rebase替换掉服务器上的提交历史,让提交历史看上去更友好:
8

服务器上的提交用rebase覆盖了之后变成了C4。

这个时候对于你来说,需要再次合并这个新内容——即使内容没有什么改变。rebase会改变那些commit的SHA-1校验值,这样Git会把它们当做新的commit(尽管你的本地早已有了C4的内容)。

为了保持代码同步,你迟早需要并入其他小伙伴提交的内容。上面这种情况会让你的本地的提交历史里面同时出现C4和C4‘,这两个commit有不一样的SHA-1校验值,但却拥有同样的作者、日期和commit说明。这就是问题,这样的提交历史如果提交到了服务器,别的人再去合并你的代码到他们自己本地的电脑上,则每个人都会有这样一个莫名的提交。

关于rebase的使用,我们更应该把它当成一种在提交到服务器之前清理本地提交历史的手段,而不当成合并的唯一手段。

sourceTree使用rebase

容笔者啰嗦一下关于sourceTree的一些使用。英文好的可以移步去这个网址看一下。
Interactive rebase in SourceTree

写在最后
讲真,非常感谢屏幕前你的看完这篇冗长的文章。写的东西有人看,认真的看,没有什么比这个更欣慰的了。

关于rebase就讲到这里啦。总的来说是啰嗦了一点,但是笔者认为还是很有必要的。

很多时候,我们评价一段好的代码,看的是它的健壮性,可维护性,可拓展性…..

那么同样,一个优秀的工程师也是方方面面的,做项目不同于写demo,追求团队的效率,优化整个团队的工作习惯,正是作为一个团队成员,从这种一点一滴的小事去理解,去分享的。

本文作者:梁紫枫(点融黑帮),江湖诨名疯子。来自点融Clients团队。喜欢打扫房间和健身的巨蟹座男子。

随着点融网新一轮融资,点融网即将开始大规模的扩张,需要各种优秀人才的加入,如果你觉得自己够优秀,欢迎加入我们!获取更多职位信息,请关注点融黑帮。