Git如何解决合并问题?

时间:2009-03-04 21:43:11

标签: svn git version-control

SVN通过使分支非常便宜而使分支变得更容易,但合并仍然是SVN中的一个真正的问题--Git据说可以解决这个问题。

Git能实现这一目标,以及如何实现?

(免责声明:我所知道的关于Git的全部内容都是基于Linus的讲座 - 这里的总git noob)

5 个答案:

答案 0 :(得分:76)

Git不会阻止合并中的冲突,但即使他们不共享任何父级祖先,也可以协调历史记录。
(通过The grafts file (.git/info/grafts),这是一个列表,每行一个,提交后跟其父项,您可以修改为“对帐”目的。)
那里非常强大。

但要真正瞥见“如何合并”,你can start by turning to Linus himself,并意识到这个问题与“算法”无关:

  

Linus :我个人,我希望有一些非常可重复且非聪明的东西。我理解的东西告诉我它无法做到   坦率地说,合并单个文件历史记录而不考虑所有其他文件的历史记录会让我“唉”。

     

合并的重要部分不在于它如何处理冲突(无论如何,如果它们都有趣的话,需要由人来验证),但是它应该将历史合并在一起以便你拥有未来合并的新坚实基础。

     

换句话说,重要的部分是琐碎的部分:父母的命名,并跟踪他们的关系。不是冲突。

     

看起来99%的SCM人似乎认为解决方案是更加聪明的内容合并。完全错过了这一点。


所以Wincent Colaiuta补充说(强调我的):

  

不需要花哨的元数据,重命名跟踪等   您需要存储的唯一内容是每次更改之前和之后树的状态。

     

重命名了哪些文件?哪些被复制?哪些被删除了?添加了哪些行?哪些被删除?哪条线在它们内部发生了变化?哪个文本块从一个文件复制到另一个文件?   你不应该关心这些问题,你当然不应该保留特殊的跟踪数据,以帮助你回答它们:对树的所有更改(添加,删除,重命名,编辑等) )在树的两个状态之间的delta中隐式编码;您只需 跟踪 内容

     

绝对可以(并且应该)推断出所有内容

     

Git打破了模式,因为它考虑的是内容,而不是文件   它不跟踪重命名,它跟踪内容。并且它在整个树级别上这样做   这与大多数版本控制系统完全不同   它不打算尝试存储每个文件的历史记录;它改为将历史存储在树级   执行差异时,您要比较两棵树,而不是两个文件。

     

另一个从根本上聪明的设计决策是Git如何合并   合并算法很聪明,但他们不会试图太聪明。明确的决定是自动做出的,但是当有疑问时,由用户决定。
  这是应该的方式。您不希望机器为您做出这些决定。你永远不会想要它   这是Git合并方法的基本见解:虽然每个其他版本控制系统都试图变得更聪明,但Git很乐意自我描述为“愚蠢的内容管理器”,并且它更适合它。

答案 1 :(得分:18)

现在普遍同意这种3向合并算法(可能还有重命名检测和处理更复杂的历史等增强功能),它考虑了当前分支('我们的')上的版本,合并分支上的版本( '他们的')和合并分支的共同祖先版本('祖先')是(从实际的角度来看)解决合并的最佳方法。在大多数情况下,对于大多数内容树级别合并(要采用哪个版本的文件)就足够了;很少需要处理内容冲突,然后diff3算法就足够了。

要使用3向合并,您需要了解合并分支的共同祖先(称为合并基础)。为此,您需要知道这些分支之间的完整历史记录。什么Subversion之前(当前)版本1.5缺乏(没有第三方工具,如SVK或svnmerge)是 合并跟踪 ,即记住合并提交什么父母(什么提交)用于合并。如果没有这些信息,就无法在重复合并的情况下正确计算共同的祖先。

考虑以下图表:

---.---a---.---b---d---.---1
        \        /
         \-.---c/------.---2

(可能会受到损坏......有能力在这里绘制ASCII艺术图表会很好)
当我们合并提交'b'和'c'(创建提交'd')时,共同的祖先是分支点,提交'a'。但是当我们想要合并提交'1'和'2'时,现在共同的祖先是提交'c'。如果不存储合并信息,我们就必须错误地认为它是提交'a'。

Subversion(1.5版之前)和早期的CVS之间的合并很难,因为你必须自己计算共同的祖先,并在进行合并时手动提供有关祖先的信息。

Git在提交对象中存储有关提交的所有父项(在合并提交的情况下多于一个父项)的信息。这样你就可以说Git存储了DAG(直接非循环图)的修订,存储和记忆提交之间的关系。


(我不确定Subversion如何处理下面提到的问题)

另外在Git中合并可以解决另外两个复杂问题:文件重命名(当一方重命名文件时,其他方面没有;我们想要重命名,我们希望得到更改应用于正确的文件)和纵横交错合并(更复杂的历史记录,当有多个共同的祖先时)。

    合并期间
  • 文件重命名使用基于启发式相似性得分(文件内容的相似性和路径名相似性)进行管理 重命名检测 。 Git检测合并分支(和祖先)中哪些文件彼此对应。在实践中,它适用于现实世界的案例。
  • 纵横交错合并,请参阅definition at revctrl.org wiki,(并且存在多个合并基础)通过 递归合并策略进行管理 ,它会生成单个虚拟共同祖先。

答案 2 :(得分:9)

上面的答案都是正确的,但我认为他们错过了git易于合并的中心点。 SVN合并要求您跟踪并记住已合并的内容,这是一个巨大的PITA。来自他们的文档:

svn merge -r 23:30 file:///tmp/repos/trunk/vendors

现在这不是杀手,但是如果你忘记它是23-30包容还是23-30独家,或者你是否已经合并了其中的一些提交,你就被清醒了,你必须弄明白避免重复或遗漏提交的答案。如果你去分支,上帝会帮助你。

使用git它只是git merge而且所有这一切都无缝地发生,即使你已经挑选了几个提交或做了许多奇妙的git-land事情。

答案 3 :(得分:5)

据我所知,合并算法并不比其他版本控制系统更智能。但是,由于git的分布式特性,不需要集中合并。每个开发人员都可以随时将其他开发人员的小更改重新绑定或合并到他的树中,因此产生的冲突往往会更小。

答案 4 :(得分:-9)

Git只是让一个糟糕的合并搞砸了其他人的存储库变得更加困难。

唯一真正的好处是Git在合并时要快得多,因为一切都是在本地完成的,而且是用C语言编写的。

SVN,使用得当,完全可以使用。