Git如何解决合并问题?
SVN通过使分支真的很便宜而使分支更容易,但合并在SVN中仍然是一个真正的问题 – Git可以解决这个问题。
Git能做到这一点,以及如何?
(免责声明:我所知道的Git是基于Linus讲座 – 总git noob在这里)
Git不会阻止合并中的冲突,但即使他们不共享任何父代祖先,也可以调和历史。
(通过移植文件( .git/info/grafts
) ,这是一个列表,每行一个,后面跟着一个父母,你可以修改这个“调和”的目的。
那么非常强大。
但要真正看到“如何融合”,你可以先转向Linus ,并意识到这个问题不是关于“algorithm”的问题:
Linus :我个人而言 ,我想要有一些非常重复和非巧妙的东西。 我理解的东西或告诉我,它不能做到这一点。
坦率地说,合并单个文件的历史, 而不考虑所有其他文件的历史,使我走“呃”。合并的重要部分不在于它如何处理冲突(无论如何,如果这些冲突真的很有趣的话,需要由人来validation),而是应该将历史合在一起,以便为未来的合并奠定新的坚实基础。
换句话说,重要的部分是微不足道的部分:父母的命名和跟踪他们的关系。 不是冲突。
看起来似乎有99%的SCM似乎认为解决scheme是对内容合并更聪明。 这完全忽略了这一点。
所以Wincent Colaiuta补充道(强调我的):
不需要花哨的元数据,重命名跟踪等等。
你需要存储的唯一东西是每次更改之前和之后的树的状态。哪些文件被重命名? 哪些被复制? 哪些被删除? 什么行被添加? 哪些被删除? 哪些行内部发生了变化? 哪些文本块被从一个文件复制到另一个?
你不应该关心任何这些问题,你当然不应该保留特殊的跟踪数据,以帮助你回答它们: 树的所有变化(添加,删除,重命名,编辑等)都是隐含的编码在树的两个状态之间的三angular形中 ; 你只要跟踪 内容 。绝对一切都可以(也应该)推断出来 。
Git打破了模式,因为它考虑的内容,而不是文件。
它不跟踪重命名,它跟踪内容。 而且它在整个树层面都是如此。
这是大多数版本控制系统的根本偏离。
它并不打算存储每个文件的历史; 而是将其存储在树级别。
当你执行比较时,你比较两棵树,而不是两个文件。另一个基本的聪明的devise决定是Git如何合并。
合并algorithm很聪明,但他们不会太聪明。 毫不含糊的决定是自动做出的,但是如果有疑问则由用户来决定。
这是应该的。 你不想让机器为你做出这些决定。 你永远不会想要它。
这是Git合并方法的基本见解:当所有其他版本控制系统都在努力变得更加智能时,Git会自我形容为“愚蠢的内容pipe理器”,而且更好。
现在普遍认为,这种3路合并algorithm(或许具有增强function,如重命名检测和处理更复杂的历史),它考虑了当前分支上的版本('我们'),合并分支上的版本('他们' )和合并分支(“祖先”)的共同祖先版本(从实际angular度来看)是解决合并的最佳方式。 在大多数情况下,对于大多数内容树层级合并(取哪个版本的文件)就足够了; 很less需要处理内容冲突,然后使用diff3algorithm就足够了。
要使用3路合并,您需要知道合并分支的共同祖先(称为合并基础)。 为此,你需要知道这些分支之间的完整历史。 什么Subversion之前(当前)版本1.5缺乏(没有第三方工具,如SVK或svnmerge)是合并跟踪 ,即记住合并提交什么父母(什么提交)用于合并。 没有这些信息,就不可能在重复合并的情况下计算正确的共同祖先。
考虑下面的图表:
---.---a---.---b---d---.---1 \ / \-.---c/------.---2
(这可能会被弄坏……这里有能力绘制ASCII艺术图) 。
当我们合并提交'b'和'c'(创build提交'd')时,共同的祖先是分支点,提交'a'。 但是当我们要合并提交'1'和'2'时,现在共同的祖先是提交'c'。 在不存储合并信息的情况下,我们不得不错误地断定它是提交'a'。
Subversion(版本1.5之前)和早期的CVS进行了合并,因为您必须自己计算共同的祖先,并在进行合并时手动提供有关祖先的信息。
Git在提交对象中存储关于一个提交的所有父母的信息(在合并提交的情况下,多于一个父代)。 这样你可以说Git存储修改的DAG(直接非循环图),存储和记忆提交之间的关系。
(我不确定Subversion如何处理下面提到的问题)
另外在Git中的合并可以处理两个额外的复杂问题: 文件重命名 (当一边重命名一个文件,其他的没有;我们想要重命名,我们想要得到改变应用到正确的文件)和纵横交错的合并 (更复杂的历史,当有超过一个共同的祖先)。
- 在合并过程中的文件重命名使用基于启发式相似度得分(文件内容的相似性和path名的相似性被考虑) 重命名检测 。 Git检测合并分支(和祖先)中的哪些文件相互对应。 在实践中,它对现实世界的案例非常有效。
- 交叉合并 ,请参见revctrl.org wiki中的定义 (以及多个合并基础的存在)通过使用recursion合并策略进行pipe理,该合并策略生成单个虚拟共同祖先。
上面的答案都是正确的,但我认为他们错过了GIT的中心点,容易合并我。 SVN合并需要你跟踪并记住合并的内容,这是一个庞大的PITA。 从他们的文档:
svn merge -r 23:30 file:///tmp/repos/trunk/vendors
现在,这不是杀手,但是如果你忘记了是23-30还是23-30,或者你已经合并了一些提交,那么你就会陷入困境,你必须找出避免的答案重复或缺less提交。 上帝帮助你,如果你分支分支。
有了git,它就是git merge,所有这些都可以无缝地发生,即使你已经挑选了几个提交或做了任何幻想的git-land的东西。
据我所知,合并algorithm并不比其他版本控制系统更智能。 但是,由于git的分布性,不需要集中合并。 每个开发人员都可以随时将其他开发人员的小改动合并到他的树中,因此出现的冲突往往更小。
Git只是使得把一个糟糕的合并搞乱了别人的仓库变得更加困难。
唯一真正的好处是Git合并的速度要快得多,因为一切都是在本地完成的,而且是用C语言编写的。
SVN,正确使用,是完全可用的。