需要获取两次 Git 提交之间的所有文件差异(添加、修改、重命名)

Need to get all file differences (added, modified, renamed) between two Git commits

我正在尝试 导出 所有在两次提交之间存在差异的文件,这些差异是:

检测重命名可能很困难,因为我将在 Windows 7 环境中进行导出,因此 somefile.phpSomeFile.php;但我会将它们上传到 *nix 环境,该环境确实将这些文件视为不同的,因此需要尽可能识别和导出它们。

我正在使用以下命令:

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $head_commit_id $older_commit_id | xargs tar -cf project.tar -T -

但是我注意到它没有导出 new/added 文件,也没有导出重命名的文件;然后我发现 git diff-tree 默认情况下不进行重命名检测,所以据我所知,我需要在命令中添加 --find-renames?

为什么不使用这个简单的状态命令:

git diff --name-only SHA1 SHA2
# or

# --name-status will display the name and the status of the files
git diff --name-status SHA1 SHA2

# To display untracked files use the -u
git status -u

并且在 git 中,您应该仅使用 git mv 命令重命名文件。

一样,可以使用"user-friendly"(或瓷器)命令git diff代替git diff-tree,这就是 Git 所谓的 plumbing 命令,用于脚本。但是,您应该了解这意味着什么。

由于瓷器命令是为人类设计的,因此它们试图以人类可读的方式呈现事物。这意味着它们服从某个人在各种配置文件中为 himself/herself 设置的任何设置。这包括 diff.renamesdiff.renameLimit 配置。他们还可以修改输出,使眼球更容易处理,但计算机程序更难处理。最糟糕的是,他们可能 将他们的输出从一个 Git 版本更改 到另一个版本,如果人们似乎更喜欢一些默认设置。

由于脚本不是用于上述目的,它们的行为方式是可预测的,输出不会改变,也不依赖于配置项。这样,无论您要求什么,您都会得到:您将以可靠的形式获得可靠的输出,因此如果您编写自己的可靠代码,它不仅在今天有效,就一个案例而言;它将在未来继续工作,在所有可能的情况下。1

最后,这意味着如果您使用 git diff-tree 并设置正确的标志,您将获得更可靠的输出。如果您使用 git diff,您的重命名检测取决于:

正如您所发现的,重命名检测的输出是 两个 路径名,这不是您可以通过管道传输到归档程序的东西。存档器通常会遇到文件 删除 的问题(这也许是 archivesbackups[=108= 之间的一个经典区别] / snapshots;注意这两个也和版本控制有关)。

如果您的目标是所有文件的一种联合——即,如果差异显示添加了一个名为 A 的文件,删除了一个名为 D 的文件,并且文件 [=18] =] 是通过重命名旧名称 O 创建的(也许还对其进行了修改:注意 Git 的 相似性指数 字母 [=18 之后的数字=]), 然后你希望收集文件 A, 忽略文件 D, 并收集文件 R 而忽略文件 O — 那么, 你 wantnot 首先检测重命名!如果您检测重命名——默认情况下git diff-tree不检测——同样的差异将显示为:添加文件A、删除文件D,删除文件 O,并添加文件 R。因此 git diff-tree 和包含 AM 并排除 Ddiff-filter 就足够了。不太清楚如何处理 T,它用于类型更改:例如,从普通文件到符号 link,或者从文件到子存储库提交哈希(什么 Git 为子模块调用 gitlink 条目。

同样,您不想启用复制检测:C状态,如R,显示相似性指数和 路径名。如果您将其禁用,您只需将新路径名作为 Added 文件。

即使你做了所有这些,你仍然会陷入陷阱。假设提交哈希 C1 有一个名为 problem 的文件,并且(可能稍后)提交哈希 C2 有两个名为 problem/Aproblem/B。这意味着原始文件 problem 在这两点之间被 删除 ,因为大多数系统(包括 Git 本身)禁止同时拥有 文件 名为 problem 和一个名为 problem 目录 包含各种文件。假设每个 tar-archive 本身是 而不是 一个完整的快照——您忽略了 C1 之间未修改的文件]C2提取这些快照的过程必须是附加的:提取较早的快照,然后在较早的快照之上提取较晚的快照。当文件 problem 正在创建目录 problem 时,此过程将 失败 。显然,您可以检查此类问题并删除有问题的文件(您现在可以明白为什么我将文件命名为 problem :-) ),但更一般地说,因为您没有将 "delete" 指令存储在首先,您不会知道,在将来您使用这些存档重建快照的情况下,某些文件根本不属于该快照。

这个问题的经典解决方案是在 update-archives 前加上某种清单或指令。如果您决定使用这样的解决方案,那么,取决于你想要在清单或指令中的详细信息,你可能想做第一遍来检测 exact 重命名 and/or exact 份。)


1显然,新添加的功能会给每个人带来问题,不仅是脚本,也不仅仅是人类,但是 Git 人们确实努力不创造 不必要的 依赖管道命令的脚本的问题。例如,考虑 new impetus to push Git toward using some flavor of SHA-256 instead of, or in addition to, SHA-1。由于 SHA-1 产生 160 位哈希值,而 SHA-256 产生 256 位哈希值,因此它们必须分别表示为 40 位和 64 位十六进制数字。 Linus 建议默认将 256 位哈希值缩写为 40 个字符,以帮助 假定 40 个字符的现有脚本,但我预见到一些问题...:-)