不同版本间 Git 合并的最佳实践
Posted
技术标签:
【中文标题】不同版本间 Git 合并的最佳实践【英文标题】:Best Practices for Git Merge between different versions 【发布时间】:2021-10-16 05:51:46 【问题描述】:假设我们使用Gitflow,我们从develop
中分离出一个发布分支,最终将合并到main
和develop
。
在release
上只有质量改进。
其中大多数需要部署到集成阶段,因此它们在几个 pom.xml
(多模块)和 package.json
中的版本会更新并标记在 release
分支上。
在develop
上,未来版本会定期(不稳定)开发功能,并且已相应地设置了版本。
有时,release
的改进会合并回develop
。
出现合并冲突,在下图中用 X 标记。
main ----------------------o----
/
release o---o-----o-o-o
/ \ \ \
develop ----o---o---x--o--x-o-x----
^
we are here |
例子:
在release
上,版本号为1.0.0-SNAPSHOT
。
在develop
上,分支后版本号为1.1.0-SNAPSHOT
。
新功能进入develop
,版本号保持不变。
release
中的版本偶尔会递增(并标记)为1.0.1
、1.0.2
、1.0.3
等等。
当我想将版本 1.0.x 合并到 1.1.0 而共同祖先是 1.0.0 时,当然存在冲突。
(我们完全理解那里发生的事情,不需要解释。)
$ git checkout develop
$ git merge --no-commit --no-ff release
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
...
Auto-merging client/package.json
CONFLICT (content): Merge conflict in client/package.json
Automatic merge failed; fix conflicts and then commit the result.
我们正在寻找处理这种情况的想法。 一些研究表明,这不是一个不常见的问题,所以我找到了几个建议。 大多数情况下,据说只是手动解决冲突。 但我仍然渴望找到一种可以在脚本中自动化的方法。 也许有一些 Git 魔法可以提供帮助? 也许我们一开始就很糟糕?
下面的讨论带来了更好的形象,我们在“只有错误修正!”:
https://softwareengineering.stackexchange.com/questions/390536/git-flow-pull-requests-multi-destination-branches-and-conflicts-how-is-it-sup方法 0 -- 不要这样做?
自从我们的团队开始进行这种版本增量和标记以来,我一直不确定这是否是一种好的做法。但它基本上是有效的,我们一起定义了工作流程的细节,我们的客户、合作伙伴和测试团队要求我们交付候选版本,就好像它是实际版本一样。当 x.y.z 版本测试成功后,它会原样进入生产环境,然后 release
合并到 main
中。但问题依然存在:一旦在 main
中制作了修补程序并应该向后移植到 develop
,我们将再次遇到版本冲突。
方法一——樱桃采摘?
对不起,我不会这样做。 我经常读到樱桃采摘是邪恶的。 他们会反对 Gitflow。
http://www.draconianoverlord.com/2013/09/07/no-cherry-picking.html Git - Is cherry-picking a bad practice if I want to have a consistent workflow? https://medium.com/@vitalishcharbin/danger-of-cherry-pick-42a56141577f Why does cherry-picking make the repo unstable?方法 2 -- 接受手动解决?
这就是我们现在所做的。该过程不是自动化的。每次更改release
中的版本号时,以下合并都会出现必须手动解决的冲突。我们接受它,但对此不满意。
方法 3——不要那么频繁地合并?
我想那将是不好的做法。我们希望将质量改进合并到我们所有的分支机构。
方法 4 -- 使用合并选项 --ours
或类似选项?
问题是合并冲突的自动“解决”是基于文件的,据我所知,不是基于行或基于块的。
我们需要保留来自develop
的版本号,但是这些文件中的其他更改 pom.xml
或package.json
可能位于任一侧并且不能被盲目地覆盖,因此我们会处理此类冲突想要手动查看和解决。
不过,我愿意接受这方面的任何建议!
方法 5 -- 将版本号移动到单独的文件中?
通过这种方式,我们会将冲突减少到一个可以使用--ours
轻松解决的位置。
虽然使用较新的 Maven 版本似乎是可能的,但我不知道 package.json
引用外部定义的版本号的方法。
有没有人在这方面有很好的经验,并会推荐这种方式走得更远?
方法 6 -- 在 develop
中准备和重置版本?
我看到jgitflow-maven-plugin
的这种行为已经超过 6 年没有维护了。我们可以在develop
中提交,将release
版本写入文件,然后合并,将版本改回原来的版本。
我不喜欢有与实际开发无关的额外提交,而且我认为不可能清理 Git 历史记录。
所以这将是一个有趣的后续问题:我知道我可以将 D 变基/压缩为 C,但我不知道如何将 A 或 B 变基/压缩为 C。还有其他人吗?
-----B---------
\
---A---C---D---
方法 7 -- 在 release
中准备版本?
与之前的方法类似,我们可以在release
中提交,写入目标版本,然后合并到develop
而不会发生冲突。然后,我们不需要在release
中进行还原提交,而只需将分支指针移回git reset --hard HEAD^
和/或根本不推送它,因此这个准备提交将位于两个分支“之间”。
-----B-----------
\
B'
\
---A-----C---D---
下面的文章描述了使用中间分支的类似事情(以满足拉取请求的要求),但它是几个手动步骤并不能解决我的挑战。
https://medium.com/@jshvarts/dealing-with-conflicts-when-merging-release-to-develop-da289a572f0d https://www.reddit.com/r/git/comments/e7zwl6/dealing_with_conflicts_when_merging_release_to/方法 8 -- 准备版本而不提交?
我最喜欢的解决方案是只在本地 develop
中编写目标版本而不提交,然后将 release
合并到该版本上......但 git merge
不允许这样做。我没有看到任何覆盖此行为并忽略未合并的开关
error: Your local changes to the following files would be overwritten by merge:
client/package.json
...
pom.xml
Please commit your changes or stash them before you merge.
Aborting
搜索网络告诉我存储本地更改,但这当然不是一种选择。
方法 9 -- 编写程序来解决冲突?
我认为这些冲突结构良好,甚至可以完全预测,因此应该可以编写一个小的 shell 脚本来 grep/sed 冲突,以便自动解决并提交合并。 但我犹豫着在这里付出巨大的努力,希望得到其他人的启迪!
【问题讨论】:
【参考方案1】:注意:此类事情的“最佳实践”很难定义,因为每个人的情况可能不同。
话虽如此,我们的一个项目与您的情况相似:我们使用 Git Flow,并且我们的 develop
分支构建号始终与 release
分支构建号不同。我们潜在的理想解决方案尚未实现,但可能类似于您建议的方法 8,我们将在提交中将版本注入构建管道而不对其进行硬编码(即,甚至不修改版本文件全部)。但是,这样做的缺点是您无法仅根据代码知道特定提交所代表的版本。但是你可以用一个特定的版本来标记提交,如果我们实现了这可能就是我们要做的。我们还可以将提交 ID 与版本信息一起烘焙到工件元数据中,以便于查找。
我们目前使用的解决方案是方法 4、5 和 7 的组合。我们分离版本文件(您的方法 5),并且每次创建release
(或hotfix
)分支时,第一次提交仅将版本文件更改为即将发布的版本(您的方法 7)。我们确保release
总是 中包含main
的尖端,因此无论何时我们将release
部署到生产环境中,我们都可以干净地将release
合并到main
。 (请注意,我们仍然按照 Git Flow 的建议使用 --no-ff
,但关键是如果我们愿意,我们可以快进。)
现在,在你完成 release
分支到 main
后,Git Flow 建议将 release
合并回 develop
,但我们发现将 main
合并回 develop
效率稍高一些,因此提示的main
也在develop
上,但有时我们也会在部署之前将release
合并回develop
,如果release
上出现重要的错误修复。无论哪种方式,这两个合并回develop
将始终与develop
上的版本文件发生冲突,我们使用您的方法4 自动选择这些文件的develop
版本。这使得合并返回可以完全自动化,但是,有时还有其他冲突需要手动解决,就像develop
和release
同时发生的正常开发过程一样。但至少它通常是干净的。
请注意,我们方法的副作用是我们的版本文件在develop
和main
上总是不同,这对我们来说很好。
【讨论】:
【参考方案2】:使用外部工具管理版本怎么样?
我们为此使用GitVersion。现在我不确定是否有更聪明的方法,但一个蛮力的方法是在你的 pom.xml 中有类似 <version>$env.GitVersion_SemVer</version>
的东西,其中 env.GitVersion_SemVer 是 GitVersion 的输出。
【讨论】:
以上是关于不同版本间 Git 合并的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章