Skip to content

test-fixture isolation: git -C does not survive an exported GIT_DIR #2840

Description

@kyle-sexton

Finding

The repo's fixture-isolation idiom for test scripts is git -C "$fixture" ...
(scripts/check-stale-base-overlap.test.sh:22, scripts/sync-standards-contract.test.sh:69,
and now the two portability suites via #2839). It is a good readability and
copy-paste guard. It is not an isolation guarantee.

git config's default --local scope, and git init's target, both follow the
GIT_DIR environment variable in preference to the working directory and in
preference to -C. GIT_DIR is exactly what git exports into every hook it
invokes. So any test suite run from inside a git hook — or from any process that
inherited GIT_DIR — writes its fixture identity into the caller's repository,
whichever isolation idiom the suite uses.

Reproduction

Both forms poison the caller's .git/config under an exported GIT_DIR:

# form 1: cd-scoped subshell (the pre-#2839 shape)
GIT_DIR=<real repo>/.git
BEFORE:  (no local user.* keys)
subshell rc=0   gitdir-it-used=<real repo>/.git
AFTER:   user.email=test@example.com
         user.name=test

# form 2: the established `git -C "$fx"` idiom
== git -C "$fx" init/config under exported GIT_DIR ==
fixture has .git? NO
caller config user.*:  user.email=test@example.com  user.name=test

Only clearing the inherited state isolates the fixture:

== git -C with GIT_DIR/GIT_WORK_TREE unset in the subshell ==
fixture2 has .git? yes
caller config user.*:  (clean)

Why this matters here

A poisoned user.email silently re-authors commits to test@example.com. Such a
commit fails this repo's required_signatures rule with no_user and cannot be
force-pushed over — the branch has to be abandoned and the tree rebuilt (this is
what happened to #2827 -> #2830).

Worktrees share the main clone's .git/config, so one leak poisons every
worktree of the repo at once.

Not done, deliberately

#2839 does not add unset GIT_DIR GIT_WORK_TREE hardening. It is a new shape
across roughly 30 test files, and nothing in this repo currently runs the suites
with GIT_DIR set (no core.hooksPath, no installed git hooks in the clone, and
CI invokes them as plain workflow steps). Filing the mechanism so the decision is
made deliberately rather than rediscovered after the next incident.

Options

  1. Do nothing; accept that fixture isolation depends on GIT_DIR being unset.
  2. Add a shared test-harness preamble that clears GIT_DIR/GIT_WORK_TREE once,
    rather than per-call (lib/ already hosts shared test helpers).
  3. Add a portability/hygiene gate asserting fixture-building test scripts clear
    the inherited git environment.

Related

No linked issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions