16

挂钩运行时pre-commit,存储库可能不干净。因此,如果您天真地运行您的测试,它们将不会与您正在提交的内容相冲突,而是与您的工作树中发生的任何事情相冲突。

显而易见的事情是git stash --keep-index --include-untracked在开头pre-commitgit pop结尾处。这样您就可以针对(纯)索引进行测试,这正是我们想要的。

git add --patch不幸的是,如果您使用(特别是如果您编辑大块),这会生成合并冲突标记,因为stash@{0}提交后的内容可能与工作树不匹配。

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

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

git stash --keep-index --include-untracked如何在不引入合并冲突标记且不修改 post-commit 的情况下将我的工作树恢复到它之前的任何状态HEAD

4

5 回答 5

4

git write-treepre-commit在钩子中很有用。它将一棵树写入索引的 repo(如果提交完成,该树将被重用。)

将树写入 repo 后,您可以使用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
于 2016-04-02T14:12:57.373 回答
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。

于 2014-05-29T12:11:32.407 回答
2

如果克隆整个 repo 过于昂贵,也许您只需要工作目录的副本。制作副本比尝试处理冲突更简单。例如:

#!/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 中提交之后一样,因此您可以在那里运行测试。您应该以一种比这里更安全的方式获得一个临时目录,但是为了使最终的 diff 工作(或简化脚本和更早的 cd),它必须是工作目录的子目录。

于 2012-09-08T23:28:43.417 回答
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/^\? /')

调用 3 次可能效率低下git status,但此代码比调用一次、存储在内存中并循环结果要简单。而且我不认为将结果放入临时文件并从磁盘读取三遍会更快。也许。我不知道。这是第一关。随意批评。

于 2018-05-10T17:26:42.550 回答
-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 ...
于 2012-09-14T15:32:25.000 回答