如果我在存在合并冲突的情况下尝试进行“计算差异并应用补丁”的操作,下面我们就看看具体会发生什么情况:
$ git show 10e96e46 --patch > out.patch$ git apply out.patcherror: patch failed: content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown:17error: content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown: patch does not apply
这一过程无法成功完成,它并未提供任何解决冲突或处理问题的方案。
而真正运行 git cherry-pick时的实际情况却大为不同,我遭遇到了一处合并冲突:
$ git cherry-pick 10e96e46error: could not apply 10e96e46... wiphint: After resolving the conflicts, mark them withhint: "git add/rm
因此,看起来 “Git 正在应用一个补丁”这样的理解方式并不十分准确。但这里的错误信息确实标明了 “无法应用10e96e46”,这么看来,这种理解又不完全是错的。这到底是怎么回事呢?
我也参阅了 Git 的源代码,试图理解 git apply的功能。它似乎(不出意外地)在apply.c中实现。这段代码解析了一个补丁文件,并通入目标文件来寻找应该在何处应用补丁。核心逻辑似乎在这里:思路好像是从补丁建议的行数开始,然后向前向后找寻。
/* * There's probably some smart way to do this, but I'll leave * that to the smart and beautiful people. I'm simple and stupid. */ backwards = current; backwards_lno = line; forwards = current; forwards_lno = line; current_lno = line;for (i = 0; ; i++) { ...
这个处理过程不禁让人觉得非常直白、与之前的期望相符。
Git 三路应用的工作方式
git apply命令中也有一个--3way参数,可以实现三路合并。因此,我们实际上可以通过如下方式,使用git apply来大体实现git cherry-pick的功能:
$ git show 10e96e46 --patch > out.patch$ git apply out.patch --3wayApplied patch to 'content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown' with conflicts.U content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown
但要注意,参数 --3way并不只用到了补丁文件的内容!补丁文件开始的部分是:
index d63ade04..65778fc0 100644
d63ade04和65778fc0是旧/新文件版本在 Git 对象数据库中的 ID,因此 Git 可以用这些 ID 来执行三路补丁操作。但如果有人将补丁文件通过邮件发送给你,而你并没有新/旧版本的文件,就无法执行这个操作:如果你缺少 blob,将会出现如下错误:
$ git apply out.patcherror: repository lacks the necessary blob to perform 3-way merge.