15

这是一个很难解释的情况,所以请耐心等待。我有一个 Mercurial 存储库,其中包含 2 个主要分支defaultdev

工作通常在dev的命名分支(功能分支)中完成。任何时候都可能有很多特性分支。一旦在该分支中完成工作,它就会合并回dev

当准备发布时,另一个命名分支是从dev(发布分支)中创建的。有时有必要从发布中排除整个功能。如果是这种情况,则将功能分支合并到dev的合并变更集退出新的发布分支。

一旦发布分支准备好发布,它就会合并到默认值中(因此默认值始终代表生产中代码的状态)。开发分支和功能分支上的工作继续正常进行。

当需要进行另一个版本时,会出现问题,包括在前一个版本中取消的功能。一个新的发布分支正常创建(关闭dev)。这个新的发布分支现在包含从前一个发布分支回退的功能(因为回退是在发布分支上执行的,并且合并变更集仍然在开发分支上)。

这一次,当 release 分支准备好发布并合并到default时,由于先前版本分支中的合并回退而回退的任何更改都不会合并到default中。为什么会这样?既然新的发布分支包含了所有的特性分支变更集(什么都没有被撤销),为什么默认分支也没有接收到所有这些变更集呢?

如果以上所有内容都难以理解,这里是 TortoiseHg 的屏幕截图,显示了基本问题。“branch1”和“branch2”是功能分支,“release”和“release2”是发布分支:

在此处输入图像描述

4

2 回答 2

26

我认为问题在于合并的工作方式与您想象的不同。你写

既然新的发布分支包含了所有的特性分支变更集(什么都没有被撤销),为什么默认分支也没有接收到所有这些变更集呢?

当您合并两个分支时,将其视为将所有更改从一个分支应用到另一个分支是错误的。因此default分支不会“接收”来自release2. 我知道这是我们通常对合并的看法,但这是不准确的。

合并两个变更集时真正发生的情况如下:

  1. Mercurial 找到两个变更集的共同祖先。

  2. 对于两个变更集之间不同的每个文件,Mercurial使用祖先文件、第一个变更集中的文件和第二个变更集中的文件运行三向合并算法。

在您的情况下,您正在合并修订版 11 和 12。最不常见的祖先是修订版 8。这意味着 Mercurial 将从修订版开始在文件之间运行三向合并:

  • 修订版 8:无退出

  • 修订版 11:功能分支已退出

  • 修订版 12:无退出

在三向合并中,变化总是胜过没有变化。Mercurial 发现文件在 8 和 11 之间发生了更改,并且在 8 和 12 之间没有看到任何更改。因此它在合并中使用修订版 11 中的更改版本。这适用于任何三向合并算法。完整的合并表如下所示,其中old, new, ... 是三个文件中匹配的块的内容:

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)

由于这种令人惊讶的合并行为,我担心根本不应该退出合并变更集。如果您尝试退出合并,Mercurial 2.0 及更高版本将中止并抱怨。

一般来说,可以说三向合并算法假设所有变化都是好的。因此,如果您合并branch1dev然后稍后通过回退撤消合并,则合并算法将认为状态比以前“更好”。这意味着您不能只是在稍后重新合并以恢复已撤消的更改branch1dev

您可以做的是在合并到default. 您只需合并并始终保留要合并到的发布分支中的更改default

$ hg update default
$ hg merge release2 --tool internal:other -y
$ hg revert --all --rev release2
$ hg commit -m "Release 2 is the new default"

这将回避问题并迫使default就像release2. 这假设在default没有合并到发布分支的情况下绝对不会进行任何更改。

如果您必须能够发布带有跳过功能的版本,那么“正确”的方法是根本不合并这些功能。合并是一项强有力的承诺:您告诉 Mercurial,合并变更集现在拥有来自其两个祖先的所有好东西。只要 Mercurial 不会让您在合并时选择自己的基本修订版,三向合并算法就不会让您改变主意退出。

但是,您可以做的是撤销 backout。这意味着您将功能分支中的更改重新引入发布分支。所以你从一个图表开始

release: ... o --- o --- m1 --- m2
                        /      /
feature-A:   ... o --- o      /
                             /
feature-B:  ... o --- o --- o 

您现在认为 A 功能不好,并且您取消了合并:

release: ... o --- o --- m1 --- m2 --- b1
                        /      /
feature-A:   ... o --- o      /
                             /
feature-B:  ... o --- o --- o 

然后,您将另一个功能合并到您的发布分支中:

release: ... o --- o --- m1 --- m2 --- b1 --- m3
                        /      /             /
feature-A:   ... o --- o      /             /
                             /             /
feature-B:  ... o --- o --- o             /
                                         /
feature-C:  ... o --- o --- o --- o --- o 

如果您现在想重新引入 A 功能,那么您可以退出b1

release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2
                        /      /             /
feature-A:   ... o --- o      /             /
                             /             /
feature-B:  ... o --- o --- o             /
                                         /
feature-C:  ... o --- o --- o --- o --- o 

我们可以将增量添加到图表中,以更好地显示何时何地发生了什么变化:

                     +A     +B     -A     +C     --A
release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2

在第二次退出之后,您可以再次合并feature-A,以防在那里添加了新的变更集。您要合并的图表如下所示:

release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2
                        /      /             /
feature-A:   ... o -- a1 - a2 /             /
                             /             /
feature-B:  ... o --- o --- o             /
                                         /
feature-C:  ... o --- o --- o --- o --- o 

你合并a2b2。共同的祖先将是a1。这意味着您在三向合并中需要考虑的唯一更改是a1anda2a1and之间的更改b2。这里b2已经有大部分的变化“在”a2所以合并会很小。

于 2012-02-29T14:20:24.317 回答
0

马丁的回答是,像往常一样,在钱上,但我只是想加上我的 2p。

另一种思考方式是回退不会删除任何内容,它会添加反向更改。

因此,当您合并时,您没有做:

Branch after changes <-> Branch before changes => Result with changes

你正在做的:

Branch after changes <-> Branch after changes with removal of changes => Result with changes removed.

基本上第一个版本做得很糟糕。最好在版本中挑选特性,而不是包含所有内容并挑选特性。Graft在这里可能会帮助你,但我还没有尝试过在愤怒中使用它,但还不知道所有的陷阱。

于 2012-02-29T17:54:49.870 回答