目录
背景
已经有了 git merge,为什么还需要 git rebase?
一个不太妙的场景
一个更不太妙的场景
git rebase 派上用场
什么时候用 git rebase?
什么时候不能用 git rebase
参考链接
背景
上周是我第一次用 git rebase 这个命令。在此之前,如果要同步别的分支上的内容,用的都是 git merge。
git rebase 作为一个比较“高级”的 git 命令,很早就有耳闻,但也一直没真正搞懂过,每次一看到特别复杂的图解说明就放弃了。最近在公司的一个比较大、比较正规的项目上工作,就想还是把 git rebase 搞懂吧!能用的话就尽量用。搞懂之后发现其实一点儿也不复杂。
已经有了 git merge,为什么还需要 git rebase?
首先需要明确的一点是,从代码的层面来说,git rebase 实现的功能和 git merge 完全相同,都是把某个分支的代码同步到当前分支上。假设没有冲突或者采用相同的方式解决冲突,那么两个命令得到的结果在代码层面上是相同的。唯一的区别在 commit 历史上。
按理来说 git merge 应该是先发明出来的那个,用着不错,能解决问题,但如果只用 git merge 的话,commit 历史可能不太美观。
一个不太妙的场景
设想以下这个很可能出现的场景:
你基于 master 创建了一个新的分支 A;
你在分支 A 上开发,新增了一些 commit;
完成一些功能并通过测试之后,准备合并到 master 上了。但在步骤 2 期间,有一些其他分支已经合并到了 master,并且和你的代码有冲突。于是你不得不先用 git merge master 把 master 上最新的内容同步到分支 A 上并解决冲突。这就导致分支 A 上又多了一个 Merge branch 'master' into A 的 commit;
这时候终于可以向 master 提交 PR 了。在合并 PR 的时候,你没有选择 squash (压缩 commit),所以分支 A 上所有不存在于 master 上的 commit,都会被加入到 master 中。这其中也包括那个 Merge branch 'master' into A 的 commit。
长此以往,master 上可能就会有很多类似 Merge branch 'master' into A 的 commit!这可能会让 master 的 commit 历史非常混乱。
一个更不太妙的场景
其实我上面举的例子还不是最坏的。。现实世界中还可能出现更让人无奈的事。比如下面这个场景:
你来到一个非常大、非常严格的项目上开发。首先基于 master 创建了一个新的分支 A;
你在分支 A 上开发,并新增了几个 comit。本地测试没有任何问题,但尝试 push 到在 GitHub (或者 GitLab)上的代码库时却失败了,提示自动化测试没有通过;
经过一番折腾过后,你发现是因为 master 上已经有了新功能,是针对这部分功能的测试没有通过(多么荒唐的机制!但我不止一次碰到过这种事,之前我做测试的时候碰到过,现在做开发的时候仍然碰到了);
于是你不得不合并 master,分支 A 上就多了一个 Merge branch 'master' into A 的 commit;
这时候终于可以向 master 提交 PR 了。但你的 PR 会经过严格的 review,偏偏 review 的人在印度,反馈很慢,在两周的时间里(哈哈,别以为不可能,还有比这更长的),你针对他们挑出来的问题提交了一些新的 commit,但为了能够 push 成功,你不得不执行很多次 git merge master,生成了很多个 Merge branch 'master' into A 的 commit(真事。。);
如果这时候没有 squash 直接合并到 master 上,那 master 的 commit 历史里就会出现很多个 Merge branch 'master' into A 。。
这样一来,master 的 commit 历史就会更容易变得极为混乱了。。
git rebase 派上用场
如果把以上场景中所有的 git merge master 换成 git rebase master,就不会出现这个问题了(当然,如果合并 PR 的时候 squash 一下其实也可以)。
为啥呢?因为 git rebase master 会这样做:
把你自从创建分支 A 以来,提交的全部 commit 都拿走,保存起来;
把 master 上所有新的 commit 同步过来。这会是一个 fast-forward,完成之后目前分支 A 和 master 就一模一样了;
假设你有 3 个 commit,分别是 commit a、 commit b、 commit c。这时候会把 commit a 拿回来,如果和当前代码没有冲突还好,如果有冲突的话你就需要先解决冲突,然后重新 commit(也就是说,这个新的 commit 就不是之前的 commit a 了);
重复这个过程,依次分别把 commit b 和 commit c 拿回来,有冲突的话你需要解决冲突。
看到没有?这样做的好处是,你的分支上不会出现 Merge branch 'master' into A 这样的 commit 了!commit 历史会极为清晰友好。
换句话说,这就好比是:
让你基于最新的 master 新建一个分支 B;
把你在分支 A 上的新 commit 都 cherry-pick 过来(如果你不知道这个命令的话,可以简单理解为“拿过来”);
最后再用分支 B 替换 分支 A。
什么时候用 git rebase?
明白了 git rebase 的原理之后,就很好理解应该什么时候使用了。简单来说,如果你在自己的分支 A 上开发,需要同步 master (或者别的你需要提 PR 的分支)的代码,那么就应该用 git rebase 而不是 git merge。但也有例外,见下节。
如果是反过来,从 master 上收集分支 A 上的最新代码,那么还是应该用常规的 git merge。
什么时候不能用 git rebase
git rebase 虽然好,但有个致命的问题:它会修改既有的 commit 历史。
假设:
你在分支 A 上工作;
另一名合作的开发人员也把分支 A 拉到了他的本地;
你在本地的分支 A 上执行了 git rebase master,然后 push 到远程代码库(这个时候其实已经就出问题了,因为 rebase 修改了历史,push 会失败并提示你本地的分支和远程的已经 diverge 了。这时候需要 push -f 才行);
这时候对另一名开发人员来说,自己本地的分支 A 和远程的分支 A 就已经 diverge 了。这样会造成比较混乱的局面(我倒是没试过,但想想就很乱);
所以,如果你的分支已经被别人拉到本地了,那么就不能用 git rebase,而要用 git merge 了!
参考链接
When do you use Git rebase instead of Git merge?
Git branch diverged after rebase
————————————————
版权声明:本文为CSDN博主「倪琛」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/VoisSurTonChemin/article/details/121058059