git 与包括未跟踪文件在内的工作树的差异

git diff with respect to working tree including untracked files

我有一个旧的提交 A。我现在在提交 B=HEAD!=A,并且想比较我的工作目录的状态,包括未跟踪的文件,与 A.

原因是当前未跟踪的文件是提交 A 的一部分,因此它们不会出现在我想要的 diff 中,因此不会混乱。

作为解决方法,我可以在差异之前手动 git add <all the untracked files> 并在差异之后手动 git reset <all the untracked files>。但是,有许多未跟踪的文件,我想以最大程度的原子性和鲁棒性的方式来执行此操作,因为我在脚本中执行所有这些操作。

编辑:This question 还涉及针对未跟踪文件的差异。但是,那里的答案需要手动添加和稍后删除未跟踪的文件,我更喜欢一种更自动地执行此操作的解决方案,并且以原子方式在脚本上下文中防止错误发生中断。接受的答案正是这样做的,并且确实与链接问题的答案不同。

你可以看看:

git add --intent-to-add(与git add -N相同)

这只会将一个空条目附加到索引,而不是文件本身。使用 diff 命令时会看到差异。

GIT 文档:

--intent-to-add

This is useful for, among other things, showing the unstaged content of such files with git diff and committing them with git commit -a.> Record only the fact that the path will be added later. An entry for the path is placed in the index with no content.

正如其他人所建议的,您可以使用 git add -N 将虚拟条目添加到索引中。 运行:

git diff <hash>

然后将给定的与当前工作树进行比较,使用现有索引(现在具有未跟踪文件的条目)来决定将哪个工作树版本与给定的<hash><hash> 可以是提交 ID、树 ID、分支名称或 Git 可以解析为树的任何内容)。但这确实弄乱了索引,正如你所说,你:

would like to do this in a maximally atomic and robust way

诀窍是使用 临时 索引。

这里的初始提交有文件 barfoo;第二次提交删除了文件 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 比较。因此,如果我们 运行 它没有参数,我们会将 commit 11b241c 与 index/work-tree 进行比较(因为我们 git add -Amostly在这一点上,我们是比较索引还是工作树并不重要:它们本质上是相同的,取模 .gitignore 指令)。

trap ... 行确保我们删除了临时索引,无论脚本是被 ^C(信号 2,SIGINT)终止,还是网络断开(信号 1,SIGHUP)或 QUIT(信号3, SIGQUIT) or TERMINATE (signal 15, SIGTERM), or just normally exits (0, not a signal, just normal termination).

恼人的是,Git 坚持认为该文件要么根本不存在,要么有一个索引文件签名——空文件是不允许的——所以我们删除了 mktemp 创建的临时文件,所以git add 步骤可以使用正确的签名创建它。