`git log -p`:显示差异或生成补丁?

`git log -p`: showing diff or generating patch?

在阅读git的文档时,我似乎发现了一个矛盾。

this official tutorial of git, git log -p is said to show the history of commits together with complete diff info. However, in the documentation of git-log中,-p选项据说是产生一个补丁文件,而不是直接输出。此外,描述 "they do not produce the output described above" 令人困惑,因为 "described above" 非常模糊,至少对我而言。

除了上面给出的部分,我只发现one other place提到了-p选项,这与教程中的描述相符,而不是补丁部分。另外,当我在我的计算机上 运行 git log -p 时,它会显示提交历史以及差异信息,但我没有看到生成任何补丁文件。那么文档的这两部分是否矛盾?还是我误解了"generating patch files"的过程?谢谢!

git log -p 生成每次提交与其上一次提交的差异。差异以补丁文件格式显示。

但默认不生成真实文件,默认输出内容到stdout。为简单起见,它在您的终端中输出内容。

您可以像这样通过重定向将输出写入真实文件,

git log -p > ./test.txt

有很多 Git 文档是……次优的,我们应该说。

重要的是要认识到每个 Git 提交都会保存一个快照,而不是更改,因为这解释了 Git 在几个更棘手的情况下的行为。各种 Git 命令——包括 git diffgit log——然后可以提取 两个 快照并进行比较。将旧快照与较新快照进行比较的结果——或 "left side" 与 "right side" 的比较结果,因为你可以反转它并将较新快照与较旧快照进行比较——是 diff补丁.

准备此类差异/补丁的默认方法是生成一系列指令,如果严格遵守这些指令,会将每个 left-side 文件转换为相应的 right-side 文件。这些说明的一般形式为:期望此特定上下文在左侧和右侧文件中都可见,然后从 left-side 文件中删除任何 - 行并添加任何right-side 文件中的 + 行。 如果 left-side 文件来自(单数)parent一些提交,right-side 文件来自提交本身,它告诉你有人在该文件中更改了什么。

毫无疑问,您已经看到了这个输出,它甚至可能有些道理。

但是,您正在阅读的文档是从多个输入片段和 git log description to which you linked was written to be read after this other description of the default output of git diff-tree 自动编译的,其中包括以下特定文本:

in-place edit  :100644 100644 bcd1234 0123456 M file0
copy-edit      :100644 100644 abcd123 1234567 C68 file1 file2
rename-edit    :100644 100644 abcd123 1234567 R86 file1 file3
create         :000000 100644 0000000 1234567 A file4
delete         :100644 000000 1234567 0000000 D file5
unmerged       :000000 000000 0000000 0000000 U file6

当然,git log -p 根本不会生成该输出 — 因此 git log 文档不会 包含 这一部分。但是 git log -p 确实 产生与 git diff-tree -p 相同的输出。当 git diff-tree -p 文档的 later 部分使用短语 "do[es] not produce the output described above" 时,它在谈论 :100644 ... 东西。

Tell me lies, tell me sweet little lies

回到 git log -p

的说法

show[s] the history of commits together with complete diff info

——好吧,这也是错误的。这里的问题是 完整 信息对于 git log -p 来说太复杂了。具体来说,merge 提交被定义为具有 两个或更多 parent 提交.

的任何提交

每次提交都会保存所有文件的快照。但是每个提交 记录了一些 parent 或前身,提交哈希 ID。大多数提交只有一个 parent。在这种特殊且非常常见的情况下,git log 可以 运行 git diff 左侧有(单数)parent,提交(也是单数)在左侧右边。这样你就可以看到 parent 和 child 之间发生了什么变化:那个提交的作者在那个提交中改变了什么。

但是有些提交有 两个 parents。这些提交称为 merge 提交; git merge 命令倾向于构建它们。 (我们不能说它 总是 构建它们,因为 - 正如 Git 命令非常常见 - git merge 实际上可以执行几个不同的任务之一,具体取决于情况和一些 command-line 参数。)鉴于这种合并提交,git log 不只是选择 一个 parent,然后向您展示parent 的快照和提交的快照之间的差异。它不会选择两个 parent 并比较它们——这通常不是很明智,也不会告诉你任何关于合并结果的信息——它甚至不会 尝试 进行比较所有三个同时提交,至少不是默认情况下。

相反,git log 对 two-parent(或超过 two-parent)merge 提交的作用是向您展示记录消息,然后 根本不用费心去显示差异。 这实际上是大多数情况下最实用的事情,这就是为什么 git log 会这样做。但这立即告诉我们,我们绝对没有得到全貌!

可以从合并提交中获取差异

请注意,有一个很好的简单线性提交链:

A <-B <-C ... <-F <-G <-H   <--master

git log 所做的是从 last 提交开始——它有一些哈希 ID,但在这里我只是将其称为 H - 并向您展示其作者身份和日志消息,然后提取两张快照,一张来自 parent G,一张来自 H 本身,然后对它们进行比较。然后它继续——或向后——提交 G。现在它向您显示 G 的作者和日志消息,然后提取 FG 的 parent)和 G 的快照并比较它们。如此重复,Git 向后移动,commit-by-commit,从 child 提交到 parent。它就在 merges 那里 git log 根本不打扰 diff-ing。

git show 命令与 git log 非常相似:它主要做 git log 做的事情,但只针对 一个 提交。也就是说,如果您给 git show 提交 G 的哈希 ID,它将向您显示 G 的作者信息、它的日志消息,以及与 F 的差异到 G,但随后就停在那里——它不会继续显示 F。但是,如果您将 git show 指向 merge 提交,它 显示差异,至少有时是这样。它显示的是 combined diff,在这些手册页中对其进行了进一步描述。重要的是要注意组合 diff still 故意遗漏了一些东西。特别是,请密切注意文档的(单独的)部分,其中提到:

combined diff lists only files which were modified from all parents.

这实际上是 有用的。有时, 很有帮助。不过,文档并不是很清楚。在这种情况下,不清楚为什么 git log 什么都不显示,而 git show 产生组合差异。

这里发生的事情是 git loggit show 以及各种其他命令 可以 做这种特殊的 combined-diff 事情.但默认情况下,git log 不会打扰。你可以给 git log 一个 -c--cc 标志——注意第一个是 "one dash, one c" 第二个是 "two dashes, two c's"——使 git log为合并生成组合差异。 git show 命令 默认 --cc 行为。

最后,请注意,您可以改为给 git loggit show 一个 -m 标志。在这种情况下,命令将更加特殊地处理合并:对于合并提交 C 有两个 parents P1P2,这两个命令实际上是 运行:

git 差异 <em>P1 C</em>
git差异<em>P2 C</em>

在向您显示通常的 header 信息(作者和日志消息)之后。

在所有情况下,除非您使用 --graph,否则 git log 不会 为您提供足够的信息来重现实际的提交图——这对于理解 git merge。但那是另一天...

https://github.com/j6t hos noticed this post, and proposes:

A poster on Whosebug was confused that the documentation of git-log promised to generate "patches" or "patch files" with -p, but there were none to be found.
Rewrite the corresponding paragraph to talk about "patch text" to avoid the confusion.

Shorten the language to say "X does Y" in place of "X does not Z, but Y".

可以读作:

Running git diff without the --raw option, or git log, git diff-index, git-diff-tree, git diff-files with the -p option produces patch text instead of the usual output.
You can customize the creation of patch text via the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables.


这将 Git 2.24(2019 年第 4 季度)翻译成此文档修复:

参见 commit 0eb7c37 (15 Sep 2019), and commit 6fae6bd (16 Sep 2019) by Johannes Sixt (j6t)
(由 Junio C Hamano -- gitster -- in commit 980351d 合并,2019 年 10 月 7 日)

diff, log doc: say "patch text" instead of "patches"

diff,日志文档:说 "patch text" 而不是 "patches"

A poster on Whosebug was confused that the documentation of git-log promised to generate "patches" or "patch files" with -p, but there were none to be found. Rewrite the corresponding paragraph to talk about "patch text" to avoid the confusion.

Shorten the language to say "X does Y" in place of "X does not Z, but Y".

Cross-reference the referred-to commands like the rest of the file does.

Enumerate git-show because it includes the description as well.

Mention porcelain commands before plumbing commands because I guess that the paragraph is read more frequently in their context.


git generate-patch documentation 现在是:

git log (...) with the -p option produces patch text.