-
场景复现
a. 原始A分支有一个file,里面有一行,"hello,world1"
b. 切出新的B分支,此时B分支和A分支内容一样,都只有一行
c. 切回A分支,file增加一行,“hello,world2”,此时A分支有两行
d. 切回B分支,cherry-pick A分支刚才的commit,此时B分支也有两行
d. B分支将“hello,world2”这行删除
e. A分支合并B分支
提问:
此时A分支file中应该有几行?内容是什么?
-
类似的一个场景
a. 原始A分支有一个file,里面有一行,"hello,world1"
b. 切出新的B分支,此时B分支和A分支内容一样,都只有一行
c. 切回A分支,删除这一行,此时A分支的file一行都没有
d. A分支合并B分支
提问:
此时A分支file中应该有几行?内容是什么?
-
先揭晓答案
-
第一个场景,A分支file最终的内容是有2行
hello,world1 hello,world2
第二个场景,A分支file最终的内容是1L都没有
-
-
原因分析
- git的分支合并主要是采用三路合并,通过比较共同的祖先,生成合并结果
- 对于第一个场景
- A分支相对于共同的祖先,是增加了一行,变化了,所以合并
- B分支做了一顿操作,但是最后结果是相对于祖先,没有变化,所以不合并
- 所以最终合并结果就是2L
- 对于第二个场景
- A分支相对于共同的祖先,删除了一行,变化了,所以合并
- B分支相对共同的祖先,什么都没变化,所以不合
- 所以最终合并结果就是0L
- 所以三路合并归根是跟祖先的commit做对比,双方都有不同的变化则都会合进来,如果变化的是相同的文件则如可能出现冲突,交由用户自行解决
-
继续分析第一个场景
-
现实中这个场景
master分支是主干分支,是进行最新版本的内容开发,包含了许多新版本内容
pre-release分支是预发布分支,主要是当前线上版本内容
当master分支发现了一个bug后,fix后,通常是通过cherry-pick到pre-release分支
但是pre-release分支发现bug没有解决,又继续在pre-release修改,如删除
再merge回master,就可能出现场景中的问题
但相对来说,这种情况出现的概率较低
但是确实潜在隐患
-
-
让我们换一种思路解决
master分支file有一行"hello,world"
切pre-release分支
-
master发现bug
此时从master切一个bug_fix分支,修复bug,如增加"hello,fix bug"
-
将bug_fix分支分别合并到master和pre-release
注意:git merge --no-ff -m "merge from bug-fix" bug-fix,加上--no-ff参数
此时pre-release分支发现有问题,直接再该分支修改,删除了"hello,fix bug"这一行
pre-release合回master
-
提问:此时master file的内容是什么呢?
- 此时file的内容就是"hello,world"
-
原因:
- 根据三路合并,在合并分支的时候,master和pre-release的祖先提交都是merge bug-fix那个commit
- 而对比后,发现master没有变化,而pre-release删除了一行
- 则最终合并是把pre-relese的变化合并过来了
-
对比cherry-pick
- 最大的不同点是bug_fix合并的时候在master/pre-release生成了共同的有关联的commit
- 而如果直接从master cherry-pick到pre-release,是生成一个新的commit,而这个commit和原来之前master的commit没有任何关系。所以三路合并的时候要继续找祖先...
-
注意:
- 这只是一个sample,因为实际上应该要在pre-release切分支,如果在mater切,则会把master的版本内容merge到pre-release
-
其他的办法
- 保证对于同一个bug的修改一定要在同一分支修改...
-
已经有先例了
-
Stop cherry-picking, start merging: Index
- Part 1: The merge conflict
- Part 2: The merge conflict that never happened (but should have)
- Part 3: Avoiding problems by creating a new merge base
- Part 4: Exploiting the recursive merge algorithm
- Part 5: Exploiting the three-way merge
- Part 6: Replacing the temporary fix with the permanent fix
- Part 7: Preventing a change from leaving a branch
- Part 8: How to merge a partial cherry-pick
- Part 9: Chasing the commit
- Part 10: Web-based workflow for VSTS
- Stop merging if you need to cherry-pick
-
Intro to Cherry Picking with Git
Cherry picking is commonly discouraged in developer community. The main reason is because it creates a duplicate commit with the same changes and you lose the ability to track the history of the original commit. If you can merge, then you should use that instead of cherry picking. Use it with caution! 即不建议使用cherry pick,因为它创建了一个重复的提交,和原来的提交无法track...
-
-x When recording the commit, append a line that says "(cherry picked from commit …)" to the original commit message in order to indicate which commit this change was cherry-picked from. This is done only for cherry picks without conflicts. Do not use this option if you are cherry-picking from your private branch because the information is useless to the recipient. If on the other hand you are cherry-picking between two publicly visible branches (e.g. backporting a fix to a maintenance branch for an older release from a development branch), adding this information can be useful.
-
trunk based
-
解释
1. If you’re lucky, you get a merge conflict. If you’re not lucky, your modification is simply ignored. - 即会出现冲突,或者修改被忽略(ABA) 2. Okay, back to our original story. Creating the patch branch and merging it into both the master and feature branches preserves the connection between the two commits in the respective branches, and in particular identifies them as being two manifestations of the same underlying change (namely, commit P). The resulting merge of the two branches recognizes this relationship and doesn’t double-apply the change. - 这个也是上面6提到的,创建一个patch分支,然后分别merge,会让两个分支有共同的base - 即这个patch用于下次分支合并的base 3. If the two branches never merge, then there’s no need to get all fancy with your cherry-picking
-
初步结论
-
使用cherry-pick存在ABA问题
-
如果在master发现线上的bug,则不直接在master修改
-
而是在pre-relese分支创建一个bug-fix分支,修改,然后分别合并到master/pre-release
如果可以在pre-release修改,就改完后直接合回master
注:正式线上会是online
-
或者说
- master发现bug,fixed了一个commit
- cherry-pick到pre-release了
- 如果此时pre-release发现该bug还有问题,需要继续修改
- 那么先在pre-release切一个bug-fix分支,先分别合并一次
- 在bug-fix上继续修改,修正后再合并
-
或者说从master cherry pick到pre-release
- 加上-x选项 即git cherry-pick -x commitId
- (cherry picked from commit ...)
- 在pre-release分支向master分支合并的时候关注一下原始的commit是否出现ABA问题,做一下相关检查即可
-
2019.9.29 讨论结果
- 遇到一个bug,优先在线上分支修改
- online切bug-fix分支
- pre-release根据选择是切分支还是直接在pre-release修改
- pre-release可以直接合并回master
- 放弃在master修改bug,然后cherry-pick
- 因为cherry-pick的ABA问题
- 客户端之前的做法是master和pre-release相互cherry-pick,不merge,也没问题(只要cherry-pick和merge不一起)
- 如果一定要使用cherry-pick,一定要注意问题(ABA问题,不和merge一起,-x选项等)
- 一定要理解git的三路合并原理
- 遇到一个bug,优先在线上分支修改