mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Auto-dismiss the continue-rebase prompt when it becomes stale
The prompt offering to continue a rebase/merge is opened from a refresh and then left to sit until the user acts on it. But the operation can change out from under it: a coding agent (or the user in another terminal) might continue or abort it, or advance it to a commit with new conflicts. The prompt then becomes stale — pressing continue fails with "no rebase in progress" or acts on the wrong state. Track whether the prompt is showing, and on each refresh dismiss it if the operation is no longer in the "resolved, ready to continue" state that the prompt is offering to act on. This runs on the same refreshes that would open it (including the background poll and the refresh on window focus), so the prompt disappears on its own shortly after the operation moves on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ad507d67f4
commit
90d8ef499c
|
|
@ -18,6 +18,13 @@ import (
|
|||
|
||||
type MergeAndRebaseHelper struct {
|
||||
c *HelperCommon
|
||||
|
||||
// Whether the "continue the rebase/merge?" prompt is currently on screen.
|
||||
// We use this to auto-dismiss it if the operation stops being in the state
|
||||
// that the prompt is offering to act on (e.g. it was continued or aborted
|
||||
// externally), so the user isn't left with a stale prompt. Only accessed on
|
||||
// the UI thread.
|
||||
continueRebasePromptShowing bool
|
||||
}
|
||||
|
||||
func NewMergeAndRebaseHelper(
|
||||
|
|
@ -259,10 +266,17 @@ func (self *MergeAndRebaseHelper) AbortMergeOrRebaseWithConfirm() error {
|
|||
|
||||
// PromptToContinueRebase asks the user if they want to continue the rebase/merge that's in progress
|
||||
func (self *MergeAndRebaseHelper) PromptToContinueRebase() error {
|
||||
self.continueRebasePromptShowing = true
|
||||
self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.Continue,
|
||||
Prompt: fmt.Sprintf(self.c.Tr.ConflictsResolved, self.c.Git().Status.WorkingTreeState().CommandName()),
|
||||
HandleClose: func() error {
|
||||
self.continueRebasePromptShowing = false
|
||||
return nil
|
||||
},
|
||||
HandleConfirm: func() error {
|
||||
self.continueRebasePromptShowing = false
|
||||
|
||||
// By the time we get here, we might have unstaged changes again,
|
||||
// e.g. if the user had to fix build errors after resolving the
|
||||
// conflicts, but after lazygit opened the prompt already. Ask again
|
||||
|
|
@ -300,6 +314,27 @@ func (self *MergeAndRebaseHelper) PromptToContinueRebase() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DismissContinueRebasePromptIfShowing closes the "continue the rebase/merge?"
|
||||
// prompt if it's currently on screen. It's called when the operation is no
|
||||
// longer in the state the prompt is offering to act on (e.g. it was continued
|
||||
// or aborted outside lazygit, or new conflicts have appeared), so that the
|
||||
// user isn't left with a prompt whose "continue" would now be wrong or fail.
|
||||
// Must be called on the UI thread.
|
||||
func (self *MergeAndRebaseHelper) DismissContinueRebasePromptIfShowing() {
|
||||
if !self.continueRebasePromptShowing {
|
||||
return
|
||||
}
|
||||
|
||||
self.continueRebasePromptShowing = false
|
||||
|
||||
// Guard against popping something else: while our prompt is up no other
|
||||
// popup can open, and confirming or closing it would have cleared the flag,
|
||||
// so if it's set the confirmation context is ours.
|
||||
if self.c.Context().Current() == self.c.Contexts().Confirmation {
|
||||
self.c.Context().Pop()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
||||
checkedOutBranch := self.c.Model().Branches[0]
|
||||
checkedOutBranchName := checkedOutBranch.Name
|
||||
|
|
|
|||
|
|
@ -807,16 +807,30 @@ func (self *RefreshHelper) refreshStateFiles(background bool) error {
|
|||
}
|
||||
|
||||
repoState := self.c.State().GetRepoState()
|
||||
if self.c.Git().Status.WorkingTreeState().None() {
|
||||
workingTreeState := self.c.Git().Status.WorkingTreeState()
|
||||
if workingTreeState.None() {
|
||||
// No operation is in progress (any more), so forget that we started one.
|
||||
// This also covers an operation that was finished or aborted externally.
|
||||
repoState.SetMergeOrRebaseStartedInLazygit(false)
|
||||
} else if conflictFileCount == 0 && prevConflictFileCount > 0 && repoState.GetMergeOrRebaseStartedInLazygit() {
|
||||
// The conflicts of an operation we started have just been resolved (e.g.
|
||||
// in the user's editor). Offer to continue it. We only do this for
|
||||
// operations we started ourselves; prompting for one that was started
|
||||
// outside lazygit (e.g. by a coding agent) would be confusing.
|
||||
self.c.OnUIThread(func() error { return self.mergeAndRebaseHelper.PromptToContinueRebase() })
|
||||
}
|
||||
|
||||
if workingTreeState.Any() && conflictFileCount == 0 {
|
||||
if prevConflictFileCount > 0 && repoState.GetMergeOrRebaseStartedInLazygit() {
|
||||
// The conflicts of an operation we started have just been resolved
|
||||
// (e.g. in the user's editor). Offer to continue it. We only do this
|
||||
// for operations we started ourselves; prompting for one that was
|
||||
// started outside lazygit (e.g. by a coding agent) would be confusing.
|
||||
self.c.OnUIThread(func() error { return self.mergeAndRebaseHelper.PromptToContinueRebase() })
|
||||
}
|
||||
} else {
|
||||
// Either there's no operation in progress any more, or new conflicts have
|
||||
// appeared. Either way, a "continue?" prompt we're showing is now stale
|
||||
// (e.g. the operation was continued or aborted outside lazygit), so
|
||||
// dismiss it rather than leave the user with a prompt that would fail.
|
||||
self.c.OnUIThread(func() error {
|
||||
self.mergeAndRebaseHelper.DismissContinueRebasePromptIfShowing()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
fileTreeViewModel.RWMutex.Lock()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
var ContinuePromptDismissedWhenResolvedExternally = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "When the prompt to continue a merge is showing and the merge is then continued outside lazygit, dismiss the prompt",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shared.CreateMergeConflictFile(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Common().PretendMergeOrRebaseStartedInLazygit()
|
||||
|
||||
// Resolve the conflict and refresh so lazygit prompts us to continue.
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU file").IsSelected(),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Shell().UpdateFile("file", "resolved content")
|
||||
}).
|
||||
Press(keys.Universal.Refresh)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Continue")).
|
||||
Content(Contains("All merge conflicts resolved. Continue the merge?"))
|
||||
|
||||
// While the prompt is up, the merge is continued outside lazygit (e.g. by
|
||||
// a coding agent).
|
||||
t.Shell().ContinueMerge()
|
||||
|
||||
// Simulate lazygit noticing the change (as it would on its next refresh or
|
||||
// when the window regains focus); the stale prompt is dismissed.
|
||||
t.FocusIn()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
IsEmpty()
|
||||
|
||||
t.Views().Information().Content(DoesNotContain("Merging"))
|
||||
},
|
||||
})
|
||||
|
|
@ -163,6 +163,7 @@ var tests = []*components.IntegrationTest{
|
|||
config.NegativeRefspec,
|
||||
config.RemoteNamedStar,
|
||||
config.SidePanelsInPerRepoConfig,
|
||||
conflicts.ContinuePromptDismissedWhenResolvedExternally,
|
||||
conflicts.Filter,
|
||||
conflicts.MergeFileBoth,
|
||||
conflicts.MergeFileCurrent,
|
||||
|
|
|
|||
Loading…
Reference in a new issue