jesseduffield.lazygit/pkg/gui/controllers/helpers/sub_commits_helper.go
Stefan Haller 805738034f Remove refresh mutexes made redundant by bouncing
Now that every refresh scope writes its model updates on the UI thread
via onUIThreadUnlessRepoChanged, the per-scope mutexes that used to
serialize concurrent worker-goroutine access are redundant:
Model().Commits, .SubCommits, .Authors, the status view content, and
.PullRequests/.PullRequestsMap are all now written only on the UI
thread, and their readers already ran there. setSubCommits only existed
to take the lock, so it's inlined to match refreshSubCommitsWithLimit,
which writes Model().SubCommits directly.

The worker phases still *read* some of these fields (the commit
selection range, MergeRebasingCommits), but those reads race a
concurrent refresh's bounced write regardless of the mutex — the write
happens in the bounce, outside the locked region — so the mutex never
protected them. That residual read race belongs to the broader -race
effort, not to these locks.

RefreshingBranchesMutex is deliberately kept. It is load-bearing for a
reason unrelated to data races: at the INITIAL startup stage two
refreshBranches run concurrently — an immediate one with an empty
reflog (non-recency order) and an async one with the freshly-loaded
reflog (recency order). The mutex serializes them so the recency write's
bounce is enqueued last and wins. Without it the stale non-recency write
can land last, reordering the branches list (caught by the recency-sort
e2e tests: cherry_pick/*, branch/rebase_*).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:09:33 +02:00

74 lines
2.2 KiB
Go

package helpers
import (
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type SubCommitsHelper struct {
c *HelperCommon
refreshHelper *RefreshHelper
}
func NewSubCommitsHelper(
c *HelperCommon,
refreshHelper *RefreshHelper,
) *SubCommitsHelper {
return &SubCommitsHelper{
c: c,
refreshHelper: refreshHelper,
}
}
type ViewSubCommitsOpts struct {
Ref models.Ref
RefToShowDivergenceFrom string
TitleRef string
Context types.Context
ShowBranchHeads bool
}
func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
git_commands.GetCommitsOptions{
Limit: true,
FilterPath: self.c.Modes().Filtering.GetPath(),
FilterAuthor: self.c.Modes().Filtering.GetAuthor(),
IncludeRebaseCommits: false,
RefName: opts.Ref.FullRefName(),
RefForPushedStatus: opts.Ref,
RefToShowDivergenceFrom: opts.RefToShowDivergenceFrom,
MainBranches: self.c.Model().MainBranches,
HashPool: self.c.Model().HashPool,
},
)
if err != nil {
return err
}
self.c.Model().SubCommits = commits
self.refreshHelper.RefreshAuthors(commits)
subCommitsContext := self.c.Contexts().SubCommits
subCommitsContext.SetSelection(0)
subCommitsContext.SetParentContext(opts.Context)
subCommitsContext.SetWindowName(opts.Context.GetWindowName())
subCommitsContext.SetTitleRef(utils.TruncateWithEllipsis(opts.TitleRef, 50))
subCommitsContext.SetRef(opts.Ref)
subCommitsContext.SetRefToShowDivergenceFrom(opts.RefToShowDivergenceFrom)
subCommitsContext.SetLimitCommits(true)
subCommitsContext.SetShowBranchHeads(opts.ShowBranchHeads)
subCommitsContext.ClearSearchString()
subCommitsContext.GetView().ClearSearch()
subCommitsContext.GetView().TitlePrefix = opts.Context.GetView().TitlePrefix
self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
subCommitsContext.FocusLine(true)
self.c.Context().Push(self.c.Contexts().SubCommits, types.OnFocusOpts{})
return nil
}