【问题标题】:When SVN fails to merge and Mercurial succeeds当 SVN 合并失败而 Mercurial 成功时
【发布时间】:2011-06-26 04:56:23
【问题描述】:

在它出现之前,我已经查看了关于这个主题的几个主题,包括这些:

我正在考虑为我们的开发组切换到 Mercurial(来自 Subversion)。在我这样做之前,我正在做通常的优点/缺点列表。

我的“优点”之一是 Mercurial 中的合并更胜一筹,但到目前为止,我还没有找到令人信服的证据。也就是说,在 HgInit.com 上发表了我无法验证的声明:

例如,如果我稍微改变一个函数,然后将它移动到其他地方,Subversion 不会真正记住这些步骤,所以当需要合并时,它可能会认为一个新函数刚刚出现蓝色的。而 Mercurial 会分别记住这些内容:函数已更改,函数已移动,这意味着如果您也稍微更改了该函数,Mercurial 更有可能成功合并我们的更改。

这将是非常引人注目的功能,但据我所知,这只是热空气。我一直无法验证上述说法。

我创建了一个 Mercurial 存储库,做了一个 Hello World,然后克隆了它。在其中,我修改了函数,提交它,然后移动它,然后提交它。在另一个中,我只是在函数中添加了另一个输出行并提交。

当我合并时,我得到的合并冲突与使用 Subversion 时基本相同。

我知道 mercurial 可以更好地跟踪 file renames,并且 DVCS 除了合并之外还有其他优势,但我对这个示例很感兴趣。 Joel Spolsky 是不是在这里偏离了基地,还是我错过了什么?

我在这方面没有经验,但似乎因为 Mercurial 保留了更多信息,它在理论上可以在合并方面做得更好(如果开发人员也经常签入)。例如,我认为 Mercurial 通过比较多个更改来获取上下文更改是可行的,例如,我修改一个函数、签入、移动函数、签入,然后 Mercurial 将这两个部分关联起来。

但是,Mercurial 的合并似乎并没有真正利用添加的信息,并且似乎与 Subversion 的操作方式相同。这是正确的吗?

【问题讨论】:

  • +1 有趣 - 我们正在将我们的数 GB svn 存储库迁移到 mercurial,其中一大卖点是更容易合并。
  • 我认为这句话中的关键部分是“更有可能”。如果您观看视频,Linus Torvalds 将 Git 描述为切片面包之后的下一个最佳产品,他也声称 Git 可以跟踪在代码库中移动的函数。我猜大部分是推销员炒作,因为我已经验证 Mercurial 和 Git 实际上都无法跟踪从一个文件移动到另一个文件的代码。该代码仅被跟踪为“在此处删除某些内容”和“在此处添加了其他内容”。
  • 话虽如此,在使用了 Mercurial 之后,我现在不会使用 Subversion。即使它不符合此处描述的特定场景,它在合并方面也很出色。所以,是的,如果你能遵循 Mercurial 的做事方式,Mercurial 在合并方面比 Subversion 更好。如果你不这样做,你可以把事情搞得一团糟(但你也可以在 Subversion 中这样做。)
  • 是的,这就是我遇到问题的确切短语。在没有概率方法的自动合并之类的事情中,如何“更有可能”成功?最后,合并变更集和合并最终结果在 Mercurial 和 SVN 中似乎相同

标签: svn mercurial merge branching-and-merging


【解决方案1】:

据我所知,如果有人说 Mercurial 跟踪移动的代码小于文件大小的块,那就大错特错了。它确实独立于代码更改跟踪文件重命名,因此如果 Dave 重命名文件并且 Helen 更改文件中的某些内容,它可以自动合并,但据我所知,Subversion 也可以这样做! (CVS 不能。)

但是,Mercurial 的合并逻辑比 Subversion 的要好得多:它可以记住冲突解决方案。考虑这个历史图表:

base ---> HELEN1 ---> merge1 --> HELEN2 -> merge2
     \--> DAVE1 ---/                    /
                   \--> DAVE2 ---------/

Helen 和 Dave 独立进行了更改。 Helen 拉出 Dave 的树并合并,然后在此基础上进行了另一项更改。与此同时,戴夫继续编码,没有费心从海伦那里拉出来。然后海伦又拉了戴夫的树。 (也许 Dave 正在开发主要的开发主干,而 Helen 正在开发一个功能分支,但她想定期与主干更改同步。)在构建“merge2”时,Mercurial 会记住在“merge1”中完成的所有冲突解决方案并且只显示 Helen new 冲突,但 Subversion 会让 Helen 从头开始​​重新进行合并。 (有一些方法可以避免使用 Subversion 执行此操作,但它们都涉及额外的手动步骤。Mercurial 会为您处理。)

有关更多信息,请阅读为 Monotone 开发的 mark-merge algorithm,而 AFAIK 现在已被 Mercurial 和 Git 使用。

【讨论】:

  • 我不认为这是正确的,至少对于 SVN 的 v1.5。根据svnbook.red-bean.com/en/1.5/…,SVN 确实记得以前的合并,甚至允许您单独合并来自特定修订的更改(又名,挑选樱桃),然后稍后“完成”合并而不重做该工作。
  • 另外,有趣的链接到标记合并算法。我还没有通读它,但我会的。但是,在进一步挖掘中,根据this,似乎 Mercurial 不会自行进行 ANY 合并
  • 我承认从 1.1 版开始就没有使用过 SVN,但我仍然经常看到人们抱怨它存在的问题,如果它记住以前的合并,他们就不会遇到这些问题。也许他们都还在使用 1.1。 ;-) 至于 Mercurial 和您发布的链接,他们没有很好地解释“解决冲突”之间的区别,这需要一个非常普遍的外部工具 AFAIK,决定是否存在冲突被解决,这就是标记合并所做的工作。
  • 啊,确实有道理。我很惭愧地承认,尽管我已经使用了多年,但我对 SVN 如何进行合并仍然缺乏了解。但似乎 VCS 的不同之处仅在于找到需要合并的位置,然后使用外部工具来完成这项工作。我一直认为他们只是使用外部工具来解决冲突(例如 - KDiff)。
【解决方案2】:

AFAIK,SVN 在内部完成所有合并 - 合并工具仅用于存在冲突的情况,因为(显然)它需要告诉您并让您修复它。

非冲突案例基于应用补丁 - 即,svn 将采用您在修订中所做的更改,并将这些更改应用于目标。 SVN 的最新版本(从 1.5 开始)会记住您之前所做的合并,将此信息存储在与目录关联的属性中。与 1.5 相比,1.6 在处理这些属性方面做得更好。

SVN 通过比较两棵树并比较它们来进行合并 - see the book - 除非要合并的树不相关,否则它才会执行 diff 类型的合并操作(或您指定 --ignore-ancestry 选项)。 Here's a brief description 会发生什么。 当你合并过去的冲突时,你可以看到这一点——一旦你解决了一个棘手的修订合并,svn 会记住哪些修订被合并,并将再次应用这些更改。您可以通过尝试来证明这一点 - 分支、编辑 2 个文件、合并以产生冲突。仅在同一行上编辑分支文件,然后合并 - 即使目标文件没有更改,它也会弹出冲突,但是因为合并正在将更改应用于已更改的行,而该行已从分支文件的预期(即就像补丁一样,它显示了它认为将要更改的行应该是什么)。实际上,您不会看到这一点,因为您不会反复拒绝合并更改。

然而,SVN 在重命名文件时做得很差,因为它将它们作为删除+添加操作进行跟踪。其他 SCM 做得更好 - 但即使他们也无法真正判断文件是否被重命名,或者被删除和添加,尤其是当该文件也被修改时。 Git 使用一些启发式方法来尝试确定这一点,但我看不出它保证成功。在我们有一个挂钩到文件系统的 SCM 之前,我认为情况会一直如此。

【讨论】:

    【解决方案3】:

    两件事:

    1. Mercurial 确实有内部代码来进行合并,如果内部“预合并”失败,它只会调用外部合并工具。

      您链接到the HG Book,它说没有用于处理冲突的内置工具(与没有内置合并不同)和the Mercurial wiki,其中指出 Mercurial 将尝试在内部进行合并在调用外部程序之前。

    2. 您链接到一个问题,my answer 给出了 Mercurials 成功而 Subversion 在合并中失败的明确案例。这是在 Mercurial 和 Subversion 中使用内部合并代码的开箱即用比较。

    【讨论】:

    • 感谢您的澄清。是的,我在那里读了一点。但就第 2 点而言,我明白了,mercurial 处理重命名更好。但我的理解是合并两个文件(就内容而言)在 SVN 和 Hg 之间基本相同。 Joel 暗示 mercurial 会以某种方式从保留整个变更集的过程中收集更多信息,从而引发了这个问题。在我看来,它唤起了增量合并的想法,其中两个 changset 有很多增量更改,这些更改为 mercurial 提供了足够的上下文,以便以某种方式更智能地进行合并。但显然不是
    • 不,Mercurial 的合并机制没有魔法 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-01
    • 2011-10-16
    • 2018-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    相关资源
    最近更新 更多