From ea03ae5ee379d4bd4e279a54a068b49d7106a9d4 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 29 Nov 2024 17:27:55 +0100 Subject: [PATCH 1/7] Cleanup: remove a no-op Focus() call --- .../tests/interactive_rebase/drop_todo_commit_with_update_ref.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go index 5b960129f22..02efec9da3a 100644 --- a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go +++ b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go @@ -38,7 +38,6 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{ ). NavigateToLine(Contains("commit 02")). Press(keys.Universal.Edit). - Focus(). Lines( Contains("pick").Contains("CI commit 07"), Contains("pick").Contains("CI commit 06"), From 4624d496a2c7fc40168c2e8d9ac2b2c45e703d9f Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 29 Nov 2024 17:33:27 +0100 Subject: [PATCH 2/7] Add test for editing the last commit of a branch in a stack The test demonstrates that the "update-ref" todo after the selected commit is missing, which means when we amend the commit it'll break the stack. --- .../edit_last_commit_of_stacked_branch.go | 79 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 80 insertions(+) create mode 100644 pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go diff --git a/pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go b/pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go new file mode 100644 index 00000000000..92571173e5d --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go @@ -0,0 +1,79 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var EditLastCommitOfStackedBranch = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Edit and amend the last commit of a branch in a stack of branches, and ensure that it doesn't break the stack", + ExtraCmdArgs: []string{}, + Skip: false, + GitVersion: AtLeast("2.38.0"), + SetupConfig: func(config *config.AppConfig) { + config.GetUserConfig().Git.MainBranches = []string{"master"} + config.GetAppState().GitLogShowGraph = "never" + }, + SetupRepo: func(shell *Shell) { + shell. + CreateNCommits(1). + NewBranch("branch1"). + CreateNCommitsStartingAt(2, 2). + NewBranch("branch2"). + CreateNCommitsStartingAt(2, 4) + + shell.SetConfig("rebase.updateRefs", "true") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("CI commit 05").IsSelected(), + Contains("CI commit 04"), + Contains("CI * commit 03"), + Contains("CI commit 02"), + Contains("CI commit 01"), + ). + NavigateToLine(Contains("commit 03")). + Press(keys.Universal.Edit). + Lines( + Contains("pick").Contains("CI commit 05"), + Contains("pick").Contains("CI commit 04"), + /* EXPECTED: + Contains("update-ref").Contains("branch1"), + */ + Contains("<-- YOU ARE HERE --- * commit 03").IsSelected(), + Contains("CI commit 02"), + Contains("CI commit 01"), + ) + + t.Shell().CreateFile("fixup-file", "fixup content") + t.Views().Files(). + Focus(). + Press(keys.Files.RefreshFiles). + Lines( + Contains("??").Contains("fixup-file").IsSelected(), + ). + PressPrimaryAction(). + Press(keys.Files.AmendLastCommit) + t.ExpectPopup().Confirmation(). + Title(Equals("Amend last commit")). + Content(Contains("Are you sure you want to amend last commit?")). + Confirm() + + t.Common().ContinueRebase() + + t.Views().Commits(). + Focus(). + Lines( + Contains("CI commit 05"), + Contains("CI commit 04"), + /* EXPECTED: + Contains("CI * commit 03"), + ACTUAL: */ + Contains("CI commit 03"), + Contains("CI commit 02"), + Contains("CI commit 01"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 40435b91666..bbccc029422 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -211,6 +211,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.DropTodoCommitWithUpdateRef, interactive_rebase.DropWithCustomCommentChar, interactive_rebase.EditFirstCommit, + interactive_rebase.EditLastCommitOfStackedBranch, interactive_rebase.EditNonTodoCommitDuringRebase, interactive_rebase.EditRangeSelectOutsideRebase, interactive_rebase.EditTheConflCommit, From 0766b14afd0df0f414c39255c86dc09a983a08d0 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 29 Nov 2024 17:07:48 +0100 Subject: [PATCH 3/7] Add test to auto-amend a commit after pressing `e` on it Auto-amending is a little-known feature of git that is very convenient once you know it: whenever you stop at a commit marked with `edit` in an interactive rebase, you can make changes and stage them, and when you continue the rebase they automatically get amended to the commit you had stopped at. This is so convenient because making changes to a commit is one of the main reasons why you edit a commit. Unfortunately this currently doesn't work in lazygit because we don't actually use `edit` to stop at the first commit (instead, we add a `break` todo after it, which doesn't have the auto-amend functionality). We'll improve this later in this branch. --- .../interactive_rebase/edit_and_auto_amend.go | 58 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 59 insertions(+) create mode 100644 pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go diff --git a/pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go b/pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go new file mode 100644 index 00000000000..3171893ce72 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go @@ -0,0 +1,58 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var EditAndAutoAmend = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Edit a commit, make a change and stage it, then continue the rebase to auto-amend the commit", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell. + CreateNCommits(3) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("commit 03"), + Contains("commit 02"), + Contains("commit 01"), + ). + NavigateToLine(Contains("commit 02")). + Press(keys.Universal.Edit). + Lines( + Contains("commit 03"), + MatchesRegexp("YOU ARE HERE.*commit 02").IsSelected(), + Contains("commit 01"), + ) + + t.Shell().CreateFile("fixup-file", "fixup content") + t.Views().Files(). + Focus(). + Press(keys.Files.RefreshFiles). + Lines( + Contains("??").Contains("fixup-file").IsSelected(), + ). + PressPrimaryAction() + + t.Common().ContinueRebase() + + t.Views().Commits(). + Focus(). + Lines( + Contains("commit 03"), + Contains("commit 02").IsSelected(), + Contains("commit 01"), + ) + + t.Views().Main(). + /* EXPECTED: + Content(Contains("fixup content")) + ACTUAL: */ + Content(DoesNotContain("fixup content")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index bbccc029422..65b6c6a6d67 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -210,6 +210,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.DropCommitInCopiedBranchWithUpdateRef, interactive_rebase.DropTodoCommitWithUpdateRef, interactive_rebase.DropWithCustomCommentChar, + interactive_rebase.EditAndAutoAmend, interactive_rebase.EditFirstCommit, interactive_rebase.EditLastCommitOfStackedBranch, interactive_rebase.EditNonTodoCommitDuringRebase, From 016d46526cf71042c0e538d0b8b42b99ef2b1a2a Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 29 Nov 2024 16:49:18 +0100 Subject: [PATCH 4/7] Add test for editing several commits right after a merge commit This is very similar to edit_range_select_outside_rebase.go, except that it selects commits right after, and including, a merge commit. This test already works correctly. The reason we add it is that we are going to have two different implementations of the `e` command depending on whether the last selected commit is a merge commit, and we want to make sure they both work with a range selection. --- ...nge_select_down_to_merge_outside_rebase.go | 43 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 44 insertions(+) create mode 100644 pkg/integration/tests/interactive_rebase/edit_range_select_down_to_merge_outside_rebase.go diff --git a/pkg/integration/tests/interactive_rebase/edit_range_select_down_to_merge_outside_rebase.go b/pkg/integration/tests/interactive_rebase/edit_range_select_down_to_merge_outside_rebase.go new file mode 100644 index 00000000000..364b04518c6 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/edit_range_select_down_to_merge_outside_rebase.go @@ -0,0 +1,43 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" + "github.com/jesseduffield/lazygit/pkg/integration/tests/shared" +) + +var EditRangeSelectDownToMergeOutsideRebase = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Select a range of commits (the last one being a merge commit) to edit outside of a rebase", + ExtraCmdArgs: []string{}, + Skip: false, + GitVersion: AtLeast("2.22.0"), // first version that supports the --rebase-merges option + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shared.CreateMergeCommit(shell) + shell.CreateNCommits(2) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + TopLines( + Contains("CI ◯ commit 02").IsSelected(), + Contains("CI ◯ commit 01"), + Contains("Merge branch 'second-change-branch' into first-change-branch"), + ). + Press(keys.Universal.RangeSelectDown). + Press(keys.Universal.RangeSelectDown). + Press(keys.Universal.Edit). + Lines( + Contains("edit CI commit 02").IsSelected(), + Contains("edit CI commit 01").IsSelected(), + Contains(" CI ⏣─╮ <-- YOU ARE HERE --- Merge branch 'second-change-branch' into first-change-branch").IsSelected(), + Contains(" CI │ ◯ * second-change-branch unrelated change"), + Contains(" CI │ ◯ second change"), + Contains(" CI ◯ │ first change"), + Contains(" CI ◯─╯ * original"), + Contains(" CI ◯ three"), + Contains(" CI ◯ two"), + Contains(" CI ◯ one"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 65b6c6a6d67..ce7220873cc 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -214,6 +214,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.EditFirstCommit, interactive_rebase.EditLastCommitOfStackedBranch, interactive_rebase.EditNonTodoCommitDuringRebase, + interactive_rebase.EditRangeSelectDownToMergeOutsideRebase, interactive_rebase.EditRangeSelectOutsideRebase, interactive_rebase.EditTheConflCommit, interactive_rebase.FixupFirstCommit, From 17bb3970c1a270b785c9854df329cb5d797b1857 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 29 Nov 2024 16:16:44 +0100 Subject: [PATCH 5/7] Filter out merge commits when generating todo changes in InteractiveRebase We will need this because under some conditions we are going to use this function to edit a range of commits, and we can't set merge commits to "edit". This corresponds to the code in startInteractiveRebaseWithEdit which has similar logic. It is a bit unfortunate that we will have these two different ways of setting todos to edit: startInteractiveRebaseWithEdit does it after stopping in the rebase, in the Then function of its refresh, but InteractiveRebase does it in the daemon with a ChangeTodoActionsInstruction. It still makes sense though, given how InteractiveRebase works. This not only affects "edit", but also "drop", "fixup", and "squash". Previously, when trying to use these for a range selection that includes a merge commit, they would fail with the cryptic error message "Some todos not found in git-rebase-todo"; now they simply exclude the merge commit. I'm not sure if one is better or worse than the other, and we should probably simply disable the commands when a merge commit is selected, but that's out of scope in this PR. --- pkg/commands/git_commands/rebase.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index a1362d72546..3d1d36635eb 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -145,11 +145,11 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, startIdx baseHashOrRoot := getBaseHashOrRoot(commits, baseIndex) - changes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) daemon.ChangeTodoAction { + changes := lo.FilterMap(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) (daemon.ChangeTodoAction, bool) { return daemon.ChangeTodoAction{ Hash: commit.Hash, NewAction: action, - } + }, !commit.IsMerge() }) self.os.LogCommand(logTodoChanges(changes), false) From d84986880effd20130e7c6bd43e388c7e19e2377 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 29 Nov 2024 16:24:12 +0100 Subject: [PATCH 6/7] Extract helper methods We'll reuse them in the next commit. --- .../controllers/local_commits_controller.go | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 8d2d31700bc..0d6121d7019 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -9,6 +9,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" "github.com/jesseduffield/lazygit/pkg/gui/context" + "github.com/jesseduffield/lazygit/pkg/gui/context/traits" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/style" @@ -532,10 +533,7 @@ func (self *LocalCommitsController) startInteractiveRebaseWithEdit( ) error { return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.EditCommit) - selectedIdx, rangeStartIdx, rangeSelectMode := self.context().GetSelectionRangeAndMode() - commits := self.c.Model().Commits - selectedHash := commits[selectedIdx].Hash - rangeStartHash := commits[rangeStartIdx].Hash + selectionRangeAndMode := self.getSelectionRangeAndMode() err := self.c.Git().Rebase.EditRebase(commitsToEdit[len(commitsToEdit)-1].Hash) return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions( err, @@ -554,23 +552,41 @@ func (self *LocalCommitsController) startInteractiveRebaseWithEdit( } } - // We need to select the same commit range again because after starting a rebase, - // new lines can be added for update-ref commands in the TODO file, due to - // stacked branches. So the selected commits may be in different positions in the list. - _, newSelectedIdx, ok1 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool { - return c.Hash == selectedHash - }) - _, newRangeStartIdx, ok2 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool { - return c.Hash == rangeStartHash - }) - if ok1 && ok2 { - self.context().SetSelectionRangeAndMode(newSelectedIdx, newRangeStartIdx, rangeSelectMode) - } + self.restoreSelectionRangeAndMode(selectionRangeAndMode) return nil }}) }) } +type SelectionRangeAndMode struct { + selectedHash string + rangeStartHash string + mode traits.RangeSelectMode +} + +func (self *LocalCommitsController) getSelectionRangeAndMode() SelectionRangeAndMode { + selectedIdx, rangeStartIdx, rangeSelectMode := self.context().GetSelectionRangeAndMode() + commits := self.c.Model().Commits + selectedHash := commits[selectedIdx].Hash + rangeStartHash := commits[rangeStartIdx].Hash + return SelectionRangeAndMode{selectedHash, rangeStartHash, rangeSelectMode} +} + +func (self *LocalCommitsController) restoreSelectionRangeAndMode(selectionRangeAndMode SelectionRangeAndMode) { + // We need to select the same commit range again because after starting a rebase, + // new lines can be added for update-ref commands in the TODO file, due to + // stacked branches. So the selected commits may be in different positions in the list. + _, newSelectedIdx, ok1 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool { + return c.Hash == selectionRangeAndMode.selectedHash + }) + _, newRangeStartIdx, ok2 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool { + return c.Hash == selectionRangeAndMode.rangeStartHash + }) + if ok1 && ok2 { + self.context().SetSelectionRangeAndMode(newSelectedIdx, newRangeStartIdx, selectionRangeAndMode.mode) + } +} + func (self *LocalCommitsController) findCommitForQuickStartInteractiveRebase() (*models.Commit, error) { commit, index, ok := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool { return c.IsMerge() || c.Status == models.StatusMerged From debfe1a21f8045ae1afce87b35e9897197493c2d Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 29 Nov 2024 16:24:28 +0100 Subject: [PATCH 7/7] Improve editing a commit In 67b8ef449c we changed the "edit" command to insert a "break" after the selected commit, rather than setting the selected todo to "edit". The reason for doing this was that it now works for merge commits too. Back then, I claimed "In most cases the behavior is exactly the same as before." Unfortunately that's not true, there are two reasons why the previous behavior was better (both are demonstrated by tests earlier in this branch): - when editing the last commit of a branch in the middle of a stack of branches, we are now missing the update-ref todo after it, which means that amending the commit breaks the stack - it breaks auto-amending (see the added test earlier in this branch for an explanation) For these reasons, we are going back to the previous approach of setting the selected commit to "edit" whenever possible, i.e. unless it's a merge commit. The only scenario where this could still be a problem is when you have a stack of branches, and the last commit of one of the branches in the stack is a merge commit, and you try to edit that. In my experience with stacked branches this is very unlikely, in almost all cases my stacked branches are linear. --- .../controllers/local_commits_controller.go | 18 ++++++++++++++++-- .../interactive_rebase/edit_and_auto_amend.go | 3 --- .../edit_last_commit_of_stacked_branch.go | 5 ----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 0d6121d7019..96c2ea18b16 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -116,7 +116,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ }, { Key: opts.GetKey(editCommitKey), - Handler: self.withItems(self.edit), + Handler: self.withItemsRange(self.edit), GetDisabledReason: self.require( self.itemRangeSelected(self.midRebaseCommandEnabled), ), @@ -511,11 +511,25 @@ func (self *LocalCommitsController) drop(selectedCommits []*models.Commit, start return nil } -func (self *LocalCommitsController) edit(selectedCommits []*models.Commit) error { +func (self *LocalCommitsController) edit(selectedCommits []*models.Commit, startIdx int, endIdx int) error { if self.isRebasing() { return self.updateTodos(todo.Edit, selectedCommits) } + commits := self.c.Model().Commits + if !commits[endIdx].IsMerge() { + selectionRangeAndMode := self.getSelectionRangeAndMode() + err := self.c.Git().Rebase.InteractiveRebase(commits, startIdx, endIdx, todo.Edit) + return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions( + err, + types.RefreshOptions{ + Mode: types.BLOCK_UI, Then: func() error { + self.restoreSelectionRangeAndMode(selectionRangeAndMode) + return nil + }, + }) + } + return self.startInteractiveRebaseWithEdit(selectedCommits) } diff --git a/pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go b/pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go index 3171893ce72..8c569ede622 100644 --- a/pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go +++ b/pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go @@ -50,9 +50,6 @@ var EditAndAutoAmend = NewIntegrationTest(NewIntegrationTestArgs{ ) t.Views().Main(). - /* EXPECTED: Content(Contains("fixup content")) - ACTUAL: */ - Content(DoesNotContain("fixup content")) }, }) diff --git a/pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go b/pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go index 92571173e5d..35d89e8e989 100644 --- a/pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go +++ b/pkg/integration/tests/interactive_rebase/edit_last_commit_of_stacked_branch.go @@ -39,9 +39,7 @@ var EditLastCommitOfStackedBranch = NewIntegrationTest(NewIntegrationTestArgs{ Lines( Contains("pick").Contains("CI commit 05"), Contains("pick").Contains("CI commit 04"), - /* EXPECTED: Contains("update-ref").Contains("branch1"), - */ Contains("<-- YOU ARE HERE --- * commit 03").IsSelected(), Contains("CI commit 02"), Contains("CI commit 01"), @@ -68,10 +66,7 @@ var EditLastCommitOfStackedBranch = NewIntegrationTest(NewIntegrationTestArgs{ Lines( Contains("CI commit 05"), Contains("CI commit 04"), - /* EXPECTED: Contains("CI * commit 03"), - ACTUAL: */ - Contains("CI commit 03"), Contains("CI commit 02"), Contains("CI commit 01"), )