为什么一个分支中的Mercurial退出会影响其他分支?

时间:2012-02-29 13:54:39

标签: version-control mercurial branch dvcs three-way-merge

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

工作通常在 dev (功能分支)的命名分支中完成。任何时候都可能有许多功能分支。在该分支中完成工作后,它将合并回 dev

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

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

当需要执行其他版本时,会出现问题,包括在先前版本中退出的功能。正常创建新版本分支(从 dev 开始)。此新版本分支现在包含从先前版本分支退出的功能(因为退出是在发布分支上执行的,并且合并更改集仍保留在 dev 分支上)。

这次,当发布分支准备好发布并合并到 default 时,由于先前版本分支中的合并退出而退出的任何更改都不会合并到< EM>默认。为什么会这样?由于新版本分支包含所有功能分支变更集(没有任何内容被撤消),为什么默认分支也不会收到所有这些变更集?

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

enter image description here

2 个答案:

答案 0 :(得分:26)

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

  

由于新版本分支包含所有功能分支变更集(没有任何内容被撤消),为什么默认分支也不会收到所有这些变更集?

当您合并两个分支时,将其视为将所有更改从一个分支应用到另一个分支是错误的。因此,default分支不会“接收”release2的任何变更集。我知道这就是我们通常认为合并的方式,但这是不准确的。

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

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

  2. 对于两个变更集之间不同的每个文件,Mercurial使用祖先文件运行three-way merge algorithm,第一个变更集中的文件和第二个变更集中的文件。

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

    • 修订版8:无后退

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

    • 修订版12:无后退

    在三方合并中,变革总是胜过没有变化。 Mercurial发现文件已经在8到11之间进行了更改,并且它在8到12之间没有变化。因此它在合并中使用了修订版11中的更改​​版本。这适用于任何三向合并算法。完整的合并表看起来像oldnew,...是三个文件中匹配的帅哥的内容:

    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)
    

    由于这种惊人的合并行为,我害怕merge changeset shouldn't be backed out。如果您尝试撤销合并,Mercurial 2.0及更高版本将中止并投诉。

    一般来说,可以说三向合并算法假定所有变化都是好的。因此,如果您将branch1合并到dev中,然后稍后使用退出撤消合并,那么合并算法将认为状态比以前“更好”。这意味着您不能在以后将branch1重新合并到dev以获得退回的更改。

    您可以做的是在合并到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"
    

    这将解决问题,并强制defaultrelease2一样。这假定在default没有合并到发布分支的情况下绝对没有进行任何更改。

    如果您必须能够使用跳过的功能发布版本,那么“正确”的方法是根本不合并这些功能。合并是一个坚定的承诺:你告诉Mercurial合并变更集现在拥有来自其祖先的所有好东西。只要Mercurial不会让你pick your own base revision when merging,三向合并算法就不会让你改变对退出的看法。

    但是,您可以做的是撤销退出。这意味着您将更改从功能分支重新引入发布分支。所以你从像

    这样的图表开始
    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。这意味着您在三向合并中需要考虑的唯一更改是a1a2以及a1b2之间的更改。此处b2已经在a2中进行了大量更改,因此合并将很小。

答案 1 :(得分: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可能会对你有所帮助,但我还没有尝试使用它来解决所有陷阱。