git 如何在合并时比较两个文件?
How does git compare two files while merging?
git如何比较两个文件。哪些算法用于比较两个文件?合并时是否逐行比较?
我不确定合并时两个文件的比较是否会产生冲突。
它使用delta compression。我们要明白,当我们add
一个文件get时,我们创建了一个对象,它的sha sum被计算并记录在索引中。 git 所做的是,通过 git-repack
,它将压缩对象(使用增量压缩进行压缩)打包(文件)。当您进行提交时,git 正在获取未压缩的对象并使用一些内部规则,它正在创建一个包含对象之间差异和相似之处的文件。此包的创建使用增量压缩。
您要问的就是这种增量压缩,也就是增量差分。我想这个算法如何工作的范围超出了这个问题,所以这里有一些参考资料可以帮助你。
理解 git merge
的关键是 Git 不会比较 两个 事物。 Git 比较 三 事物。
Git 不能直接比较所有三个。它必须一次比较它们两个。其中两件事是文件的两个分支提示版本(或分支提示提交;我稍后会详细讨论),但是 Git 不会将那些 相互比较。这是第三个进来的地方:第三个文件是文件的 merge base 版本。
请记住,合并的目标是合并更改。但是 Git 不存储更改。 Git 存储 快照 。每个提交都会完整且完整地存储每个文件:给定一个提交,Git 得到整个 README.md
,整个 main.py
,无论其他文件在这个特定的提交中,这就是提交中的版本.
要从快照中获取更改,我们需要两个 个快照:旧快照和新快照。然后我们玩Spot the Difference的游戏。对于 Git,即 git diff
:您将旧提交的哈希 ID 和新提交的哈希 ID 赋予它,它会为两者之间更改的每个文件创建差异。 git diff
的输出是一系列指令:删除这些行,添加这些其他行。如果拍摄原始快照并应用说明,您将获得新快照。
不过,当我们合并时,我们想要将(比如说)爱丽丝完成的工作与合并鲍勃所做的工作。那么 Git 所做的是:
- 找到最好的 共享 提交,Alice 和 Bob 都开始了。
- 将 shared commit 的文件与 Alice 的文件进行比较。这就是 爱丽丝改变的东西。
- 将 shared 提交的文件与 Bob 的文件进行比较。这是 Bob 所做的更改。
我们将共享提交(Alice 和 Bob 开始的那个)称为 合并基础。这是合并的第三个输入。 Git 使用存储库中的历史记录(提交)自动查找此合并基础提交。这意味着您需要拥有 Alice 的 和 Bob 的提交,以及导致这两个分支提示的所有提交,以便您也有共同的起点提交。
请记住,每次提交及其快照都会记录一些有关快照的信息:例如,创建者的姓名和电子邮件地址。 他们成功时有一个 date-and-time-stamp,还有一条日志消息,他们可以用来解释 为什么 他们成功了。它还存储其直接 parent 提交的原始哈希 ID:他们使用的提交,通过 git checkout
,从他们 他们的 [=144] 开始=] 提交。这些父哈希 ID 形成一个 backwards-looking 链:如果 Alice 和 Bob 都从提交 H
开始,并且 Alice 进行了两次提交 I
和 J
并且 Bob 进行了两次提交 K
和 L
,向后链看起来像这样:
I <-J <-- (Alice's latest)
/
... <-F <-G <-H
\
K <-L <-- (Bob's latest)
Git会自动找到H
,Alice和Bob都是从这里开始的。1
找到 H
、Git 现在,实际上运行这两个 git diff
命令:
git差异--find-renames<em>hash-of-H</em><em>hash-of-J</em>
: 爱丽丝改变了什么
git差异--find-renames<em>hash-of-H</em><em>hash-of-L</em>
:鲍勃改变了什么
合并过程现在合并了这些更改。对于 H
中的每个文件:
- 爱丽丝是否更改了文件? Bob 是否更改了文件?
- 如果文件都没有更改,则使用文件的任何副本:三个都相同。
- 如果 Alice 更改了文件而 Bob 没有,则使用 Alice 的版本。
- 如果 Bob 更改了文件而 Alice 没有,则使用 Bob 的版本。
- 如果双方都更改了文件,合并他们的更改。这是合并冲突 可能 发生的地方。
Does [Git] compare line by line while merging?
这个问题的答案既不是也不是。正如您现在看到的,Alice 的版本与 Bob 的版本没有比较。 是一个比较——有点像line-by-line; git diff
所做的任何比较——base 版本与爱丽丝的版本,base 版本与鲍勃的。整个过程通过对两对 提交 进行完整的 commit-wide 比较开始。在那个 commit-wide 比较中,发现 Alice 和 Bob 都更改了 一些特定文件 , 现在 line-by-line,或者真的 diff-hunk-by-diff-hunk,比较很重要。但它们来自 third 版本。
I don't want to check each time manually using "git diff".
您不必这样做。如果你想要,你可以,但是要做到这一点,你需要找到merge-base提交,也许使用git merge-base
。但如果你不想,那么......不要吨。 Git 会找到 merge-base 提交; Git 将执行两个单独的 git diff
操作; Git 将合并 Alice 的更改和 Bob 的更改,如果更改的行重叠——或者在某些情况下,abut,或者如果两者跨越文件结尾。
(对于 Git,如果 Alice 和 Bob 都对 exactly 相同的行进行了 exactly 相同的更改,Git 只复制一份更改。其他 VCS 可能会在这里声明冲突,要么是出于懒惰——他们不检查更改是否相同,只是它们重叠了——要么是偏执狂:如果两者更改相同行,也许正确的结果是 而不是 只是为了使用一份更改。Git 只是说 "the correct result is one copy of the change"。)
在任何情况下,Git 将 combined 更改应用于文件的 merge base 版本。这就是结果,可能存在合并冲突(并在文件的 work-tree 副本中合并冲突标记)。
最后,注意两个git diff
命令中的--find-renames
。 Git 将尝试判断 Alice and/or Bob 是否重命名了 merge-base 提交中的任何文件。如果是这样,Git 将尝试在最终结果中保留重命名。无论是 Alice 还是 Bob 进行了重命名,都是如此。如果 Alice 和 Bob 都重命名了文件,Git 不知道要使用哪个最终名称,并声明一个 rename/rename 冲突。如果 Alice 或 Bob 删除 文件而另一个人修改它,则会出现类似的问题,如果 Alice 和 Bob 添加 new 同名文件。这些类型的冲突就是我所说的 高级 冲突:它们影响整个文件(and/or 它们的名称)而不是 中的个别行一份文件。如果您使用 -Xours
或 -Xtheirs
选项,low-level 冲突(文件中的行)和 high-level 之间的差异很重要。
1即使爱丽丝只做了 一个 提交,比方说 J
,在 Carol 的一个提交之上I
是 Carol 在 H
上创造的。共同的起点仍然是H
。 Git 甚至不查看每个提交的作者身份:它只是从两个分支提示向后工作。
有几种合并策略。 Git.
中默认使用 3-way merge 算法 recurse
三向算法使用最后一次共同提交。
例如:
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
在上面table中,更改是对齐的。提交 C(祖先提交)中的行是我们的参考。 git 比较参考线的邻居。在示例中,我们有 4 个插槽:
- 在 a 行上方:提交 e 添加新行 d
- 在 a 行下方:提交 f 添加新行 e
- 在 b 行下方:提交 e 添加新行 f
- 在 c 行下方:提交 g 添加新行 g
如您所见,只有一个分支(commit E,commit F)可以添加新内容,或者两个分支都可以添加相同的内容。否则,发生合并冲突。
git如何比较两个文件。哪些算法用于比较两个文件?合并时是否逐行比较?
我不确定合并时两个文件的比较是否会产生冲突。
它使用delta compression。我们要明白,当我们add
一个文件get时,我们创建了一个对象,它的sha sum被计算并记录在索引中。 git 所做的是,通过 git-repack
,它将压缩对象(使用增量压缩进行压缩)打包(文件)。当您进行提交时,git 正在获取未压缩的对象并使用一些内部规则,它正在创建一个包含对象之间差异和相似之处的文件。此包的创建使用增量压缩。
您要问的就是这种增量压缩,也就是增量差分。我想这个算法如何工作的范围超出了这个问题,所以这里有一些参考资料可以帮助你。
理解 git merge
的关键是 Git 不会比较 两个 事物。 Git 比较 三 事物。
Git 不能直接比较所有三个。它必须一次比较它们两个。其中两件事是文件的两个分支提示版本(或分支提示提交;我稍后会详细讨论),但是 Git 不会将那些 相互比较。这是第三个进来的地方:第三个文件是文件的 merge base 版本。
请记住,合并的目标是合并更改。但是 Git 不存储更改。 Git 存储 快照 。每个提交都会完整且完整地存储每个文件:给定一个提交,Git 得到整个 README.md
,整个 main.py
,无论其他文件在这个特定的提交中,这就是提交中的版本.
要从快照中获取更改,我们需要两个 个快照:旧快照和新快照。然后我们玩Spot the Difference的游戏。对于 Git,即 git diff
:您将旧提交的哈希 ID 和新提交的哈希 ID 赋予它,它会为两者之间更改的每个文件创建差异。 git diff
的输出是一系列指令:删除这些行,添加这些其他行。如果拍摄原始快照并应用说明,您将获得新快照。
不过,当我们合并时,我们想要将(比如说)爱丽丝完成的工作与合并鲍勃所做的工作。那么 Git 所做的是:
- 找到最好的 共享 提交,Alice 和 Bob 都开始了。
- 将 shared commit 的文件与 Alice 的文件进行比较。这就是 爱丽丝改变的东西。
- 将 shared 提交的文件与 Bob 的文件进行比较。这是 Bob 所做的更改。
我们将共享提交(Alice 和 Bob 开始的那个)称为 合并基础。这是合并的第三个输入。 Git 使用存储库中的历史记录(提交)自动查找此合并基础提交。这意味着您需要拥有 Alice 的 和 Bob 的提交,以及导致这两个分支提示的所有提交,以便您也有共同的起点提交。
请记住,每次提交及其快照都会记录一些有关快照的信息:例如,创建者的姓名和电子邮件地址。 他们成功时有一个 date-and-time-stamp,还有一条日志消息,他们可以用来解释 为什么 他们成功了。它还存储其直接 parent 提交的原始哈希 ID:他们使用的提交,通过 git checkout
,从他们 他们的 [=144] 开始=] 提交。这些父哈希 ID 形成一个 backwards-looking 链:如果 Alice 和 Bob 都从提交 H
开始,并且 Alice 进行了两次提交 I
和 J
并且 Bob 进行了两次提交 K
和 L
,向后链看起来像这样:
I <-J <-- (Alice's latest)
/
... <-F <-G <-H
\
K <-L <-- (Bob's latest)
Git会自动找到H
,Alice和Bob都是从这里开始的。1
找到 H
、Git 现在,实际上运行这两个 git diff
命令:
git差异--find-renames<em>hash-of-H</em><em>hash-of-J</em>
: 爱丽丝改变了什么git差异--find-renames<em>hash-of-H</em><em>hash-of-L</em>
:鲍勃改变了什么
合并过程现在合并了这些更改。对于 H
中的每个文件:
- 爱丽丝是否更改了文件? Bob 是否更改了文件?
- 如果文件都没有更改,则使用文件的任何副本:三个都相同。
- 如果 Alice 更改了文件而 Bob 没有,则使用 Alice 的版本。
- 如果 Bob 更改了文件而 Alice 没有,则使用 Bob 的版本。
- 如果双方都更改了文件,合并他们的更改。这是合并冲突 可能 发生的地方。
Does [Git] compare line by line while merging?
这个问题的答案既不是也不是。正如您现在看到的,Alice 的版本与 Bob 的版本没有比较。 是一个比较——有点像line-by-line; git diff
所做的任何比较——base 版本与爱丽丝的版本,base 版本与鲍勃的。整个过程通过对两对 提交 进行完整的 commit-wide 比较开始。在那个 commit-wide 比较中,发现 Alice 和 Bob 都更改了 一些特定文件 , 现在 line-by-line,或者真的 diff-hunk-by-diff-hunk,比较很重要。但它们来自 third 版本。
I don't want to check each time manually using "git diff".
您不必这样做。如果你想要,你可以,但是要做到这一点,你需要找到merge-base提交,也许使用git merge-base
。但如果你不想,那么......不要吨。 Git 会找到 merge-base 提交; Git 将执行两个单独的 git diff
操作; Git 将合并 Alice 的更改和 Bob 的更改,如果更改的行重叠——或者在某些情况下,abut,或者如果两者跨越文件结尾。
(对于 Git,如果 Alice 和 Bob 都对 exactly 相同的行进行了 exactly 相同的更改,Git 只复制一份更改。其他 VCS 可能会在这里声明冲突,要么是出于懒惰——他们不检查更改是否相同,只是它们重叠了——要么是偏执狂:如果两者更改相同行,也许正确的结果是 而不是 只是为了使用一份更改。Git 只是说 "the correct result is one copy of the change"。)
在任何情况下,Git 将 combined 更改应用于文件的 merge base 版本。这就是结果,可能存在合并冲突(并在文件的 work-tree 副本中合并冲突标记)。
最后,注意两个git diff
命令中的--find-renames
。 Git 将尝试判断 Alice and/or Bob 是否重命名了 merge-base 提交中的任何文件。如果是这样,Git 将尝试在最终结果中保留重命名。无论是 Alice 还是 Bob 进行了重命名,都是如此。如果 Alice 和 Bob 都重命名了文件,Git 不知道要使用哪个最终名称,并声明一个 rename/rename 冲突。如果 Alice 或 Bob 删除 文件而另一个人修改它,则会出现类似的问题,如果 Alice 和 Bob 添加 new 同名文件。这些类型的冲突就是我所说的 高级 冲突:它们影响整个文件(and/or 它们的名称)而不是 中的个别行一份文件。如果您使用 -Xours
或 -Xtheirs
选项,low-level 冲突(文件中的行)和 high-level 之间的差异很重要。
1即使爱丽丝只做了 一个 提交,比方说 J
,在 Carol 的一个提交之上I
是 Carol 在 H
上创造的。共同的起点仍然是H
。 Git 甚至不查看每个提交的作者身份:它只是从两个分支提示向后工作。
有几种合并策略。 Git.
中默认使用 3-way merge 算法 recurse三向算法使用最后一次共同提交。
例如:
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
在上面table中,更改是对齐的。提交 C(祖先提交)中的行是我们的参考。 git 比较参考线的邻居。在示例中,我们有 4 个插槽:
- 在 a 行上方:提交 e 添加新行 d
- 在 a 行下方:提交 f 添加新行 e
- 在 b 行下方:提交 e 添加新行 f
- 在 c 行下方:提交 g 添加新行 g
如您所见,只有一个分支(commit E,commit F)可以添加新内容,或者两个分支都可以添加相同的内容。否则,发生合并冲突。