什么使DVCS中的合并变得容易?

时间:2010-04-10 13:25:11

标签: svn git version-control mercurial dvcs

我在Joel on Software阅读:

  

使用分布式版本控制,   分布式部分实际上不是   最有趣的部分。

     

有趣的是这些   系统根据变化来思考,而不是   在版本方面。

HgInit

  

当我们必须合并时,Subversion   试图看看这两个修订 - 我的   修改过的代码,并修改了   代码 - 它试图猜测如何   在一个大的邪恶中将他们粉碎在一起   一塌糊涂。它通常会失败,产生   “合并冲突”的页面和页面   简单来说,这不是真正的冲突   Subversion失败的地方   弄清楚我们做了什么。

     

相比之下,我们在工作的时候   Mercurial分别在Mercurial   忙于保持一系列变更。   所以,当我们想要合并我们的代码时   在一起,Mercurial实际上有一个   更多信息:它知道   我们每个人都改变了,能做什么   重新应用这些变化,而不是   只看最终产品和   试图猜测如何把它   在一起。

通过查看SVN的存储库文件夹,我的印象是Subversion将每个修订版维护为 changeset 。据我所知,Hg正在使用 changeset snapshot ,而Git纯粹使用 snapshot 来存储数据。

如果我的假设是正确的,那么必须有其他方法使DVCS中的合并变得容易。那是什么?

*更新:

  • 我对技术角度更感兴趣,但非技术角度的答案是可以接受的
  • 更正:
    1. Git的概念模型完全基于快照。快照可以存储为其他快照的差异,只是差异纯粹用于存储优化。 - Rafał Dowgirdcomment
  • 从非技术角度来看:
    1. 它只是文化:如果合并困难,DVCS根本不会工作,因此DVCS开发人员投入大量时间和精力使合并变得容易。 CVCS用户OTOH习惯于糟糕的合并,因此开发人员没有动力让它工作。 (为什么当你的用户为某些废话付出同样的代价时,能做出好的东西?)
      ...
      总结一下:DVCS的重点是拥有许多分散的存储库,并不断地来回合并变更。如果没有良好的合并,DVCS根本就没用。然而,CVCS仍然可以通过糟糕的合并来生存,特别是如果供应商可以调整其用户以避免分支。 - Jörg W Mittaganswer
  • 从技术角度来看:
    1. 录制真实DAG的历史确实有帮助!我认为主要区别在于CVCS并不总是将合并记录为与几个父母的变更集,从而丢失了一些信息。 - tonfacomment
    2. 因为 合并跟踪 ,以及 每个修订版知道其父级 的更基本事实。 ...当每个修订(每次提交),包括合并提交,知道其父项(对于合并提交,意味着拥有/记住多个父项,即合并跟踪),您可以重建图(DAG =直接非循环图)的修订历史。如果您知道修订图,则可以找到要合并的提交的共同祖先。当您的DVCS知道如何 找到共同的祖先 时,您不需要将其作为参数提供,例如在CVS中。

      请注意,两个(或更多)提交可能有多个共同的祖先。 Git利用所谓的“递归”合并策略,它合并了合并基础(共同的祖先),直到你留下一个虚拟/有效的共同祖先(在一些简化中),并且可以做简单的3路合并。 - Jakub Narębskianswer

同时检查How and/or why is merging in Git better than in SVN?

9 个答案:

答案 0 :(得分:32)

DVCS中没有什么特别可以使合并更容易。它只是文化:如果合并困难,DVCS 根本无法工作,因此DVCS开发人员投入大量时间和精力使合并变得容易。 CVCS用户OTOH习惯于糟糕的合并,因此开发人员没有动力让它工作。 (为什么当你的用户为某些废话付出同样的代价时,你可以做些好事?)

Linus Torvalds在他的一个Git演讲中说,当他在Transmeta使用CVS时,他们在开发周期中将整整一周放在一边进行合并。而且每个人都认为这是正常的事态。如今,在合并窗口期间,Linus在短短几个小时内完成了数百次合并。

如果CVCS用户只是去他们的供应商并说这个废话是不可接受的,那么CVCS可以和DVCS一样具有良好的合并能力。但他们陷入了Blub悖论:他们只是不知道这是不可接受的,因为他们从未看到一个有效的合并系统。他们不知道那里有更好的东西。

当他们尝试DVCS时,他们将所有的善良神奇地归因于“D”部分。

理论上,由于集中性,CVCS应该具有更好的合并功能,因为它们具有整个历史的全局视图,不像DVCS只是每个存储库有一个小片段。

回顾一下:DVCS的整点是拥有许多分散的存储库,并且不断地来回合并变更。如果没有良好的合并,DVCS根本就没用。然而,CVCS仍然可以通过糟糕的合并而存在,特别是如果供应商可以调整其用户以避免分支。

所以,就像软件工程中的其他一切一样,这是一个努力的问题。

答案 1 :(得分:23)

在Git和其他DVCS合并很容易,因为一些神秘的系列变更集视图(除非你使用Darcs,其补丁理论,或一些Darcs启发的DVCS;它们是少数尽管如此,Joel仍在讨论,但由于 合并跟踪 ,以及每个修订版知道其父母的更为基本的事实。为此,您需要(我认为)整个树/完整存储库提交...但遗憾的是,它限制了部分检出的能力,并且只提交了文件子集。

当每个修订(每次提交)(包括合并提交)知道其父项(对于合并提交,这意味着拥有/记住多个父项,即合并跟踪)时,您可以重建图表(DAG) =直接非循环图)修订历史。如果您知道修订图,则可以找到要合并的提交的共同祖先。当您的DVCS知道自己如何找到共同的祖先时,您不需要将其作为参数提供,例如在CVS中。

请注意,两个(或更多)提交可能有多个共同的祖先。 Git利用所谓的“递归”合并策略,它合并了合并基础(共同祖先),直到你剩下一个虚拟/有效的共同祖先(在某些简化中),并且可以做简单的3路合并。

创建了重命名检测的Git,以便能够处理涉及文件重命名的合并。 (这支持Jörg W Mittag论证,DVCS有更好的合并支持,因为他们必须拥有它,因为合并比CVCS更常见,其合并隐藏在'update'命令中,在update-then-commit工作流中,cf Eric S. Raymond Understanding Version Control(WIP)。

答案 2 :(得分:10)

部分原因当然是技术论点,即DVCS存储的信息比SVN更多(DAG,副本),并且还有一个更简单的内部模型,这就是为什么它能够执行更准确的合并,如上所述其他回复。

然而,可能更重要的区别在于,因为您拥有本地存储库,所以您可以进行频繁的小型提交,并且还经常提取和合并传入的更改。这更多地是由于“人为因素”造成的,人类使用集中式VCS与DVCS的方式不同。

使用SVN,如果你更新并且存在冲突,SVN将合并它可以在你的代码中插入标记,但不能。这个问题的一大问题是,在解决所有冲突之前,您的代码现在将不再处于可行状态。

这使您无法完成您想要实现的工作,因此通常SVN用户在处理任务时不会合并。结合这一点,SVN用户也倾向于让更改在一次大型提交中累积,以免破坏其他人的工作副本,并且分支和合并之间会有很长的时间。

使用Mercurial,您可以在较小的增量提交之间更频繁地合并传入的更改。根据定义,这将导致更少的合并冲突,因为您将处理更新的代码库。

如果如果发生了冲突,你可以决定推迟合并并自行完成。这尤其使合并变得不那么烦人。

答案 3 :(得分:7)

哇,5段文章的攻击!

简而言之,没有什么比这更容易了。这很难,我的经验表明确实发生了错误。但是:

  • DVCS强制您处理合并,这意味着花几分钟时间熟悉现有的工具来帮助您解决问题。仅这一点有帮助。

  • DVCS鼓励您经常合并,这也有帮助。

你引用的hginit片段,声称Subversion无法进行三向合并,并且Mercurial通过查看两个分支中的所有变更集进行合并,这两个方面都是错误的。

答案 4 :(得分:2)

有一点是svn合并被巧妙地打破了;请参阅http://blogs.open.collab.net/svn/2008/07/subversion-merg.html我怀疑这与svn记录mergeinfo相关,即使是在挑选合并时也是如此。在处理边框情况时添加一些简单的错误,并且svn作为CVCS的当前海报子项使它们看起来很糟糕而不是所有正确的DVCS。

答案 5 :(得分:1)

我认为其他人提到的变革集DAG会产生很大的不同。 DVCS:es需要基本级别的拆分历史记录(和合并),而我认为CVCS:es(更旧)从第1天开始构建,首先跟踪修订和文件,并在事后添加合并支持。

所以:

  • 当标记/分支与源目录树分开跟踪时,合并很容易进行并跟踪,因此整个仓库可以一次合并。
  • 由于DVCS:es有本地回购,这些都很容易创建,所以事实证明很容易将不同的模块保存在不同的存储库中,而不是在大型回购中跟踪它们。 (因此,repo-wide合并不会导致与svn / cvs中相同的中断,其中一个repo通常包含许多不需要的模块,这些模块需要具有单独的合并历史记录。)
  • CVS / SVN允许工作目录中的不同文件来自不同的版本,而DVCS:es通常对整个WC都有一个版本,总是(即使文件被恢复为早期版本,它也会显示在状态上进行了修改,因为它与签出的修订版中的文件不同.SVN / CVS不会始终显示此内容。)

我认为,混合这些概念(如Subversion所做的那样)是一个很大的错误。例如,源树中有分支/标签,因此您必须跟踪哪些文件修订已合并到其他文件。这显然比跟踪哪些修订已合并更复杂。

所以,总结一下:

  • DVCS:需要轻松合并,基于此功能设置。做出设计决策,以便这些合并很容易做到并跟踪(通过DAG),并实现其他功能(分支/标签/子模块)以适应这种情况,而不是相反。
  • CVCS:从一开始就有一些功能(例如模块)使一些事情变得简单,但是使repo-wide合并非常难以实现。

至少这是我对cvs,svn,git和hg的体验。 (可能还有其他的CVCS:es也有这个东西。)

答案 6 :(得分:1)

我发现DVCS更容易的一件事是,每个开发人员都可以将他们自己的更改合并到他们想要的存储库中。在合并自己的代码时,处理合并冲突要容易得多。我曾经在一些贫穷的灵魂通过找到每个开发人员来解决合并冲突的地方工作过。

使用DVCS,你可以做一些事情,比如克隆存储库,将两个开发人员的工作合并到克隆中,测试更改,然后从克隆合并回主存储库。

很酷的东西。

答案 7 :(得分:1)

作为一个历史记录,现在陈旧的PRCS系统也知道共同的祖先,并且可以有效地合并,虽然它没有分发(它是建立在RCS文件之上!)。这意味着它可以有效地迁移到git,同时保留历史记录。

答案 8 :(得分:-10)

可能是DVCS用户从不做那些使得合并变得困难的事情,比如改变和重命名/复制项目中的大多数文件的重构,或者从文件前端中使用的stratch API重新设计。