一个有关 Git 的一个分支模式的疑问

2021-07-17 16:34:00 +08:00
 ryougifujino

最近我一直在看 https://martinfowler.com/articles/branching-patterns.html#long-lived-release-branch 这篇文章

看到 Variation: Long Lived Release Branch 这个部分的时候

If merged in we must be careful that the head of the release branch exactly matches the head of mainline. One way to do this is to revert all the of fixes that have been applied to mainline before merging.

看到这部分的时候突然有些疑问,它的意思是向 release 分支上合并的时候,先要把应用到 mainline 上的那些 fixes revert 掉。

比如 M7 那里向 release 合并到时候,要先把 F1 和 F2 revert 掉(一开始我理解的是把 mainline 上的那两个小黄点 revert 掉,后来感觉不对,应该是 release 上的 F1 和 F2,因为 mainline 上的 revert 了后修复就没了,bug 就继续在 mainline 上存在了)。

但我感觉这么做是没必要的,为什么要 revert ?直接合并不也一样的吗?后来我就发了一个邮件问了一下 Martin,他回我说“The problem occurs if F1 or F2 don't merge cleanly into mainline. In that case the M4-7 likely won't merge cleanly on top of v.2.2. But if you reset the release branch to the prior release, then you can replay the mainline commits without running into that kind of problem.”

他说因为 F1 和 F2 向 mainline 合并的时候可能不会 merge cleanly (我暂且理解为有冲突的合并),所以 M4-7 合回来的时候也有可能不会 merge cleanly 。

但是我又自己测试了一下,在向 mainline 合并的修复有冲突的情况下,冲突进行解决和完成合并,最后再往 release 合并的时候直接快进合并了,并没有遇到不能 merge cleanly 的情况。

所以我的疑问是,大家看看我上面的理解对不对,这个 revert 是不是真的有必要?是否真的如同大佬所说,不进行 revert,向 release 合并的时候可能出现不能 merge cleanly 的情况?

2408 次点击
所在节点    git
8 条回复
msg7086
2021-07-17 17:57:30 +08:00
他表明了一种可能性,而不是一种必然性。
你这里的 F1 和 F2,与上面两个黄色的 F1'和 F2'已经是不同的提交了。
当包含相似但不同提交的分支最终合并到一起的时候,什么事情都可能发生。

这就是为什么:
1. 我们一般不会用 long lived release branch
2. 我们一般不在 merge in 的时候顺便解决冲突

你这样做对团队的要求更高,因为潜在造成 merge conflict,所以你的很多 merge commit 都是内部自带修改的。换句话说,你的补丁不再是 F1,而是 F1 加上 merge commit 两次修改。如果以后有人要 revert 你的修改,他把 F1 给 revert 了,很有可能就有漏掉的 merge commit 改动没有被包括进去。所以我个人的习惯是 merge 必须是 clean merge 。

再说这个 long lived release branch,实现的效果就是每次到 merge 点的时候两条分支内容相等。
那既然分支已经相等了,那么 release branch 的历史其实已经没有用了。
那么还不如直接从发布 commit 直接重新分叉一个 branch 出来来得简单。

换句话说,你图上 F2 和 M4-7 中间这条线,是可以直接删掉的。
删掉以后就是我们常用的 release branch 方案了。
msg7086
2021-07-17 18:01:36 +08:00
另外,与其先 revert 再 merge,你可以切到 release 分支,reset hard 到 mainline,然后 reset soft 到 release,然后原地提交,就能做到他提到的「 we copy mainline into this release branch 」了。
ryougifujino
2021-07-17 21:15:52 +08:00
@msg7086 #2 具体这个例子来说,“reset hard 到 mainline”是不是指 reset hard 到 M7,然后再 reset soft,再提交?可这感觉不就是复制了一个 M7 这个提交吗?
ryougifujino
2021-07-17 21:56:39 +08:00
@msg7086 #1 我又实际试了一下,我觉得从 M7 那里合并回来的时候,会不会因为 F1 和 F2 产生 merge conflict 分两种情况。
第一种:F1 或 F2 向 mainline 合并( merge )的时候有冲突,这时候在 mainline 产生的 merge commit 解决了冲突,最后再向 release merge 的时候确实直接快进了(可以想象成 release 指针到了树的前面),git 确实自己就可以处理好。
第二种:F1 或 F2 向 mainline 合并( cherry-pick )的时候有冲突,解决冲突后产生了 F1'和 F2',最后再向 release merge 的时候确实可能产生冲突(因为这时候并没有产生树形历史)。
statumer
2021-07-17 22:38:20 +08:00
我觉得你对 longlived release branch 理解有误
f1, f2 提交到 mainline 的方式应该是 cherrypick 或者 rebase,而不是 merge,因此 m7 和 f2 是没有祖先关系,因此也实现不了你说的顺利合并。
ryougifujino
2021-07-17 23:01:01 +08:00
@statumer #5 我也觉得确实是 cherry-pick 才会出现这种情况,为什么我会认为是 merge 呢,这里有两个原因。

第一个原因是 https://martinfowler.com/articles/branching-patterns.html#release-branch 这一节中 release 的 fixes 到 mainline 的时候的用词是“merge”( Any fixes to these defects are created on the release branch and merged to mainline.)。

第二个原因是 Martin 回我的时候 F1 和 F2 到 mainline 的时候也是用的 merge 。

所以我先入为主的认为是用的“merge”命令,如果用 cherry-pick 的话确实就说得通,两条线没有形成树形结构,M7 那里 merge 回 release 的时候确实有可能产生冲突。

但是如果 F1 和 F2 使用“merge”命令合到 mainline 到话形成了树形结构,M7 合回来的时候确实不会有冲突(如果不加--no-ff 的话 release 指针直接快进到 M7 结点了)。
msg7086
2021-07-18 10:24:51 +08:00
@ryougifujino #3 最终的结果就是两个分支内容完全相同。

至于实现的方式怎么都行其实。cherry pick 也好 rebase 也好 merge 也好 reset+commit 也好都是达到这个效果。
ccde8259
2021-07-19 08:55:09 +08:00
图形化理解
不做 Revert 会有两根线 一根从 M1-3 切出来 再从 F1 切出去形成一个 Merge Commit 再从 F2 切出去形成一个 Merge Commit 最后 M4-7 回来 这种情况不算 cleanly
做 Revert 以后只剩一根从 M1-3 出去带 F1’和 F2’从 M4-7 回来一根线

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/790089

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX