现在你在 GitHub 上有了你的测试仓库,我可以克隆它。
这是git log --all --decorate --oneline --graph的输出:
+ * fbe54a9 (HEAD -> master, origin/master, origin/HEAD) adding deleted file
* aaf2163 deleting D file
* 32c853b deleting file
* 836a3f2 adding test file
+ * 9e627fd adding test file
* 4a06e68 adding test file
* 4844174 adding test file
* 41cd5e7 deleting testc file
* 6a92b1a adding 3rd file
* 83fe860 adding testB file
* 145f2ad added 1st file
我标记了(用加号 +)您在示例 git diff-tree -r 和 git diff --diff-filter=D 命令中指定的两个提交哈希 ID,以便我们可以查看和/或特别调查这些提交。
您选择了提交 9e627fd adding test file 作为差异的左侧,并提交了 fbe54a9 adding deleted file 作为提交的右侧。让我们看看这两个提交中实际包含哪些文件:
$ git ls-tree -r 9e627fd
100644 blob 9d8a4e73e4f8897352d20eda060a812dab709fc9 file-A.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testB.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testD.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testE.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testc.txt
$ git ls-tree -r fbe54a9
100644 blob 9d8a4e73e4f8897352d20eda060a812dab709fc9 file-A.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testB.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testD.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testE.txt
100644 blob 5e92c1b25f72158c2378b31d29bc443ee327af81 testF.txt
这些提交确实有一些不同的文件名称。请注意,所有四个 test?.txt 文件都具有相同的文件 content(相同的 blob 哈希 ID)。对于git diff-tree,这并不重要:
$ git diff-tree -r --no-commit-id --name-only 9e627f fbe54a9
testF.txt
testc.txt
这看起来很有希望。让我们使用--name-status 而不是--name-only 来获取每个文件的状态:
$ git diff-tree -r --no-commit-id --name-status 9e627f fbe54a9
A testF.txt
D testc.txt
所以git diff-tree 似乎在这里工作得很好。
现在让我们看看git diff——但在我们添加--diff-filter=D之前,让我们运行它而不使用--diff-filter,然后再次使用--name-status,这样我们就可以看到git diff对这两个特定提交的比较:
$ git diff --name-status 9e627f fbe54a9
R100 testc.txt testF.txt
这里我们解释了为什么--diff-filter=D 什么都不打印:根据this diff,没有删除文件!但是,有一个 重命名 文件,其名称在左侧提交中为 testc.txt,在右侧提交中为 testF.txt。 R100 表示旧testc.txt 的内容与新testF.txt 的内容100% 匹配。
如果我们不想让 Git 检测重命名,我们必须告诉它不要检测重命名。 (此配置在 2.9 之前的 Git 版本中是默认配置,但在 2.9 及更高版本中不是。)因此,如果我们的 Git 版本是 2.9 或更高版本,或者如果我们已将 diff.renames 配置为 true 或以其他方式启用它,我们需要稍微改变一下命令行命令:
$ git diff --diff-filter=D --name-only 9e627f fbe54a9
$ git -c diff.renames=false diff --diff-filter=D --name-only 9e627f fbe54a9
testc.txt
使用默认的 diff.renames=true 设置,git diff 使用重命名检测来声明名为 testc.txt 的文件已重命名为名为 testF.txt 的文件,正如我们在未过滤的 --name-status 输出中看到的那样。因此过滤以仅显示D 状态文件失败。但是对于-c diff.renames=false,git diff 不会尝试重命名检测,并且会像git diff-tree -r 那样行事:它表明文件testc.txt 已被删除。同时,--diff-filter=D 告诉 Git 不要告诉我们新添加的testF.txt。
瓷器与管道
git diff-tree 命令属于 Git 称为 plumbing 的命令类别,而 git diff 属于 Git 称为 porcelain 的类别。这里的主要区别在于它们的使用方式:管道命令旨在以简单且可预测的方式运行,并具有适合其他命令使用的输出。 Porcelain 命令旨在以对每个用户最有用的方式运行,因此它们的输出可能是彩色的,而且——这是这个特定问题的关键——可能取决于用户配置。本例中,用户配置项为diff.renames的设置。 git diff-tree 命令不使用 diff.renames,但 git diff 命令使用,因此其输出会根据您的特定配置(在本例中为 Git 年份)而变化。