77

你如何在不痛苦的情况下扭转合并对极化分支的影响?

这个问题困扰了我好几个月,终于放弃了。

您有 1 个存储库,有 2 个 命名分支。A和B。

发生在 A 上的变化将不可避免地发生在 B 上。

直接发生在 B 上的更改绝不能发生在 A 上。

在这样的配置中,将“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 发生了重大变化,这留下了长期存在的问题“分支 A 中的更改是否与分支 B 中刚刚出现(并退出)的变化相冲突,这看起来像是一个变化改为在分支 A 上“

关于历史重写技巧:

所有这些追溯解决方案的问题如下:

  1. 我们有 9000 个提交。
  2. 因此,新鲜克隆需要半小时
  3. 如果在某处甚至存在一个错误的存储库克隆,它就有可能重新与原始存储库联系,并再次将其撞毁。
  4. 每个人都已经克隆了这个存储库,现在已经过去了几天,正在进行提交。
  5. 一个这样的克隆恰好是一个实时站点,所以“擦除那个并从头开始”=“大诺诺”

(我承认,以上许多内容有点愚蠢,但它们不在我的控制范围内)。

唯一可行的解​​决方案是假设人们可以并且将会做错所有事情,并且有一种方法可以“消除”这种错误。

4

5 回答 5

50

我想我找到了一个解决方案,它可以永久修复错误的合并,并且不需要您手动检查任何差异。诀窍是回到历史并生成与错误合并并行的提交。

所以我们有一个存储库,每个维护版本的单个产品都有单独的分支。就像问题中提出的情况一样,在早期版本的分支上所做的所有更改(即该版本中的错误修复)最终都必须全部合并到更高版本的分支中。

所以具体来说,如果在 BRANCH_V8 上签入某些内容,则必须将其合并到 BRANCH_V9。

现在其中一位开发人员犯了以下错误:他将 BRANCH_V9 中的所有更改合并到 BRANCH_V8 中(即错误方向的合并)。此外,在那次糟糕的合并之后,他在注意到自己的错误之前执行了一些额外的提交。

所以情况如下图日志所示。

o BRANCH_V8 - 13 - 错误合并后的重要提交
|
o BRANCH_V8 - 12 - 从 BRANCH_V9 的错误合并
|\
| o BRANCH_V8 - 11 - 添加对 BRANCH_V8 的注释(即最后已知的良好状态)
| |
○ | BRANCH_V9 - 10 - 在 BRANCH_V9 上的最后一次提交
| |

我们可以按如下方式修复此错误:

  1. 将本地目录更新到 BRANCH_V8 的最后一个良好状态:hg update 11
  2. 创建最后一个良好状态的新孩子:
    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 - 13 - 错误合并后的重要提交
      | |
      | o BRANCH_V8 - 12 - 从 BRANCH_V9 的错误合并
      |/|
      ○ | BRANCH_V8 - 11 - 添加对 BRANCH_V8 的评论
      | |
      | o BRANCH_V9 - 10 - BRANCH_V9 上的最后一次提交
      
  3. 将新生成的 head 与发生错误合并的修订版合并,并在提交之前丢弃所有更改。不要简单地合并两个头,因为这样您也会丢失合并后发生的重要提交!

    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 - 13 - 错误合并后的重要提交
      | |
      ○ | BRANCH_V8 - 12 - 从 BRANCH_V9 错误合并
      |\|
      | o BRANCH_V8 - 11 - 在 BRANCH_V8 上添加评论
      | |
      ○ | 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 的错误合并
| | |
○ | | BRANCH_V8 - 13 - 错误合并后的重要提交
|/ /
○ | BRANCH_V8 - 12 - 从 BRANCH_V9 错误合并
|\|
| o BRANCH_V8 - 11 - 在 BRANCH_V8 上添加评论
| |
○ | 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 丢弃错误的合并
      | | | |
      | ○ | | BRANCH_V8 - 14 - 在 BRANCH_V8 上生成提交以纠正来自 BRANCH_V9 的错误合并
      | | | |
      | | ○ | BRANCH_V8 - 13 - 错误合并后的重要提交
      | | |/
      +---o BRANCH_V8 - 12 - 从 BRANCH_V9 错误合并
      | |/
      | o BRANCH_V8 - 11 - 在 BRANCH_V8 上添加评论
      | |
      ○ | 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 丢弃错误的合并
      | | | |
      | | ○ | BRANCH_V8 - 14 - 在 BRANCH_V8 上生成提交以纠正来自 BRANCH_V9 的错误合并
      | | | |
      +-----o BRANCH_V8 - 13 - 错误合并后的重要提交
      | | |
      o---+ BRANCH_V8 - 12 - 从 BRANCH_V9 错误合并
      |/ /
      | o BRANCH_V8 - 11 - 在 BRANCH_V8 上添加评论
      | |
      ○ | 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 的最后良好状态
| | |\
○ | | | BRANCH_V8 - 16 - 合并两个用于从错误合并中恢复的头
|\| | |
| o---+ BRANCH_V8 - 15 - 从 BRANCH_V9 丢弃错误的合并
| | | |
| | | o BRANCH_V8 - 14 - 在 BRANCH_V8 上生成提交以纠正来自 BRANCH_V9 的错误合并
| | | |
○ | | | BRANCH_V8 - 13 - 错误合并后的重要提交
|/ / /
o---+ BRANCH_V8 - 12 - 从 BRANCH_V9 错误合并
|/ /
| o BRANCH_V8 - 11 - 在 BRANCH_V8 上添加评论
| |
○ | BRANCH_V9 - 10 - 在 BRANCH_V9 上的最后一次提交
| |

在所有这些步骤之后,您不必手动检查任何差异,BRANCH_V8 和 BRANCH_V9 是正确的,并且将来从 BRANCH_V8 到 BRANCH_V9 的合并也将是正确的。

于 2010-01-19T11:46:42.393 回答
2

In a pinch, you can export the repository to a bunch diffs, edit history, and then glue back together just what you want - into a new repository, so no risk of damage. Probably not too bad for your example, but I don't know what the real history looks like.

I referenced this page while performing a simpler operation:

http://strongdynamic.blogspot.com/2007/08/expunging-problem-file-from-mercurial.html

于 2008-11-06T08:46:53.013 回答
2

好的,首先在与损坏的存储库 (hg init) 不同的目录中创建一个新的存储库。现在,将最后一个已知的好版本拉到新的存储库中;确保不要拉坏合并,拉动它之前的所有内容。在旧存储库中,更新到 A 的最后一个已知良好版本,然后执行以下操作:

hg graft r1 r2 r3

其中 r1-3 是在拙劣的合并后所做的更改。此时您可能会遇到冲突;修复它们。

这应该会针对 A 的最后一个已知良好版本产生新的变化。将这些新更改拉入新存储库。只是为了仔细检查你没有错过任何东西,对旧存储库进行 hg 传入。如果您看到除了拙劣的合并和 r1-3 以外的任何内容,请拉动它。

扔掉旧的存储库。你完成了。合并根本不在新存储库中,您不必重写历史记录。

于 2012-04-18T05:18:39.933 回答
1

因此,您只想将 B 中的一些变更集合并到 A 中吗?像您一直在做的那样退出变更集是一个非常糟糕的主意,因为您已经遭受了痛苦。

您应该使用移植扩展或拥有第三个分支,您可以在其中进行常见更改以合并到 A 和 B。

于 2008-11-06T18:39:54.207 回答
1

在 freenode 上与#mercurial 上的一些乐于助人的人进行了多次讨论之后,mpm 提供了一个似乎适用于我的测试用例的部分解决方案(我生成了一个假存储库,试图复制该场景)

但是,在我的实际存储库中,由于我不太了解的原因,它仍然不够完美。

以下是当前提出的解决此问题的方法的图表:

[原图丢失]

现在解决的问题不大,但我仍然需要比较差异(即: b46:b11 vs b46:b8 , a43:a10 vs a43:a9 )并手动编辑一些更改。

在我获得适用于任何存储库的有保证的方式之前,不要关闭这个问题/回答。

重要的

任何尝试这些东西的人都应该首先克隆他们的存储库并像沙箱一样使用它。正如您应该对任何合并过程所做的那样,因为这样如果出现问题,您可以将其丢弃并重新开始。

于 2008-11-06T20:32:25.990 回答