You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have searched the issue list for similar bug reports.
Summary
Running prek as the pre-commit hook during git commit in a linked git worktree (git worktree add) can replace the worktree's git index with the file tree of a cached hook repository. After the run, every index-reading git command fails:
fatal: unable to read d3e222c562aeccbbd7e19d83e81daf6c54c4410d
The staged blobs only exist in the hook repo's object database, not the user repo's. All hooks report "Passed" — the corruption is only discovered on the next index read (a later hook, or git status after the commit fails with error: Error building trees).
Root cause chain:
git commit exports GIT_INDEX_FILE to the hook. From a linked worktree these are absolute paths (<main>/.git/worktrees/<name>/index, plus absolute GIT_DIR); from a main worktree GIT_INDEX_FILE is relative (.git/index), which is why only linked worktrees are affected.
prek keeps these variables in its environment (its own git commands need them) and passes them to every subprocess.
Since Install Node hooks through git url #2394 (v0.4.11), node hook environments are installed by giving npm the cached clone as a git+file:// URL. npm's pacote GitFetcher then shells out to git with the inherited environment.
git clone honors an inherited GIT_INDEX_FILE: its initial checkout writes the user's index, filling it with the hook repo's tree.
The errant command, captured with GIT_TRACE during the repro below:
Triggering requires a cold node env (first install, or env invalidated by a prek upgrade). A warm repo cache does not protect — npm clones from it. Python pre-commit strips exactly these variables for its internal git operations (no_git_env() in pre_commit/git.py); prek's own clone commands already do the same via isolate_from_git_env(), but the variables still leak through non-git subprocesses like npm.
Minimal repro (no real commit needed — simulates the env git exports to the hook):
tmp=$(mktemp -d)&&cd"$tmp"
git init repro &&cd repro
git config user.email t@t
git config user.name t
cat > .pre-commit-config.yaml <<'EOF'repos: - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.45.0 hooks: - id: markdownlintEOFprintf'# Doc\n'> README.md
git add .&& git commit -m init
echo change > staged.txt && git add .
git ls-files --stage # snapshot: 3 entries# Simulate the environment `git commit` exports to the# pre-commit hook when committing from a linked worktree# (absolute GIT_DIR / GIT_INDEX_FILE):
gitdir=$(git rev-parse --absolute-git-dir)export GIT_DIR="$gitdir"export GIT_INDEX_FILE="$gitdir/index"export PREK_HOME="$tmp/prek-home"
prek run --all-files
git ls-files --stage # ACTUAL: the hook repo's ~90 files
git status # fatal: unable to read d3e222c5...
Expected: the index is unchanged by prek run.
Actual: the index contains markdownlint-cli's tree; the repo is unusable until the index is rebuilt.
The same corruption reproduces end-to-end with prek installed as the git hook, a workspace-mode monorepo, and git add . && git commit in a fresh linked worktree.
prek's own log shows a normal, fully successful run — the index write happens inside
npm's git subprocesses during hook env installation and never surfaces in prek's log.
The errant command is visible by running the repro with GIT_TRACE=<file>:
trace: built-in: git clone file:///…/prek/repos/29c5ebd8dba238ce /…/prek/cache/npm/_cacache/tmp/git-cloneXXXX --recurse-submodules
Before submitting
Summary
Running prek as the
pre-commithook duringgit commitin a linked git worktree (git worktree add) can replace the worktree's git index with the file tree of a cached hook repository. After the run, every index-reading git command fails:The staged blobs only exist in the hook repo's object database, not the user repo's. All hooks report "Passed" — the corruption is only discovered on the next index read (a later hook, or
git statusafter the commit fails witherror: Error building trees).Root cause chain:
git commitexportsGIT_INDEX_FILEto the hook. From a linked worktree these are absolute paths (<main>/.git/worktrees/<name>/index, plus absoluteGIT_DIR); from a main worktreeGIT_INDEX_FILEis relative (.git/index), which is why only linked worktrees are affected.git+file://URL. npm's pacote GitFetcher then shells out to git with the inherited environment.git clonehonors an inheritedGIT_INDEX_FILE: its initial checkout writes the user's index, filling it with the hook repo's tree.The errant command, captured with
GIT_TRACEduring the repro below:Triggering requires a cold node env (first install, or env invalidated by a prek upgrade). A warm repo cache does not protect — npm clones from it. Python pre-commit strips exactly these variables for its internal git operations (
no_git_env()inpre_commit/git.py); prek's own clone commands already do the same viaisolate_from_git_env(), but the variables still leak through non-git subprocesses like npm.Minimal repro (no real commit needed — simulates the env git exports to the hook):
prek run.The same corruption reproduces end-to-end with prek installed as the git hook, a workspace-mode monorepo, and
git add . && git commitin a fresh linked worktree.Recovery for affected repos:
The mechanism is also demonstrable without prek —
git clonealone clobbers a foreign index through the env var:Platform
macOS 26.5 arm64 (Darwin 25.5.0); git 2.50.1 (Apple Git-155)
Version (current: 0.4.12)
prek 0.4.12 (Homebrew, built 2026-08-03) — affects ≥ 0.4.11 (
git+file://node install, #2394).pre-commit-config.yaml
Log file