在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的问题如下:

  1. 我们有9000个提交。
  2. 因此新鲜克隆需要半小时
  3. 如果某个地方 甚至存在一个不好的版本库,那么它就有可能重新与原始版本库联系起来,然后再次重新开始。
  4. 每个人都已经克隆了这个仓库,现在已经过了好几天了。
  5. 一个这样的克隆,恰好是一个现场,所以“抹那个,从头开始”=“大诺诺”

(我承认,上面的很多都有些蠢,但是他们不在我的控制范围之内)。

唯一可行的解​​决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上的最后提交
 |  |

我们可以修复这个错误,如下所示:

  1. 更新您的本地目录到BRANCH_V8的最后一个良好状态: hg update 11
  2. 创build最后一个良好状态的新孩子:
    1. 改变一些文件$EDITOR some/file.txt (这是必要的,因为Mercurial不允许空的提交)
    2. 提交这些更改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上的最后提交
      
  3. 将新生成的头部与发生错误合并的修订合并,并在提交之前丢弃所有更改。 不要简单地合并这两个头,因为这样你会失去合并后发生的重要提交!

    1. 合并: hg merge 12 (忽略任何冲突)
    2. 扔掉所有的变化: hg revert -a --no-backup -r 14
    3. 提交更改: 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上的剩余重要提交。

  4. 在BRANCH_V8上合并两个头:
    1. 合并: hg merge
    2. 提交: 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的剩余变更。

详细:

  1. 把你的工作目录切换到BRANCH_V9: hg update BRANCH_V9
  2. 合并BRANCH_V8的最后一个良好状态(即,您生成的提交来修复错误的合并)。 这种合并是一种像任何常规合并一样的合并,即。 冲突应该像往常一样解决,什么都不需要扔掉。
    1. 合并: hg merge 14
    2. 提交: 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上的最后提交
       |  |
      
  3. 合并在BRANCH_V8 +的修复上的错误合并,并丢弃所有的变化:
    1. 合并: hg merge 15
    2. 恢复所有更改: hg revert -a --no-backup -r 17
    3. 提交合并: 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上的最后提交
       |  |
      
  4. 合并在BRANCH_V8的剩余变更中:
    1. 合并: hg merge BRANCH_V8
    2. 提交: 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这个问题/采取一个答案,直到我得到一个保证的方式,在任何存储库上工作。

重要

任何人尝试这个东西,应该克隆他们的存储库,并像沙箱一样玩它。 正如你应该对任何合并过程所做的那样,因为如果出错的话,你可以把它扔出去并重新开始。