You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Git预提交钩子中创建临时干净工作区运行单元测试?

需求与问题

我希望预提交钩子中的单元测试能在忘记暂存单元测试所需的修改或文件时失败,但Git忽略的文件/文件夹(比如Node.js项目里的node_modules目录)除外。看起来git stash可以帮我创建这种临时状态,同时测试后必须能完全恢复到原有状态。

为了明确想要的具体行为,我编写了如下bash脚本:

#!/bin/bash
mkdir temporary-repo && cd temporary-repo || exit 1
git init > /dev/null 2>&1
printf '%s' "0" > ignored
printf '%s' "ignored" > .gitignore
printf '%s' "0" > modified-staged
printf '%s' "0" > modified-unstaged
printf '%s' "0" > modified-unstaged-undo
printf '%s' "0" > modified-unstaged-update
git add . && git commit -m "Initial commit"  > /dev/null 2>&1
printf '%s' "1" > new-staged
printf '%s' "1" > modified-staged
printf '%s' "1" > new-unstaged-undo
printf '%s' "1" > new-unstaged-update
printf '%s' "1" > modified-unstaged-undo
printf '%s' "1" > modified-unstaged-update
git add .
printf '%s' "0" > new-unstaged-undo
printf '%s' "0" > modified-unstaged-undo
printf '%s' "1" > new-unstaged
printf '%s' "1" > modified-unstaged
printf '%s' "2" > new-unstaged-update
printf '%s' "2" > modified-unstaged-update
before="$(git status)"

#####################
# CREATE STASH HERE #
#####################

echo "--- Created stash ---"
test "1" = "$(cat new-unstaged-undo)" || echo "ERR: new-unstaged-undo != 1"
test "1" = "$(cat modified-unstaged-undo)" || echo "ERR: modified-unstaged-undo != 1"
test "1" = "$(cat new-staged)" || echo "ERR: new-staged != 1"
test "1" = "$(cat modified-staged)" || echo "ERR: modified-staged != 1"
test -f new-unstaged && echo "ERR: new-unstaged should not exist"
test "0" = "$(cat modified-unstaged)" || echo "ERR: modified-unstaged != 0"
test "1" = "$(cat new-unstaged-update)" || echo "ERR: new-unstaged-update != 1"
test "1" = "$(cat modified-unstaged-update)" || echo "ERR: modified-unstaged-update != 1"
test "0" = "$(cat ignored)" || echo "ERR: ignored != 0"

##################
# POP STASH HERE #
##################

echo "--- Popped stash ---"
test "0" = "$(cat new-unstaged-undo)" || echo "ERR: new-unstaged-undo != 0"
test "0" = "$(cat modified-unstaged-undo)" || echo "ERR: modified-unstaged-undo != 0"
test "1" = "$(cat new-staged)" || echo "ERR: new-staged != 1"
test "1" = "$(cat modified-staged)" || echo "ERR: modified-staged != 1"
test "1" = "$(cat new-unstaged)" || echo "ERR: new-unstaged != 1"
test "1" = "$(cat modified-unstaged)" || echo "ERR: modified-unstaged != 1"
test "2" = "$(cat new-unstaged-update)" || echo "ERR: new-unstaged-update != 2"
test "2" = "$(cat modified-unstaged-update)" || echo "ERR: modified-unstaged-update != 2"
test "0" = "$(cat ignored)" || echo "ERR: ignored != 0"

after="$(git status)"
diff -y <(echo "$before") <(echo "$after")
cd .. && rm -rf temporary-repo

尝试1

# Create stash
git stash -k -u

# Pop stash
git stash pop

失败测试输出:

--- Created stash ---
--- Popped stash ---
ERR: new-unstaged-undo != 0
ERR: modified-unstaged-undo != 0
ERR: new-unstaged-update != 2
ERR: modified-unstaged-update != 2
On branch master                                                On branch master
Changes to be committed:                                        Changes to be committed:
  (use "git restore --staged <file>..." to unstage)               (use "git restore --staged <file>..." to unstage)
        modified:   modified-staged                                     modified:   modified-staged
                                                              >         modified:   modified-unstaged
        modified:   modified-unstaged-undo                              modified:   modified-unstaged-undo
        modified:   modified-unstaged-update                  <
        new file:   new-staged                                          new file:   new-staged
        new file:   new-unstaged-undo                         <
        new file:   new-unstaged-update                       <

Changes not staged for commit:                                | Unmerged paths:
  (use "git add <file>..." to update what will be committed)  |   (use "git restore --staged <file>..." to unstage)
  (use "git restore <file>..." to discard changes in working  |   (use "git add <file>..." to mark resolution)
        modified:   modified-unstaged                         |         both modified:   modified-unstaged-update
        modified:   modified-unstaged-undo                    |         both added:      new-unstaged-undo
        modified:   modified-unstaged-update                  |         both added:      new-unstaged-update
        modified:   new-unstaged-undo                         <
        modified:   new-unstaged-update                       <

Untracked files:                                                Untracked files:
  (use "git add <file>..." to include in what will be committ     (use "git add <file>..." to include in what will be committ
        new-unstaged                                                    new-unstaged

尝试2

# Create stash
stash=$(git stash create -q)
git stash store "$stash"
git stash show -p | git apply --reverse
git diff --cached | git apply

# Pop stash
git reset --hard -q
git stash apply --index -q
git stash drop -q

失败测试输出:

--- Created stash ---
ERR: new-unstaged should not exist
--- Popped stash ---
On branch master                                                On branch master
Changes to be committed:                                        Changes to be committed:
  (use "git restore --staged <file>..." to unstage)               (use "git restore --staged <file>..." to unstage)
        modified:   modified-staged                                     modified:   modified-staged
        modified:   modified-unstaged-undo                              modified:   modified-unstaged-undo
        modified:   modified-unstaged-update                            modified:   modified-unstaged-update
        new file:   new-staged                                          new file:   new-staged
        new file:   new-unstaged-undo                                   new file:   new-unstaged-undo
        new file:   new-unstaged-update                                 new file:   new-unstaged-update

Changes not staged for commit:                                  Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)      (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working      (use "git restore <file>..." to discard changes in working 
        modified:   modified-unstaged                                   modified:   modified-unstaged
        modified:   modified-unstaged-undo                              modified:   modified-unstaged-undo
        modified:   modified-unstaged-update                            modified:   modified-unstaged-update
        modified:   new-unstaged-undo                                   modified:   new-unstaged-undo
        modified:   new-unstaged-update                                 modified:   new-unstaged-update

Untracked files:                                                Untracked files:
  (use "git add <file>..." to include in what will be committ     (use "git add <file>..." to include in what will be committ
        new-unstaged                                                    new-unstaged

尝试1能创建运行单元测试所需的理想状态,但无法恢复到原有状态;尝试2能成功恢复原状态,却无法暂存/移除new-unstaged文件。

请问有没有办法通过Git实现我想要的行为?


内容的提问来源于stack exchange,提问作者Tobias Bergkvist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 03:15:56