Git pre-commit/pre-push 挂钩到 运行 提交单元测试,工作树上有未提交的更改
Git pre-commit/pre-push hook to run unit tests on commits, with uncommited changes on the working tree
我目前有一个简单的 pre-commit
挂钩用于我的项目(在 R 中,虽然这是这个问题的附带)运行s 单元测试:
#!/bin/sh
a=$(Rscript tests/testthat.R)
exit $a
其中 tests/testthat.R
是处理所有测试的包装文件。
这个钩子有一个问题,但是:如果我进行部分提交(这样仍有未提交的更改),测试将 运行 工作树的当前状态,包括未提交的变化。
所以,假设我完成了 "Step 1" 正在做的事情,但忘记提交。然后我从 "Step 2" 开始,但后来意识到我忘记提交 "Step 1"。如果 "Step 2" 由于我未完成的更改而当前处于损坏状态,我将无法对 "Step 1" 进行部分提交,因为测试将检测到 "Step 2" 有缺陷。
那么,有没有办法在实际提交的版本上挂钩 运行?我的想法是一个挂钩,它可以有效地临时签出提交,运行对该签出进行测试,删除签出,然后定义是否允许提交通过。但是鉴于这个钩子在提交完成之前触发,我假设不可能检查出来。
我也愿意接受 pre-push
钩子。这似乎更合理,因为钩子接收被推送的提交的 SHA,所以我上面的想法似乎更合理:获取最新的 SHA,创建一个临时目录,检查 SHA,运行 测试,删除目录。但后来我的问题变成了这是否实际上是建议的方法,或者是否由于我的无知而使事情过于复杂。
此 article 建议在 运行 测试之前存储未提交的更改,然后在测试 运行 后取消存储它们。相关代码片段:
# pre-commit.sh
STASH_NAME="pre-commit-$(date +%s)"
git stash save -q --keep-index $STASH_NAME
# Test prospective commit
...
STASHES=$(git stash list)
if [[ $STASHES == "$STASH_NAME" ]]; then
git stash pop -q
fi
最终 git stash
manual page 描述了这个确切的用例:
You can use git stash push --keep-index
when you want to make two or more commits out of the changes in the work tree, and you want to test each change before committing:
# ... hack hack hack ...
$ git add --patch foo # add just first part to the index
$ git stash push --keep-index # save all other changes to the stash
$ edit/build/test first part
$ git commit -m 'First part' # commit fully tested change
$ git stash pop # prepare to work on all other changes
# ... repeat above five steps until one commit remains ...
$ edit/build/test remaining parts
$ git commit foo -m 'Remaining parts'
就这么简单
git stash push --keep-index
#
# testing...
#
git stash pop
不过,在钩子中使用它有一个极端情况的风险:你可能有一个你忘记的旧的、不相关的存储,并且可能想要进行干净的提交(不留下任何未提交的更改)。
在这种情况下,对 git stash push --keep-index
的调用实际上不会创建存储(返回 "No local changes to save")。但是当测试完成后,git stash pop
会找到旧的 stash,至少会导致头痛。
所以我实际的 pre-commit
钩子看起来像:
#/bin/sh
hasChanges=$(git diff)
if [ -n "$hasChanges" ]; then
git stash push --keep-index
fi
#
# testing...
#
if [ -n "$hasChanges" ]; then
git stash pop
fi
exit $testSuccess
基本上,使用git diff
查看跟踪文件是否有任何变化。如果有,请将它们藏起来,然后弹出。否则,不要为隐藏操作而烦恼。
我目前有一个简单的 pre-commit
挂钩用于我的项目(在 R 中,虽然这是这个问题的附带)运行s 单元测试:
#!/bin/sh
a=$(Rscript tests/testthat.R)
exit $a
其中 tests/testthat.R
是处理所有测试的包装文件。
这个钩子有一个问题,但是:如果我进行部分提交(这样仍有未提交的更改),测试将 运行 工作树的当前状态,包括未提交的变化。
所以,假设我完成了 "Step 1" 正在做的事情,但忘记提交。然后我从 "Step 2" 开始,但后来意识到我忘记提交 "Step 1"。如果 "Step 2" 由于我未完成的更改而当前处于损坏状态,我将无法对 "Step 1" 进行部分提交,因为测试将检测到 "Step 2" 有缺陷。
那么,有没有办法在实际提交的版本上挂钩 运行?我的想法是一个挂钩,它可以有效地临时签出提交,运行对该签出进行测试,删除签出,然后定义是否允许提交通过。但是鉴于这个钩子在提交完成之前触发,我假设不可能检查出来。
我也愿意接受 pre-push
钩子。这似乎更合理,因为钩子接收被推送的提交的 SHA,所以我上面的想法似乎更合理:获取最新的 SHA,创建一个临时目录,检查 SHA,运行 测试,删除目录。但后来我的问题变成了这是否实际上是建议的方法,或者是否由于我的无知而使事情过于复杂。
此 article 建议在 运行 测试之前存储未提交的更改,然后在测试 运行 后取消存储它们。相关代码片段:
# pre-commit.sh
STASH_NAME="pre-commit-$(date +%s)"
git stash save -q --keep-index $STASH_NAME
# Test prospective commit
...
STASHES=$(git stash list)
if [[ $STASHES == "$STASH_NAME" ]]; then
git stash pop -q
fi
最终 git stash
manual page 描述了这个确切的用例:
You can use
git stash push --keep-index
when you want to make two or more commits out of the changes in the work tree, and you want to test each change before committing:# ... hack hack hack ... $ git add --patch foo # add just first part to the index $ git stash push --keep-index # save all other changes to the stash $ edit/build/test first part $ git commit -m 'First part' # commit fully tested change $ git stash pop # prepare to work on all other changes # ... repeat above five steps until one commit remains ... $ edit/build/test remaining parts $ git commit foo -m 'Remaining parts'
就这么简单
git stash push --keep-index
#
# testing...
#
git stash pop
不过,在钩子中使用它有一个极端情况的风险:你可能有一个你忘记的旧的、不相关的存储,并且可能想要进行干净的提交(不留下任何未提交的更改)。
在这种情况下,对 git stash push --keep-index
的调用实际上不会创建存储(返回 "No local changes to save")。但是当测试完成后,git stash pop
会找到旧的 stash,至少会导致头痛。
所以我实际的 pre-commit
钩子看起来像:
#/bin/sh
hasChanges=$(git diff)
if [ -n "$hasChanges" ]; then
git stash push --keep-index
fi
#
# testing...
#
if [ -n "$hasChanges" ]; then
git stash pop
fi
exit $testSuccess
基本上,使用git diff
查看跟踪文件是否有任何变化。如果有,请将它们藏起来,然后弹出。否则,不要为隐藏操作而烦恼。