本文主要讲解下Git Rebase的基本概念用法、其内部原理以及咱们在真实项目中使用Git Rebase应该遵循的原则以及为啥须要遵循这些原则。github
相信对于rebase确定不会陌生,就好像上图描述的过程同样,当你使用rebase命令的时候,即好像将你须要去rebase的分支拔下来而后从新插到另外一个分支上。官方对于rebase的描述为:app
“git-rebase: Forward-port local commits to the updated upstream head”— git doc
翻译一下,就是讲你在某个分支上的全部提交记录移花接木到另外一个分支上。这边须要强调一个概念:reapply,使用rebase并非简单地好像你用ctrl-x/ctrl-v进行剪切复制同样,rebase会依次地将你所要操做的分支的全部提交应用到目标分支上。也就是说,实际上在执行rebase的时候,有两个隐含的注意点:ui
在重放以前的提交的时候,Git会建立新的提交,也就是说即便你重放的提交与以前的如出一辙Git也会将之当作新的独立的提交进行处理。spa
Git rebase并不会删除老的提交,也就是说你在对某个分支执行了rebase操做以后,老的提交仍然会存放在.git文件夹的objects目录下。若是你对于Git是如何存放你的提交不太了解的话能够参考这篇文章:Understanding git for real by exploring the .git directory翻译
基于以上表述,咱们能够得出如下相对更准确的Git rebase的工做流程:指针
从上图能够看出,在对特征分支进行rebase以后,其等效于建立了新的提交。而且老的提交也没有被销毁,只是简单地不能再被访问或者使用。在对于分支的章节咱们曾经说起,一个分支只是一个执行提交的指针。所以若是既没有分支或者Tag指向某个提交,该提交将没法再被访问使用,可是该提交会一直存在于你的文件系统中,占用着你的磁盘存储。code
“No one shall rebase a shared branch” — Everyone about rebaseblog
估计你也确定看过这个原则,不过可能表述不同罢了。本章节就是用实例的角度来探讨下,为啥不能再一个共享的分支上进行Git rebase操做。所谓共享的分支,便是指那些存在于远端而且容许团队中的其余人进行Pull操做的分支。假设如今Bob和Anna在同一个项目组中工做,项目所属的仓库和分支大概是下图这样:开发
如今Bob为了图一时方便打破了原则,正巧这时Anna在特征分支上进行了新的提交,此时的结构图大概是这样的:
当Bob打算推送本身的分支到远端的时候,它收到了以下的警告:
Git尝试着使用fast-forward来合并你的分支,具体的细节咱们会在其余博客中进行讨论,这边只须要明白远端的Git Server被Bob搞得一头雾水,不知道应该如何去合并。此时Bob为了推送他的本地的提交,只能选择强行合并,即告诉远端:不要再尝试着合并我推送给你的和你已经有点提交,一切按照我推送过去的来。那么Git会进行以下操做:
而后呢,当Anna也进行推送的时候,她会获得以下的提醒:
这个消息很正常,没啥特殊的,只是Git提醒Anna她本地的版本与远程分支并不一致,在Anna提交以前,分支中的Commit序列是以下这样的:
A--B--C--D' origin/feature // GitHub A--B--D--E feature // Anna
在进行Pull操做以后,Git会进行自动地合并操做,结果大概是这样的:
这个第M个提交即表明着合并的提交,也就是Anna本地的分支与Github上的特征分支最终合并的点,如今Anna解决了全部的合并冲突而且能够Push她的代码,在Bob进行Pull以后,每一个人的Git Commit结构为:
到这里,看到上面这个混乱的流线图,相信你对于Rebase和所谓的黄金准则也有了更形象深刻的理解。这还只是仅有两我的,一个特征分支的项目由于误用rebase产生的后果。若是你团队中的每一个人都对公共分支进行rebase操做,那还不得一团乱麻。另外,相信你也注意到,在远端的仓库中存有大量的重复的Commit信息,这会大大浪费咱们的存储空间。若是你还以为这么什么,那咱们来假设下还有一哥们Emma,第三个开发人员,在他进行了本地Commit而且Push到远端以后,仓库变为了: