参考文档:
git rebase详解(图解+最简单示例,一次就懂)
解决git rebase操作后推送远端分支不成功的问题
Push dialog (Mercurial, Git)

1. 什么是 rebase

中文翻译:变基,就是改变基底的意思。

首先通过简单的提交节点图解感受一下 rebase 在干什么

两个分支 master 和 feature,其中 feature 是在提交点 B 处从 master 上拉出的分支

master 上有一个新提交 M,feature 上有两个新提交 C 和 D

36efc2704d174acab598c4b9addd3694

此时切换到 feature 分支上,执行如下命令,相当于是想要把 master 分支合并到 feature 分支(又称 将 feature 变基到 master)

git checkout feature
git rebase master

//这两条命令等价于git rebase master feature

下图为变基后的提交节点图,解释一下其工作原理:

12b959efcc454da5a15b9fdec493d61b

  • 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 操作的提交记录)

2. 示例

按照上面的图解构造了提交记录,如下图所示:(ABM 是 master 分支线,ABCD 是 feature 分支线。这里画成了 master 变色分叉出来,这不影响理解,知道是表示两个分支两条线即可!)

7c8dec65210643ddaa9de34641636838

此时,在 feature 分支上执行 git rebase master

变基完成以后,ABCD 是原来的 feature 分支线,ABMC’D’ 是新的 feature 分支线,ABM 是 master 分支线(没有变化)

edf11c808bca47d6b6eb9caf64785c07

3. 推荐使用场景

搞来搞去那么多,这其实是最重要的。不同公司,不同情况有不同使用场景,不过大部分情况推荐如下:

  1. 拉公共分支最新代码的时候使用 rebase,也就是 git pull -r 或 git pull --rebase,但有个缺点就是 rebase 以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛。(如果使用 merge,多出无意义的一条提交记录“Merge … to …”)
  2. 往公共分支上合代码的时候,先使用 git rebase master , 再在 master 使用 merge。

4. 变基后推送失败

失败的原因

git 的 push 操作默认是假设远端的分支和你本地的分支可以进行 fast-forward 操作,换句话说就是这个 push 命令假设你的本地分支和远端分支的唯一区别是你本地有几个新的 commit,而远端没有。

但是 rebase 改变了基底,不能使用 fast-forward 会报错。

解决办法

  1. feature分支只有你一个人在开发
    此时没有其他人会进行提交操作,那么可以直接进行强制推送 git push --force origin feature,–force 可以直接理解为用你本地分支的状态区覆盖掉远端 origin 分支的状态,也就是执行过后,本地的分支什么样,远端分支就什么样
  2. feature分支有多人开发
    此时如果你贸然的使用 –force 命令,会有覆盖掉其他人提交代码的风险。比如,小明和小红两个人同时在 feature 分支上进行开发,小明已经在 feature 分支上提交了一部分代码,而小红此时执行了 rebase 操作,所以如果想要推送到远端仓库就必须使用 - -force 参数,而小红推送成功之后就会覆盖掉小明提交的代码(前面说过 –force 就是用本地状态覆盖掉远端状态)。在这种情况下,推荐另外一种更安全的命令 git push --force-with-lease origin feature 使用该命令在强制覆盖前会进行一次检查如果其他人在该分支上有提交会有一个警告,此时可以避免福改代码的风险。

总结

不管当前分支是否只有自己在使用,在 rebase 之后,需要强制推送到远端分支时,使用 git push --force-with-lease origin feature 参数来保证分支安全。

idea 的强制推送默认使用 --force-with-lease,若别人提交则会报错,提示需要手动强制覆盖。