Skip to content

fix: resolve the checkout root correctly inside a git worktree - #218

Merged
josemarluedke merged 1 commit into
mainfrom
fix/worktree-edit-urls
Aug 21, 2026
Merged

fix: resolve the checkout root correctly inside a git worktree#218
josemarluedke merged 1 commit into
mainfrom
fix/worktree-edit-urls

Conversation

@josemarluedke

Copy link
Copy Markdown
Owner

Edit URLs are wrong for anyone running Docfy from a linked git worktree.

getRepoEditUrl computes path.relative(getRepoInfo(root).root, root). git-repo-info's own typings document the trap:

root: The root directory for the Git repo or submodule. If in a worktree, this is the directory containing the original copy, not the worktree.

So the relative path picks up the worktree's location and the URL points at a path that doesn't exist on the remote:

git-repo-info .root          : /Users/me/code/docfy            ← main checkout
git rev-parse --show-toplevel: /Users/me/code/docfy/.wt/branch ← actual worktree
→ relative = .wt/branch/docs   instead of   docs

This already caused damage

Four committed .gjs templates shipped with .claude/worktrees/<branch>/ baked into their @editUrl values — every "Edit this page" link on those demo pages 404'd until #214 fixed them by hand.

It is also the real reason repo-info.test.ts and generating-edit-url.test.ts fail locally. I spent most of the unified 11 work describing those as environmental noise and repeating that in PR descriptions. They were this bug, and treating them as noise is what let the bad templates through.

The fix

A worktree's metadata directory holds a gitdir file pointing at that worktree's .git file, whose parent is the top level we want.

One subtlety worth flagging for review: detecting a worktree requires comparing worktreeGitDir against commonGitDir, not checking worktreeGitDir for truthiness. It is always populated — outside a worktree it simply equals commonGitDir. My first pass got this wrong and would have taken the correction path always.

Regression test

CI runs in a normal checkout, so this fix could regress invisibly there. The new test builds a real repository and worktree in a temp directory and asserts both resolve identically.

I verified it actually catches the bug rather than just passing:

with fix without fix
edit url from the worktree …/edit/main/docs/{filepath} …/edit/**linked**/docs/{filepath}
edit url from the main checkout …/edit/main/docs/{filepath} …/edit/main/docs/{filepath}

The second row matters as much as the first — it shows the fix doesn't alter normal-checkout behaviour.

The test uses fs.realpathSync on the temp dir because os.tmpdir() is a symlink on macOS and git records the resolved path; comparing against the unresolved one would fail there but pass on Linux.

Result

@docfy/core now passes 59/59 across 12 files in a worktree, where 6 tests in 3 files used to fail. And building test-app-vite from a worktree no longer rewrites the committed templates — removing the manual path-stripping step #215 needed three separate times.

Also verified: pnpm -r run compile clean, @docfy/ember-vite 60/60, @docfy/plugin-with-prose 2/2, both app builds succeed, lint and prettier clean.

🤖 Generated with Claude Code

Edit URLs were wrong for anyone running Docfy from a linked git worktree.
`getRepoEditUrl` computed `path.relative(getRepoInfo(root).root, root)`, and
git-repo-info's own typings document that `root` "is the directory containing
the original copy, not the worktree" — so the relative path picked up the
worktree's location and produced links to paths that do not exist on the remote.

This is not hypothetical. It is why four committed .gjs templates shipped with
`.claude/worktrees/<branch>/` in their @editUrl values, fixed in #214, and it is
the real reason the repo-info and generating-edit-url tests failed locally. I
had been calling those failures environmental for most of this work; they were
this bug.

A worktree's metadata directory holds a `gitdir` file pointing at that
worktree's `.git` file, whose parent is the top level we want. Outside a
worktree `worktreeGitDir` equals `commonGitDir`, so there is nothing to correct
— note that detecting a worktree requires comparing the two rather than
checking `worktreeGitDir` for truthiness, since it is always populated.

Adds a regression test that builds a real repository and worktree in a temp
directory. CI runs in a normal checkout, so without it this fix could regress
invisibly. Confirmed the test fails without the fix (`edit/linked/docs/` instead
of `edit/main/docs/`) while its main-checkout counterpart still passes.

@docfy/core's suite now passes fully in a worktree — 59/59 across 12 files,
where 6 tests in 3 files used to fail. Building test-app-vite in a worktree no
longer rewrites the committed templates either, which removes the manual
path-stripping step that #215 needed three times.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@josemarluedke josemarluedke added the Type: Bug Something isn't working label Aug 21, 2026
@josemarluedke
josemarluedke merged commit 459d5a2 into main Aug 21, 2026
9 checks passed
@josemarluedke
josemarluedke deleted the fix/worktree-edit-urls branch August 21, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant