Skip to content

fix(segment): resolve a relative gitdir: pointer against its .git file's folder - #7801

Merged
JanDeDobbeleer merged 9 commits into
JanDeDobbeleer:claude/msix-appinstaller-auto-update-4893ryfrom
e-kulikov:fix/git-relative-gitdir
Aug 25, 2026
Merged

fix(segment): resolve a relative gitdir: pointer against its .git file's folder#7801
JanDeDobbeleer merged 9 commits into
JanDeDobbeleer:claude/msix-appinstaller-auto-update-4893ryfrom
e-kulikov:fix/git-relative-gitdir

Conversation

@e-kulikov

Copy link
Copy Markdown
Contributor

Fixes #7797.

Prerequisites

  • I have read and understood the contributing guide.
  • The commit message follows the conventional commits guidelines.
  • Tests for the changes have been added.
  • Docs have been added/updated where relevant.

Description

hasWorktree stored a relative gitdir: pointer raw and only made it absolute when it started with
.., so any other relative spelling (./.bare, .bare, subdir/git) resolved against the process's
working directory instead of the directory holding the .git file — the segment simply vanished
outside that one directory, with no error. isBareRepo had the same class of bug through a bare
filepath.Join on an already-absolute second argument.

This PR:

  • resolves the pointer unconditionally and consistently in both hasWorktree and isBareRepo,
  • replaces the strings.LastIndex(mainSCMDir, "/worktrees/") substring test with a structural
    classifier (worktreeAdminIndex) that validates the match is a genuine worktree administrative
    directory rather than any path merely containing the word,
  • extends the same idea to the /modules/ submodule classifier, which had an equivalent
    false-positive: a --separate-git-dir or bare pointer under a path containing a modules component
    was misclassified as a submodule. The new isModuleAdminDir verifies a core.worktree back-reference
    to the checkout instead of testing the raw string.

None of this changes behaviour for any layout that resolves correctly today — verified with new tests
covering plain clones, linked worktrees, submodules, submodule worktrees, bare repositories via a .git
file, and --separate-git-dir, from multiple process working directories.

This is part of a stacked series

This branch is the bottom of a four-part stack fixing related defects in the git segment's directory
handling, all opened against main:

main
 └─ fix/git-relative-gitdir            (this PR)      → #7797
     └─ fix/git-worktree-common-dir                    → #7798
         └─ fix/git-bare-head-upstream                 → #7799
             └─ feat/statusline-payload-pwd             → #7800

Each of the other three branches is rebased/merged on top of this one, so their diffs will include
this PR's commits until it merges — that's expected for a stack on plain GitHub (no base-branch
retargeting available across forks). This PR itself has no dependency and can be reviewed and merged
on its own.

@github-actions

Copy link
Copy Markdown

📦 Release binary size report

Compares this PR's release-equivalent build against the latest published release, per OS (amd64).

OS Baseline This PR Delta
darwin 13.79 MB 13.79 MB -0.0 KB (+0%)
linux 13.46 MB 13.45 MB -4.0 KB (-0.03%)
windows 14.02 MB 13.98 MB -39.8 KB (-0.28%)

🎉 Binary size shrank on at least one platform.

@JanDeDobbeleer
JanDeDobbeleer changed the base branch from main to claude/msix-appinstaller-auto-update-4893ry August 25, 2026 09:34
e-kulikov and others added 9 commits August 25, 2026 11:34
Define and test a normalized worktrees/<name> shape check before
discovery starts using it. This isolates classification from filesystem
probing and covers Unix and Windows separator styles.
The old fixture conflates gitfile contents with the directory discovery
must probe and models worktree back-references inconsistently. Separate
those roles so later fixes detect the wrong resolution base without
pinning directory ownership reserved for D3.
Path components named worktrees are not sufficient evidence of a linked
worktree. Normalize the admin path and require its gitdir metadata to
resolve back to the discovered checkout before setting IsWorkTree.
Git resolves relative gitdir pointers from the directory containing the
.git file, not from the process working directory. Convert first,
resolve every pointer, and retain the raw value for submodule detection.
filepath.Join concatenates absolute gitdir pointers and can poison the
memoized config read. Initialize WSL command state first, then convert
and resolve the pointer before reading core.bare.
A .git pointer whose spelling merely contains a modules path component
took the submodule branch, so `git init --separate-git-dir` aimed at
/srv/modules/project.git had repoRootDir set to the git dir instead of
the working tree root and rendered .RepoName as "project.git".

The shape rule that classifies /worktrees/ does not transfer: a
submodule's name is its path, so it may contain separators, it may nest,
and the folder in front of modules is only called .git when the
superproject has no --separate-git-dir of its own. What does hold is that
git records core.worktree in a submodule's git dir and never in a
--separate-git-dir target, so resolving it back to the checkout is the
same kind of proof the worktree branch takes from its gitdir metadata.

Detection stays on the raw pointer. Testing the resolved path instead
would pull every genuine worktree below a folder named modules into this
branch, which is the regression the /worktrees/ ordering already avoids.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The parameter was called path, shadowing the runtime/path package that
git.go imports, which gocritic's importShadow flags.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Go 1.26 lets new take an expression, which the modernize check in CI
flags the helper and all 26 of its call sites for. The module already
requires go 1.26.0, so the builtin is available.

This step never ran in CI on this branch: the Fieldalignment step above it
failed first and aborted the job, so the finding only surfaced locally.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TestEnabledInBareLayout and TestIsBareRepoResolvesPointer hardcoded
"/repo" instead of following the platform-aware TestRootPath convention
every other test in the file uses, which failed the windows-latest run.

filepath.IsAbs is the real one, not the mocked GOOS, so on a Windows
runner "/repo/.bare" has no volume name and IsAbs returns false. Checked
against go1.26.5 internal/filepathlite: volumeNameLen("/repo/.bare") is 0,
while "C:/repo/.bare" gives 2 and the remainder starts with a separator,
so both asserted fields are absolute on either platform now.

TestIsBareRepoResolvesPointer did not fail, but only by accident:
"/repo/.bare" took the Windows disk-relative branch of resolveGitPath and
landed on the same string, so its "absolute pointer" case never exercised
the absolute branch it names. Its expected config path and the path the
old filepath.Join produced are now derived from the pointer instead of
written out, so they cannot drift apart per platform.

Field order in the case struct follows fieldalignment, which CI runs
separately from golangci-lint. Its -fix pass drops comments, so the field
documentation is restored by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JanDeDobbeleer
JanDeDobbeleer force-pushed the fix/git-relative-gitdir branch from 831f28d to 2f9ea29 Compare August 25, 2026 09:34
@JanDeDobbeleer
JanDeDobbeleer merged commit a9a2d8a into JanDeDobbeleer:claude/msix-appinstaller-auto-update-4893ry Aug 25, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Relative gitdir: pointer resolves against the process's working directory, not the .git file's own directory

2 participants