Show a waiting status while creating a branch

Creating a branch checks it out, and checking out a distant ref (a tag
or a commit far from HEAD) can take a noticeable while. NewBranch ran
that synchronously in the prompt's confirm handler, on the UI thread, so
the UI froze — no spinner, no repaint — until it finished.

Move the branch creation (and the autostash path) onto a worker with a
waiting status, mirroring CheckoutRef, and refresh from the worker so
the UI thread stays live and the spinner keeps animating.

Push the branches context from the refresh's Then rather than up front:
the refresh already batches its UI updates, so switching panels there
lands the switch in the same frame as the refreshed branch list instead
of flashing the pre-refresh list while the checkout is still running.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-07-08 15:46:57 +02:00
parent bfd3b7b47e
commit 63bd2d98c0
2 changed files with 48 additions and 30 deletions

View file

@ -364,16 +364,22 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest
}
refresh := func() {
if self.c.Context().Current() != self.c.Contexts().Branches {
self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{})
}
self.c.Refresh(types.RefreshOptions{
self.c.RefreshFromWorker(types.RefreshOptions{
Mode: types.SYNC,
BatchUIUpdates: true,
BranchSelection: types.SelectCheckedOutBranch,
CommitSelection: types.SelectHeadCommit,
SelectTopReflogCommit: true,
Then: func() error {
// Switch to the branches panel only now, in the same batched
// frame that applies the refreshed data, so the panel switch
// and the new branch appear together rather than flashing the
// old branch list while the checkout is still in progress.
if self.c.Context().Current() != self.c.Contexts().Branches {
self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{})
}
return nil
},
})
}
@ -387,34 +393,44 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest
if newBranchName != suggestedBranchName {
newBranchFunc = self.c.Git().Branch.NewWithoutTracking
}
if err := newBranchFunc(newBranchName, from); err != nil {
if IsSwitchBranchUncommittedChangesError(err) {
// offer to autostash changes
self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.AutoStashTitle,
Prompt: self.c.Tr.AutoStashPrompt,
HandleConfirm: func() error {
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
return err
}
if err := newBranchFunc(newBranchName, from); err != nil {
return err
}
err := self.c.Git().Stash.Pop(0)
// Branch switch successful so re-render the UI even if the pop operation failed (e.g. conflict).
refresh()
return err
},
})
return nil
// Creating the branch checks it out, which can take a while when
// the ref we're branching off is distant, so do it on a worker.
return self.c.WithWaitingStatus(self.c.Tr.CreatingBranchStatus, func(gocui.Task) error {
if err := newBranchFunc(newBranchName, from); err != nil {
if IsSwitchBranchUncommittedChangesError(err) {
// offer to autostash changes
self.c.OnUIThread(func() error {
self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.AutoStashTitle,
Prompt: self.c.Tr.AutoStashPrompt,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.CreatingBranchStatus, func(gocui.Task) error {
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
return err
}
if err := newBranchFunc(newBranchName, from); err != nil {
return err
}
err := self.c.Git().Stash.Pop(0)
// Branch switch successful so re-render the UI even if the pop operation failed (e.g. conflict).
refresh()
return err
})
},
})
return nil
})
return nil
}
return err
}
return err
}
refresh()
return nil
refresh()
return nil
})
},
})

View file

@ -427,6 +427,7 @@ type TranslationSet struct {
UndoingStatus string
RedoingStatus string
CheckingOutStatus string
CreatingBranchStatus string
CommittingStatus string
RewordingStatus string
RevertingStatus string
@ -1576,6 +1577,7 @@ func EnglishTranslationSet() *TranslationSet {
UndoingStatus: "Undoing",
RedoingStatus: "Redoing",
CheckingOutStatus: "Checking out",
CreatingBranchStatus: "Creating branch",
CommittingStatus: "Committing",
RewordingStatus: "Rewording",
RevertingStatus: "Reverting",