退出 Mercurial 上的向后合并 [英] Backing Out a backwards merge on Mercurial

查看:17
本文介绍了退出 Mercurial 上的向后合并的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这个问题已经困扰了我几个月,我终于放弃了.

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

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

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

在这样的配置中,将B"合并到A"会在存储库中产生一个可怕的问题,因为对 B 的所有更改都出现在 A 中,就好像它们是在 A 中所做的一样.

从这种情况中恢复的唯一正常"方法似乎是退出"合并,即:

 hg up -r Ahg 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. 一个这样的克隆,恰好是一个实时站点,所以擦除那个并从头开始"=big nono"

(我承认,上面的许多内容都有些愚蠢,但它们超出了我的控制范围).

唯一可行的解​​决方案是假设人们可以并且做错一切,并且有一种方法可以消除"这种错误.

解决方案

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

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

因此,具体而言,如果在 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 在 BRANCH_V8 上生成提交以纠正 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. merge : 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 上的最后一次提交||

    即.BRANCH_V8 上有两个头:一个包含错误合并的修复,另一个包含在合并后立即发生的 BRANCH_V8 上遗留的重要提交.

  4. 合并 BRANCH_V8 上的两个头:

    1. 合并:hg合并
    2. commit : hg commit -m "合并了两个用于从错误合并中恢复的头"

现在 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. merge : hg 合并 14
    2. commit : 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合并15
    2. 恢复所有更改:hg revert -a --no-backup -r 17
    3. 提交合并:hg commit -m合并来自 BRANCH_V8 的错误合并及其修复并将其全部丢弃"现在的情况 :<前>@ 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. merge : hg 合并 BRANCH_V8
    2. commit : hg commit -m "从 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 也是正确的.

How do you reverse the effect of a merge on polarised branches without dying of agony?

This problem has been plaguing me for months and I have finally given up.

You have 1 Repository, with 2 Named Branches. A and B.

Changes that occur to A will inevitably occur on B.

Changes that occur directly on B MUST NEVER occur on A.

In such a configuration, merging "B" into "A" produces a dire problem in the repository, as all the changes to B appear in A as if they were made in A.

The only "normal" way to recover from this situation appears to be "backing out" the merge, ie:

 hg up -r A 
 hg backout -r BadMergeRev --parent BadMergerevBeforeOnA 

Which looks all fine and dandy, until you decide to merge later in the correct direction, and you end up with all sorts of nasty things happening and code that was erased / commented out on specifically branch B suddenly becomes unerased or uncommented.

There has not been a working viable solution to this so far other than "let it do its thing, and then hand fix all the problems" and that to be honest is a bit fubar.

Here is an image clarifying the problem:

[Original image lost]

Files C & E ( or changes C & E ) must appear only on branch b, and not on branch a. Revision A9 here ( branch a, revno 9 ) is the start of the problem.

Revisions A10 and A11 are the "Backout merge" and "merge the backout" phases.

And revision B12 is mercurial, erroneously repeatedly dropping a change that was intended not to be dropped.

This Dilemma has caused much frustration and blue smoke and I would like to put an end to it.

Note

It may be the obvious answer to try prohibiting the reverse merge from occurring, either with hooks or with policies, I have found the ability to muck this up is rather high and the chance of it happening so likely that even with countermeasures, you must still assume that inevitably, it will happen so that you can solve it when it does.

To Elaborate

In the model I have used Seperate files. These make the problem sound simple. These merely represent arbitrary changes which could be a separate line.

Also, to add insult to injury, there have been substantial changes on branch A which leaves the standing problem "do the changes in branch A conflict with the changes in branch B which just turned up ( and got backed out ) which looks like a change on branch A instead "

On History Rewriting Tricks:

The problem with all these retro-active solutions is as follows:

  1. We have 9000 commits.
  2. Cloning freshly thus takes half an hour
  3. If there exists even one bad clone of the repository somewhere, there is a liklihood of it comming back in contact with the original repository, and banging it up all over again.
  4. Everyone has cloned this repository already, and now several days have passed with on-going commits.
  5. One such clone, happens to be a live site, so "wiping that one and starting from scratch" = "big nono"

( I admit, many of the above are a bit daft, but they are outside of my control ).

The only solutions that are viable are the ones that assume that people can and will do everything wrong, and that there is a way to 'undo' this wrongness.

解决方案

I think I found a solution which permanently fixes the bad merge, and which does not require you to manually check any diffs. The trick is to go back in history and generate commits parallel to the bad merge.

So we have repository with separate branches per maintained version of a single product. Like the situation posed in the question, all changes made on a branch of an earlier version (ie. the bugfixes for in that version) must all eventually be merged to the branches of the later versions.

So specifically, if something is checked in on BRANCH_V8, it must be merged to BRANCH_V9.

Now one of the developers makes following mistake : he merges all changes from BRANCH_V9 into BRANCH_V8 (ie. a merge in the wrong direction). Furthermore, after that bad merge he performs some extra commits before he notices his mistake.

So the situation is as shown in the graphical log below.

o  BRANCH_V8 - 13 - important commit right after the bad merge
|
o    BRANCH_V8 - 12 - wrong merge from BRANCH_V9
|
| o  BRANCH_V8 - 11 - adding comment on BRANCH_V8 (ie. last known good state)
| |
o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
| |

We can fix this mistake as follows:

  1. update your local directory to the last good state of BRANCH_V8: hg update 11
  2. Create a new child of that last good state :

    1. change some file $EDITOR some/file.txt (this is necessary because Mercurial does not allow empty commits)
    2. commit these changes hg commit -m "generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9"
      The situation now looks as follows :

      o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      |
      | o  BRANCH_V8 - 13 - important commit right after the bad merge
      | |
      | o  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      |/|
      o |  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      | o  BRANCH_V9 - 10 - last commit on BRANCH_V9
      

  3. Merge the newly generated head with the revision in which the bad merge happened, and throw away all changes before committing. Do not simply merge the two heads, because you will then lose the important commit which happened after the merge as well!

    1. merge : hg merge 12 (ignore any conflicts)
    2. throw away all changes : hg revert -a --no-backup -r 14
    3. commit the changes : hg commit -m "throwing away wrong merge from BRANCH_V9" The situtation now looks like :

      o    BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
      |
      | o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      | |
      +---o  BRANCH_V8 - 13 - important commit right after the bad merge
      | |
      o |  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      ||
      | o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
      | |
      

    Ie. there are two heads on BRANCH_V8: one which contains the fix of the bad merge, and the other containing the left-over important commit on BRANCH_V8 which happened right after the merge.

  4. Merge the two heads on BRANCH_V8 :

    1. merge : hg merge
    2. commit : hg commit -m "merged two heads used to revert from bad merge"

The situation in the end on BRANCH_V8 is now corrected, and looks like this:

o    BRANCH_V8 - 16 - merged two heads used to revert from bad merge
|
| o    BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
| |
| | o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
| | |
o | |  BRANCH_V8 - 13 - important commit right after the bad merge
|/ /
o |  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
||
| o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
| |
o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
| |

Now the situation on BRANCH_V8 is correct. The only problem remaining is that the next merge from BRANCH_V8 to BRANCH_V9 will be incorrect, as it will merge in the 'fix' for the bad merge as well, which we do not want on BRANCH_V9. The trick here is to merge from BRANCH_V8 to BRANCH_V9 in separate changes :

  • First merge, from BRANCH_V8 to BRANCH_V9, the correct changes on BRANCH_V8 from before the bad merge.
  • Second merge in the merge mistake and its fix, and, without needing to check anything, throw away all changes
  • Thirdly merge in the remaining changes from BRANCH_V8.

In detail:

  1. Switch your working directory to BRANCH_V9 : hg update BRANCH_V9
  2. Merge in the last good state of BRANCH_V8 (ie. the commit you generated to fix the bad merge). This merge is a merge like any regular merge, ie. conflicts should be resolved as usual, and nothing needs to be thrown away.

    1. merge : hg merge 14
    2. commit : hg commit -m "Merging in last good state of BRANCH_V8" The situation is now :

      @    BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8
      |
      | | o    BRANCH_V8 - 16 - merged two heads used to revert from bad merge
      | | |
      | +---o  BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
      | | | |
      | o | |  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      | | | |
      | | o |  BRANCH_V8 - 13 - important commit right after the bad merge
      | | |/
      +---o  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      | |/
      | o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
      | |
      

  3. Merge in the bad merge on BRANCH_V8 + its fix, and throw away all changes :

    1. merge : hg merge 15
    2. revert all changes : hg revert -a --no-backup -r 17
    3. commit the merge : hg commit -m "Merging in bad merge from BRANCH_V8 and its fix and throwing it all away" Current situation :

      @    BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away
      |
      | o    BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8
      | |
      +-----o  BRANCH_V8 - 16 - merged two heads used to revert from bad merge
      | | | |
      o---+ |  BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
      | | | |
      | | o |  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      | | | |
      +-----o  BRANCH_V8 - 13 - important commit right after the bad merge
      | | |
      o---+  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      |/ /
      | o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
      | |
      

  4. Merge in the left-over changes from BRANCH_V8 :

    1. merge : hg merge BRANCH_V8
    2. commit : hg commit -m "merging changes from BRANCH_V8"

In the end the situation looks like this:

@    BRANCH_V9 - 19 - merging changes from BRANCH_V8
|
| o    BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away
| |
| | o    BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8
| | |
o | | |  BRANCH_V8 - 16 - merged two heads used to revert from bad merge
|| | |
| o---+  BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
| | | |
| | | o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
| | | |
o | | |  BRANCH_V8 - 13 - important commit right after the bad merge
|/ / /
o---+  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
|/ /
| o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
| |
o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
| |

After all these steps, in which you do not have to check any diff manually, BRANCH_V8 and BRANCH_V9 are correct, and future merges from BRANCH_V8 to BRANCH_V9 will be correct as well.

这篇关于退出 Mercurial 上的向后合并的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆