#7798·oh-my-posh

.Remotes, .WorktreeCount and .RepoName are wrong outside a repository's main working tree

Author: e-kulikovCreated Aug 13, 2026Updated Aug 13, 2026

Code of Conduct

  • I agree to follow this project's Code of Conduct

What happened?

Measured across ten positions (plain clone; a clone with two linked worktrees; --separate-git-dir plus its own linked worktree; a bare-via-.git-file layout plus its worktree; a submodule plus its own linked worktree):

Position .Ref .WorktreeCount len .Remotes .RepoName
plain clone main 0 2 correct
clone + worktrees, main working tree main 2 2 correct
linked worktree of that clone ok 0 ✗ (2) 0 ✗ (2) correct
--separate-git-dir clone main 0 0 ✗ (2) correct
linked worktree of that correct branch 0 ✗ (1) 0 ✗ (2) empty ✗
bare-via-.git-file, root empty ✗ 0 ✗ (1) 1 correct
linked worktree of that main 0 ✗ (1) 0 ✗ (1) empty ✗
submodule, primary main 1 2 correct
linked worktree of a submodule correct branch 1 0 ✗ (2) empty ✗

The worst case: without fetch_status, a submodule's linked worktree displays the primary submodule's branch, not its own. The checkout is on subfeature; the prompt says main. Verified directly — the common module directory's HEAD holds ref: refs/heads/main, the worktree admin directory's HEAD holds ref: refs/heads/subfeature, and the segment reads the former.

Root cause: hasWorktree (src/segments/git.go:450-525) repoints g.scmDir to the common git directory in a linked worktree but leaves g.mainSCMDir at the per-worktree metadata directory (or, in --separate-git-dir/bare layouts, at the working-tree root) — the opposite of what a submodule's own linked worktree needs, where the common-vs-per-worktree split runs the other way. Every reader that needs the common directory but is handed the wrong one breaks the same way:

  • getGitConfig() (:624-643) reads config from mainSCMDirRemotes() (:1239-1259) returns the empty map wherever that's wrong, with no fallback (unlike getRemoteURL()/getPushRemote()).
  • WorktreeCount() (:1108-1128) looks for <mainSCMDir>/worktrees, a path that only exists in the main working tree of a plain clone.
  • repoName() (:1285-1296) searches mainSCMDir for the literal substring .git/worktrees — empty in any layout whose git directory isn't literally named .git (e.g. a .bare layout), or whose common directory isn't where the search expects.

This is not an exotic-layout problem: tools that automate worktrees (agent sessions, git-town, ad-hoc scripts) create them in a directory of their own choosing — the repository stays an ordinary clone, and all three fields break as soon as the prompt renders inside one of those worktrees.

Expected: .Remotes, .WorktreeCount and .RepoName read from the repository's actual common git directory in every layout, and a submodule's linked worktree reports its own branch, not its parent's.

Theme

yaml
version: 3
final_space: false
blocks:
  - type: prompt
    alignment: left
    segments:
      - type: git
        style: plain
        properties:
          fetch_status: true
        template: 'Ref={{ .Ref }} Remotes={{ len .Remotes }} WT#={{ .WorktreeCount }} Repo={{ .RepoName }}'
bash
git clone <url> repo
git -C repo worktree add /somewhere/else/feature feature
oh-my-posh print primary --plain --config ./wt.omp.yaml --pwd /somewhere/else/feature --shell zsh
# Remotes=0 WT#=0 — both wrong; git -C /somewhere/else/feature remote -v and
# git -C /somewhere/else/feature worktree list show the truth

What OS are you seeing the problem on?

Linux, macOS, Windows

Which shell are you using?

zsh

Log output

[TRACE] terminal.go:FileContent(/repo/.git/worktrees/feature/config) - 12µs   # no such file
[DEBUG] git.go:getGitConfig → error memoized in sync.Once; Remotes() returns the empty map
[DEBUG] terminal.go:HasFolder(/repo/.git/worktrees/feature/worktrees) → false; WorktreeCount() = 0

Source: JanDeDobbeleer/oh-my-posh