Hi Peff, On Tue, 16 Feb 2021, Jeff King wrote: > The perf suite gets confused when test_perf_default_repo is pointed at a > worktree (which includes when it is run from within a worktree at all, > since the default is to use the current repository). > > Here's an example: > > $ git worktree add ~/foo > Preparing worktree (new branch 'foo') > HEAD is now at 328c109303 The eighth batch > $ cd ~/foo > $ make > [...build output...] > $ cd t/perf > $ ./p0000-perf-lib-sanity.sh -v -i > [...] > perf 1 - test_perf_default_repo works: > running: > foo=$(git rev-parse HEAD) && > test_export foo > > fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree. > Use '--' to separate paths from revisions, like this: > 'git <command> [<revision>...] -- [<file>...]' > > The problem is that we didn't copy all of the necessary files from the > source repository (in this case we got HEAD, but we have no refs!). We > discover the git-dir with "rev-parse --git-dir", but this points to the > worktree's partial repository in .../.git/worktrees/foo. > > That partial repository has a "commondir" file which points to the main > repository, where the actual refs are stored, but we don't copy it. This > is the correct thing to do, though! If we did copy it, then our scratch > test repo would be pointing back to the original main repo, and any ref > updates we made in the tests would impact that original repo. > > Instead, we need to either: > > 1. Make a scratch copy of the original main repo (in addition to the > worktree repo), and point the scratch worktree repo's commondir at > it. This preserves the original relationship, but it's doubtful any > script really cares (if they are testing worktree performance, > they'd probably make their own worktrees). And it's trickier to get > right. > > 2. Collapse the main and worktree repos into a single scratch repo. > This can be done by copying everything from both, preferring any > files from the worktree repo. > > This patch does the second one. With this applied, the example above > results in p0000 running successfully. > > Reported-by: Derrick Stolee <dstolee@xxxxxxxxxxxxx> > Signed-off-by: Jeff King <peff@xxxxxxxx> > --- I think you'll also need the equivalent of: -- snip -- diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index 22d727cef83..0949c360ec4 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -84,7 +84,7 @@ test_perf_create_repo_from () { cp -R "$objects_dir" "$repo/.git/"; } && for stuff in "$source_git"/*; do case "$stuff" in - */objects|*/hooks|*/config|*/commondir) + */objects|*/hooks|*/config|*/commondir|*/gitdir) ;; *) cp -R "$stuff" "$repo/.git/" || exit 1 -- snap -- > Having written that, it occurs to me that an even simpler solution is to > just always use the commondir as the source of the scratch repo. It does > not produce the same outcome, but the point is generally just to find a > suitable starting point for a repository. Grabbing the main repo instead > of one of its worktrees is probably OK for most tests. Good point: we probably also need to exclude `*/worktrees/*`, but that is a bit trickier as we would not want to exclude, say, `refs/heads/worktrees/cleanup`. Ciao, Dscho > > t/perf/perf-lib.sh | 31 ++++++++++++++++++++++--------- > 1 file changed, 22 insertions(+), 9 deletions(-) > > diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh > index e385c6896f..1226be4005 100644 > --- a/t/perf/perf-lib.sh > +++ b/t/perf/perf-lib.sh > @@ -70,27 +70,40 @@ test_perf_do_repo_symlink_config_ () { > test_have_prereq SYMLINKS || git config core.symlinks false > } > > +test_perf_copy_repo_contents () { > + for stuff in "$1"/* > + do > + case "$stuff" in > + */objects|*/hooks|*/config|*/commondir) > + ;; > + *) > + cp -R "$stuff" "$repo/.git/" || exit 1 > + ;; > + esac > + done > +} > + > test_perf_create_repo_from () { > test "$#" = 2 || > BUG "not 2 parameters to test-create-repo" > repo="$1" > source="$2" > source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)" > objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)" > + common_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-common-dir)" > mkdir -p "$repo/.git" > ( > cd "$source" && > { cp -Rl "$objects_dir" "$repo/.git/" 2>/dev/null || > cp -R "$objects_dir" "$repo/.git/"; } && > - for stuff in "$source_git"/*; do > - case "$stuff" in > - */objects|*/hooks|*/config|*/commondir) > - ;; > - *) > - cp -R "$stuff" "$repo/.git/" || exit 1 > - ;; > - esac > - done > + > + # common_dir must come first here, since we want source_git to > + # take precedence and overwrite any overlapping files > + test_perf_copy_repo_contents "$common_dir" > + if test "$source_git" != "$common_dir" > + then > + test_perf_copy_repo_contents "$source_git" > + fi > ) && > ( > cd "$repo" && > -- > 2.30.1.989.g5e01c2f281 > >