Skip to content

Commit 4ec9262

Browse files
authored
Ask to auto-stage unstaged files when continuing a rebase after resolving conflicts (#3879)
- **PR Description** When lazygit sees that all conflicts were resolved, it auto-stages all previously conflicted files and asks to continue the rebase. (There is [a PR](#3870) open to make this optional.) It is a common situation for this popup to be opened in the background, while the user is still busy fixing build errors in their editor. In this case, coming back to lazygit and confirming the continue prompt would result in an error because not all files are staged. Improve this by opening another popup in this case, asking to stage the newly modified files too and continue. See #3111 (comment) and the following discussion further down in that issue.
2 parents 3cffed9 + ba21d4e commit 4ec9262

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

pkg/gui/controllers/helpers/merge_and_rebase_helper.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,36 @@ func (self *MergeAndRebaseHelper) PromptToContinueRebase() error {
247247
Title: self.c.Tr.Continue,
248248
Prompt: self.c.Tr.ConflictsResolved,
249249
HandleConfirm: func() error {
250+
// By the time we get here, we might have unstaged changes again,
251+
// e.g. if the user had to fix build errors after resolving the
252+
// conflicts, but after lazygit opened the prompt already. Ask again
253+
// to auto-stage these.
254+
255+
// Need to refresh the files to be really sure if this is the case.
256+
// We would otherwise be relying on lazygit's auto-refresh on focus,
257+
// but this is not supported by all terminals or on all platforms.
258+
if err := self.c.Refresh(types.RefreshOptions{
259+
Mode: types.SYNC, Scope: []types.RefreshableView{types.FILES},
260+
}); err != nil {
261+
return err
262+
}
263+
264+
root := self.c.Contexts().Files.FileTreeViewModel.GetRoot()
265+
if root.GetHasUnstagedChanges() {
266+
return self.c.Confirm(types.ConfirmOpts{
267+
Title: self.c.Tr.Continue,
268+
Prompt: self.c.Tr.UnstagedFilesAfterConflictsResolved,
269+
HandleConfirm: func() error {
270+
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
271+
if err := self.c.Git().WorkingTree.StageAll(); err != nil {
272+
return err
273+
}
274+
275+
return self.genericMergeCommand(REBASE_OPTION_CONTINUE)
276+
},
277+
})
278+
}
279+
250280
return self.genericMergeCommand(REBASE_OPTION_CONTINUE)
251281
},
252282
})

pkg/i18n/english.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ type TranslationSet struct {
296296
ReflogCommitsTitle string
297297
ConflictsResolved string
298298
Continue string
299+
UnstagedFilesAfterConflictsResolved string
299300
RebasingTitle string
300301
RebasingFromBaseCommitTitle string
301302
SimpleRebase string
@@ -1278,6 +1279,7 @@ func EnglishTranslationSet() *TranslationSet {
12781279
GlobalTitle: "Global keybindings",
12791280
ConflictsResolved: "All merge conflicts resolved. Continue?",
12801281
Continue: "Continue",
1282+
UnstagedFilesAfterConflictsResolved: "Files have been modified since conflicts were resolved. Auto-stage them and continue?",
12811283
Keybindings: "Keybindings",
12821284
KeybindingsMenuSectionLocal: "Local",
12831285
KeybindingsMenuSectionGlobal: "Global",
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package branch
2+
3+
import (
4+
"github.com/jesseduffield/lazygit/pkg/config"
5+
. "github.com/jesseduffield/lazygit/pkg/integration/components"
6+
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
7+
)
8+
9+
var RebaseConflictsFixBuildErrors = NewIntegrationTest(NewIntegrationTestArgs{
10+
Description: "Rebase onto another branch, deal with the conflicts. While continue prompt is showing, fix build errors; get another prompt when continuing.",
11+
ExtraCmdArgs: []string{},
12+
Skip: false,
13+
SetupConfig: func(config *config.AppConfig) {},
14+
SetupRepo: func(shell *Shell) {
15+
shared.MergeConflictsSetup(shell)
16+
},
17+
Run: func(t *TestDriver, keys config.KeybindingConfig) {
18+
t.Views().Commits().TopLines(
19+
Contains("first change"),
20+
Contains("original"),
21+
)
22+
23+
t.Views().Branches().
24+
Focus().
25+
Lines(
26+
Contains("first-change-branch"),
27+
Contains("second-change-branch"),
28+
Contains("original-branch"),
29+
).
30+
SelectNextItem().
31+
Press(keys.Branches.RebaseBranch)
32+
33+
t.ExpectPopup().Menu().
34+
Title(Equals("Rebase 'first-change-branch'")).
35+
Select(Contains("Simple rebase")).
36+
Confirm()
37+
38+
t.Common().AcknowledgeConflicts()
39+
40+
t.Views().Files().
41+
IsFocused().
42+
SelectedLine(Contains("file")).
43+
PressEnter()
44+
45+
t.Views().MergeConflicts().
46+
IsFocused().
47+
SelectNextItem().
48+
PressPrimaryAction()
49+
50+
t.Views().Information().Content(Contains("Rebasing"))
51+
52+
popup := t.ExpectPopup().Confirmation().
53+
Title(Equals("Continue")).
54+
Content(Contains("All merge conflicts resolved. Continue?"))
55+
56+
// While the popup is showing, fix some build errors
57+
t.Shell().UpdateFile("file", "make it compile again")
58+
59+
// Continue
60+
popup.Confirm()
61+
62+
t.ExpectPopup().Confirmation().
63+
Title(Equals("Continue")).
64+
Content(Contains("Files have been modified since conflicts were resolved. Auto-stage them and continue?")).
65+
Confirm()
66+
67+
t.Views().Information().Content(DoesNotContain("Rebasing"))
68+
69+
t.Views().Commits().TopLines(
70+
Contains("first change"),
71+
Contains("second-change-branch unrelated change"),
72+
Contains("second change"),
73+
Contains("original"),
74+
)
75+
},
76+
})

pkg/integration/tests/file/discard_all_dir_changes.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ var DiscardAllDirChanges = NewIntegrationTest(NewIntegrationTestArgs{
9494
}).
9595
Tap(func() {
9696
t.Common().ContinueOnConflictsResolved()
97+
t.ExpectPopup().Confirmation().
98+
Title(Equals("Continue")).
99+
Content(Contains("Files have been modified since conflicts were resolved. Auto-stage them and continue?")).
100+
Cancel()
101+
t.GlobalPress(keys.Universal.CreateRebaseOptionsMenu)
102+
t.ExpectPopup().Menu().
103+
Title(Equals("Merge options")).
104+
Select(Contains("continue")).
105+
Confirm()
97106
}).
98107
Lines(
99108
Contains("dir").IsSelected(),

pkg/integration/tests/test_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var tests = []*components.IntegrationTest{
5151
branch.RebaseAbortOnConflict,
5252
branch.RebaseAndDrop,
5353
branch.RebaseCancelOnConflict,
54+
branch.RebaseConflictsFixBuildErrors,
5455
branch.RebaseCopiedBranch,
5556
branch.RebaseDoesNotAutosquash,
5657
branch.RebaseFromMarkedBase,

0 commit comments

Comments
 (0)