Capture moveCommitsToNewBranch's model reads on the UI thread

The two move helpers run inside the WithWaitingStatus worker that
withNewBranchNamePrompt dispatches to, but read Model().Files/Submodules
(to decide whether to auto-stash) and Model().Commits (the unpushed
commits to cherry-pick off the base branch) from there, racing the UI
thread's model writes. Compute mustStash — needed by both paths — at the
top, and the unpushed commits in the off-of-main menu item, on the UI
thread, and pass them into the helpers.
This commit is contained in:
Stefan Haller 2026-07-06 17:26:30 +02:00
parent b4a976834f
commit fceba31212

View file

@ -424,6 +424,8 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error {
return err
}
mustStash := IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
withNewBranchNamePrompt := func(baseBranchName string, f func(string) error) error {
prompt := utils.ResolvePlaceholderString(
self.c.Tr.NewBranchNameBranchOff,
@ -462,7 +464,9 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error {
Title: self.c.Tr.MoveCommitsToNewBranch,
Prompt: prompt,
HandleConfirm: func() error {
return withNewBranchNamePrompt(currentBranch.Name, self.moveCommitsToNewBranchStackedOnCurrentBranch)
return withNewBranchNamePrompt(currentBranch.Name, func(newBranchName string) error {
return self.moveCommitsToNewBranchStackedOnCurrentBranch(newBranchName, mustStash)
})
},
})
return nil
@ -482,27 +486,31 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error {
{
Label: fmt.Sprintf(self.c.Tr.MoveCommitsToNewBranchFromBaseItem, shortBaseBranchName),
OnPress: func() error {
commitsToCherryPick := lo.Filter(self.c.Model().Commits, func(commit *models.Commit, _ int) bool {
return commit.Status == models.StatusUnpushed
})
return withNewBranchNamePrompt(shortBaseBranchName, func(newBranchName string) error {
return self.moveCommitsToNewBranchOffOfMainBranch(newBranchName, baseBranchRef)
return self.moveCommitsToNewBranchOffOfMainBranch(newBranchName, baseBranchRef, commitsToCherryPick, mustStash)
})
},
},
{
Label: fmt.Sprintf(self.c.Tr.MoveCommitsToNewBranchStackedItem, currentBranch.Name),
OnPress: func() error {
return withNewBranchNamePrompt(currentBranch.Name, self.moveCommitsToNewBranchStackedOnCurrentBranch)
return withNewBranchNamePrompt(currentBranch.Name, func(newBranchName string) error {
return self.moveCommitsToNewBranchStackedOnCurrentBranch(newBranchName, mustStash)
})
},
},
},
})
}
func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(newBranchName string) error {
func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(newBranchName string, mustStash bool) error {
if err := self.c.Git().Branch.NewWithoutCheckout(newBranchName, "HEAD"); err != nil {
return err
}
mustStash := IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
if mustStash {
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
return err
@ -532,12 +540,7 @@ func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(newBranchNa
return nil
}
func (self *RefsHelper) moveCommitsToNewBranchOffOfMainBranch(newBranchName string, baseBranchRef string) error {
commitsToCherryPick := lo.Filter(self.c.Model().Commits, func(commit *models.Commit, _ int) bool {
return commit.Status == models.StatusUnpushed
})
mustStash := IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
func (self *RefsHelper) moveCommitsToNewBranchOffOfMainBranch(newBranchName string, baseBranchRef string, commitsToCherryPick []*models.Commit, mustStash bool) error {
if mustStash {
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
return err