From 5055c4fb654b1780330f941c27e828fbf44ebcd8 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 17 Jul 2026 13:37:23 +0200 Subject: [PATCH] Fetch GitHub pull requests as a background task Every full refresh includes the PULL_REQUESTS scope, and the worker it spawns inherited the refresh's foreground/background flag. Full foreground refreshes happen at startup, after switching repos or worktrees, and when the terminal regains focus, so the GitHub API request ran as a foreground task there, keeping Busy() true until it completed. On a healthy network that's a few hundred milliseconds and nobody notices; on a very slow one the request can stall for minutes, and every attempt to switch repos in that window was refused with "Can't switch repositories while an operation is in progress" even though lazygit looked completely idle. (The request has no visible status; at most, a background fetch hanging on the same bad network was showing its "Fetching..." spinner, pointing the blame at the wrong operation.) The switch-safety guard only needs to wait for operations whose remaining git commands would run against the wrong repo after a switch. The pull-request fetch runs no git commands at all, and its model writes are dropped when the repo generation has changed in the meantime, so there is no reason for it to block switching. Co-Authored-By: Claude Fable 5 --- pkg/gui/controllers/helpers/refresh_helper.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 4fedf9c7d..a7c18aeb8 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -365,7 +365,17 @@ func (self *RefreshHelper) performRefresh(options types.RefreshOptions, calledFr } if scopeSet.Includes(types.PULL_REQUESTS) { - self.onWorker(env.background, func(gocui.Task) error { + // Fetching pull requests talks to the GitHub API over the network; on + // a bad connection that request can stall for a long time. It runs no + // git commands against the repo, and its model writes are guarded by + // the repo generation (a repo switch mid-fetch simply drops the + // result), so it is safe to run as a background task even when the + // enclosing refresh is a foreground one — a foreground task would + // block repo switching for as long as the request takes. The env copy + // makes the downstream UI-thread bounces background as well. + prEnv := env + prEnv.background = true + self.c.OnWorkerBackground(func(gocui.Task) error { branchesAndRemotesWg.Wait() t := time.Now() @@ -373,7 +383,7 @@ func (self *RefreshHelper) performRefresh(options types.RefreshOptions, calledFr // Model().Branches/Remotes: those writes are bounced onto the // UI thread and may not have landed on this worker yet. The // wait above orders us after both loads have stashed theirs. - self.refreshGithubPullRequests(loadedBranches, loadedRemotes, env) + self.refreshGithubPullRequests(loadedBranches, loadedRemotes, prEnv) self.c.Log.Infof("refreshed pull requests in %s", time.Since(t)) return nil })