业务实践中,经常会出现代码双合并的情况,比如发现一个线上缺陷后,需要在主干和发布分支同时拉取修复分支,在修改缺陷后,分别向主干和发布分支发起合并,从而完成对发布版本和未来版本的问题修复
由于需要两次修改、两次合并,所以这里会多一倍的工作量,对人力投入产出比有考验,同时两次合并要求强一致性,所以对多合并方式由一定的要求,那怎么样才能优雅的双合并呢?
首先我们来构建不同的场景,最简单的场景如下:
这种场景下,因为修改内容少,几乎不存在心智要求和人力消耗的问题,很多同学都会选择凭借记忆手动在两个分支上提交代码,然而,做过工程的同学都知道:
人是项目中,最不稳定的因素
所以这种方式最大的问题就是:一致性不强
那应该怎么办呢?不急,当我们把场景复杂化后,这个问题自然就解决了,我们把少量修改这个限定条件去掉
这种场景下,因为修改内容多,凭借记忆手动在两个分支提交代码已经不现实了,这对心智要求和人力消耗比较高,所以,我们得另想办法,既然不能凭借记忆,那就把修改点完整的复制过来!这时,你想到了一个骚操作,直接在本地拷贝两个代码仓库,分别拉取两个分支,将提交修改的分支涉及的文件,全部拷贝到目标分支上来,然后提交!
当你在为这个骚操作自鸣得意的时候,突然发现,貌似事情不是这么简单,因为这里会存在两个问题
1.磁盘占用double,在很多的大仓项目中,这会导致电脑运行比较吃力
2.无法智能解决冲突,如果两个分支代码不强一致,假如:在提交修改的分支和目标分支上在拷贝文件上有不同的代码处理逻辑,这样会强制覆盖
那怎么办呢?我们首先来解决第一个问题,即磁盘占用double的问题,这时,我们想到,其实还有一个命令可以直接将另一个分支的部分目录文件覆盖过来,即:checkout bugfix/xxx path
事实上,我们还可以把这个场景更复杂一下,以便更加贴近业务场景,我们把单次这个限制条件去掉
所谓多次连续无过程意义的修改是指:虽然提交了很多次修改,但都围绕一个问题或者一个需求反复多次的修改,在目标分支上只需要一次最终修改记录即可。相信经常提交代码的同学,应该能够Get到我的意思了
所以在这个场景下,多次连续无过程意义的修改基本等同于单次修改的场景
回到主线,刚才我们说到,使用checkout bugfix/xxx path可以直接将另一个分支的部分目录文件覆盖过来,那么具体的操作如下:
git checkout bugfix/product_list_error_release
git checkout bugfix/product_list_error deployment/qci/coreData
通过这个命令,我们解决了磁盘占用double的问题
但,依然无法解决如果两个分支代码不强一致的情况,无法智能解决冲突的问题(即如果在提交修改的分支和目标分支上在拷贝文件上有不同的代码处理逻辑,这样会强制覆盖),事实上,看过Git那些事系列:从业务场景到高级技巧的完整指南(一)的同学会注意到,这个命令还会存在一个问题:若bugfix/product_list_error 分支有删除文件,则Checkout命令无法将删除文件的操作带到目标分支,即:bugfix/product_list_error_release上来
当然,如果不涉及到“两个分支代码不强一致的情况”的这种情况,多次连续无过程意义修改进行双合并场景,checkout bugfix/xxx path是一个很好的选择
如果涉及到上面的这种情况,应该怎么办呢?
事实上,我们还可以把这个场景更复杂一下,我们把无过程意义的限定条件去掉
在这个场景下,我们即需要解决智能合并的问题,也需要保留所有的提交记录,这里就用到了Merge命令
从上图可以,相比于rebase,Merge可以保留完整的过程提交记录,同时会进行智能合并
git checkout bugfix/product_list_error_release
git merge bugfix/product_list_error
当然,解决问题是有代价的
如果两个分支代码不强一致的情况,则会存在一定人工解决代码冲突的成本,心智要求高,人力消耗大
这里很有可能出现新的问题,所以,如无必要,master和发布分支最好保持强一致!!!
好的,我们现在解决了上面的所有问题,那么我们再把这个场景更复杂一下,我们增加一个部分提交这个限定条件
部分修改,意味着我们并不要所有的提交修改,这时,我们需要把工作区回到需要合并的那次提交上来
然后基于当前的提交点拉取一个临时分支,再向目标分支发起Merge即可
git checkout product_list_error
git checkout commit_id
git checkout -b bugfix/product_list_error_temp
git checkout bugfix/product_list_error_release
git merge bugfix/product_list_error_temp
当然,这个方案的问题和上面方案的问题是相似的,即
如果两个分支代码不强一致的情况,则会存在一定人工解决代码冲突的成本,心智要求高,人力消耗大
这里很有可能出现新的问题,所以,如无必要,master和发布分支最好保持强一致!!!
好的,我们现在解决了上面的所有问题,那么我们再把这个场景更复杂一下,我们把连续这个限定条件去掉
非连续,即我们无法找到一个可以提交之前修改的comiit_id,那么只能分步提交,这里就需要用到了git cherry-pick命令
git checkout product_list_error_release
git cherry-pick commit_id 8ee75ea6a70c9bd940762568829474775ecae573
git cherry-pick commit_id 63e604104080f2b260c2ebdf4348b6667be92e97
当然,这个方案的问题和上面方案相比,心智要求更高,人力消耗更大
如果两个分支代码不强一致的情况,则每一次cherry-pick都可能会存在一定人工解决代码冲突的成本
这里每一次cherry-pick很有可能出现新的问题,所以,如无必要,master和发布分支最好保持强一致!!!
好的,现在我们解决了所有的问题,总结一下:
其中:灰色:一致性不强 蓝色:两个分支必须以强一致为前提 深蓝色:两个分支不强制一致但不一致会消耗人力