为什么在Mercurial中分支和合并比Subversion更容易?

93

在Subversion或CVS中处理多个合并到分支的操作只有亲身经历过才会明白其困难之处。在Mercurial(以及可能任何其他分布式系统)中跟踪分支和合并要容易得多,但我不知道为什么。是否还有其他人知道原因?

我的问题源于这样一个事实,即在Mercurial中,您可以采用类似Subversions/CVS中央代码库的工作方式,并且一切都能正常运作。您可以在同一分支上进行多次合并,而不需要无尽的纸张来记录提交编号和标签名称。

我知道Subversion的最新版本具有跟踪分支合并的功能,因此您不会遇到完全相同程度的麻烦,但这是他们方面的一项巨大而重要的发展,它仍然无法做到开发团队希望它能做到的一切。

这其中必须存在某种根本性的差异。

6个回答

115
在Subversion(和CVS)中,仓库是首要的。在Git和Mercurial中,没有像仓库这样的概念;这里变更是核心主题。
在CVS/SVN中的麻烦来自于这些系统不记得变更的父级。在Git和Mercurial中,一个提交不仅可以有多个子级,还可以有多个父级!
可以很容易地使用其中一个图形工具gitkhgview观察到这一点。在下面的示例中,分支#2从提交A处派生出来,此后已经合并了一次(在M处与提交B合并):
o---A---o---B---o---C         (branch #1)
     \       \
      o---o---M---X---?       (branch #2)

请注意A和B有两个孩子,而M有两个父母。这些关系被记录在存储库中。假设分支#2的维护者现在想要将来自分支#1的最新更改合并,他们可以发出如下命令:

$ git merge branch-1

而工具将自动知道base是B--因为它记录在提交M中,这是#2顶点的祖先--并且它必须合并在B和C之间发生的任何事情。CVS不记录此信息,SVN在1.5版本之前也没有记录。在这些系统中,图形看起来像:

o---A---o---B---o---C         (branch #1)
     \    
      o---o---M---X---?       (branch #2)

其中M只是一个巨大的“压缩”提交,包括从A到B之间发生的所有内容,应用于M之上。请注意,完成后,除了在人类可读的注释中可能存在外,M的来源以及折叠在一起的提交数量都没有留下任何痕迹,使历史变得更加深不可测。

更糟糕的是,执行第二次合并变成了一场噩梦:必须找出第一次合并时的合并基础(而且必须知道已经有了一次合并!),然后将该信息呈现给工具,以便它不会尝试重放A..B到M之上。在密切合作时,所有这些都已经很困难了,但在分布式环境中则根本不可能。

(相关的)问题是没有办法回答问题:“X是否包含B?”其中B是一个可能重要的错误修复。那么,为什么不在提交中记录这些信息,因为在合并时它是已知的

附言:我对SVN 1.5+合并记录功能没有任何经验,但工作流程似乎比分布式系统更加牵强。如果确实如此,那可能是因为--如上面的评论中提到的--重点放在了仓库组织而不是变更本身上。


6
SVN 1.5+会在合并提交M上放置一个SVN属性,列出其中包含的已合并提交A-B。因此您可以获得相同的信息。 - Simon D
有点类似。从技术上讲,SVN的合并跟踪类似于Git所称的“cherry-picking”,但还加入了一些魔法以使用户更容易使用;在语义上与Git、Hg和Bzr进行合并时有所不同。我想实际上这并没有太大的区别,只要你不关心DAG(http://eagain.net/articles/git-for-computer-scientists/)。 - Damien Diederen
2
糟糕,没有+100的按钮。谢谢。 - Charlie Flowers
我假设你所说的 SVN 1.5 特性是使用 svn:mergeinfo 属性吗? - Tyler
@MatrixFrog:是的,这正是svn:mergeinfo所做的。 - sleske
@CharlieFlowers 你可以随时颁发悬赏! - Josiah Yoder

12
因为Subversion(至少版本1.4及以下)不会追踪已经合并的内容。对于Subversion来说,合并基本上与任何提交一样,而在其他版本控制软件比如Git中,已经合并的内容是被记住的。

7
未被任何已提供答案所触及,Hg在合并变更时提供了更高效的能力,这是因为它使用更多信息(来源于hginit.com):
例如,如果我稍微改动了一个函数,然后将其移动到其他地方,Subversion实际上并不会记住这些步骤,因此当进行合并时,它可能会认为一个新函数突然出现了。而Mercurial会分别记住这些事情:函数已更改、函数已移动,这意味着,如果你也稍微改动了那个函数,Mercurial成功合并我们的变更的可能性就更大。
当然,记住上次合并的内容(大多数回答中提到的点)也是很重要的。
然而,这两个改进都是有问题的,因为subversion 1.5+以属性的形式存储了额外的合并信息:有了这些信息,没有明显的理由可以解释为什么subversion合并不能像Hg或Git一样成功实现合并。我不知道它是否做到了,但肯定听起来subversion开发人员正在努力解决这个问题。

4

我认为这部分原因可能是因为Subversion有一台中央服务器以及绝对时间线的修订。Mercurial完全分布式,并没有参考绝对时间线。这确实允许Mercurial项目形成更复杂的分支层次结构,通过子项目添加功能和测试循环,但是团队现在需要更积极地跟踪合并以保持最新状态,因为他们不能只需更新就完成。


3
在Subversion(和CVS)中,仓库是首要的。在git和mercurial中,没有真正意义上的仓库概念;这里的核心主题是变更。我对如何实现这两种方式没有想过太多,但我的印象(基于痛苦的经验和大量阅读)是,这种差异使得在非仓库系统中进行合并和分支变得更加容易。

1

我只有使用Subversion的经验,但我可以告诉你,TortoiseSVN中的合并屏幕非常复杂。幸运的是,他们包括了一个模拟运行按钮,这样你就可以看到你是否做对了。复杂性在于配置你想要合并到哪里。一旦你为合并设置好了,合并通常会顺利进行。然后,您需要解决所有冲突,然后将合并后的工作副本提交到存储库。

如果Mercurial可以使合并配置更容易,那么我可以说,这将使合并比Subversion更容易100%。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接