测试预提交挂钩中将要提交的内容

问题描述 投票:0回答:5

pre-commit
挂钩运行时,存储库可能不干净。因此,如果您天真地运行测试,它们不会反对您所提交的内容,而是反对您工作树中发生的任何内容。

显而易见的做法是在

git stash --keep-index --include-untracked
的开头添加
pre-commit
,在末尾添加
git pop
。这样您就可以针对(纯)索引进行测试,这就是我们想要的。

不幸的是,如果您使用

git add --patch
(特别是当您编辑帅哥时),这会生成合并冲突标记,因为
stash@{0}
的内容在提交后可能与工作树不匹配。

另一种常见的解决方案是克隆存储库并在新的临时存储库中运行测试。有两个问题:

  1. 我们还没有提交,所以我们无法轻松获取处于即将提交状态的存储库的副本;和
  2. 我的测试可能对当前工作目录的位置敏感,例如由于本地环境配置。

如何将我的工作树恢复到

git stash --keep-index --include-untracked
之前的任何状态,而不引入合并冲突标记,并且不修改提交后
HEAD

git githooks pre-commit-hook pre-commit git-index
5个回答
4
投票

git write-tree

pre-commit
钩子中很有用。它将一棵树写入索引的存储库(如果提交完成,该树将被重用。)

将树写入存储库后,您可以使用

git archive | tar -x
将树写入临时目录。

例如:

#!/bin/bash

TMPDIR=$(mktemp -d)
TREE=$(git write-tree)
git archive $TREE | tar -x -C $TMPDIR

# Run tests in $TMPDIR

RESULT=$?
rm -rf "$TMPDIR"
exit $RESULT

2
投票

如果克隆整个存储库成本太高,也许您只需要工作目录的副本。制作副本比尝试处理冲突更简单。例如:

#!/bin/sh -e

trap 'rm -rf $TMPD' 0
mkdir ${TMPD=$PWD/.tmpdir}
git ls-tree -r HEAD | while read mod type sha name; do
    if test "$type" = blob; then
        mkdir -p $TMPD/$( dirname "$name" ) 
        git show $sha > $TMPD/"$name";
        chmod $mod $TMPD/"$name"
    fi
done
cd $TMPD
git diff --cached HEAD | patch
# Run tests here

这将转储树的状态,因为它将在 $TMPD 中提交后,因此您可以在那里运行测试。您应该以比此处更安全的方式获得一个临时目录,但为了使最终的差异能够工作(或者更早地简化脚本和 cd),它必须是工作目录的子目录。


2
投票

如果您有能力使用临时目录(即制作当前结账的完整副本),您可以使用临时目录,如下所示:

tmpdir=$(mktemp -d) # Or put it wherever you like
git archive HEAD | tar -xf - -C "$tmpdir"
git diff --staged | patch -p1 -d "$tmpdir"
cd "$tmpdir"
...

这基本上是 William Pursell 的解决方案,但利用了

git archive
,这使得代码更简单,我预计会更快。

或者,首先通过 cd'ing:

cd somewhere
git -C path/to/repo archive HEAD | tar -xf -
git -C path/to/repo diff --staged | patch -p1
...

git -C
需要 Git 1.8.5。


0
投票

我发现以下内容很有用:

## bash
 declare -a files
 readarray -t files < <(git status --porcelain  | perl -ane 'print $F[1],qq(\n) if m/^[ACM] /')
 #
 declare -a delfiles
 readarray -t delfiles < <(git status --porcelain  | perl -ane 'print $F[1],qq(\n) if m/^D /')
 #
 declare -a huhfiles
 readarray -t huhfiles < <(git status --porcelain  | perl -ane 'print $F[1],qq(\n) if m/^\? /')

调用

git status
三次可能效率较低,但这段代码比调用一次、存储在内存中并循环结果要简单一些。而且我不认为将结果放入临时文件并从磁盘上读取三次会更快。或许。我不知道。这是第一关。欢迎批评指正。


-1
投票

我终于找到了我一直在寻找的解决方案。仅检查提交之前索引的状态,并且它使索引和工作树与提交之前完全相同。

如果您发现任何问题或更好的方法,请回复,无论是评论还是您自己的答案。

这假设在运行时没有其他东西会尝试存储或以其他方式修改 git 存储库或工作树。这没有任何保证,可能是错误的,并且会让你的代码付之东流。谨慎使用。

# pre-commit.sh
REPO_PATH=$PWD
git stash save -q --keep-index --include-untracked # (stash@{1})
git stash save -q                                  # (stash@{0})

# Our state at this point:
# * clean worktree
# * stash@{0} contains what is to be committed
# * stash@{1} contains everything, including dirt

# Now reintroduce the changes to be committed so that they can be tested
git stash apply stash@{0} -q

git_unstash() {
    G="git --work-tree \"$REPO_PATH\" --git-dir \"$REPO_PATH/.git\"" 
    eval "$G" reset -q --hard             # Clean worktree again
    eval "$G" stash pop -q stash@{1}      # Put worktree to original dirty state
    eval "$G" reset -q stash@{0} .        # Restore index, ready for commit
    eval "$G" stash drop -q stash@{0}     # Clean up final remaining stash
}
trap git_unstash EXIT

... tests against what is being committed go here ...
© www.soinside.com 2019 - 2024. All rights reserved.