git - git在合并时如何比较两个文件?
问题描述
git如何比较两个文件。哪些算法用于比较两个文件?合并时是否逐行比较?
我无法确定合并时两个文件的比较是否会产生冲突。
解决方案
理解的关键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 使用您的存储库中的历史记录(提交)自动找到此合并基础提交。这意味着您需要同时拥有 Alice和Bob 的提交,以及导致这两个分支提示的所有提交,以便您还拥有共同的起点提交。
请记住,每个提交及其快照都会记录有关快照的一些信息:例如,创建者的姓名和电子邮件地址。他们制作的时间有一个日期和时间戳,还有一条日志消息可以用来解释他们制作它的原因。它还存储其直接父提交的原始哈希 ID:他们使用的提交, via git checkout
,从他们提交之前开始。这些父哈希 ID 形成一个向后看的链:如果 Alice 和 Bob 都从 commit 开始H
,Alice 做了两次提交I
,J
Bob 做了两次提交K
and L
,那么向后的链看起来像这样:
I <-J <-- (Alice's latest)
/
... <-F <-G <-H
\
K <-L <-- (Bob's latest)
Git 会自动找到H
Alice 和 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 进行了重命名,这都是正确的。如果 Alice和Bob 都重命名了文件,Git 不知道要使用哪个最终名称,并声明重命名/重命名冲突。如果 Alice 或 Bob删除文件而另一个人修改文件,则会出现类似的问题,如果 Alice 和 Bob 添加一个具有相同名称的新文件,则会发生最后一个冲突。这种冲突我称之为高层冲突:它们影响整个文件(和/或它们的名称),而不是文件中的单个行。如果以及何时使用-Xours
or-Xtheirs
选项,低级别冲突(文件中的行)和高级别冲突之间的差异很重要。
1即使 Alice 只进行了一次提交,例如J
,在 Carol 的一次提交之上(比如)Carol 所做的I
一次提交,这仍然有效H
。共同的出发点仍然是H
。Git 甚至不查看每个提交的作者身份:它只是从两个分支提示向后工作。
推荐阅读
- r - 如何使用 dplyr 检测面板数据中变量随时间的变化?
- firebase - 隐藏 Firebase 托管上的敏感数据
- nginx - Nginx ProxyPassReverse 设置
- python - 使用 py2neo 连接 Neo4j Aura 云数据库
- javascript - 在 div contenteditable 内自由移动图像
- java - 无法从 Spring Cloud Config Server 获取值到我的 Config-Client(limits-service)
- javascript - 如何在加载时检查 JavaScript 时间
- python - csv中的数据,双引号括起来的数据
- python - Python中spdiags的排名问题
- flutter - 如何缓存 Future 返回的值?