git如何比较两个文件。哪些算法用于比较两个文件?合并时是否逐行比较?
我不确定合并时两个文件的比较是否会产生冲突。
答案 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都以共享提交)称为合并基础。这是合并的第三个输入。 Git使用存储库中的历史记录(即提交)自动找到此合并基础提交。这意味着您需要同时拥有Alice的和 Bob的提交,以及所有导致这两个分支技巧的提交,以便您还具有公共起点提交。
请记住,每个提交及其快照都会记录有关快照的某些信息 :例如,创建快照的人的姓名和电子邮件地址。他们制作 的日期和时间标记,以及一条日志消息,他们可以用来解释为什么为什么。它还存储了其直接 parent 提交的原始哈希ID:他们通过git checkout
使用的提交从他们提交 提交之前开始。这些父哈希ID构成了一个向后看的链:如果Alice和Bob都从提交H
开始,并且Alice进行了两次提交I
和J
,而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
中的每个文件:
[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> {1>}的提交(例如说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个广告位:
如您所见,只有一个分支(commit E,commit F)可以添加新的东西,或者它们两个都可以添加相同的东西。否则,将发生合并冲突。
答案 2 :(得分:-1)
它使用delta compression。我们必须了解,当我们在获取中add
一个文件时,我们创建了一个对象,该对象的总和被计算并记录在索引中。 git的作用是,通过git-repack
,它将压缩的对象(使用增量压缩压缩的对象)放入一个包(文件)中。在进行提交时,git会获取未压缩的对象并使用一些内部规则,它正在创建一个包含对象之间差异和相似之处的文件。这个包的创建使用增量压缩。
这种增量压缩(只是增量差异)就是您要问的问题。我猜想这种算法的工作范围超出了这个问题,因此这里有一些参考资料可以帮助您前进。