fangorn/ex_git_objectstore
public
Test suite corrupts the developer's real repository: pre-push hook leaks GIT_DIR into mix test #75
Links
No links yet.
.githooks/pre-push runs mix test. Git exports GIT_DIR (and GIT_WORK_TREE, GIT_INDEX_FILE, …) into hook processes, so every git push runs the whole test suite pointed at the repository being pushed. Tests that shell out to git — 20 test files do — then operate on that repo instead of their own fixture.
Reproduced deliberately
mix test -> 1030 passed
mix test (under the pre-push hook) -> 74 failed
GIT_DIR=<throwaway repo> mix test -> 74 failed # same count, on purpose
Damage observed on a real developer checkout
All four of the outcomes the existing test_helper.exs comment warns about actually happened here:
core.bareset totrue— git then refused to treat the checkout as a work tree at all (fatal: this operation must be run in a work tree)remote.origin.urlrewritten to a fixture’s local HTTP server (http://127.0.0.1:61703/repo)origin/mainforce-updated to a fixture commit (9190f30)- a spurious
.git/shallowwritten, truncating a branch’s history to 1 commit and makinggit pushfail withremote unpack failed: invalid_command: shallow … - ~140 fixture commits (
first,second,a,b,delta-friendly,pushed,rewritten,commit 1…commit 10) committed onto a real branch, per its reflog
A stray side branch with commits dated 2026-07-10 is still sitting in the repo, so this has happened at least once before.
All of the above was repaired by hand; git fsck is clean.
Why the existing guard doesn’t cover it
test_helper.exs sets GIT_CEILING_DIRECTORIES to the project root, which stops git’s upward discovery from escaping into the project’s .git. That guard is correct and necessary — but GIT_DIR bypasses discovery entirely, so the ceiling never gets consulted.
A scrub alone is not the fix — verified
The obvious patch is to System.delete_env the redirecting variables in test_helper.exs before ExUnit.start/1. I wrote it and measured it:
GIT_DIR=<repo> mix test -> 1032 passed (was 74 failed)
…and it moved the corruption rather than stopping it. With GIT_DIR gone, the same tests fell back to discovery and wrote their fixture commits into the worktree’s own repository instead — because the fixture directories live under the project root, and GIT_CEILING_DIRECTORIES only blocks discovery from above the ceiling, not from below it. That is exactly how the ~140 commits above got written.
So the scrub was deliberately left out of fangorn/ex_git_objectstore#48; shipping half a fix here would just relocate the blast radius.
What a real fix probably looks like
Every System.cmd("git", …) in the suite should be unable to reach any repository but its own fixture, by construction rather than by hoping discovery fails:
- pass
env: [{"GIT_DIR", fixture_git_dir}, {"GIT_WORK_TREE", fixture_work_tree}, …]explicitly per invocation, via one shared helper rather than 20 call sites, and - scrub the inherited variables in
test_helper.exsso nothing leaks in, and - keep fixture directories outside the project tree (
System.tmp_dir!/0) so discovery-from-below cannot reach the project’s.giteven if acd:target is missing.
The existing TestIsolationTest is the right place for regression coverage; it needs cases for an inherited GIT_DIR and for discovery-from-below.
Interim
Until this is fixed, git push in this repo is unsafe. Pushing with --no-verify after running mix test manually is the safe order of operations, which is what fangorn/ex_git_objectstore#48 did.
Found while profiling the diff engine for fangorn/anvil#367.