作为comments 中的mentioned,git diff 已经在没有其他参数的情况下这样做了。
不过,这里值得指出其他一些事情。特别是,Git 在任何时候都有1三个我称之为每个文件的“活动副本”。假设您在 current 提交中有上面提到的这三个文件,以及名为 README.md 和 LICENSE.md 的文件。因此,当前提交中有五个文件:
README.md
LICENSE.md
f1.c
f2.c
f3.c
这五个文件是“活动的”,因为它们位于当前提交的 in 中,可供您与您现在正在做的事情进行比较,或使用git reset --hard 来例如,将所有内容恢复到这五个文件中的状态。
同时,这五个文件已复制到 Git 的索引中。2您无法看到这些副本,3 但它们存在。事实上,一旦你完成,它们就是将进入你的下一次提交的副本。 git add 命令是关于更新这些副本,这些副本最初与当前提交的副本相匹配。所以 Git 的索引中包含相同的五个文件,直到你开始运行 git add 无论如何:复制新文件,替换以前的副本,或者将全新的文件复制到 Git 的索引中,例如添加第六个文件。
最后,还有五个文件的普通日常副本。它们位于 Git 所称的 working tree 或 work-tree 中。这些是你的,你可以随意处理:当你运行git checkout 时,Git 将它们out 从提交中复制到 Git 的索引中,然后复制到你的工作树中,4 从那时起,您可以对它们做任何您喜欢的事情。如果您更改了它们,并希望这些更新进入您的下一次提交,您需要运行git add,它将更新复制回 Git 的索引,将索引副本更新为匹配您的工作树副本。
当你运行git status时,这个命令:
- 打印出有用的信息,例如您所在的分支名称,然后
- 运行一个比较:你看到的那个
git diff --staged;那么
- 再进行一次比较:您看到的带有
git diff 的比较没有额外选项。
这两个git diffs 是这里感兴趣的:
-
第一个将当前提交中的所有(在本例中为五个)文件(HEAD:README.md、HEAD:LICENSE.md 等)与 Git 索引中的所有文件进行比较。当这两个文件匹配时,git status 什么也没说。当它们以某种方式不同时,git status 会在 staged for commit 类别下打印文件的名称。
-
第二个git diff 将 Git 索引中的所有文件与工作树中的所有同名文件进行比较。无论这两者在哪里相同,Git 什么都不说;它们不同的地方,它说这个文件是not staged for commit。
请注意,所有三个副本都可能不同。检查一些提交,以便所有五个(或任何数量)文件都是相同的。修改其中之一的副本,然后运行 git add。现在 HEAD 副本与其他两个不匹配,但 Git 的索引副本和您的副本匹配。但是现在修改你的副本再次。现在所有三个文件都不同了,只要您的第二次更改不仅仅是将文件改回来。运行git status 现在将显示一个文件为both staged for commit 和 not staged for commit。
运行 git diff --staged5 比较 HEAD 与索引。运行git diff 比较索引与工作树。 git diff 的形式还有很多,例如 git diff <em>commit1 commit2</em>。在思考这个问题时通读the git diff documentation:如果你想比较HEAD 提交和你当前的工作树,你应该运行什么,跳过它们之间的索引?(答案很简单,但是在用脚注 6 作弊之前尝试自己找到它。6)
1有时,Git 的每个文件最多有 5 个 个副本。特别是,在冲突合并期间,Git 将从三个提交中检索副本,并将所有三个提交到它的索引中。很少——例如当你使用git rm 删除文件时——这个数字会降到三个以下,但随后我们会遇到一个形而上学/哲学问题,即“如果森林里没有文件,当你把它砍下来”之类的东西。 ?
2这里有两点值得注意:
- Git 的索引还有两个名称:暂存区和缓存。这三个都指的是同一件事。
- 索引中的内容实际上并不是每个文件的副本,而是模式、名称和 blob-hash 的三元组。这就是 Git 对文件进行预压缩和预去重以进行提交或与某些现有提交共享文件的方式。
不过,这些东西表现得像文件的副本,因此将它们视为副本可能是最简单的。如果您开始使用脚注 3 中提到的命令,您将只能分辨出其中的区别。
3你实际上可以看到它们,有点。运行git ls-files --stage 转储索引内容。请注意,这会为每个暂存文件生成一行输出,包括git status 会默默跳过的文件,因为它们已暂存但未修改。 “暂存中的文件 = 提交中的文件”的情况是 为什么 git status 不谈论该文件。如果您正在处理一个包含数万个文件的大型项目,那么您可能对没有更改的 29997 个文件不太感兴趣;你只关心是的3个文件。运行 git ls-files 很好,它只查看所有暂存文件,但是当它输出 30000 行文件信息时,就没什么用了。
git update-index 命令是一个类似的极低级命令,可让您直接操作文件的索引副本。由于这个确实写在索引上,所以在使用的时候要多加小心,但它确实有一些非常特殊的用途。
4Git 利用其索引中的内容来优化结帐。例如,假设您正在提交a123456,因为您运行了git checkout main 或其他任何东西。然后运行git checkout dev,它选择提交b789abc 进行签出。很有可能在 Git 的索引和工作树中的 30000 个文件中,有 29735 个左右的文件在这两个提交中相同,只剩下 256 个不同 文件. checkout 命令可以将所有未更改的文件保留在 Git 的索引和工作树中,只更新剩余的文件。这要快得多。
对于我们这些习惯于旧版本控制系统的人来说,当我们第一次运行 git checkout 并立即得到提示而不是五分钟的休息时,我们认为 Git 坏了。 ? 这个索引的东西很聪明。不过,当你第一次习惯 Git 时,它确实会带来很长的路要走。
5git diff --cached 和 git diff --staged 做同样的事情:--cached 和 --staged 在这里是同义词。 --cached 标志是指索引的第三个名称,cache。在git rm --cached 中出现了相同的名称。由于某种原因,git rm 中仍然没有 --staged 的同义词。 Git 命令在这里不是很一致,主要是出于历史原因。
6git diff HEAD。 (现在,如果您想比较索引/暂存区域中的内容,与某些特定提交,该怎么办?)