From e0b8dbf48c0dfd1039f2df743952f4032b5fc21b Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Mon, 20 Jul 2026 14:24:03 +0200 Subject: [PATCH] Resolve the refresh's file reads against its repo root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The refresh workers read a few files at paths relative to the process working directory: the submodule config read of .gitmodules, the files refresh's check for conflict markers, and the submodule stash's existence check. Git commands are pinned to the repo their instance was created for, but these Go file reads still followed the cwd, so a background refresh crossing a repo switch would read the new repo's files while computing data for the old one. Join them with the worktree root of the instance they belong to. (Most git-state file reads — working tree state, rebase todos, bisect info — already resolve against RepoPaths and need no change.) This also fixes the submodule stash's existence check for nested submodules: it stat'ed submodule.Path, which is relative to the parent module, against the repo root — now it uses the submodule's full path, matching the stash command right below it. Co-Authored-By: Claude Fable 5 --- pkg/commands/git_commands/submodule.go | 11 ++++++++--- pkg/gui/controllers/helpers/refresh_helper.go | 7 ++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pkg/commands/git_commands/submodule.go b/pkg/commands/git_commands/submodule.go index 3eb081701..d8c1208bc 100644 --- a/pkg/commands/git_commands/submodule.go +++ b/pkg/commands/git_commands/submodule.go @@ -28,10 +28,15 @@ func NewSubmoduleCommands(gitCommon *GitCommon) *SubmoduleCommands { } func (self *SubmoduleCommands) GetConfigs(parentModule *models.SubmoduleConfig) ([]*models.SubmoduleConfig, error) { - gitModulesPath := ".gitmodules" + // Resolve the path against the repo this commands object was created for + // rather than the process working directory, so that a read from a + // still-running refresh keeps addressing that repo after the user + // switched to another one. + dir := self.repoPaths.WorktreePath() if parentModule != nil { - gitModulesPath = filepath.Join(parentModule.FullPath(), gitModulesPath) + dir = filepath.Join(dir, parentModule.FullPath()) } + gitModulesPath := filepath.Join(dir, ".gitmodules") file, err := os.Open(gitModulesPath) if err != nil { if os.IsNotExist(err) { @@ -180,7 +185,7 @@ func (self *SubmoduleCommands) ConflictSideLog(path string, side string, otherSi func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error { // if the path does not exist then it hasn't yet been initialized so we'll swallow the error // because the intention here is to have no dirty worktree state - if _, err := os.Stat(submodule.Path); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(self.repoPaths.WorktreePath(), submodule.FullPath())); os.IsNotExist(err) { self.Log.Infof("submodule path %s does not exist, returning", submodule.FullPath()) return nil } diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 27f465f69..8ae62d9dd 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -1,6 +1,7 @@ package helpers import ( + "path/filepath" "strings" "sync" "sync/atomic" @@ -1273,7 +1274,11 @@ func (self *RefreshHelper) refreshStateFiles(captured capturedFilesState, env re prevConflictFileCount++ } if file.HasInlineMergeConflicts { - hasConflicts, err := mergeconflicts.FileHasConflictMarkers(file.Path) + // Join with the refresh's repo root rather than relying on the + // process working directory, which may already point at another + // repo if the user switched while this refresh was in flight. + hasConflicts, err := mergeconflicts.FileHasConflictMarkers( + filepath.Join(env.git.RepoPaths.WorktreePath(), file.Path)) if err != nil { self.c.Log.Error(err) } else if !hasConflicts {