Subject: [BUG] t/perf scripts lose GIT_PERF_* when used with --verbose-log

From: Jeff King <[email-masked]>
Date: Tue, 2 Jun 2026 03:56:46 -0400

Imagine I have a perf script like this:

  #!/bin/sh
  test_description=foo
  . ./perf-lib.sh
  echo >&2 "large_repo = $GIT_PERF_LARGE_REPO"
  test_perf_large_repo
  [...some actual tests...]

If I run the command below, I'd expect it to use linux.git as the test
repo (and print to stderr telling me so). And it does:

  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./p1234-foo.sh
  large_repo = /path/to/linux.git
  [...]

This is courtesy of 32b74b9809 (perf: do allow `GIT_PERF_*` to be
overridden again, 2025-04-04). But that breaks if we use --tee or any
other option which implies it:

  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./p1234-foo.sh --verbose-log
  large_repo = /home/peff/compile/git/t/..
  [...]

What happens in the happy path is this:

  0. The script sources perf-lib.sh.

  1. perf-lib.sh stashes GIT_PERF_* in a variable to restore later.

  2. perf-lib.sh sources GIT-BUILD-OPTIONS, which overwrites the
     environment.

  3. perf-lib.sh sources test-lib.sh.

  4. test-lib.sh itself sources GIT-BUILD-OPTIONS.

  5. Eventually test-lib.sh finishes, returning control to perf-lib.sh.

  6. perf-lib.sh restores GIT_PERF_* from the stashed copy. All is well.

But if --tee or --verbose-log is used, then step 5 never happens!
Instead test-lib.sh re-executes a second copy of the script piped to
tee. And that re-executed copy sees the environment we had after step 4,
with all of GIT_PERF_* coming from GIT-BUILD-OPTIONS. So even though it
tries to do the save/restore, its step 1 never sees the original
environment (so it "saves" nothing useful).

This is especially insidious if you use the "./run" program to compare
versions. It reads GIT-BUILD-OPTIONS, too, and also knows how to
preserve GIT_PERF_*, courtesy of 79d301c767 (t/perf/run: preserve
GIT_PERF_* from environment, 2026-01-06). But it reads GIT_TEST_OPTS
from the build-options file and runs each script with it. So while this
may work:

  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./run HEAD p1234-foo.sh
  [...]
  === Running 1 tests in /home/peff/compile/git/t/perf/build/1211f0ef99a75931f170bc2a838172a45300ad63/bin-wrappers ===
  large_repo = /path/to/linux.git
  [...]

you may get spooky action at a distance from whenever you last ran make:

  $ make -C ../.. GIT_TEST_OPTS=--verbose-log
  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./run HEAD p1234-foo.sh
  [...]
  === Running 1 tests in /home/peff/compile/git/t/perf/build/1211f0ef99a75931f170bc2a838172a45300ad63/bin-wrappers ===
  large_repo = /home/peff/compile/git/t/..

Doubly confusing if that GIT_TEST_OPTS is in your config.mak (because
you want normal "make test" to run under prove but still keep logs, and
you put it in the file ages ago).

I don't think this can be fixed by perf-lib.sh. The problem is internal
to test-lib.sh, which is overwriting the environment when it sources
GIT-BUILD-OPTIONS, without any opportunity for perf-lib to act before
getting re-executed. It would require test-lib.sh itself to have some
notion of "here are some stashed variables; restore them via eval".

Which just feels like stacking band-aids upon band-aids. The original
problem started with 4638e8806e (Makefile: use common template for
GIT-BUILD-OPTIONS, 2024-12-06), though one could argue that even before
then the precedence rules were kind of sketchy (it just made things much
worse because now it crops up even if you don't set GIT_PERF_LARGE_REPO
in your config.mak at all).

So I dunno. I couldn't quite bring myself to write a patch, but I
thought I'd at least write a warning to the list in case anybody else is
bit by it.

-Peff