在Mercurial上倒退合并
你如何扭转合并对极化分支的影响,而不会痛苦?
这个问题困扰了我好几个月 ,终于放弃了。
你有1个知识库,有2个命名分支。 A和B.
发生在A上的变化将不可避免地发生在B上。
B上直接发生的变化不得在A上发生
在这样的configuration中,将“B”合并为“A”会在存储库中产生可怕的问题,因为对B的所有改变都显示在A中,就好像它们是在A中做出的一样。
从这种情况中恢复的唯一“正常”方式似乎是“撤销”合并,即:
hg up -r A hg backout -r BadMergeRev --parent BadMergerevBeforeOnA
看起来一切都很好,直到你决定以正确的方向合并,最终发生各种各样令人讨厌的事情,特别是分支B上被删除/注释的代码突然变得没有注释或没有注释。
到目前为止,还没有一个可行的解决办法,除了“让它做事,然后解决所有的问题”,而且说实话有点贵。
这是澄清问题的图像:
[原始图像丢失]
文件C&E(或更改C&E)必须仅出现在分支b上,而不出现在分支a上。 这里的修订A9(分支a,revno 9)是问题的开始。
修订版A10和A11是“退出合并”和“合并退出”阶段。
修订版B12是多余的,错误地重复放弃了不打算放弃的改变。
这种困境已经引起了许多挫折和蓝烟,我想结束它。
注意
试图禁止发生反向并购,这可能是一个显而易见的答案,无论是用钩子还是政策,我已经发现把这个问题弄糟的能力是相当高的,发生这种可能性的可能性很大,即使采取反措施,你也必须假设不可避免的,它会发生,所以你可以解决它。
详细说明
在模型中我使用了分离文件。 这些使问题听起来很简单。 这些只是代表可能是一个单独的行的任意改变 。
另外,为了增加对损害的侮辱,分支A发生了实质性的变化,这留下了一个长期的问题:“分支的变化与分支B的变化发生冲突,分支B刚刚出现(并退回),看起来像一个变化在分支A上代替“
历史重写技巧:
所有这些追溯解决scheme的问题如下:
- 我们有9000个提交。
- 因此新鲜克隆需要半小时
- 如果某个地方 甚至存在一个不好的版本库,那么它就有可能重新与原始版本库联系起来,然后再次重新开始。
- 每个人都已经克隆了这个仓库,现在已经过了好几天了。
- 一个这样的克隆,恰好是一个现场,所以“抹那个,从头开始”=“大诺诺”
(我承认,上面的很多都有些蠢,但是他们不在我的控制范围之内)。
唯一可行的解决scheme就是那些假设人们可以而且会做一切错误的方法,而且有一种方法可以“消除”这种错误。
我想我find了一个永久解决不好的合并的解决scheme,并且不需要你手动检查任何差异。 诀窍是回顾历史并生成与错误合并并行的提交。
所以我们有一个独立的分支机构,每个产品的维护版本都有不同的分支 就像问题中提出的情况一样,在早期版本的分支上进行的所有更改(即该版本中的错误修正)都必须最终被合并到更高版本的分支中。
具体来说,如果在BRANCH_V8上签入了某些内容,则必须将其合并到BRANCH_V9。
现在,其中一个开发人员犯了一个错误:他将BRANCH_V9中的所有更改合并到BRANCH_V8中(即错误方向的合并)。 而且,在这个糟糕的合并之后,他会在发现错误之前执行一些额外的提交。
所以情况如下图所示。
坏的合并之后的重要提交 | o BRANCH_V8 - 12 - 从BRANCH_V9错误合并 | \ | o BRANCH_V8 - 11 - 添加对BRANCH_V8的评论(即,最后一个已知的良好状态) | | o | BRANCH_V9 - 10 - BRANCH_V9上的最后提交 | |
我们可以修复这个错误,如下所示:
- 更新您的本地目录到BRANCH_V8的最后一个良好状态:
hg update 11
- 创build最后一个良好状态的新孩子:
- 改变一些文件
$EDITOR some/file.txt
(这是必要的,因为Mercurial不允许空的提交) - 提交这些更改
hg commit -m "generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9"
现在情况如下:o BRANCH_V8 - 14 - 在BRANCH_V8上生成提交,以纠正BRANCH_V9中的错误合并 | | 坏的合并之后的重要提交 | | | o BRANCH_V8 - 12 - 从BRANCH_V9错误合并 | / | o | BRANCH_V8 - 11 - 增加对BRANCH_V8的评论 | | | BRANCH_V9 - 10 - BRANCH_V9上的最后提交
- 改变一些文件
-
将新生成的头部与发生错误合并的修订合并,并在提交之前丢弃所有更改。 不要简单地合并这两个头,因为这样你会失去合并后发生的重要提交!
- 合并:
hg merge 12
(忽略任何冲突) - 扔掉所有的变化:
hg revert -a --no-backup -r 14
- 提交更改:
hg commit -m "throwing away wrong merge from BRANCH_V9"
现在看起来像这样:o BRANCH_V8 - 15 - 从BRANCH_V9扔掉错误的合并 | \ | o BRANCH_V8 - 14 - 在BRANCH_V8上生成提交,以纠正BRANCH_V9中的错误合并 | | 错误合并之后的重要提交 | | o | BRANCH_V8 - 12 - 从BRANCH_V9错误合并 | \ | | o BRANCH_V8 - 11 - 增加对BRANCH_V8的评论 | | o | BRANCH_V9 - 10 - BRANCH_V9上的最后提交 | |
IE浏览器。 BRANCH_V8上有两个头:一个包含错误合并的修复,另一个包含合并后发生的BRANCH_V8上的剩余重要提交。
- 合并:
- 在BRANCH_V8上合并两个头:
- 合并:
hg merge
- 提交:
hg commit -m "merged two heads used to revert from bad merge"
- 合并:
BRANCH_V8上的情况现在已经得到纠正,如下所示:
o BRANCH_V8 - 16 - 合并了两个头,用于恢复错误合并 | \ | o BRANCH_V8 - 15 - 从BRANCH_V9扔掉错误的合并 | | \ | | o BRANCH_V8 - 14 - 在BRANCH_V8上生成提交,以纠正BRANCH_V9中的错误合并 | | | o | | BRANCH_V8 - 13 - 错误合并后的重要提交 | / / o | BRANCH_V8 - 12 - 从BRANCH_V9错误合并 | \ | | o BRANCH_V8 - 11 - 增加对BRANCH_V8的评论 | | o | BRANCH_V9 - 10 - BRANCH_V9上的最后提交 | |
现在BRANCH_V8的情况是正确的。 剩下的唯一问题是,从BRANCH_V8到BRANCH_V9的下一个合并将是不正确的,因为它将在坏的合并的“修复”中合并,而我们不希望在BRANCH_V9上进行合并。 这里的技巧是从BRANCH_V8到BRANCH_V9进行单独的更改:
- 首先合并从BRANCH_V8到BRANCH_V9,在坏的合并之前在BRANCH_V8上进行正确的改变。
- 第二个合并错误及其修复,并且不需要检查任何东西,扔掉所有的变化
- 第三,合并来自BRANCH_V8的剩余变更。
详细:
- 把你的工作目录切换到BRANCH_V9:
hg update BRANCH_V9
- 合并BRANCH_V8的最后一个良好状态(即,您生成的提交来修复错误的合并)。 这种合并是一种像任何常规合并一样的合并,即。 冲突应该像往常一样解决,什么都不需要扔掉。
- 合并:
hg merge 14
- 提交:
hg commit -m "Merging in last good state of BRANCH_V8"
现在情况是:@ BRANCH_V9 - 17 - 在BRANCH_V8的最后状态中合并 | \ | | o BRANCH_V8 - 16 - 合并后的两个头用于恢复错误合并 | | | \ | + --- o BRANCH_V8 - 15 - 从BRANCH_V9丢掉错误的合并 | | | | | o | | BRANCH_V8 - 14 - 在BRANCH_V8上生成提交以纠正BRANCH_V9中的错误合并 | | | | | | o | BRANCH_V8 - 13 - 错误合并后的重要提交 | | | / + --- o BRANCH_V8 - 12 - 从BRANCH_V9错误合并 | | / | o BRANCH_V8 - 11 - 增加对BRANCH_V8的评论 | | o | BRANCH_V9 - 10 - BRANCH_V9上的最后提交 | |
- 合并:
- 合并在BRANCH_V8 +的修复上的错误合并,并丢弃所有的变化:
- 合并:
hg merge 15
- 恢复所有更改:
hg revert -a --no-backup -r 17
- 提交合并:
hg commit -m "Merging in bad merge from BRANCH_V8 and its fix and throwing it all away"
现状:@ BRANCH_V9 - 18 - 从BRANCH_V8和它的修复合并成一个坏的合并,并把它扔掉 | \ | o BRANCH_V9 - 17 - 合并在BRANCH_V8的最后状态 | | \ + ----- o BRANCH_V8 - 16 - 合并后的两个头用于恢复错误合并 | | | | o --- + | BRANCH_V8 - 15 - 从BRANCH_V9扔掉错误的合并 | | | | | | o | BRANCH_V8 - 14 - 在BRANCH_V8上生成提交以纠正BRANCH_V9中的错误合并 | | | | 错误合并之后的重要提交 | | | o --- + BRANCH_V8 - 12 - 从BRANCH_V9错误合并 | / / | o BRANCH_V8 - 11 - 增加对BRANCH_V8的评论 | | o | BRANCH_V9 - 10 - BRANCH_V9上的最后提交 | |
- 合并:
- 合并在BRANCH_V8的剩余变更中:
- 合并:
hg merge BRANCH_V8
- 提交:
hg commit -m "merging changes from BRANCH_V8"
- 合并:
最后的情况是这样的:
@ BRANCH_V9 - 19 - 合并来自BRANCH_V8的变更 | \ | o BRANCH_V9 - 18 - 从BRANCH_V8及其修复中合并为不良合并,并将其全部丢弃 | | \ | | o BRANCH_V9 - 17 - 合并在BRANCH_V8的最后状态 | | | \ o | | | BRANCH_V8 - 16合并了两个头,用于恢复错误合并 | \ | | | | o --- + BRANCH_V8 - 15 - 从BRANCH_V9丢掉错误的合并 | | | | | | | o BRANCH_V8 - 14 - 在BRANCH_V8上生成提交,以纠正BRANCH_V9中的错误合并 | | | | o | | | BRANCH_V8 - 13 - 错误合并后的重要提交 | / / / o --- + BRANCH_V8 - 12 - 从BRANCH_V9错误合并 | / / | o BRANCH_V8 - 11 - 增加对BRANCH_V8的评论 | | o | BRANCH_V9 - 10 - BRANCH_V9上的最后提交 | |
在所有这些步骤之后,您不必手动检查任何差异,BRANCH_V8和BRANCH_V9是正确的,未来从BRANCH_V8合并到BRANCH_V9也是正确的。
在一个捏,你可以导出存储库一束差异,编辑历史,然后粘贴到一起,只是你想要的 – 进入一个新的存储库,所以没有损害的风险。 你的例子可能不是太糟糕,但我不知道真实的历史是什么样子。
我在执行一个更简单的操作时引用了这个页面:
http://strongdynamic.blogspot.com/2007/08/expunging-problem-file-from-mercurial.html
好的,首先在一个单独的目录中创build一个新的空的仓库(hg init)。 现在,把最后一个已知的好版本包括到新的版本库中。 确保你不拉错误的合并, 并把它拉到一切之前。 在旧版本库中,更新到A的最后一个已知的正确版本,并执行以下操作:
hg graft r1 r2 r3
其中r1-3是在合并后进行的更改。 此时你可能会发生冲突; 修复它们。
这应该对最后一个已知的A版本产生新的变化。 将这些新更改拉入新的存储库。 只要仔细检查一下,你没有错过任何东西,对旧的存储库做一个macros传入。 如果你看到除了拙劣的合并和r1-3之外的任何东西,就把它拉出来。
抛弃旧的存储库。 你完成了。 合并不是在新的存储库中,你永远不必重写历史。
那么你想把B中的一些变更集合到A中? 像你一样,退出变更集是一个非常糟糕的主意,因为你已经受了影响。
您应该使用移植扩展名或具有第三个分支进行常见更改合并到A和B.
经过与freenode上#mercurial的一些有帮助的人进行了许多讨论后,mpm提供了一个似乎适用于我的testing案例的部分解决scheme(我生成了一个假的存储库,试图复制该场景)
但是,在我的实际存储库中,由于我不太了解的原因,它还不够完美。
下面是目前提出的解决这个问题的方法的图表:
[原始图像丢失]
它现在没有什么问题需要解决,但是我仍然需要比较差异(即:b46:b11 vs b46:b8,a43:a10 vs a43:a9),然后手动编辑一些变化。
不要closures这个问题/采取一个答案,直到我得到一个保证的方式,在任何存储库上工作。
重要
任何人尝试这个东西,应该克隆他们的存储库,并像沙箱一样玩它。 正如你应该对任何合并过程所做的那样,因为如果出错的话,你可以把它扔出去并重新开始。