正如其他人所建议的,您可以使用git add -N 将虚拟条目添加到索引中。运行:
git diff <hash>
然后将给定的工作树与当前的工作树进行比较,使用现有的索引(现在有未跟踪文件的条目)来决定将哪些工作树版本与给定的<hash>(<hash> 可能是提交 ID、树 ID、分支名称或 Git 可以解析为树的任何内容)。但这确实弄乱了索引,正如你所说,你:
希望以最大程度的原子性和健壮的方式做到这一点
诀窍是使用 临时 索引。
这里的初始提交有文件bar和foo;第二次提交删除了文件foo。当前工作树有foo 复活和一个新文件,如git status --short 所示。
$ git log --all --decorate --oneline --graph
* 11b241c (HEAD -> master) remove foo
* 8527327 initial
$ git status --short
A diffscript
A foo
注意初始提交是8527327,这是我们作为参数传递给diffscript的:
$ ./diffscript 8527327
diff --git a/diffscript b/diffscript
new file mode 100755
index 0000000..8d5c978
--- /dev/null
+++ b/diffscript
@@ -0,0 +1,6 @@
+#! /bin/sh
+export GIT_INDEX_FILE=$(mktemp) || exit
+rm -f $GIT_INDEX_FILE
+trap "rm -f $GIT_INDEX_FILE" 0 1 2 3 15
+git add -A
+git diff ${1:-HEAD}
index 3ec370c..d319048 100644
--- a/foo
+++ b/foo
@@ -1 +1 @@
-I am a foo
+I was foo, am now resurrected
这个diffscript 默认与HEAD 不同。因此,如果我们在不带参数的情况下运行它,我们会将 commmit 11b241c 与索引/工作树进行比较(因为我们 git add -A 它大部分与我们比较是与索引还是工作树无关——树,此时:它们本质上是相同的,以 .gitignore 指令为模)。
trap ... 行确保我们删除临时索引,无论脚本是由 ^C(信号 2,SIGINT)还是网络断开(信号 1,SIGHUP)或 QUIT(信号 3,SIGQUIT)终止或 TERMINATE(信号 15,SIGTERM),或只是正常退出(0,不是信号,只是正常终止)。
令人讨厌的是,Git 坚持认为该文件要么根本不存在,要么具有索引文件签名(不允许空文件),因此我们删除了 mktemp 制作的临时文件,以便 git add 步骤可以创建它带有正确的签名。