Git show 与 Git log --numstat 输出差异

Mik*_*ike 7 git

我将 git show 和 git log 与相同的选项进行比较。我在不同的提交上得到不同的结果。我还没有真正深入研究文档来弄清楚这是为什么,但我猜测这与每个命令如何解释修订列表以及提交图的样子有关?例如,FFmpeg 上的演示:

案例1 git日志:

> git log -n1 --numstat --format='%H' 00049f193d07cec0409069bc51d0dcb8ab9da837
00049f193d07cec0409069bc51d0dcb8ab9da837
Run Code Online (Sandbox Code Playgroud)

案例1 git显示:

> git show -n1 --numstat --format='%H' 00049f193d07cec0409069bc51d0dcb8ab9da837
00049f193d07cec0409069bc51d0dcb8ab9da837

4       0       libavcodec/mpegaudiodecheader.c
Run Code Online (Sandbox Code Playgroud)

案例2 git日志:

> git log -n1 --numstat --format='%H' 001d668d40b5f87d19271c7d5521368b5187425b
001d668d40b5f87d19271c7d5521368b5187425b

2       5       libavformat/dvenc.c
2       6       libavformat/gxfenc.c
5       0       libavformat/internal.h
2       5       libavformat/movenc.c
2       5       libavformat/mxfenc.c
7       0       libavformat/utils.c
Run Code Online (Sandbox Code Playgroud)

案例2 git显示:

> git show -n1 --numstat --format='%H' 001d668d40b5f87d19271c7d5521368b5187425b
001d668d40b5f87d19271c7d5521368b5187425b

2       5       libavformat/dvenc.c
2       6       libavformat/gxfenc.c
5       0       libavformat/internal.h
2       5       libavformat/movenc.c
2       5       libavformat/mxfenc.c
7       0       libavformat/utils.c
Run Code Online (Sandbox Code Playgroud)

基本上,我很困惑为什么对于情况 1,log 省略了更改的文件 ( libavcodec/mpegaudiodecheader.c),而 show 包含它们,然后在情况 2 中,输出是相同的。

以供参考:

> git diff --numstat 00049f193d07cec0409069bc51d0dcb8ab9da837 00049f193d07cec0409069bc51d0dcb8ab9da837^
0       4       libavcodec/mpegaudiodecheader.c
Run Code Online (Sandbox Code Playgroud)

tor*_*rek 6

Joseph K. Strauss的答案是正确的(这是合并提交),但并不完整。缺失的信息分散在 Git 文档和源代码中,这就是为什么它们很难找到。

首先,00049f193d07cec0409069bc51d0dcb8ab9da837 确实是合并提交。它的两个父母是d832020bd853f84b96a3fdf3e0a385d8492ec8c8fcbcc561e0fdc95a7dd48b92db53846726aec27e(我们不需要知道它们的确切数字,但不妨记录它们以显示其“合并性”)。

文档给git show我们一个提示:

任何差异生成命令都可以使用-c或选项在显示合并时--cc生成组合差异。这是显示与git-diff(1)git-show(1)合并时的默认格式。另请注意,您可以-m为这些命令中的任何一个提供选项,以强制与合并的各个父级生成差异。

这里缺少的是-c和本身的描述--cc,在两个链接的手册页中都找不到它,但git diff-tree. 然而,在我们开始之前,值得先查看文档git diff我们在其中找到了以下内容:

“git-diff-tree”、“git-diff-files”和“git-diff --raw”可以采用-c--cc选择为合并提交生成 diff 输出。[截图示例]
请注意,组合差异仅列出所有父级修改的文件。

(我的粗体字,但这确实非常重要;我们稍后会再次看到它,我将再次使用粗体)。现在我们可以跳回到,在那里我们找到和 的git diff-tree实际描述:-c--cc

-c
      此标志更改合并提交的显示方式(这意味着仅当命令给出一棵树状或时它才有用--stdin)。它同时显示每个父项与合并结果的差异,而不是一次显示父项与结果之间的成对差异(这就是该选项的-m作用)。此外,它仅列出所有父级修改的文件

--cc
      此标志更改合并提交补丁的显示方式,与该-c选项类似。它暗示了-c-p选项,并通过省略无趣的块来进一步压缩补丁输出,这些块的父项中的内容只有两个变体,并且合并结果选择其中之一而不进行修改。当所有块都无趣时,不会显示提交本身和提交日志消息,就像任何其他“空差异”情况一样。

请注意,这告诉我们这--cc是默认值,git show但没有提及git log。事实证明,git log默认情况下只是完全抑制合并差异输出,而git showset --cc. 前者似乎没有在任何地方记录,但可以在 Git 源代码中找到,在builtin/log.c和中revision.c

[revision.c]
void init_revisions(struct rev_info *revs, const char *prefix)
{
        memset(revs, 0, sizeof(*revs));

        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
[snip]
Run Code Online (Sandbox Code Playgroud)

revs->ignore_merges = 1这将为所有命令设置默认操作以忽略合并 ( );想要进行合并的命令需要清除该标志(这也在 参考资料中指出Documentation/technical/api-revision-walking.txt)。

git showgit log以及其他几个)都在 中实现builtin/log.c,其中部分包含以下内容:

static void log_setup_revisions_tweak(struct rev_info *rev,
                                      struct setup_revision_opt *opt)
{
        if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) &&
            rev->prune_data.nr == 1)
                DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES);

        /* Turn --cc/-c into -p --cc/-c when -p was not given */
        if (!rev->diffopt.output_format && rev->combine_merges)
                rev->diffopt.output_format = DIFF_FORMAT_PATCH;

        /* Turn -m on when --cc/-c was given */
        if (rev->combine_merges)
                rev->ignore_merges = 0;
}
Run Code Online (Sandbox Code Playgroud)

这是组合差异选项(如果选择)可以显示合并的地方。同时,对于git show

static void show_setup_revisions_tweak(struct rev_info *rev,
                                       struct setup_revision_opt *opt)
{
        if (rev->ignore_merges) {
                /* There was no "-m" on the command line */
                rev->ignore_merges = 0;
                if (!rev->first_parent_only && !rev->combine_merges) {
                        /* No "--first-parent", "-c", or "--cc" */
                        rev->combine_merges = 1;
                        rev->dense_combined_merges = 1;
                }
        }
        if (!rev->diffopt.output_format)
                rev->diffopt.output_format = DIFF_FORMAT_PATCH;
}
Run Code Online (Sandbox Code Playgroud)

因此git show检查是否-m已指定。如果没有,它会在内部打开-m,然后打开,--cc除非存在三个显式选项中的任何一个:-c--cc--first-parent。前两个是有道理的(不要覆盖用户的设置),但第三个很奇怪。(也许这是为了避免以后引起问题,例如,如果我们进行组合比较,但只拉入一个父 ID。)

但仍然不太明显的是它们为何不同:

$ git log --no-walk --numstat 00049f193d07cec0409069bc51d0dcb8ab9da837 
[snip output: log message, with no diff-stats]
$ git show --numstat 00049f193d07cec0409069bc51d0dcb8ab9da837
[snip log message]
4       0       libavcodec/mpegaudiodecheader.c
Run Code Online (Sandbox Code Playgroud)

如果我们简单地添加-mgit log以便清除标志rev->ignore_merges),我们就会得到针对父母双方的 numstat 差异。但是,如果我们--cc也添加,我们会得到与以下相同的结果git show

$ git log -m --cc --no-walk --numstat 00049f193d07cec0409069bc51d0dcb8ab9da837
[snip log message]
4       0       libavcodec/mpegaudiodecheader.c
Run Code Online (Sandbox Code Playgroud)

现在稍微思考一下就可以清楚为什么我们只看到一个文件:它是唯一一个来自父母双方都进行了更改的文件。 这与任何组合差异具有相同的约束,并且果然,替换--cc-c(使用相同的git log)会产生相同的结果。

底线,可以说是

如果没有-m-c、 或--ccgit log则打印合并的日志消息,但从不尝试显示合并提交与其父项的差异。如果没有任何这些选项,则git show设置--cc. 这大约有一半记录在案。