Run the immediate startup branch refresh before spawning the async one

At the INITIAL startup stage two branch refreshes happen: an immediate
one sorted by whatever reflog we have (empty, so not by recency), and an
async one that loads the reflog first and re-sorts by recency. Until now
the async one was spawned first and the immediate one ran afterwards;
this inverts that so the immediate refresh runs before the async one is
spawned.

With RefreshingBranchesMutex still in place this is behavior-preserving
(the mutex serializes the two either way). It's a preparatory step for
replacing that mutex with a branch-load sequence guard: running the
immediate refresh first establishes a happens-before relation between
the two loads' sequence numbers, so the recency-sorted one is guaranteed
the higher sequence.

This also lets refreshReflogCommitsConsideringStartup fold into
refreshReflogAndBranches, whose two-phase logic is now all in one place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-07-03 17:35:44 +02:00
parent 805738034f
commit 6d8ab1d063

View file

@ -377,19 +377,18 @@ func getModeName(mode types.RefreshMode) string {
}
}
// during startup, the bottleneck is fetching the reflog entries. We need these
// on startup to sort the branches by recency. So we have two phases: INITIAL, and COMPLETE.
// In the initial phase we don't get any reflog commits, but we asynchronously get them
// and refresh the branches after that
// refreshReflogCommitsConsideringStartup returns the reflog commits that the
// caller should hand to refreshBranches for recency sorting. In the COMPLETE
// (normal) case that's the freshly-loaded reflog; in the INITIAL case the
// reflog is loaded asynchronously (and drives its own branches refresh once
// ready), so we return the current model value for the immediate,
// non-recency-sorted branches refresh the caller does in the meantime.
func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() []*models.Commit {
// During startup, the bottleneck is fetching the reflog entries, which we need
// in order to sort the branches by recency. So we have two phases: INITIAL and
// COMPLETE. In the INITIAL phase we don't have any reflog commits yet, so we
// show the branches right away sorted by whatever we have (typically nothing,
// i.e. not by recency), then load the reflog on a worker and refresh the
// branches again, this time recency-sorted. From then on we're in the COMPLETE
// phase and load the reflog synchronously before refreshing the branches.
func (self *RefreshHelper) refreshReflogAndBranches(refreshWorktrees bool, keepBranchSelectionIndex bool) {
switch self.c.State().GetRepoState().GetStartupStage() {
case types.INITIAL:
self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, false, self.c.Model().ReflogCommits)
self.c.OnWorker(func(_ gocui.Task) error {
reflogCommits, _ := self.refreshReflogCommits()
self.refreshBranches(false, true, true, reflogCommits)
@ -397,22 +396,10 @@ func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() []*models.Co
return nil
})
return self.c.Model().ReflogCommits
case types.COMPLETE:
reflogCommits, _ := self.refreshReflogCommits()
return reflogCommits
self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, true, reflogCommits)
}
return self.c.Model().ReflogCommits
}
func (self *RefreshHelper) refreshReflogAndBranches(refreshWorktrees bool, keepBranchSelectionIndex bool) {
loadBehindCounts := self.c.State().GetRepoState().GetStartupStage() == types.COMPLETE
reflogCommits := self.refreshReflogCommitsConsideringStartup()
self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, loadBehindCounts, reflogCommits)
}
func (self *RefreshHelper) refreshCommitsAndCommitFiles(commitSelection types.CommitSelectionBehavior) {