Git 为什么一系列提交的文件列表与同一范围内每次提交的文件列表的聚合不同?

Git 为什么一系列提交的文件列表与同一范围内每次提交的文件列表的聚合不同?,git,Git,请注意: C:\xyz\tip [master ≡]> git log --format="%h" b00bf1df0..81317ea59 81317ea59 b7d9617fc C:\xyz\tip [master ≡]> $files1 = git diff-tree --no-commit-id --name-only -r b7d9617fc C:\xyz\tip [master ≡]> $files2 = git diff-tree --no-commit-id -

请注意:

C:\xyz\tip [master ≡]> git log --format="%h" b00bf1df0..81317ea59
81317ea59
b7d9617fc
C:\xyz\tip [master ≡]> $files1 = git diff-tree --no-commit-id --name-only -r b7d9617fc
C:\xyz\tip [master ≡]> $files2 = git diff-tree --no-commit-id --name-only -r 81317ea59
C:\xyz\tip [master ≡]> $files3 = git diff-tree --no-commit-id --name-only -r b00bf1df0..81317ea59
C:\xyz\tip [master ≡]> $files1.Length
17
C:\xyz\tip [master ≡]> $files2.Length
0
C:\xyz\tip [master ≡]> $files3.Length
43
C:\xyz\tip [master ≡]>
因此,提交范围
b00bf1df0..81317ea59
只返回两次提交-
81317ea59
b7d9617fc

接下来,我将分别获取这两个提交中的文件列表,然后将整个范围作为一个文件

  • 范围(
    b7d9617fc
    )中的第一次提交产生17个文件(
    files1.Length
  • 范围(
    81317ea59
    )中的第二次提交产生0个文件(
    files2.Length
    ),这是正常的,因为它是一次合并提交
  • 但是,当我在提交范围内获取产生相同2次提交的文件时,即
    b00bf1df0..81317ea59
    它突然变成43个文件(
    files3.Length
    )。请注意,
    files1
    中的所有文件都包含在
    files3
    中,但在
    files3
    中还有很多
  • 发生了什么事?

    TL;博士 由于commit
    81317ea59
    是一个合并,
    git diff tree 81317ea59
    (带有附加选项)确实不显示任何文件,因为它什么也不做,但是
    git diff tree b00bf1df0..81317ea59
    意味着
    git diff tree b00bf1df0 81317ea59
    ,它比较通过以下方式找到的树:

    git rev-parse b00bf1df0^{tree}
    
    连同以下各项:

    git rev-parse 81317ea59^{tree}
    
    这正好是两个特定的树,所以Git比较了它们

    b00bf1df0..81317ea59
    这样的参数意味着对某些Git命令的提交范围(比如
    Git log
    ),但绝不意味着对任何
    Git diff
    命令的提交范围,因为Git的diff不能处理范围

    运行
    git-diff-tree-m-r 81317ea59
    或(我认为更好)git-show-m 81317ea59会很有帮助。
    -m
    标志使Git分别显示从合并到其每个父级的实际差异。在这里省略
    --no commit id
    选项很重要,这样在使用
    git diff tree
    时,您就可以看到从一个父级到下一个父级的转换

    长的 这些差异并不像你想象的那样

    (我不确定您使用的是什么shell,因此我将在不使用变量赋值的情况下表示这些shell。)

    设置 让我们从一开始,关于参数,有这样的说法:

    git diff树[
    --stdin
    ][
    -m
    ][
    -s
    ][
    -v
    ][
    --无提交id
    ][
    --pretty
    ] [
    -t
    ][
    -r
    ][
    -c
    |
    --cc
    ][
    --root
    ][
    ]
    [
    ][
    ..
    ]

    前两次运行的变量只有一个
    参数,因此我们继续阅读以找到:

    描述 比较通过两个树对象找到的blob的内容和模式

    如果只给定一个,则将提交与其父级进行比较(请参见下面的--stdin)

    请注意,git diff tree可以使用封装在提交对象中的树

    --name only
    -r
    选项在后面描述为将输出限制为发现不同的文件名,并递归到子树中。文档未能解释极其重要的事实,即
    b00bf1df0..81317ea59
    被解释为您编写了
    b00bf1df0 81317ea59
    ,即这是
    git diff tree
    的两棵树形式

    文档确实很糟糕地解释了第三项,这也是至关重要的,但却深藏在手册页面中。让我们来看一下最后两个选项:

    -c

    此标志更改了合并提交的显示方式(这意味着只有在命令被给定或
    --stdin
    时,此标志才有用)。它同时显示每个父级与合并结果之间的差异,而不是一次显示一个父级与一个结果之间的成对差异(这是
    -m
    选项所做的)。此外,它只列出从所有父级修改的文件

    --cc

    此标志更改合并提交修补程序的显示方式,类似于
    -c
    选项。它暗示了
    -c
    -p
    选项,并通过省略父对象中内容只有两个变体且合并结果不经修改地拾取其中一个的无趣大块来进一步压缩修补程序输出。当所有的大块头都不感兴趣时,提交本身和提交日志消息不会显示,就像在任何其他“空差异”情况下一样

    这没有提到在没有选项的情况下,当给定一个实际上是提交ID的
    树ish
    参数时,查看合并提交的默认方法是完全忽略提交。这是
    git-log
    git-diff-tree
    的默认差异行为,尽管
    git-log
    至少会首先打印日志消息

    (这是“遇到合并提交时不执行任何操作”对于
    git diff--raw
    ,行为也是如此,但对于普通的
    git diff
    ,或者
    git show
    ;这些默认为
    -c
    样式组合的diff。从文档来看,
    git diff文件
    也默认不显示合并,尽管我不确定在什么情况下
    git diff文件
    w我们甚至可以看到合并,因为它比较了索引和工作树。确实,索引可以取消合并,这与diff处理合并的方式类似:每个文件的三个合并槽提供基本和分支提示版本。可能这就是本文所指的。)

    差异 您提到commit
    81317ea59
    是一个合并(至少有两个父级)。据推测,提交
    b7d9617fc
    不是合并,而
    b00bf1df0
    是另一个可能或可能的提交
    git diff-tree --no-commit-id --name-only -r b7d9617fc
    
    git diff --name-only b7d9617fc^ b7d9617fc
    
    git diff-tree --no-commit-id --name-only -r 81317ea59
    
    git diff-tree --no-commit-id --name-only -r b00bf1df0..81317ea59
    
    git diff --name-only b00bf1df0 81317ea59