问题在于 Git 命令在为钩子脚本创建的环境中与您的正常环境中的行为方式不同。
首先,钩子脚本在其当前工作目录设置为 Git 目录本身(即.git/
非裸存储库的目录)的情况下运行。其次,钩子脚本使用 GIT_DIR 环境变量集运行并指向 Git 存储库(同样,.git/
非裸存储库的目录)。
通常,如果您尝试git reset --hard
从该.git/
目录运行,它将因以下消息而死:
fatal: This operation must be run in a work tree
但是当设置 GIT_DIR 时,Git 命令假定当前目录是工作树。由于钩子运行时的当前目录是该.git/
目录,因此您git reset --hard
实际上是将工作树文件直接“签出”到.git/
其父目录而不是其父目录(即,您现在在.git/
目录中拥有版本化内容的副本)。
希望您的存储库中所有版本化内容的路径名都与Git 在 Git 存储库本身中使用的路径名一致。如果它们确实一致,那么您git reset --hard
将覆盖存储库的一些内部结构,您可能希望从其他存储库重新克隆它。如果你确信版本控制的内容没有与 Git 的内部路径名冲突,那么你可以用这个来清理它:
# make a backup of your repository first!
(cd .git && GIT_DIR=$PWD git ls-files -cz | xargs -0 rm)
这只会删除当前跟踪的文件(它将留下已被删除的文件,但曾经在中断的钩子处于活动状态时在推送的提示提交中被跟踪)。
一种解决方案是在调用 Git 命令之前将当前工作目录更改为正常工作树并取消设置 GIT_DIR 和 GIT_WORK_TREE。
⋮
test "${PWD%/.git}" != "$PWD" && cd ..
unset GIT_DIR GIT_WORK_TREE
# you can now safely use Git commands
⋮
另一种解决方案是显式重置 GIT_DIR,在那里设置 GIT_WORK_TREE 和 chdir。Git 常见问题解答“为什么在“git push”之后我看不到远程存储库中的更改?”</a> 建议使用一个更新后脚本来执行此操作。链接脚本也更安全,因为如果在执行硬重置之前索引或工作树是脏的,它会进行存储。