当来自 git 预提交挂钩的 运行 时,`git diff` 不起作用

`git diff` does not work when run from a git pre-commit hook

我有一个 git 预提交挂钩,可以在提交之前对任何修改后的文件进行一些样式检查。

实现无关紧要,但它从调用 git diff 开始。这是我在 (repo)/.git/hooks/pre-commit 中的内容。

#!/bin/sh


echo "=== Running script..."
git diff
echo "=== Done running script..."

# Other stuf
# ....

# Always exit with 1 so pre-commit hook always fails.
# Useful for testing
exit 1

当我实际尝试提交某些内容时,pre-commit 挂钩正确触发,但 git diff 命令不输出任何内容(肯定有修改过的文件)

> git commit --all -m "foo"
=== Running script...
=== Done running script...

然而,如果我 运行 pre-commit 钩子脚本 directly/manually,它 确实 工作

> ./.git/hooks/pre-commit
=== Running script...
(... outputs git diff ...)
=== Done running script...

git 调用 hook 与我手动调用它有什么不同?无论哪种方式,它 运行 都是同一个用户(我的用户名)

我也试过 this thread 的建议,但是 unset GIT_DIR--git-dir=work-tree= 没有解决任何问题。

谢谢!

您需要使用 git diff --cached,因为更改已经开始。

作为,您在这里需要git diff --cached,但这不一定是全部。

这里有两个陷阱需要警惕。第一个是 git diff 所做的:它比较两个 (或类似树的东西)。第二个与短语 the index.

有关

索引,并为 git diff

选择树

通常,您比较的两棵树是与两个特定提交关联的树:

git diff <hash1> <hash2>

(或与 <hash1>..<hash2> 相同,与大多数 Git 命令不同, 不会 像双点.. 操作在 the SPECIFYING RANGES section of the gitrevisions documentation) 中描述。

将两棵树中的一棵作为您的 work-tree 也很常见,当您 运行:

时就会发生这种情况
git diff HEAD

例如:这会将由 HEAD 命名的提交(即当前分支尖端提交)与当前工作树进行比较。

使用:

git diff --cached

告诉 Git 将 HEAD 提交与您的 index 代表的树进行比较。 Git 的索引,也称为 暂存区 缓存 ,是 Git 构建 下一步 承诺制作。这就是为什么在提交之前必须 运行 git addgit add 命令将文件从工作树复制到索引。

使用:

git diff

完全没有参数选择索引作为第一棵树,工作树作为第二棵树。一旦所有内容都从工作树复制到索引中,这个特定的差异将是空的,这就是您在这里看到的。

指数 vs 指数

第二个陷阱在于您正在使用 git commit --all.

虽然 Git 谈论 索引,但实际上每个工作树都有一个特定的、有区别的索引,Git 实际上允许多个索引,一次只使用一个。

当您使用 git commit --all 或将文件名传递给 git commit 时,Git 会创建一个 临时 索引,它会在承诺过程。为了表明有一个临时索引在起作用,Git 设置了环境变量 GIT_INDEX_FILE。使用此临时索引而不是普通索引,直到允许并提交提交,或者直到拒绝提交。

如果提交被拒绝,Git只是删除临时索引,一切都恢复到原来的样子。如果事情不是 git add-ed,它们仍然不是 git add-ed。

如果提交被接受,临时索引变成"real"或"main"索引,更多或更少。这里事情会变得复杂,因为你可以在真正的索引中暂存一些项目,运行 git commit --allgit commit --include <paths>git commit --only <paths>,如果提交成功,两者之间的区别初始(主要或实际)索引和临时索引很重要。

如果您打算对工作树中的文件 and/or 它们在索引中的副本进行特殊处理,您可能希望简单地拒绝任何使用临时索引的尝试,以避免这些并发症。但是,这将排除 --all.

的使用