Git rebase vs merge:应该用哪一个?

背景

上周是我第一次用 git rebase 这个命令。在此之前,如果要同步别的分支上的内容,用的都是 git merge

git rebase 作为一个比较“高级”的 git 命令,很早就有耳闻,但也一直没真正搞懂过,每次一看到特别复杂的图解说明就放弃了。最近在公司的一个比较大、比较正规的项目上工作,就想还是把 git rebase 搞懂吧!能用的话就尽量用。搞懂之后发现其实一点儿也不复杂。

已经有了 git merge,为什么还需要 git rebase?

首先需要明确的一点是,从代码的层面来说,git rebase 实现的功能和 git merge 完全相同,都是把某个分支的代码同步到当前分支上。假设没有冲突或者采用相同的方式解决冲突,那么两个命令得到的结果在代码层面上是相同的。唯一的区别在 commit 历史上。

按理来说 git merge 应该是先发明出来的那个,用着不错,能解决问题,但如果只用 git merge 的话,commit 历史可能不太美观。

一个不太妙的场景

设想以下这个很可能出现的场景:

  1. 你基于 master 创建了一个新的分支 A;
  2. 你在分支 A 上开发,新增了一些 commit;
  3. 完成一些功能并通过测试之后,准备合并到 master 上了。但在步骤 2 期间,有一些其他分支已经合并到了 master,并且和你的代码有冲突。于是你不得不先用 git merge master 把 master 上最新的内容同步到分支 A 上并解决冲突。这就导致分支 A 上又多了一个 Merge branch 'master' into A 的 commit;
  4. 这时候终于可以向 master 提交 PR 了。在合并 PR 的时候,你没有选择 squash (压缩 commit),所以分支 A 上所有不存在于 master 上的 commit,都会被加入到 master 中。这其中也包括那个 Merge branch 'master' into A 的 commit。

长此以往,master 上可能就会有很多类似 Merge branch 'master' into A 的 commit!这可能会让 master 的 commit 历史非常混乱。

一个更不太妙的场景

其实我上面举的例子还不是最坏的。。现实世界中还可能出现更让人无奈的事。比如下面这个场景:

  1. 你来到一个非常大、非常严格的项目上开发。首先基于 master 创建了一个新的分支 A;
  2. 你在分支 A 上开发,并新增了几个 comit。本地测试没有任何问题,但尝试 push 到在 GitHub (或者 GitLab)上的代码库时却失败了,提示自动化测试没有通过;
  3. 经过一番折腾过后,你发现是因为 master 上已经有了新功能,是针对这部分功能的测试没有通过(多么荒唐的机制!但我不止一次碰到过这种事,之前我做测试的时候碰到过,现在做开发的时候仍然碰到了);
  4. 于是你不得不合并 master,分支 A 上就多了一个 Merge branch 'master' into A 的 commit;
  5. 这时候终于可以向 master 提交 PR 了。但你的 PR 会经过严格的 review,偏偏 review 的人在印度,反馈很慢,在两周的时间里(哈哈,别以为不可能,还有比这更长的),你针对他们挑出来的问题提交了一些新的 commit,但为了能够 push 成功,你不得不执行很多次 git merge master,生成了很多个 Merge branch 'master' into A 的 commit(真事。。);
  6. 如果这时候没有 squash 直接合并到 master 上,那 master 的 commit 历史里就会出现很多个 Merge branch 'master' into A 。。

这样一来,master 的 commit 历史就会更容易变得极为混乱了。。

git rebase 派上用场

如果把以上场景中所有的 git merge master 换成 git rebase master,就不会出现这个问题了(当然,如果合并 PR 的时候 squash 一下其实也可以)。

为啥呢?因为 git rebase master 会这样做:

  1. 把你自从创建分支 A 以来,提交的全部 commit 都拿走,保存起来;
  2. 把 master 上所有新的 commit 同步过来。这会是一个 fast-forward,完成之后目前分支 A 和 master 就一模一样了;
  3. 假设你有 3 个 commit,分别是 commit a、 commit b、 commit c。这时候会把 commit a 拿回来,如果和当前代码没有冲突还好,如果有冲突的话你就需要先解决冲突,然后重新 commit(也就是说,这个新的 commit 就不是之前的 commit a 了);
  4. 重复这个过程,依次分别把 commit b 和 commit c 拿回来,有冲突的话你需要解决冲突。

看到没有?这样做的好处是,你的分支上不会出现 Merge branch 'master' into A 这样的 commit 了!commit 历史会极为清晰友好。

换句话说,这就好比是:

  1. 让你基于最新的 master 新建一个分支 B;
  2. 把你在分支 A 上的新 commit 都 cherry-pick 过来(如果你不知道这个命令的话,可以简单理解为“拿过来”);
  3. 最后再用分支 B 替换 分支 A。

什么时候用 git rebase?

明白了 git rebase 的原理之后,就很好理解应该什么时候使用了。简单来说,如果你在自己的分支 A 上开发,需要同步 master (或者别的你需要提 PR 的分支)的代码,那么就应该用 git rebase 而不是 git merge。但也有例外,见下节。

如果是反过来,从 master 上收集分支 A 上的最新代码,那么还是应该用常规的 git merge

什么时候不能用 git rebase

git rebase 虽然好,但有个致命的问题:它会修改既有的 commit 历史。

假设:

  1. 你在分支 A 上工作;
  2. 另一名合作的开发人员也把分支 A 拉到了他的本地;
  3. 你在本地的分支 A 上执行了 git rebase master,然后 push 到远程代码库(这个时候其实已经就出问题了,因为 rebase 修改了历史,push 会失败并提示你本地的分支和远程的已经 diverge 了。这时候需要 push -f 才行);
  4. 这时候对另一名开发人员来说,自己本地的分支 A 和远程的分支 A 就已经 diverge 了。这样会造成比较混乱的局面(我倒是没试过,但想想就很乱);

所以,如果你的分支已经被别人拉到本地了,那么就不能用 git rebase,而要用 git merge 了!

参考链接

When do you use Git rebase instead of Git merge?
Git branch diverged after rebase

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值