git merge vs rebase 合并分支

Merge分支

1679236470869.png

上图是一个git的分支示例图,可见master分支和develop分支都有人写代码,其中master有3.txt,develop分支有1,2,4.txt

1679236759316.png

develop开发发现有个傻缺在master分支写代码之后,无奈将master合入当前开发分支,从树上"a41c237"后的"96ce8c9"能看出有一个明显的合并发动,其提交信息是"Merge branch 'master' into develop"

合并完还不算完,develop分支的开发发现那傻缺居然有提交了一个5.txt,于是只能再次合并"”7c856ad",从而又多了一个"Merge branch 'master' into develop"。

这样的话,如果develop分支同时开发多个feature,每个feature分支的开发都需要在develop分支合并自己的提交时手动merge一把,然后再来个"Merge branch 'master' into develop",整个历史会有合并的历史遗留

注意:这个消息通常出现在本地merge或者远程仓库的分支之间merge,如果是develop分支通过git pullgit fetch && git merge直接拉去远端仓库的develop分支造成的"合并"不会有这个消息。

既然是远程仓库内的分支merge会有这个消息,那么多个feat分支向develop分支合并自然也就会有一堆合并消息

Rebase 分支

1679235234750.png

首先提交前长这样,可见develop和master都有改动,且master的改动靠后

1679235300204.png

rebase完了长这样(作为develop分支的开发,将master分支rebase到develop分支),其含义是开发分支和master分支有个公共节点"acbcd8d",develop分支从这个节点开始把master的提交先合入,然后再接上develop之前的提交,所以mater的提交反而跑到了"116e7e9"的前面,而且没有一堆的"Merge brand master into develop"的合并commit

Rebase的做法

简要说明一下rebase的做法。既然develop要求以master分支作为新的base,那么两个分支就需要先找到"从哪里开始分成2个分支"的结合点,这个结合点后的develop分支的所有改动先拿掉,develop分支将master的后续所有改动先合入develop分支,然后再将develop分支基于这个基准点后的改动向rebase后结果进行merge

1679239556906.png

如图所示,develop分支有2,3.txt,master分支有1,4.txt,可见从1.txt的创建开始,开始有了develop分支

1679239795042.png

随后在develop分支执行一次git merge master,develop分支合并了4.txt,随后master建立5,7.txt,develop建立6.txt

1679239915082.png

执行完rebase后,develop的历史变成了1457236。于是可见develop分支是首先从1.txt,也就是有这个分支的起始点开始先合入master的改动,然后才是自己的后续改动

如果还不懂,看这个[https://www.bilibili.com/video/BV1VG411F7rB/?spm_id_from=333.337.search-card.all.click&vd_source=11a61327fa2d1390b2508dbe31773580]

警告: 当尝试rebase一个分支的时候,其原有的commit id会发生变化(例子中看看6.txt的commit id)

1679240431821.png

如图所示,在develop分支rebase之后,从develop分支派生一个新分支feat-1。然后master创建8.txt,develop创建9.txt,然后develop分支重新rebase。按照上面的理论,master的全部提交都会先于develop在1.txt之后的提交,因此是145782369,而feat-1的历史是1457236,也就是在8和9发生前的部分

1679240843226.png

这时在develop合入feat-1,develop的历史就会看起来很怪。因为feat-1的23610的commit id在develop找不到,所以相关的commit会再合入一次,导致相同的commit消息出现2次,而且还会引发冲突

1679243767068.png

这张图的上下文是

  • develop分支编辑6.txt,输入一个6
  • develop分支分出去一个feat-3分支,现在6.txt的内容是6
  • master分支创建一个新文件
  • feat-3分支也创建一个新文件
  • develop分支rebase master分支
  • develop分支修改6.txt的文件为66
  • develop分支合并feat-3分支

由于feat-3分支保留了develop分支的提交历史,历史中显示6.txt的内容是6,且这个commit会视作新的commit合入develop分支(因此之前的commit id在rebase的时候变了),而且合入动作是在6.txt的内容修改为66后发生的,因此git因为此处发生了冲突,而不认为feat-3的6.txt源于一个更早的提交。

总结

在A分支rebase B的意思是找到2个分支的公共commit节点,以这个节点作为基准,先把A分支后续的所有commit都拿掉,然后把B分支的后续所有commit都合入(历史树上看到的就是B分支的历史完全覆盖了A的),之后再将A被拿掉的commit一个一个合入到rebase后的结果中(A分支的原有commit id会发生变化)。这将导致从A分支branch出去的其它分支(比如C)中保留了A分支中rebase前的commit历史,这些分支合入A时会把这些历史都合入一次且可能和A分支中已经修改的内容发生大量冲突。因此rebase应当被视作一个危险指令,需要脑子清醒的时候执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容