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 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-07-17 13:37:23 +02:00
parent 4cf12a5b7b
commit 5055c4fb65

View file

@ -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
})