Skip to content

resolveWorktree only matches a parent named .git, so worktrees of a bare repository fail #301

Description

@delanym

Describe the bug (required)

Essentially there is no support for worktrees created off a bare git repository (a practice I imagine will become more common with agentic coding).

GitDirLocator.resolveWorktree identifies a linked worktree by matching path names — the
worktree administrative directory must sit under a directory literally named .git. Git itself
places no such constraint: git worktree add from a bare repository produces
<bare>/worktrees/<name>, where <bare> is conventionally something.git or, in the widely used
bare-clone layout, .bare.

For those repositories resolveWorktree returns the gitdir unchanged, NativeGitProvider then runs
git in its parent (<bare>/worktrees), which is not a work tree, and the build fails.

Steps to Reproduce (required)

  #!/usr/bin/env bash
  set -e
  BASE=$(mktemp -d); cd "$BASE"

  cat > pom.xml <<'EOF'
  <project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>example</groupId><artifactId>wt-repro</artifactId><version>1</version>
    <build><plugins><plugin>
      <groupId>io.github.git-commit-id</groupId>
      <artifactId>git-commit-id-maven-plugin</artifactId>
      <version>10.0.0</version>
      <configuration>
        <useNativeGit>true</useNativeGit>
        <generateGitPropertiesFile>true</generateGitPropertiesFile>
        <generateGitPropertiesFilename>${project.build.directory}/git.properties</generateGitPropertiesFilename>
      </configuration>
      <executions><execution><id>x</id><goals><goal>revision</goal></goals><phase>initialize</phase></execution></executions>
    </plugin></plugins></build>
  </project>
  EOF

  # ---- layout A: ordinary clone, plus a conventional linked worktree under <main>/.git/worktrees/
  mkdir -p A/main && cd A/main
  git init -q -b main . && git config user.email t@t && git config user.name t
  cp ../../pom.xml . && git add pom.xml && git commit -qm "commit on main"
  git worktree add -q -b feature ../wt
  cd ../wt && git config user.email t@t && git config user.name t
  cp ../../pom.xml . && echo x > f.txt && git add . && git commit -qm "commit on feature worktree"
  cd "$BASE"

  # ---- layout B: bare clone, plus a linked worktree under <bare>/worktrees/
  git clone -q --bare A/main B/.bare
  git -C B/.bare worktree add -q "$BASE/B/master" main
  cp pom.xml B/master/

  for d in A/main A/wt B/master; do
    echo "=========== $d"
    echo "  real HEAD : $(git -C $d rev-parse --abbrev-ref HEAD) @ $(git -C $d rev-parse --short=7 HEAD)"
    ( cd "$d" && mvn -q -B initialize 2>&1 | grep -E "ERROR.*(Failed|fatal)" | head -2
      if [ -f target/git.properties ]; then
        echo "  reported  : $(grep -h -E 'git.branch=|git.commit.id.abbrev=' target/git.properties | tr '\n' ' ')"
      else
        echo "  reported  : (no git.properties written)"
      fi )
  done
  echo; echo "workdir: $BASE"

Actual output

=========== A/main
  real HEAD : main @ 88ae4cd
  reported  : git.branch=main git.commit.id.abbrev=88ae4cd
=========== A/wt
  real HEAD : feature @ 920ca94
  reported  : git.branch=main git.commit.id.abbrev=88ae4cd
=========== B/master
  real HEAD : main @ 88ae4cd
[ERROR] Failed to execute goal io.github.git-commit-id:git-commit-id-maven-plugin:10.0.0:revision (x) on project wt-repro: Git command exited with invalid status [128]: directory: `/tmp/tmp.hxld3BHkek/B/.bare/worktrees`, command: `git describe --always --dirty=-dirty --match=* --abbrev=7`, stdout: ``, stderr: `fatal: this operation must be run in a work tree
  reported  : (no git.properties written)

Expected: every row reports the branch and commit of the checkout the build ran in.

Two separate defects are visible:

  1. B/master fails outright — the bare-repository worktree layout is not recognised at all.
  2. A/wt silently reports the wrong commit — the worktree is recognised, but resolution lands
    on the main checkout, so the build is stamped with main @ 88ae4cd instead of
    feature @ 920ca94. This is
    worktree processing not complete git-commit-id-maven-plugin#882, and it shares the root cause below.

Is there a (public) project where this issue can be reproduced? (optional)

No response

Your Environment (optional)

No response

Context (optional)

Root cause

static File resolveWorktree(File fileLocation) {
  Path parent = fileLocation.toPath().getParent();
  if (parent == null) {
    return fileLocation;
  } 
  if (parent.endsWith(Path.of(".git", "worktrees"))) {
    return parent.getParent().toFile();
  } 
  return fileLocation;
} 
  • parent.endsWith(Path.of(".git", "worktrees")) fails for <bare>/worktrees/<name> whenever the
    repository directory is not named exactly .git.bare/worktrees/master, froggy.git/worktrees/x
    — which is defect 1. GitDirLocatorTest.testWorktreeResolution currently asserts
    "a.git/worktrees/b" as a noop case, so this is pinned by a test; I believe that assertion is
    itself the bug, since that path is exactly what git worktree add creates from a bare clone.
  • parent.getParent() is the main checkout's git directory, not the linked worktree's, which is
    defect 2.

Both come from inferring the layout from path names. Git records it explicitly instead:
<worktree-admin-dir>/gitdir holds the absolute path of that worktree's own .git file — its parent
is the working tree — and <worktree-admin-dir>/commondir points at the shared repository. Reading
those (or asking git rev-parse --path-format=absolute --git-common-dir --show-toplevel) fixes both
defects: native git would then run inside the worktree being built, which both succeeds for a bare
host repository and reports that worktree's own branch/commit.

Note on JGit

With <useNativeGit>false</useNativeGit> the same bare-repo layout fails differently —
Could not get HEAD Ref, are you sure you have some commits in the dotGitDirectory (currently set to <bare>/worktrees/<name>)? — consistent with the long-standing
git-commit-id/git-commit-id-maven-plugin#215. A fix to the native path won't cover JGit.

Workaround

Setting the environment explicitly makes native git resolve the right worktree and produces correct
properties:

GIT_DIR=$(git rev-parse --absolute-git-dir) GIT_WORK_TREE=$(git rev-parse --show-toplevel) mvn verify

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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