首页 > 解决方案 > git在合并时如何比较两个文件?

问题描述

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

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

标签: gitgit-merge

解决方案


理解的关键git merge是 Git 不会比较件事。Git 比较了三件事。

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

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

要从快照中获取更改,我们需要两个快照:旧的和新的。然后我们玩找不同的游戏。对于 Git,那就是git diff:你给它旧提交的哈希 ID 和新提交的哈希 ID,它会为两者之间更改的每个文件创建一个差异。的输出git diff是一系列指令:删除这些行,添加这些其他行。如果您拍摄原始快照并应用说明,您将获得新快照。

但是,当我们合并时,我们希望将(比如说)Alice所做的工作与 Bob 所做的工作结合起来。所以 Git 所做的是:

  • 找到 Alice 和 Bob 都开始使用的最佳共享提交。
  • 共享提交的文件与 Alice 的文件进行比较。这就是爱丽丝所改变的。
  • 共享提交的文件与 Bob 的文件进行比较。这就是鲍勃改变的地方。

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

请记住,每个提交及其快照都会记录有关快照的一些信息:例如,创建者的姓名和电子邮件地址。他们制作的时间有一个日期和时间戳还有一条日志消息可以用来解释他们制作它的原因。它还存储其直接提交的原始哈希 ID:他们使用的提交, via git checkout,从他们提交之前开始。这些父哈希 ID 形成一个向后看的链:如果 Alice 和 Bob 都从 commit 开始H,Alice 做了两次提交IJBob 做了两次提交Kand L,那么向后的链看起来像这样:

                I <-J   <-- (Alice's latest)
               /
... <-F <-G <-H
               \
                K <-L   <-- (Bob's latest)

Git 会自动找到HAlice 和 Bob 的起点。1

找到H后,Git 现在实际上运行了这两个git diff命令:

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

合并过程现在结合了这些更改。对于每个文件H

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

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

这个问题的答案既不是也不是。如您现在所见,Alice 的版本与 Bob 的版本没有可比性。有一个比较——逐行比较;基本版本与Alicegit diff的比较,基本版本与 Bob 的比较有相同的比较。整个过程通过对两对提交进行完整的提交范围比较开始。在提交范围的比较中,发现 Alice 和 Bob 都更改了一些特定的文件现在逐行或真正的 diff-hunk-by-diff-hunk 比较很重要。但它们来自第三个版本。

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

你不必。如果你愿意,你可以,但要做到这一点,你需要找到合并基础提交,使用git merge-base也许。但如果你不想,那么……不要。 Git会找到基于合并的提交;Git会做这两个独立的git diff操作;Git会将 Alice 的更改与 Bob 的更改结合起来,如果更改的行重叠,或者在某些情况下,abut或两者都跨越到文件末尾,则声明冲突。

(对于 Git,如果 Alice 和 Bob 都对完全相同的行进行了完全相同的更改,Git 只会复制一份更改。其他 VCS 可能会在这里声明冲突,或者出于懒惰——他们不会检查更改是否是一样的,只是它们重叠——或者妄想症:如果两者都更改了相同的行,那么正确的结果可能不仅仅是使用更改的一个副本。Git 只是说“正确的结果是更改的一个副本”。)

在任何情况下,Git 都会将合并后的更改应用于文件合并基础版本。这就是结果,可能存在合并冲突(以及文件的工作树副本内的合并冲突标记)。

最后,注意--find-renames两个git diff命令中的 。Git 将尝试判断 Alice 和/或 Bob 是否重命名了合并基础提交中的任何文件。如果是这样,Git 将尝试在最终结果中保留重命名。无论是 Alice 还是 Bob 进行了重命名,这都是正确的。如果 AliceBob 都重命名了文件,Git 不知道要使用哪个最终名称,并声明重命名/重命名冲突。如果 Alice 或 Bob删除文件而另一个人修改文件,则会出现类似的问题,如果 Alice 和 Bob 添加一个具有相同名称的文件,则会发生最后一个冲突。这种冲突我称之为高层冲突:它们影响整个文件(和/或它们的名称),而不是文件中的单个。如果以及何时使用-Xoursor-Xtheirs选项,低级别冲突(文件中的行)和高级别冲突之间的差异很重要。


1即使 Alice 只进行了一次提交,例如J,在 Carol 的一次提交之上(比如)Carol 所做的I一次提交,这仍然有效H。共同的出发点仍然是H。Git 甚至不查看每个提交的作者身份:它只是从两个分支提示向后工作。


推荐阅读