5.4 变基的使用和注意事项
在 Git 中整合来自不同分支的修改主要有两种方法: 合并(merge) 和 变基(rebase)。 在本节中我们将学习什么是“变基”,怎样使用“变基”,并将展示该操作的惊艳之处,以及变基的使用场景。
什么是变基?
在分支管理中我们讲到过,整合分支的最容易方法是 merge
命令。它会把两个分支的最新快照以及二者最近的祖先进行三方合并,合并的结果是生成一个新的快照并提交。
其实还有一种方法,可以把分支 b
的所有更改在 a
的基础上应用一次,然后把应用后的结果放到 b
里面,这种方法叫做“变基(rebase)”。你可以使用 rebase
命令把某一分支上的所有修改都复制到另一分支上,就好像重新播放一样。
“变基”通俗理解就是改变某个分支所基于的分支。
变基当前分支
变基使用 rebase
命令,用法如下
git rebase 新基于的分支名或commit
如果你的当前分支是基于 master
分支的 c2 提交分出的分支 a
,在 master
和 a
分支都有提交的内容,那么你的提交记录图应该是下图 1 这样的。 a
和 master
都在自己单独的一条线上,由于 c5
和 c3
的基础(父对象)都是 c2
,所以 c3
, c4
和 c5
是平行的。
当你执行了正面的命令以后,你的提交记录会变成下图 2 这样。
git rebase master
执行过变基以后, a
分支中的提交 c3 和 c4 被放到了执行时 master 的最新提交 c5
后面。正如“变基”其名一样, a
的基础被改变了。 a
在一开始的基础提交为 c2
,变基后变成了 c5
。
变基其他分支
直接使用 git rebase 新基
会把当前分支进行变基。还可以使用下面的命令对指定的分支进行变基。
git rebase 新基 目标分支
比如下面的命令可以和上面的命令达到同样的目的。
git rebase master a
唯一的区别在于, git rebase master
需要你当前活动的分支是 a
,而 git rebase master a
则可以在处于任何分支的情况下自动切换到 a
并把 a
变基到 master
。
变基到父对象的父对象
在这样一个场景里面:
有一个需求 a
你已经在分支 ba 中开发了,并且已经产生了一些提交。
但这个时候项目经理发现另一个需求 b 和需求 a 有相似的地方。
于是要求把需求 b 和 a 放到一起做,于是你基于分支 ba 创建了一个新分支 bb。
过了一段时间。。。。
项目老大:需求 a 和 b 做完了吗?
你:b 做完了,a 还没有
项目老大:先上 b,a 先不管
你:。。。。
这个时候你会怎么办?
把分支 bb 直接提测吗?不行,因为 bb 是基于 ba 创建的,而在创建的时候 ba 已经有了一些关于需求 a 的提交。
新建一个分支,手动把需求 b 的代码,复制过去吗?别人可能会这样做,相信学过我的《Git 进阶教程》的你肯定不会这样做。
使用上面的知识,把 bb 重新变基到 ba 创建时的提交是可以的,但如果这个时候你在 ba 和 bb 都有了几百个提交呢。
其实这种情况下要解决是非常简单的,只需要一行命令就可以搞定。
当然,为了防止项目老大再次变卦,可以把 b 分支给备份一下。
git rebase --onto master bb ba
只需要执行上面的代码,你就可以把 master 分支以及 ba 和 bb 共同祖先之后的 ba 里面存在但 bb 里面不存在的提交放到 ba 里面“重放”一遍,如果有冲突,处理一下冲突就可以搞定。
有冲突时变基进程会暂停,处理过冲突以后使用
git rebase --continue
继续执行变基,或者可以使用git rebase --abort
取消操作。
当别人还在苦苦查看提交记录或重新复制代码的时候,你已经搞定了,这多出来的时间当然就可以用来摸鱼学习了。
慎用变基
既然变基这么好用,为什么要慎用呢?
会让团队协作代码变的混乱
我之所以把 merge 放到基础里面讲,而把 rebase 放到进阶里面,就是因为 rebase 虽然执行起来和 merge 一样简单,但 rebase 并不适用于所有场景。
比如下面这两种场景,最好就不要用变基:
- 你所变基的目标分支还有其他人在上面提交代码
- 你们团队的提交模式不是直接提交,而是 PR。
会破坏分支间的关系
在使用 merge 整合代码后,查看提交图可以看到分支间的关系和分支创建时机。而经过变基以后,提交图会变成一条线,无法清楚的展示出各个分支间的关系。
使用不当会毁掉整个分支内容
比如上面的命令 git rebase --onto ba bb ba1
,如果在使用的时候把三个分支名的顺序给写错了,你可以把目标分支的内容给破坏掉。这个时候你就算再使用 reset 命令也无法还原到变基前的状态了。