首页 > 解决方案 > 如何确保我的 git 预提交脚本不会被愚弄?

问题描述

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

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

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

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

标签: githookpre-commit-hook

解决方案


然后它触发我的预提交脚本,该脚本恰好读取了 test.py 以确保它没有任何问题。问题是,即将提交的 test.py 和我工作树中的 test.py 是不同的!

这就是为什么你需要确保你的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
)

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


推荐阅读