如何确保我的 git 预提交脚本不会被愚弄?

How to make sure my git pre-commit script won't get fooled?

我正在编写一个 git 预提交脚本,在此过程中遇到了一些困难。我遇到的第一个是将文件添加到索引后所做的更改。例如:

然后它会触发我的预提交脚本,该脚本恰好读取 test.py 以确保它没有任何问题。问题是,即将提交的 test.py 和我的工作树中的那个是不同的!所以我的脚本基本上是在检查错误的文件,可能会遗漏一些非常重要的代码问题。经过一些研究,我发现有些人在钩子的开头做一个 git stash push ,在最后做一个 git stash pop ,以“确保”预提交脚本正在分析已提交的版本的文件,但我发现它有点冒险(见下文为什么我这么认为)而且我真的不喜欢执行 git 命令而 运行 由 [= 触发的脚本的想法44=] 命令。所以 我的第一个问题是:什么是确保我正在分析正在提交的文件而不是我的工作树中的文件的最佳方法? 也许我可以尝试直接读取 .git/objects/* 个文件吗?

那个 git 隐藏的东西让我想知道......如果我公司的一个开发人员使用我的预提交脚本决定在另一个终端切换分支,而预提交脚本是 运行 ?好吧,我在做一些测试时已经知道答案:提交将失败并显示 fatal: cannot lock ref 'HEAD' 并且 git stash pop 将出现在另一个分支中,并且可能导致冲突。另一种情况可能是开发人员在隐藏推送之后和我的预提交脚本加载文件之前修改了文件,导致我的脚本再次分析错误的文件内容,这基本上是人类的竞争条件涉及。我确实意识到这些场景有点扭曲,但我公司的开发人员并不都熟悉 git 我绝对觉得这是可能发生的事情......所以 我的第二个问题是:如何才能我确保工作树在我的预提交完成后保持完整,即使开发人员在此期间做了一些疯狂的事情? 我希望 git 会创建某种锁挂钩期间的文件会阻止开发人员做奇怪的事情,但它似乎没有。

我想如果有一个好的方法来回答我的第一个问题,第二个是无关紧要的,但我还是问了它以防万一。迫不及待地想看看你们要说的话!

这是一个非常困难的问题,如果你想全面解决它(git commitgit commit -agit commit --only foogit commit --include bar,加上其他您提到的项目)。

有人提出了一个非常好的解决方案,您可以直接使用。我自己从来没有用过,所以我在这里不特别推荐,而是看一下https://pre-commit.com/.

Then it triggers my pre-commit script which happens to read test.py to make sure nothing's wrong with it. The thing is, the on-its-way-to-be-committed test.py and the one in my working tree are different !!

这就是为什么您需要确保您的 pre-commit 脚本 运行 在索引中的文件上, 而不是 在您的工作树上。暂存提交与工作树中的实际内容不同实际上是很常见的(例如,考虑 git add -p,它允许您暂存部分文件)。

处理此问题的一种方法是将索引检出到一个临时目录中,然后 运行 在那里进行测试。您可以使用 git checkout-index 命令将索引的副本检出到临时目录中。

这是一个示例 pre-commit 挂钩,如果任何文件包含单词 BAD:

,它将拒绝提交
#!/bin/sh

echo "running checks"

# create a temporary directory
tmpdir=$(mktemp -d precommitXXXXXX)

# make sure we clean it up when we're done
trap "rm -rf $tmpdir" EXIT

# check out the index
git checkout-index --prefix=$tmpdir/ -af

# run tests in a subshell so that we end up back in the current
# directory when everything finishes.
(
  cd $tmpdir
  
  if grep -q BAD *; then
    echo "ERROR: found bad files"
    exit 1
  fi
)

我相信这也解决了您关于确保正在测试的树在测试期间保持一致的第二个问题。因为在这里您正在一个临时目录中工作,其中包含存储库的副本,所以您无需担心任何更改。