暂存区内部的git合并(merge)过程是什么?

9
Git通过merge魔法来完成合并操作,然后让用户解决实际冲突,这正是应该做的。我正在寻找关于基本的git合并以及它如何使用暂存区的低级描述的内容。
我刚刚阅读了Git Parable,还看到了这里的评论:

即使考虑到它是“寓言”而不是Git历史的详细记录(顺便说一下,你可以在Git Wiki上找到),一个观点仍然存在:用拆分更改为多个提交和/或使用脏树提交来解释暂存区是不好的实践,也就是说,有些更改未提交。暂存区的主要优势(除了成为其他SCMs隐含的待添加区域的显式版本之外)是处理冲突合并,我认为应该这样解释。

git merge 手册标识了合并的1/2/3阶段元素,但显然没有详细说明其原因和目的。
有人能否提供任何关于Git如何实现其他工具无法做到的结果(除了Linus V Bram在Wincent的博客中详细说明的内容),即所谓的微不足道部分的文章?
大多数网络文章都认为合并“只是发生了”,我没有找到任何解释问题的文章(例如,需要小提交的原因,共同提交的价值等)。

1
http://www.kernel.org/pub/software/scm/git/docs/technical/trivial-merge.txt - Josh Lee
@jleedev:一份不错的文档。我想我以前看过它。虽然它没有涵盖设计理念。 - Philip Oakley
2个回答

2

以下内容可能会解决您的一些问题,因为这是git最常见的合并方式:

git merge-file

git merge-file旨在成为RCS合并的最小克隆;也就是说,它实现了所有git(1)所需的RCS合并功能。


显然在某个阶段,RCS 是 Git 的一个依赖项:http://blogs.operationaldynamics.com/andrew/software/version-control/git-uses-rcs.html - Gerry
git merge-file 的描述(第1和2段)是一个很好的开始。我注意到了“行段”注释,这表明原子更改级别的位置。相对于逐字节比较,通常可以通过行来定位它们,因为“行”通常具有合理的熵。即git merge选择监视行更改而不是字节更改,这使其更容易正常工作。 - Philip Oakley

2
大多数版本控制系统都采用了三方合并的基本概念。它比较两个具有相同祖先的分支,所以如果代码行在两个分支之间不同,您就会知道哪个分支更改了它。如果它们都更改了它,那么您就有了必须由人解决的合并冲突。
有一些情况很难确定一个合适的共同祖先。为此进行了许多研究,其中许多涉及使用提交时跟踪的附加元数据的不同算法。
Linus的基本创新是跟踪树而不是文件。这是一种微妙的区别。通过Wincent博客中的示例来说明,考虑分支A中的文件foo。你分支出去创建分支B。在分支A中,foo被重命名为bar。在分支B中,它被删除。然后你尝试合并。
如果您正在跟踪文件,则如下所示:
在分支之前,创建文件foo的第1版。
下一次提交后,分支A指向文件foo的第2版,这是一个已删除的文件,以及新文件bar的第1版。
下一次提交后,分支B指向文件foo的第2.1版,这是一个已删除的文件。
当您合并时,将比较文件foo的第2版和第2.1版,并发现它们是相同的。没有合并冲突。分支B甚至没有一个名为bar的文件,因此也没有冲突。您最终会得到合并算法默默地接受分支A的重命名,即使在foo被删除和被重命名之间存在真正的冲突。
如果您正在跟踪树,则如下所示:
在分支之前,创建哈希值为dcb8bd7a97ab39f4c156a1a96d4b10720a39fb81的blob。创建一个树,其中包含一个标签为foo的条目,指向该哈希值。
下一次提交后,分支A指向具有标签bar的条目的树,该条目指向相同的哈希值。
下一次提交后,分支B指向一个空树。
当您合并时,将比较树,其中B显示删除,而A显示对blob dcb8bd7a97ab39f4c156a1a96d4b10720a39fb81的重命名。要求人选择哪个更好。
你可以通过使用文件跟踪版本控制系统来添加重命名的元数据来缓解其影响,但是git使用其常规标准数据结构。此外,在存在许多共同祖先的复杂合并中,元数据方式存在困难。你可以在共同祖先和两个分支头之间放置数十亿条可能的路径,而git仍然会看到具有相同哈希的blob,并能够检测重命名和删除。另外,通过电子邮件接受补丁中的更改时保留元数据也很困难。
对于同时更改的重命名文件,会变得有些棘手,但是通过跟踪树,git拥有所需的所有信息。它看到blob dcb8bd7a97ab39f4c156a1a96d4b10720a39fb81从两个分支中消失,但它还看到一个指向新blob的新树条目,并可以比较这两者。如果文件的大部分匹配,则被视为重命名。显然,如果在重命名的文件中进行了大量更改,那么在某些时候,没有任何合并算法能够帮助你。
请参见Linus在这个主题上的哲学思考的这封电子邮件

Karl:我认为你提到的“如果你进行了大量更改,这种方法就会失效”是很重要的,即使没有文件重命名。如果在合并之前,A和B分支中都有一长串的小更改和提交,那么这种情况就会发生,因为小更改序列使得更容易找到正确的插入位置。这就是我试图确认的“多个小提交”的Git视图及其合并的价值(即查找参考文献)。我一直听同事说“你总是需要手动进行大型合并”,好像每次更改都需要手动操作一样! - Philip Oakley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接