文章目录
- 一、前言
- 二、rebase 图解
- 三、应用示例
- 四、重建提交历史
- 五、rebase VS merge
- 六、拓展阅读
一、前言
rebase
使用方法
git rebase [基节点]
git rebase [基节点] [待变基节点]
rebase
后面的参数可以是两个,也可以是一个,当rebase
为一个参数的时候其实是省略了第二个参数,第二个参数为HEAD指针当前指向的那个节点。
二、rebase 图解
首先通过简单的提交节点图解感受一下rebase
作用。
两个分支master和feature,其中feature是在提交点B处从master上拉出的分支。
master上有一个新提交M,feature上有两个新提交C和D。
此时切换到feature分支上,执行如下命令,相当于是想要把master分支合并到feature分支(这一步的场景就可以类比为我们在自己的分支feature上开发了一段时间了,准备从主干master上拉一下最新改动)
git checkout feature
git rebase master//这两条命令等价于git rebase master feature
下图为变基后的提交节点图,解释一下其工作原理:
- feature:待变基分支、当前分支;
- master:基分支、目标分支;
官方解释:
当执行rebase
操作时,git会从两个分支的共同祖先开始提取待变基分支上的修改,然后将待变基分支指向基分支的最新提交,最后将刚才提取的修改应用到基分支的最新提交的后面。
结合例子解释:当在feature分支上执行git rebase master
时,git会从master和featuer的共同祖先B开始提取feature分支上的修改,也就是C和D两个提交,先提取到。然后将feature分支指向master分支的最新提交上,也就是M。最后把提取的C和D接到M后面,但这个过程是删除原来的C和D,生成新的C’和D’,他们的提交内容一样,但commit id
不同。feature自然最后也是指向D’。
通俗解释:rebase,变基,可以直接理解为改变基底。feature分支是基于master分支的B拉出来的分支,feature的基底是B。而master在B之后有新的提交,就相当于此时要用master上新的提交来作为feature分支的新基底。实际操作为把B之后feature的提交存下来,然后删掉原来这些提交,再找到master的最新提交位置,把存下来的提交再接上去(新节点新commit id),如此feature分支的基底就相当于变成了M而不是原来的B了。(注意,如果master上在B以后没有新提交,那么就还是用原来的B作为基,rebase操作相当于无效,此时和git merge
就基本没区别了,差异只在于git merge
会多一条记录Merge操作的提交记录。)
上面的例子可抽象为如下实际工作场景:张三从B拉了代码进行开发,目前提交了两次,开发到D了;李四也从B拉出来开发了并且开发完毕,他提交到了M,然后合到主干上了。此时张三想拉下最新代码,于是他在feature分支上执行了git rebase master
,即把master分支给rebase过来,由于李四更早开发完并合了主干,如此就相当于张三是基于李四的最新提交M进行的开发了。
三、应用示例
按照上面的图解构造了提交记录,如下图所示:(ABM是master分支线,ABCD是feature分支线。这里画成了master变色分叉出来,这不影响理解,知道是表示两个分支两条线即可!)
此时,在feature分支上执行git rebase master
变基完成以后,ABCD是原来的feature分支线,ABMC’D’是新的feature分支线,ABM是master分支线(没有变化)
四、重建提交历史
- 使用场景
gitbase
的另一个功能主要用来重建提交历史,什么意思呢?你在本地commit过程中有时候可能刚提交完,然后发现有个很小的地方需要修正一下,而当你修正完之后又得提交,但是这两次提交完成的任务可能一模一样,因为第二次修改的地方实在是微不足道,或者开发一个新功能的时候分多次提交,每次的变更都很小,这个时候就可以使用git rebase的另一个功能:重建提交历史。
2.使用方法
git rebase -i [地址引用]
交互式 rebase 指的是使用带参数 --interactive
的 rebase
命令, 简写为 -i
。如果你在命令后增加了这个选项, Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于理解这个提交进行了哪些更改。在实际使用时,所谓的 UI 窗口一般会在文本编辑器 —— 如 Vim —— 中打开一个文件。
当 rebase UI界面打开时, 你能做3件事:
- 调整提交记录的顺序;
- 删除不想要的提交;
- 合并提交。
简而言之,它允许你把多个提交记录合并成一个。
例如:
C2-C5的提交都是为了完成一个功能,现在我想合并这些提交:
git rebase -i HEAD~4
这样HEAD指针就会直线后退4个节点到C1,然后列出这个节点之后你想操作的节点,C2,C3,C4,C5,之后你就可以编辑操作这些节点:变更提交顺序,删除部分提交历史等,最后得到结果:
三、推荐使用场景
不同公司,不同情况有不同使用场景,不过大部分情况推荐如下:
单人开发的时候,拉公共分支最新代码的时候使用rebase,也就是git pull -r
或git pull –rebase
。这样的好处很明显,提交记录会比较简洁。但有个缺点就是rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛,所以看个人需求了。
多人开发,往公共分支上合代码的时候,使用merge
。如果使用rebase,那么其他开发人员想看主分支的历史,就不是原来的历史了,历史已经被你篡改了。举个例子解释下,比如张三和李四从共同的节点拉出来开发,张三先开发完提交了两次然后merge上去了,李四后来开发完如果rebase上去(注意李四需要切换到自己本地的主分支,假设先pull了张三的最新改动下来,然后执行<git rebase 李四的开发分支>,然后再git push到远端),则李四的新提交变成了张三的新提交的新基底,本来李四的提交是最新的,结果最新的提交显示反而是张三的,就乱套了。
正因如此,大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序。
五、rebase VS merge
首先二者都具备整合分支间变更的能力,但二者的实现手段却大不相同。git merge
总是在推进提交历史,并不会影响提交的原始状态,而git rebase
整合变更的方式则是对提交历史进行重写,但从结果上看,最后git rebase
形成的节点与
git merge`形成的节点完全相同。
rebase 优缺点
优点:
-
rebase
不产生新节点,当然也不会产生新的commit日志,但是merge
过程中会产生一条几乎“无用”的Merge日志。使用rebase
操作的最大好处在于可以让项目提交历史变得非常干净整洁。首先,它消除了git merge
操作所需创建的没有必要的合并提交。其次,rebase
会造就一个线性的项目提交历史——也就是说,可以从feature分支的顶部开始向下查找到分支的起始点,而不会碰到任何历史分叉。 -
rebase
产生冲突并合并冲突发生在操作git rebase
时,而合并冲突这个操作是开发者自己进行的;但是提交合并申请的时候一般情况下会有评审,由评审者解决冲突,开发者人多的情况下工作量可想而知。
缺点:
rebase
操作产生冲突需要依次逐个进行解决,重写提交历史可能引起混乱,对新手使用也不是很友好。
在日常的小规模项目开发中,这种差异几乎可以忽略。但是在复杂的多人协作开发场景下,随着项目迭代的不断推进和工程复杂度的不断提高,rebase
往往能助力生成相对清爽的提交历史,进而方便追溯工程的演进历史和缺陷排查。
不过为了获得这种便于理解的提交历史,却需要付出两种代价:安全性和可追溯性。如果不能遵循rebase
的黄金法则,重写项目提交历史会为协作工作流程带来潜在的灾难性后果。再次,rebase
操作丢失了合并提交能够提供的上下文信息——所以开发者就无法知道功能分支是什么时候应用了上游分支的变更。
merge
合并历史如下:
如果使用rebase
进行合并,就会呈现出比较清爽的提交日志,也不会有那么多线,而是一条直线。
六、拓展阅读
- 《Git进阶(十二):git merge 用法详解》