什么使DVCS合并变得容易?

我在软件Joel上读到:

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

有趣的部分是这些系统根据变化而不是版本来思考。

和HgInit :

当我们需要合并的时候,Subversion会试着去查看这两个版本,我的修改过的代码和你修改过的代码,然后试图猜测如何把它们合并成一个大的邪恶混乱。 它通常会失败,产生页面和页面的“合并冲突”,这并不是真正的冲突,只是Subversion未能弄清楚我们做了什么的地方。

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

通过查看SVN的存储库文件夹,我有一种印象,Subversion将每个修订版保持为变更集 。 而据我所知,Hg正在使用变更集快照,而Git纯粹是使用快照来存储数据。

如果我的假设是正确的,那么肯定还有其他方法可以使DVCS中的合并变得容易。 那些是什么?

*更新:

  • 我对技术视angular更感兴趣,但非技术视angular的答案是可以接受的
  • 更正:
    1. Git的概念模型完全基于快照。 快照可以作为其他快照的差异存储,差异仅仅是为了优化存储。 – RafałDowgird的评论
  • 从非技术angular度来看:
    1. 这仅仅是文化上的问题:如果合并困难,DVCS根本无法工作,所以DVCS开发者投入大量的时间和精力来使合并变得容易。 CVCS用户OTOH被用于糟糕的合并,所以开发人员没有动力使其工作。 (为什么当你的用户给你同样好的东西给别人一些废话时,让事情变好?)

      回顾一下:DVCS的整个目标是拥有许多分散的存储库,并不断地将变化合并到一起。 如果没有合并,DVCS根本就没用。 然而,CVCS仍然可以在糟糕的合并下生存下来,特别是如果供应商能够限制用户避免分支。 – JörgW Mittag的回答
  • 从技术angular度来看:
    1. 录制真正的历史DAG确实有帮助! 我认为主要的区别在于CVCS并不总是把一个合并作为一个变更集与几个父母一起logging,丢失了一些信息。 – tonfa的评论
    2. 因为合并跟踪 ,以及每个修订版都知道其父母的更基本的事实。 当每次修改(每个提交),包括合并提交,都知道它的父项(对于合并提交,意味着拥有/记住多个父项,即合并跟踪),可以重build修订版本的图(DAG = Direct Acyclic Graph)历史。 如果你知道修订图,你可以find你想要合并的提交的共同祖先。 当你的DVCS知道如何find共同的祖先时 ,你不需要提供它作为参数,例如在CVS中。

      请注意,可能有两个(或更多)提交的不止一个共同的祖先。 Git利用所谓的“recursion”合并策略,将合并基础(共同祖先)合并,直到剩下一个虚拟/有效的共同祖先(在一些简化中),并且可以做简单的三向合并。 – JakubNarębski的回答

检查一下在Git中如何和/或为什么合并比SVN更好?

在Git和其他DVCS合并很容易,不是因为一些神秘的变化集合视图(除非你使用Darcs,它的补丁理论,或一些达尔文启发的DVCS,但它们是less数)Joel乱了,但因为合并跟踪 ,以及每个修订版都知道其父母的更基本的事实。 为此,你需要(我认为)整个树/完整的信息库提交…不幸的是限制了部分签出的能力,并只提交了一部分文件。

当每个修订(每个提交),包括合并提交,都知道它的父母(对于合并提交,意味着拥有/记住多个父代,即合并跟踪 ),您可以重build修订历史logging的图表(DAG =直接非循环图表)。 如果你知道修订图,你可以find你想要合并的提交的共同祖先。 当你的DVCS知道如何find共同的祖先时 ,你不需要提供它作为参数,例如在CVS中。

请注意,可能有两个(或更多)提交的不止一个共同的祖先。 Git利用所谓的“recursion”合并策略,将合并基础(共同祖先)合并,直到剩下一个虚拟/有效的共同祖先(在一些简化中),并且可以做简单的三向合并。

创buildGit使用重命名检测是为了能够处理涉及文件重命名的合并。 (这支持了JörgW Mittag的观点,即DVCS有更好的合并支持,因为他们必须拥有它,因为合并比CVCS更为常见,因为合并隐藏在'update'命令中,在update-then-commit工作stream程中,cf 了解版本控制 (WIP)Eric S. Raymond)。

在DVCS中没有什么特别使得合并更容易。 这仅仅是文化上的问题:如果合并困难,DVCS 根本无法工作 ,所以DVCS开发者投入大量的时间和精力来使合并变得容易。 CVCS用户OTOH被用于糟糕的合并,所以开发人员没有动力使其工作。 (为什么当你的用户给你同样好的东西给别人一些废话时,让事情变好?)

Linus Torvalds在他的一个Git谈话中说,当他在Transmeta使用CVS时,他们在整个开发周期中搁置了整整一个星期来合并。 所有人都认为这是正常情况。 如今,在一个合并窗口中,Linus在几个小时之内完成了数百次合并。

如果CVCS用户只是去了他们的供应商,并说这个垃圾是不可接受的,那么CVCSs可以和DVCS具有同样好的合并能力。 但他们陷入了Blub悖论:他们根本不知道这是不可接受的,因为他们从来没有见过一个合理的合并体系。 他们不知道那里有更好的东西。

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

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

回顾一下:DVCS的整个目标是拥有许多分散的存储库,并不断地将变化合并到一起。 没有良好的合并,DVCS根本就没用。 然而,CVCS仍然可以在糟糕的合并下生存下来,特别是如果供应商能够限制用户避免分支。

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

部分原因当然是DVCSs存储比SVN更多的信息(DAG,副本)的技术论据,并且也具有更简单的内部模型,这就是为什么它能够执行更精确的合并,如其他响应。

然而,更重要的差别可能是因为你有一个本地仓库,你可以做频繁的小提交,也可以频繁的拖拽和合并input的改变。 这是由于“人为因素”造成的,人类与集中式VCS相对于DVCS的差异。

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

这会让你从你尝试实现的工作中分心,所以通常SVN用户在进行任务时不会合并。 结合SVN用户也倾向于让更改积累在一个单一的大型承诺,害怕打破别人的工作副本,并将有很长一段时间的分支和合并。

借助Mercurial,您可以在更小的增量提交之间更频繁地合并传入的更改。 这将按定义导致较less的合并冲突,因为您将在更新的代码库上工作。

如果结果是冲突,你可以决定推迟合并,并在自己的休闲活动。 这特别使合并更加不那么烦人。

哇,5篇散文的攻击!

总之,没有什么能让事情变得简单。 这很难,我的经验表明,错误确实发生。 但:

  • DVCS迫使你处理合并,这意味着需要花几分钟的时间熟悉现有的工具来帮助你。 这一点帮助。

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

你引用的hginit的片段声称Subversion不能进行三路合并,而Mercurial通过查看两个分支中的所有变化集合来合并,在两个方面都是错误的。

有一点是svn合并被巧妙地打破了; 看到http://blogs.open.collab.net/svn/2008/07/subversion-merg.html我怀疑这是与svn录制mergeinfo甚至樱桃采摘合并。; 在处理边界情况时,添加一些简单的错误,作为CVCS的当前海报孩子svn使他们看起来不好,而不是所有的DVCS刚刚得到正确的。

我认为正如其他人所说的那样,变更集的DAG有很大的不同。 DVCS:es需要在基本级别进行拆分历史logging(和合并),而我认为CVCS:es(这是较旧的)从第1天开始build立,以便首先跟踪修订和文件,并添加合并支持作为事后考虑。

所以:

  • 合并很容易做到,跟踪标签/分支跟源代码目录树分开的时间,所以整个回购可以一次性合并。
  • 由于DVCS拥有本地回购协议,因此易于创build,所以事实certificate很容易将不同的模块放在不同的回购站中,而不是在一个大的回购站内跟踪它们。 (所以整个repo合并不会像svn / cvs那样造成同样的中断,其中一个repo通常包含许多不相关的模块,这些模块需要单独的合并历史logging)。
  • CVS / SVN允许工作目录中的不同文件来自不同的修订版本,而DVCS:es通常对整个WC有一个修订版本(即,即使文件被恢复到早期版本,它也会显示为已修改状态,因为它与检出版本中的文件不同,SVN / CVS并不总是显示它。)

混合这些概念(就像Subversion所做的那样),我相信是一个很大的错误。 例如,在源代码树中有分支/标签,所以你必须跟踪哪些版本的文件已经被合并到其他文件中。 这显然比追踪哪些版本被合并更复杂。

所以,总结一下:

  • DVCS:ES需要简单的合并,基于它的function集。 devise决定是为了使这些合并易于执行和跟踪(通过DAG)以及其他function(分支/标签/子模块),而不是相反。
  • CVCS:es从一开始就有一些function(比如模块),这些function使得一些事情变得简单,但是实现全副作用的合并非常棘手。

至less这是我对cvs,svn,git和hg的感受。 (可能还有其他的CVCS:ES也有这个东西。)

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

同样,对于DVCS,您可以执行克隆存储库,将来自两个开发人员的工作合并到克隆中,testing更改,然后从克隆合并回主存储库。

很酷的东西。

作为一个历史logging,现在已经过时的PRCS系统也知道共同的祖先,并且可以高效地合并,虽然它不是分布式的(它是build立在RCS文件之上的!)。 这意味着它可以有效地迁移到git,同时保留历史,但是。

可能是DVCS用户从来没有做过像重构那样的改变,重命名/复制项目中的大多数文件,或者重新devise用于文件结尾的stratch API。