Git如何解决合并问题?

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

Git能做到这一点,以及如何?

(免责声明:我对Git的所有知识都基于Linus讲座 - 这里是git noob)


Git不会阻止合并中的冲突,但即使它们不共享任何父代祖先,也可以调和历史记录。
(通过嫁接文件( .git/info/grafts ),这是一个列表,每行一个,后面跟着父母的提交列表,您可以修改这个“调整”目的。)
那么非常强大。

但要真正了解“如何合并已经被认为通过”,你可以开始转向Linus自己,并意识到这个问题不是关于“算法”:

Linus:我个人而言,我想拥有非常重复和非巧妙的东西。 我理解的东西或告诉我它不能这样做。
坦率地说,合并单个文件历史记录而不考虑所有其他文件的历史记录使我走向“呃”。

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

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

看起来似乎有99%的SCM似乎认为解决方案是对内容合并更聪明。 这完全忽略了这一点。


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

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

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

绝对一切都可以(也应该)推断出来

Git打破了模式,因为它考虑的是内容,而不是文件。
它不会跟踪重命名,它会跟踪内容。 而且它在整个树层面都这样做。
这是大多数版本控制系统的根本偏离。
它不会试图存储每个文件的历史; 它会将历史存储在树级别。
当你执行比较时,你比较两棵树,而不是两个文件。

另一个根本上聪明的设计决定是Git如何合并。
合并算法很聪明,但他们不会太聪明。 毫不含糊的决定是自动做出的,但是如果有疑问则由用户自行决定。
这是它应该的方式。 你不想让机器为你做出这些决定。 你永远不会想要它。
这是Git合并方法的基本见解:当所有其他版本控制系统都在努力变得更加智能时,Git会自我描述为“愚蠢的内容管理器”,对它更好。


现在普遍认为,这种3路合并算法(或许具有增强功能,如重命名检测和处理更复杂的历史记录),它考虑了当前分支上的版本('我们'),合并分支上的版本('他们' )和合并分支('ancestor')的共同祖先的版本(从实际角度来看)是解决合并的最佳方式。 在大多数情况下,对于大多数内容树级别合并(要采用哪个版本的文件)就足够了; 很少需要处理内容冲突,然后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会检测合并分支(和祖先)中哪些文件相互对应。 在实践中,它适用于真实世界的案例。
  • 交叉合并 ,参见revctrl.org wiki中的定义(以及多个合并基础的存在)通过使用递归合并策略进行管理,该策略生成单个虚拟共同祖先。

  • 上面的答案都是正确的,但我认为他们错过了git容易合并的中心点。 SVN合并需要你跟踪并记住合并的内容,这是一个庞大的PITA。 从他们的文档:

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

    现在这不是杀手锏,但是如果你忘记了是23-30还是23-30独占,或者你是否已经合并了其中的一些提交,那么你就会陷入困境,你必须找出避免的答案重复或缺少提交。 如果你分支分支,上帝会帮助你。

    有了git,它就是git merge,所有这些都可以无缝地发生,即使你已经挑选了几个提交或做了任何幻想的git-land的东西。

    链接地址: http://www.djcxy.com/p/50825.html

    上一篇: How does Git solve the merging problem?

    下一篇: What does it mean for a method to be asynchronous?