【问题标题】:Git pre-commit/pre-push hook to run unit tests on commits, with uncommited changes on the working treeGit pre-commit/pre-push 钩子在提交时运行单元测试,在工作树上未提交的更改
【发布时间】:2020-02-05 20:49:13
【问题描述】:

我目前有一个简单的 pre-commit 用于我的项目的钩子(在 R 中,虽然这是这个问题的附带问题),它运行单元测试:

#!/bin/sh
a=$(Rscript tests/testthat.R)
exit $a

其中tests/testthat.R 是处理所有测试的包装文件。

这个钩子有一个问题,但是:如果我进行部分提交(这样仍然有未提交的更改),测试将在工作树的当前状态下运行,包括未提交的更改。

所以,假设我完成了我正在做的某件事的“第 1 步”,但忘记提交了。然后我从“第 2 步”开始,但后来意识到我忘了提交“第 1 步”。如果“步骤 2”由于我未完成的更改而当前处于损坏状态,我将无法对“步骤 1”进行部分提交,因为测试会检测到“步骤 2”有缺陷。

那么,有没有办法让钩子在实际提交的版本上运行?我的想法是一个钩子,它可以有效地临时签出提交,在该签出上运行测试,删除签出,然后定义是否允许提交通过。但是鉴于这个钩子在提交完成之前触发,我假设不可能检查它。

我也愿意接受pre-push 钩子。这似乎更合理,因为钩子接收被推送的提交的 SHA,所以我上面的想法似乎更合理:获取最新的 SHA,创建一个临时目录,签出该 SHA,运行测试,删除目录。但后来我的问题变成了这实际上是建议的方法,还是由于我的无知而使事情过于复杂。

【问题讨论】:

    标签: git hook githooks


    【解决方案1】:

    最终git stash manual page 描述了这个确切的用例:

    如果您想对工作树中的更改进行两次或多次提交,并且想在提交前测试每个更改,则可以使用git stash push --keep-index

    # ... 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 的调用实际上不会创建存储(返回“没有要保存的本地更改”)。但是当测试完成后,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 查看跟踪文件是否有任何更改。如果有,请将它们存储起来,然后再弹出它们。否则,请不要打扰 stash 操作。

    【讨论】:

      【解决方案2】:

      article 建议在运行测试之前存储未提交的更改,然后在运行测试后取消存储它们。相关代码sn-p:

      # 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
      

      【讨论】:

        猜你喜欢
        • 2020-08-20
        • 2015-07-21
        • 2014-02-15
        • 2017-12-30
        • 2021-11-05
        • 1970-01-01
        • 2014-05-16
        • 1970-01-01
        • 2015-08-19
        相关资源
        最近更新 更多