TL; DR:在您运行git mergetool
之前无法运行,并且它在中间停止并出现合并冲突。git merge
长
合并操作涉及三个而不是两个提交,并且通常比仅涉及两个提交的 diff 操作复杂得多。实际上,差异是无状态的:它获取您拥有的内容并比较事物,并向您显示比较的结果。因此,您可以运行git diff
或git difftool
随时运行。如您所见,您所做的更改git difftool
通常不会影响文件:diff 正在对每个文件的副本进行操作,并且在操作完成后副本消失。
合并不是无状态的。合并有一个独特的开始阶段,你和 Git 合作找到三个感兴趣的提交。接下来是一个扩展的中间部分,其中 Git 计算两个差异——一个从合并基础到您当前的提交,一个从合并基础到另一个提交——并尝试组合这两个差异。Git 有时可以自己完成这个阶段。有时,它不能,而且必须停下来寻求帮助。即使 Git可以自行完成此操作,您也可以指示 Git 停止,就好像它无法自行完成合并一样。
无论如何,一旦第二阶段完成,合并就会进入第三阶段,也是最后阶段。这涉及进行新的提交。新提交几乎与任何普通(非合并)提交完全相同,但有一个关键区别:新提交有两个父级,而不是通常的单亲级。
我认为,查看合并如何工作的说明很有帮助。想象一下每个提交都由某个哈希 ID 标识(实际上就是这种情况),每个提交都链接回之前的提交(实际上也是这种情况)。您的两个分支都来自一些常见的提交,如下所示:
o--o--...--T <-- alpha_critical
/
...--o--B
\
o--...--o--H <-- gamma_mine
每轮o
代表一些提交,其中包含每个文件的一些快照。较新的提交出现在右侧。常见的共享提交是标记为B
(用于 Base)的提交。由于此提交在两个分支上,并且只是一次提交,因此这里的每个文件在两个分支中都是相同的。因此,如果我们将 commitB
与 commit T
( --theirs
) 进行比较,无论有什么不同——无论 diff 会显示什么——都必须是他们所做的更改。如果我们将 commitB
与 commit H
(--ours
或HEAD
) 进行比较,那么任何不同之处都必须是您所做的更改。
Git 的工作就是将这些差异结合起来。在 Git 可以自行执行此操作的范围内,它会将组合的差异应用于来自 commit 的快照B
。如果不能,它将记录合并冲突。
该git mergetool
命令不会启动或执行合并。相反,它从 Git 放弃合并冲突的那一点开始。
假设某个名为的文件main.py
出现在提交B
(base)、H
(yours) 和T
(theirs) 中,但它在 yours 和 theirs 中都发生了更改,其中一项更改是更改了使用消息。不幸的是,他们更改使用消息的方式与您更改使用消息的方式不同。因此,Git 无法将您的更改和他们的更改结合起来。
如果您要查看main.py
工作树中的副本,该副本中将包含冲突标记,显示您的更改(来自H
)及其更改(来自T
)。如果您设置merge.conflictStyle
为diff3
,该文件还将显示该文件版本中冲突行的样子B
。
要做git mergetool
的是找出文件的三个输入版本:来自 commitB
的一个、来自 commit 的一个H
和来自 commit 的一个T
。(这些不在工作树中,但很容易用于git mergetool
。)然后它将在这三个文件以及工作树副本上调用您选择的合并工具。您的工作是更新工作树副本,使其包含正确的合并分辨率,无论是什么。
mergetool 命令对每个冲突的文件重复此操作。退出合并工具后,它会将文件标记为已正确解析(尽管存在各种安全检查,因为并非每个工具都正确报告此信息)。当没有剩余的冲突文件时,mergetool 命令停止。它只能在存在冲突文件时使用。
因此,要进行合并,您必须:
- run
git merge
,--no-commit
如果你想检查结果,即使 Git 认为 Git 做对了;
- 完成 Git 留下的任何有冲突的合并,并将它们标记为已解决:您可以手动执行此操作,也可以使用
git mergetool
; 和
- 如果合并在第 1 步后停止,请使用
git merge --continue
(或git commit
) 告诉 Git 完成合并。您还需要提供合并提交消息,就像您提供任何提交消息一样,尽管您可以使用git merge
自行构建的(相对低质量的)默认值(大多数人都这样做)。
如果在第 1 步之后合并没有停止,除非您使用--no-commit
.
合并完成后,您将得到以下结果:
o--o--...--T <-- alpha_critical
/ \
...--o--B M <-- gamma_mine (HEAD)
\ /
o--...--o--H
其中新的合并提交M
将旧的提示提交提交H
作为其第一个父项,并将提交T
(来自他们的分支)作为其第二个父项。您的分支名称现在指向新的合并提交。