我有一个mercurial存储库,有两个永久分支,默认和UAT.每隔一段时间,我们就会将新版本的应用程序部署(推广)到UAT环境中,我们通过将稳定的默认提交合并到UAT分支来实现.偶尔会在UAT分支中修复错误,这些错误修复会合并回默认值.
在UAT分支上,我需要为部署目的更改一些内容 - 连接字符串和各种环境设置.我试图做的是使在UAT分支这些变化,并承诺他们(所有为一个承诺),其合并成默认UAT之后.然后我将这一个提交虚拟合并到默认值 - 想法是因为默认现在在它的祖先中有这个提交将来错误修复从UAT合并到默认值将不会尝试重做这些特定于UAT的更改.
然而事情并没有像我希望的那样顺利.从虚拟合并提交开始到默认情况,我尝试了以下两种情况:
1) Make a few more commits to default and then "promote" to UAT (merge default onto UAT)
2) Make a bugfix on UAT and "backport" it to default (merge UAT onto default)
Run Code Online (Sandbox Code Playgroud)
在运行#1和#2之间,我已经将所有内容都删除,以便两个场景都从同一点开始.
我所看到的是,根据合并的最后一个方向,我仍然需要在执行一个或另一个合并并恢复之后检查已更改的文件 - 有时合并会尝试将默认配置放入UAT,有时将UAT配置放入合并.
如果我恢复配置更改并提交合并,那么将来在同一方向上的合并行为正常,但是当我走向另一个方向时,合并再次将错误的配置放入文件中.
我错过了什么?
我认为这个问题类似于这个问题中的问题:合并并不像你认为的那样有效.合并只是比较文件状态的问题,不是将更改从一个分支应用到另一个分支的问题.
你的出发点是这样的历史:
UAT: ... x --- y --- z
\
default: ..... a --- b --- c
Run Code Online (Sandbox Code Playgroud)
其中,x和y包含UAT的配置设置,并b为虚拟合并,没有配置设置.所以文件b看起来就像他们所做的那样a- 它们是虚拟合并的.
如果您现在default要对UAT 进行新的更改,那么您将使用:
UAT: ... x --- y
\
default: ..... a --- b --- c
Run Code Online (Sandbox Code Playgroud)
合并是在y和之间c.这是一个退化的合并,共同的祖先y本身.这意味着,之间的所有变化b,并c会"赢"在三方合并.关于帅哥如何在三方合并中合并的表格是:
ancestor local other -> merge
old old old old (nobody changed the hunk)
old old new new (they changed the hunk)
old new old new (you changed the hunk)
old new new new (hunk was cherry picked onto both branches)
old foo bar <!> (conflict, both changed hunk but differently)
Run Code Online (Sandbox Code Playgroud)
请注意,合并结果不依赖于合并的"方向":该表关于local和other列是对称的.在这里,无论是ancestor和local是y和other是c.所以表变成:
ancestor local other -> merge
old old new new (they changed the hunk)
Run Code Online (Sandbox Code Playgroud)
您可以看到合并结果始终包含new所做的更改c.
合并退化并不重要.假设你在UAT上有一个新的提交,并且这个提交没有触及配置字符串,那么你在合并时会得到相同的行为(在任一方向上,合并是对称的).
此问题的正常解决方案是外部化配置字符串.把它们放在版本控制之外的地方 - 环境变量,无版本的配置文件等.如果可以,那么将版本控制下的配置文件作为模板.然后,为UAT分支创建一个无版本的配置文件,其中包含受版本控制的配置文件.您可以根据需要覆盖此无版本配置文件中的设置.