git cherry-pick 实际上与 git show + git apply 一样吗?

Is git cherry-pick actually the same as git show + git apply?

难道是

git cherry-pick <rev>

实际上与

完全一样
git show <rev> | git apply -3 -

???

因为 运行 中的任何一个都会给我带来完全相同的结果,如果我知道它们确实是等价的,我就能更好地理解 GIT 的挑选实际上是如何工作的,因为它并不总能给我预期的结果,但这可能是因为无法应用唯一的更改并且樱桃采摘退回到三向合并(这就是 -3 在第二个上所做的命令),这将解释意外的结果。

从技术上讲,git cherry-pick 的工作方式类似于 git 合并,而不是寻找 "best revision that is present on both branches" 来检查差异和 merge 他们,合并逻辑的 "common revision" 被迫成为您正在挑选的修订的父级。

除了 , which is right and I've upvoted it, there are a bunch of other niggling details, including 关于二进制文件(git show 需要 --binary 来生成二进制补丁)并且你可能还需要 --full-index,在一些罕见的情况下缩写的 Index: 行是不够的。1

当然,git apply(带或不带 -3)不介意脏索引和工作树,而 git cherry-pick 需要干净的索引和工作树,除非您添加 -n。而且,如果您 省略 -ngit cherry-pick 继续进行提交,而 git apply 永远不会。

综上所述,是的,这两个在正常情况下在功能上应该是等价的。


1要实现这一点,blob 哈希在缩写时必须是非唯一的。这在 Git 版本 1.7.2 中已自动更正,但如果您使用 core.abbrev 或命令行标志强制使用特定的缩写长​​度,您仍然可以获得非唯一的哈希缩写。添加 --full-index 可确保您获得唯一的。

(如果你要长期保存一个差异,添加一堆 new 对象到存储库,然后尝试 git apply -3 差异,以前 明确的缩写 Index: 行现在可能变得不明确。)

注意:对于二进制文件,git apply 无论如何都不起作用,在 Git 2.34(2021 年第 4 季度)之前不会:“git apply"(man) 错误地计算了字节和未能读到二进制 hunks 的末尾。

参见 commit 46d723c (09 Aug 2021) by Jeff King (peff)
(由 Junio C Hamano -- gitster -- in commit 7e3b9d1 合并,2021 年 8 月 30 日)

apply: keep buffer/size pair in sync when parsing binary hunks

Reported-by: Xingman Chen
Signed-off-by: Jeff King

We parse through binary hunks by looping through the buffer with code like:

llen = linelen(buffer, size);

...do something with the line...

buffer += llen;
size -= llen;

However, before we enter the loop, there is one call that increments "buffer" but forgets to decrement "size".
As a result, our "size" is off by the length of that line, and subsequent calls to linelen() may look past the end of the buffer for a newline.

The fix is easy: we just need to decrement size as we do elsewhere.

This bug goes all the way back to 0660626 ("binary diff: further updates.", 2006-05-05, Git v1.4.1-rc1 -- merge).
Presumably nobody noticed because it only triggers if the patch is corrupted, and even then we are often "saved" by luck.
We use a strbuf to store the incoming patch, so we overallocate there, plus we add a 16-byte run of NULs as slop for memory comparisons.
So if this happened accidentally, the common case is that we'd just read a few uninitialized bytes from the end of the strbuf before producing the expected "this patch is corrupted" error complaint.

However, it is possible to carefully construct a case which reads off the end of the buffer.
The included test does so.
It will pass both before and after this patch when run normally, but using a tool like ASan shows that we get an out-of-bounds read before this patch, but not after.