[Bug]: Bare-root worktree layouts are silently accepted as a normal repo, making the project root the parent of every worktree#8164

openedAug 25, 2026byTtettoffensive

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

A common layout for worktree-heavy work puts a bare repo at <root>/.bare, a <root>/.git file pointing at it, and every branch as a sibling checkout — no primary working copy:

sh
1mkdir myproj && cd myproj
2git clone --bare git@github.com:owner/repo.git .bare
3echo "gitdir: ./.bare" > .git
4git worktree add develop develop
5git worktree add feature-x -b feature-x

Result:

1myproj/
2├── .bare/
3├── .git → gitdir: ./.bare
4├── develop/
5└── feature-x/
  1. Add myproj/ as a project in T3 Code.
  2. Open the Changes/Diff view, or start a thread.

Expected behavior

The root is rejected at add time with a message explaining that it is a bare-root layout and that an individual worktree should be added instead. Adding myproj/develop directly already works correctly today, so the fix is to stop accepting a root that cannot be a coherent workspace — not to add support for the layout itself.

Actual behavior

The root is accepted as an ordinary repository with no warning. detectRepository in apps/server/src/vcs/GitVcsDriver.ts gates on git rev-parse --is-inside-work-tree, and because the clone leaves core.bare=false, git reports:

1$ git -C myproj rev-parse --is-inside-work-tree --show-toplevel --git-common-dir
2true
3/path/to/myproj
4/path/to/myproj/.bare

So rootPath becomes myproj — the parent directory of every worktree. Downstream that means git status treats develop/ and feature-x/ as untracked entries, the diff/changes view is scoped to a directory that contains N independent checkouts, and any thread started there runs an agent whose cwd spans all of them. .bare/ itself is correctly rejected (--is-inside-work-tree is false), so the failure is specific to the root.

There is no bare-specific handling anywhere in the git layer — GitManager.ts mentions "bare" once, about branch names.

Impact

Minor bug or occasional failure

Version or commit

0.0.33 (macOS desktop)

Environment

macOS 26.5.2, git 2.50.1

Logs or stack traces

shell
1

Screenshots, recordings, or supporting files

No response

Workaround

Add each worktree directory as its own project and never add the root.

detectRepository already calls git rev-parse --git-common-dir alongside --show-toplevel, so the detection needs no new git calls: a common dir that sits inside rootPath but is not rootPath/.git identifies this layout.

I reran the exact bare-clone layout with real Git against main 89ee69e44 on Linux, September 5, 2026 UTC. One distinction matters: git clone --bare initially leaves core.bare=true, so detectRepository(root) rejects it. Calling T3's initRepository(root) then changes it to false; the parent is accepted and the sibling worktrees appear as untracked entries. Adding an individual worktree works. The macOS client add/start flow still needs integrated verification.

The existing candidate #8646 is not ready. I tested its full patch rebased onto main 82f64cd8 and found two real-Git counterexamples to the missing-index heuristic: it rejects a valid unborn separate-git-dir checkout after an orphan linked worktree is created, and it accepts a genuinely bare root after git read-tree --empty creates an index. The native core.bare values remain unchanged in both controls. The reproduction details are on the PR. These classification failures need fixing before a maintainer decides the rejection behavior.

GPT 6 Astra via Codex in T3 Code.

Reproduced on ed9b7ca9c5 with the web dev server on Linux and Git 2.43.0. With the reported core.bare=false configuration, adding the parent succeeds and the app counts .bare/ and sibling checkouts as changes. The literal clone --bare recipe leaves core.bare=true here; that parent is instead shown as a non-Git project. No matching open issue or fix PR found.