git在合并时如何比较两个文件?

时间:2019-07-04 13:49:32

标签: git git-merge

git如何比较两个文件。哪些算法用于比较两个文件?合并时是否逐行比较?

我不确定合并时两个文件的比较是否会产生冲突。

3 个答案:

答案 0 :(得分:1)

了解git merge的关键是Git不会比较两个事物。 Git比较项。

Git无法直接比较所有三个。它必须一次将它们两个进行比较。其中有两个是文件的两个分支提示版本(或分支提示提交;稍后我将详细讨论),但是Git不会将这些相互比较。这是第三个文件的来源:第三个文件是该文件的合并基础版本。

请记住,合并的目的是合并更改。但是Git不存储更改。 Git存储快照。每个提交都完整完整地存储每个文件:给定一个提交,Git将获得整个README.md,整个main.py,无论该特定提交中的其他文件是什么,都是提交中的版本。

要从快照中获取更改,我们需要两个 快照:旧快照和新快照。然后,我们玩Spot the Difference的游戏。对于Git,它是git diff:您将旧提交的哈希ID和新提交的哈希ID赋予它,并为这两个文件之间更改的每个文件产生差异。 git diff的输出是一系列指令:删除这些行,添加其他行。如果您使用原始快照并按照说明进行操作,则将获取新快照。

但是,当我们合并时,我们希望将(例如)爱丽丝完成的工作与鲍勃完成的工作结合。因此,Git要做的是:

  • 找到Alice和Bob都开始的最佳 shared 提交。
  • 共享提交的文件与Alice的文件进行比较。这是爱丽丝更改的内容
  • 共享提交的文件与Bob的文件进行比较。这是鲍勃所做的更改。

我们将共享提交(Alice和Bob都以共享提交)称为合并基础。这是合并的第三个输入。 Git使用存储库中的历史记录(即提交)自动找到此合并基础提交。这意味着您需要同时拥有Alice的 Bob的提交,以及所有导致这两个分支技巧的提交,以便您还具有公共起点提交。

请记住,每个提交及其快照都会记录有关快照的某些信息 :例如,创建快照的人的姓名和电子邮件地址。他们制作 的日期和时间标记,以及一条日志消息,他们可以用来解释为什么为什么。它还存储了其直接 parent 提交的原始哈希ID:他们通过git checkout使用的提交从他们提交 提交之前开始。这些父哈希ID构成了一个向后看的链:如果Alice和Bob都从提交H开始,并且Alice进行了两次提交IJ,而Bob进行了两次提交{{1} }和K,向后链看起来像这样:

L

Git会自动找到 I <-J <-- (Alice's latest) / ... <-F <-G <-H \ K <-L <-- (Bob's latest) ,这是爱丽丝和鲍勃都从这里开始的地方。 1

已经找到H,Git实际上运行了以下两个H命令:

  • git diff:爱丽丝做了什么改变
  • git diff --find-renames hash-of-H hash-of-J:Bob所做的更改

合并过程现在合并了这些更改。对于git diff --find-renames hash-of-H hash-of-L中的每个文件:

  • 爱丽丝是否更改了文件?鲍勃更改文件了吗?
  • 如果都没有更改文件,请使用文件的任何副本:三个文件都相同。
  • 如果Alice更改了文件,而Bob没有更改,请使用Alice的版本。
  • 如果Bob更改了文件而Alice没有更改文件,请使用Bob的版本。
  • 如果两个都更改了文件,请合并它们的更改。这是可能发生合并冲突的地方。
  

[Git]合并时是否逐行比较?

答案是否定的。如您现在所见,Alice的版本与Bob的版本没有任何比较。有一个比较是逐行比较的。 H所做的就是将 base 版本与Alice进行比较,并且 base 版本与Bob进行了相同的比较。通过对两对 commits 进行完整的提交范围比较,开始整个过程​​。在一次提交范围内的比较中,发现Alice和Bob逐行更改了一些特定文件现在,或者实际上是diff-hunk-按差异比较,比较很重要。但是它们来自第三版本。

  

我不想每次都使用“ git diff”手动检查。

您不必。您可以想要,但是要这样做,您可能需要使用git diff查找基于合并的提交。但是,如果您不想,那么...就不要。 Git 将找到基于合并的提交; Git 将执行两个单独的git merge-base操作; Git 会将Alice的更改与Bob的更改结合在一起,如果更改的行重叠(或者在某些情况下为abut,或者如果两者都跨到文件末尾,则声明冲突)。

(对于Git,如果Alice和Bob都对完全相同的行进行了完全相同的更改,则Git只会获取该更改的一个副本。其他VCS可能会声明这是一个冲突,要么是出于懒惰(他们不检查更改是否相同,而只是检查是否重叠),要么是妄想症:如果两者都更改了同一行,则正确的结果可能是 not 使用变更的一个副本。Git只是说“正确的结果是变更的一个副本”。)

无论如何,Git都会将 combined 更改应用于文件的合并基础版本。这就是结果,可能会产生合并冲突(以及文件的工作树副本中的合并冲突标记)。

最后,请注意两个git diff命令中的--find-renames。 Git将尝试告诉Alice和/或Bob 重命名合并基础提交中的任何文件。如果是这样,Git将尝试在最终结果中保持重命名。不管是重命名是爱丽丝还是鲍勃,都是如此。如果Alice Bob都重命名了文件,则Git不知道要使用哪个最终名称,并声明重命名/重命名冲突。如果Alice或Bob删除文件,而另一个修改文件,也会出现类似的问题,并且如果Alice和Bob都在文件中添加了 new 文件,则会发生最后一个冲突。一样的名字。这些冲突就是我所说的高级冲突:它们会影响整个文件(和/或它们的名称),而不是文件中 中的个别行。如果以及何时使用git diff-Xours选项,那么低级冲突(文件中的行)与高级冲突之间的区别就很重要。


1 即使Alice仅在Carol所做的一次-Xtheirs上做出{em> {}的提交(例如说J),此方法仍然有效在I上。常见的起点仍然是H。 Git甚至不查看每次提交的作者身份:它只是从两个分支技巧中向后起作用。

答案 1 :(得分:0)

有几种合并策略。 Git默认使用三向合并算法递归。

3向算法使用最后的公共提交。

例如:

master: A -> B -> C

创建新分支

master: A -> B -> C
                   \
branch:             D

一些新的提交

master: A -> B -> C -> E
                   \
branch:             D -> F

假设在a.txt中所做的所有更改(空单元格对应于空行)

 commit C         commit E         commit F 
----------       ----------       ----------
  line a                            line a
  line b         new line d
  line c                          new line e
                   line a           line b
                   line b         new line f
                   line c           
                 new line g         line c

如果我们合并两个分支(提交E,提交F)会发生什么。它会产生合并冲突吗?答案是否定的。因为git不会逐行比较文件。它比较了行的上下文。

对齐a.txt文件

 commit C         commit E         commit F 
----------       ----------       ----------

                 new line d

  line a-----------line a-----------line a

                                  new line e
  line b-----------line b-----------line b
                                  new line f

  line c-----------line c-----------line c
                 new line g

在上表中,更改是对齐的。提交C(祖先提交)中的行是我们的引用。 git比较参考线的邻居。在示例中,我们有4个广告位:

  • 在行a上方:commit e添加新行d
  • 在a行以下:commit f添加新行e
  • 在b行以下:commit e添加新行f
  • 在c行以下:commit g添加新行g

如您所见,只有一个分支(commit E,commit F)可以添加新的东西,或者它们两个都可以添加相同的东西。否则,将发生合并冲突。

答案 2 :(得分:-1)

它使用delta compression。我们必须了解,当我们在获取中add一个文件时,我们创建了一个对象,该对象的总和被计算并记录在索引中。 git的作用是,通过git-repack,它将压缩的对象(使用增量压缩压缩的对象)放入一个包(文件)中。在进行提交时,git会获取未压缩的对象并使用一些内部规则,它正在创建一个包含对象之间差异和相似之处的文件。这个包的创建使用增量压缩。

这种增量压缩(只是增量差异)就是您要问的问题。我猜想这种算法的工作范围超出了这个问题,因此这里有一些参考资料可以帮助您前进。

Algorithms for Delta Compression

How git treats each file

git-repack

delta differencing