From 144565ee034e6753b2b14c595aecc5b36d88e043 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 10 May 2025 18:12:02 +0200 Subject: [PATCH 1/7] Cleanup: remove unnecessary setup steps --- pkg/integration/tests/cherry_pick/cherry_pick_merge.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_merge.go b/pkg/integration/tests/cherry_pick/cherry_pick_merge.go index cc9ded0ba..77b523ecc 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_merge.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_merge.go @@ -15,8 +15,6 @@ var CherryPickMerge = NewIntegrationTest(NewIntegrationTestArgs{ EmptyCommit("base"). NewBranch("first-branch"). NewBranch("second-branch"). - Checkout("first-branch"). - Checkout("second-branch"). CreateFileAndAdd("file1.txt", "content"). Commit("one"). CreateFileAndAdd("file2.txt", "content"). From 200b34ff28702359d0b07910b38044e2a88bd635 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 9 May 2025 13:26:12 +0200 Subject: [PATCH 2/7] Shorten commit hash in custom patch menu --- pkg/gui/controllers/custom_patch_options_menu_action.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/gui/controllers/custom_patch_options_menu_action.go b/pkg/gui/controllers/custom_patch_options_menu_action.go index ae21f467c..68a419a13 100644 --- a/pkg/gui/controllers/custom_patch_options_menu_action.go +++ b/pkg/gui/controllers/custom_patch_options_menu_action.go @@ -7,6 +7,7 @@ import ( "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/utils" ) type CustomPatchOptionsMenuAction struct { @@ -46,7 +47,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error { if self.c.Git().Patch.PatchBuilder.CanRebase && self.c.Git().Status.WorkingTreeState().None() { menuItems = append(menuItems, []*types.MenuItem{ { - Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, self.c.Git().Patch.PatchBuilder.To), + Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, utils.ShortHash(self.c.Git().Patch.PatchBuilder.To)), Tooltip: self.c.Tr.RemovePatchFromOriginalCommitTooltip, OnPress: self.handleDeletePatchFromCommit, Key: 'd', From 0c0f95168c0bbdd096361170f02cdc886383f39b Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 10 May 2025 14:55:52 +0200 Subject: [PATCH 3/7] Regression test for renaming the last commit of a stacked branch I almost broke this during the development of this branch, so add a test to guard against that. The point here is that the stack remains intact, i.e. the renamed commit is the head of the lower branch, and thus shows the "*". --- .../reword_last_commit_of_stacked_branch.go | 55 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 56 insertions(+) create mode 100644 pkg/integration/tests/interactive_rebase/reword_last_commit_of_stacked_branch.go diff --git a/pkg/integration/tests/interactive_rebase/reword_last_commit_of_stacked_branch.go b/pkg/integration/tests/interactive_rebase/reword_last_commit_of_stacked_branch.go new file mode 100644 index 000000000..58505d359 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/reword_last_commit_of_stacked_branch.go @@ -0,0 +1,55 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var RewordLastCommitOfStackedBranch = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Rewords the last commit of a branch in the middle of a 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.Commits.RenameCommit). + Tap(func() { + t.ExpectPopup().CommitMessagePanel(). + Title(Equals("Reword commit")). + InitialText(Equals("commit 03")). + Clear(). + Type("renamed 03"). + Confirm() + }). + Lines( + Contains("CI commit 05"), + Contains("CI commit 04"), + Contains("CI * renamed 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 03d995d92..26d02cd76 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -274,6 +274,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.RewordCommitWithEditorAndFail, interactive_rebase.RewordFirstCommit, interactive_rebase.RewordLastCommit, + interactive_rebase.RewordLastCommitOfStackedBranch, interactive_rebase.RewordYouAreHereCommit, interactive_rebase.RewordYouAreHereCommitWithEditor, interactive_rebase.ShowExecTodos, From b02441cdca967feaad66a84b0d1f4ced357e31cf Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 10 May 2025 15:05:38 +0200 Subject: [PATCH 4/7] Regression test for moving custom patch to new commit from last commit of a stacked branch I almost broke this during the development of this branch, so add a test to guard against that. The point here is that the stack remains intact, i.e. the newly created commit is the last commit of the lower branch, and thus shows the "*". --- ...commit_in_last_commit_of_stacked_branch.go | 72 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 73 insertions(+) create mode 100644 pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go b/pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go new file mode 100644 index 000000000..645d2a120 --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go @@ -0,0 +1,72 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToNewCommitInLastCommitOfStackedBranch = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to a new commit, in the last commit of a branch in the middle of a stack", + ExtraCmdArgs: []string{}, + Skip: false, + GitVersion: AtLeast("2.38.0"), + SetupConfig: func(config *config.AppConfig) { + config.GetAppState().GitLogShowGraph = "never" + }, + SetupRepo: func(shell *Shell) { + shell. + EmptyCommit("commit 01"). + NewBranch("branch1"). + EmptyCommit("commit 02"). + CreateFileAndAdd("file1", "file1 content"). + CreateFileAndAdd("file2", "file2 content"). + Commit("commit 03"). + 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")). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Equals("▼ /").IsSelected(), + Equals(" A file1"), + Equals(" A file2"), + ). + SelectNextItem(). + PressPrimaryAction(). + PressEscape() + + t.Views().Information().Content(Contains("Building patch")) + + t.Common().SelectPatchOption(Contains("Move patch into new commit")) + + t.ExpectPopup().CommitMessagePanel(). + InitialText(Equals("")). + Type("new commit").Confirm() + + t.Views().Commits(). + IsFocused(). + Lines( + Contains("CI commit 05"), + Contains("CI commit 04"), + Contains("CI * new commit").IsSelected(), + 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 26d02cd76..6093a387a 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -311,6 +311,7 @@ var tests = []*components.IntegrationTest{ patch_building.MoveToNewCommit, patch_building.MoveToNewCommitFromAddedFile, patch_building.MoveToNewCommitFromDeletedFile, + patch_building.MoveToNewCommitInLastCommitOfStackedBranch, patch_building.MoveToNewCommitPartialHunk, patch_building.RemoveFromCommit, patch_building.RemovePartsOfAddedFile, From 50b2aa5843c2b202cfa787db6e2a543a0b4bfb70 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 10 May 2025 18:15:26 +0200 Subject: [PATCH 5/7] Add test for rewording a merge commit This currently fails with an error. --- .../interactive_rebase/reword_merge_commit.go | 56 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 57 insertions(+) create mode 100644 pkg/integration/tests/interactive_rebase/reword_merge_commit.go diff --git a/pkg/integration/tests/interactive_rebase/reword_merge_commit.go b/pkg/integration/tests/interactive_rebase/reword_merge_commit.go new file mode 100644 index 000000000..e4c1ee210 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/reword_merge_commit.go @@ -0,0 +1,56 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var RewordMergeCommit = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Rewords a merge commit which is not the current head commit", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell. + EmptyCommit("base"). + NewBranch("first-branch"). + CreateFileAndAdd("file1.txt", "content"). + Commit("one"). + Checkout("master"). + Merge("first-branch"). + NewBranch("second-branch"). + EmptyCommit("two") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("CI ◯ two").IsSelected(), + Contains("CI ⏣─╮ Merge branch 'first-branch'"), + Contains("CI │ ◯ one"), + Contains("CI ◯─╯ base"), + ). + SelectNextItem(). + Press(keys.Commits.RenameCommit). + Tap(func() { + t.ExpectPopup().CommitMessagePanel(). + Title(Equals("Reword commit")). + InitialText(Equals("Merge branch 'first-branch'")). + Clear(). + Type("renamed merge"). + Confirm() + }). + /* EXPECTED: + Lines( + Contains("CI ◯ two"), + Contains("CI ⏣─╮ renamed merge").IsSelected(), + Contains("CI │ ◯ one"), + Contains("CI ◯ ╯ base"), + ) + ACTUAL: */ + Tap(func() { + t.ExpectPopup().Alert().Title(Equals("Error")). + Content(Contains("error: 'edit' does not accept merge commits")) + }) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 6093a387a..f5f2be22a 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -275,6 +275,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.RewordFirstCommit, interactive_rebase.RewordLastCommit, interactive_rebase.RewordLastCommitOfStackedBranch, + interactive_rebase.RewordMergeCommit, interactive_rebase.RewordYouAreHereCommit, interactive_rebase.RewordYouAreHereCommitWithEditor, interactive_rebase.ShowExecTodos, From 53211012764e769fec812d86e790ba482e44098d Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 9 May 2025 22:35:08 +0200 Subject: [PATCH 6/7] Use 'break' instead of 'edit' for BeginInteractiveRebaseForCommit with merge commit BeginInteractiveRebaseForCommit is used for all the patch commands, and for rewording. It works by setting the commit we want to stop at to 'edit'; this doesn't work for merge commits. This wasn't a problem for the patch commands so far, because you typically don't use custom patches with merge commits (although we don't prevent this; maybe we should?). However, it was a problem when you tried to reword a merge commit; this previously failed with an error, as the test added in the previous commit demonstrated. Also, we want to add a new patch command that has to stop *before* the selected commit (pull patch to new commit before the original one), and this wouldn't work for the first commit in a feature branch, because it would have to set the last commit before that to 'edit', which isn't possible if that's a merge (which is likely). To fix all this, use a 'break' before the selected commit if the commit is a merge. It is important that we only do it in that case and not always, otherwise we would break the new regression tests that were added a few commits ago. --- pkg/commands/git_commands/rebase.go | 14 +++++++++++++- .../interactive_rebase/reword_merge_commit.go | 6 ------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index 7abd50027..cd7f4af11 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -400,7 +400,19 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er func (self *RebaseCommands) BeginInteractiveRebaseForCommit( commits []*models.Commit, commitIndex int, keepCommitsThatBecomeEmpty bool, ) error { - return self.BeginInteractiveRebaseForCommitRange(commits, commitIndex, commitIndex, keepCommitsThatBecomeEmpty) + if commitIndex < len(commits) && commits[commitIndex].IsMerge() { + if self.config.NeedsGpgSubprocessForCommit() { + return errors.New(self.Tr.DisabledForGPG) + } + + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ + baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex), + instruction: daemon.NewInsertBreakInstruction(), + keepCommitsThatBecomeEmpty: keepCommitsThatBecomeEmpty, + }).Run() + } else { + return self.BeginInteractiveRebaseForCommitRange(commits, commitIndex, commitIndex, keepCommitsThatBecomeEmpty) + } } func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange( diff --git a/pkg/integration/tests/interactive_rebase/reword_merge_commit.go b/pkg/integration/tests/interactive_rebase/reword_merge_commit.go index e4c1ee210..6c0969066 100644 --- a/pkg/integration/tests/interactive_rebase/reword_merge_commit.go +++ b/pkg/integration/tests/interactive_rebase/reword_merge_commit.go @@ -40,17 +40,11 @@ var RewordMergeCommit = NewIntegrationTest(NewIntegrationTestArgs{ Type("renamed merge"). Confirm() }). - /* EXPECTED: Lines( Contains("CI ◯ two"), Contains("CI ⏣─╮ renamed merge").IsSelected(), Contains("CI │ ◯ one"), Contains("CI ◯ ╯ base"), ) - ACTUAL: */ - Tap(func() { - t.ExpectPopup().Alert().Title(Equals("Error")). - Content(Contains("error: 'edit' does not accept merge commits")) - }) }, }) From f6d13330dd927579034225847420a0611070e2ed Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 10 May 2025 18:33:46 +0200 Subject: [PATCH 7/7] Add custom patch command "Move patch into new commit before the original commit" This is often useful to extract preparatory refactoring commits from a bigger one. --- pkg/commands/git_commands/patch.go | 23 +++++ .../custom_patch_options_menu_action.go | 41 +++++++++ pkg/i18n/english.go | 6 +- .../patch_building/move_to_new_commit.go | 2 +- .../move_to_new_commit_before.go | 91 +++++++++++++++++++ ...move_to_new_commit_before_no_keep_empty.go | 77 ++++++++++++++++ .../move_to_new_commit_from_added_file.go | 2 +- .../move_to_new_commit_from_deleted_file.go | 2 +- ...commit_in_last_commit_of_stacked_branch.go | 2 +- .../move_to_new_commit_partial_hunk.go | 2 +- pkg/integration/tests/test_list.go | 2 + 11 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 pkg/integration/tests/patch_building/move_to_new_commit_before.go create mode 100644 pkg/integration/tests/patch_building/move_to_new_commit_before_no_keep_empty.go diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go index 49633cf3b..0b1c3db01 100644 --- a/pkg/commands/git_commands/patch.go +++ b/pkg/commands/git_commands/patch.go @@ -314,6 +314,29 @@ func (self *PatchCommands) PullPatchIntoNewCommit( return self.rebase.ContinueRebase() } +func (self *PatchCommands) PullPatchIntoNewCommitBefore( + commits []*models.Commit, + commitIdx int, + commitSummary string, + commitDescription string, +) error { + if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx+1, true); err != nil { + return err + } + + if err := self.ApplyCustomPatch(false, false); err != nil { + _ = self.rebase.AbortRebase() + return err + } + + if err := self.commit.CommitCmdObj(commitSummary, commitDescription, false).Run(); err != nil { + return err + } + + self.PatchBuilder.Reset() + return self.rebase.ContinueRebase() +} + // We have just applied a patch in reverse to discard it from a commit; if we // now try to apply the patch again to move it to a later commit, or to the // index, then this would conflict "with itself" in case the patch contained diff --git a/pkg/gui/controllers/custom_patch_options_menu_action.go b/pkg/gui/controllers/custom_patch_options_menu_action.go index 68a419a13..92b77f514 100644 --- a/pkg/gui/controllers/custom_patch_options_menu_action.go +++ b/pkg/gui/controllers/custom_patch_options_menu_action.go @@ -64,6 +64,12 @@ func (self *CustomPatchOptionsMenuAction) Call() error { OnPress: self.handlePullPatchIntoNewCommit, Key: 'n', }, + { + Label: self.c.Tr.MovePatchIntoNewCommitBefore, + Tooltip: self.c.Tr.MovePatchIntoNewCommitBeforeTooltip, + OnPress: self.handlePullPatchIntoNewCommitBefore, + Key: 'N', + }, }...) if self.c.Context().Current().GetKey() == self.c.Contexts().LocalCommits.GetKey() { @@ -223,6 +229,41 @@ func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error { return nil } +func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommitBefore() error { + if ok, err := self.validateNormalWorkingTreeState(); !ok { + return err + } + + self.returnFocusFromPatchExplorerIfNecessary() + + commitIndex := self.getPatchCommitIndex() + self.c.Helpers().Commits.OpenCommitMessagePanel( + &helpers.OpenCommitMessagePanelOpts{ + // Pass a commit index of one less than the moved-from commit, so that + // you can press up arrow once to recall the original commit message: + CommitIndex: commitIndex - 1, + InitialMessage: "", + SummaryTitle: self.c.Tr.CommitSummaryTitle, + DescriptionTitle: self.c.Tr.CommitDescriptionTitle, + PreserveMessage: false, + OnConfirm: func(summary string, description string) error { + return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error { + self.c.Helpers().Commits.CloseCommitMessagePanel() + self.c.LogAction(self.c.Tr.Actions.MovePatchIntoNewCommit) + err := self.c.Git().Patch.PullPatchIntoNewCommitBefore(self.c.Model().Commits, commitIndex, summary, description) + if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil { + return err + } + self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{}) + return nil + }) + }, + }, + ) + + return nil +} + func (self *CustomPatchOptionsMenuAction) handleApplyPatch(reverse bool) error { self.returnFocusFromPatchExplorerIfNecessary() diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index e7311e021..c3487dd1a 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -813,6 +813,8 @@ type TranslationSet struct { MovePatchOutIntoIndexTooltip string MovePatchIntoNewCommit string MovePatchIntoNewCommitTooltip string + MovePatchIntoNewCommitBefore string + MovePatchIntoNewCommitBeforeTooltip string MovePatchToSelectedCommit string MovePatchToSelectedCommitTooltip string CopyPatchToClipboard string @@ -1896,8 +1898,10 @@ func EnglishTranslationSet() *TranslationSet { RemovePatchFromOriginalCommitTooltip: "Remove the current patch from its commit. This is achieved by starting an interactive rebase at the commit, applying the patch in reverse, and then continuing the rebase. If later commits depend on the patch, you may need to resolve conflicts.", MovePatchOutIntoIndex: "Move patch out into index", MovePatchOutIntoIndexTooltip: "Move the patch out of its commit and into the index. This is achieved by starting an interactive rebase at the commit, applying the patch in reverse, continuing the rebase to completion, and then applying the patch to the index. If later commits depend on the patch, you may need to resolve conflicts.", - MovePatchIntoNewCommit: "Move patch into new commit", + MovePatchIntoNewCommit: "Move patch into new commit after the original commit", MovePatchIntoNewCommitTooltip: "Move the patch out of its commit and into a new commit sitting on top of the original commit. This is achieved by starting an interactive rebase at the original commit, applying the patch in reverse, then applying the patch to the index and committing it as a new commit, before continuing the rebase to completion. If later commits depend on the patch, you may need to resolve conflicts.", + MovePatchIntoNewCommitBefore: "Move patch into new commit before the original commit", + MovePatchIntoNewCommitBeforeTooltip: "Move the patch out of its commit and into a new commit before the original commit. This works best when the custom patch contains only entire hunks or even entire files; if it contains partial hunks, you are likely to get conflicts.", MovePatchToSelectedCommit: "Move patch to selected commit (%s)", MovePatchToSelectedCommitTooltip: "Move the patch out of its original commit and into the selected commit. This is achieved by starting an interactive rebase at the original commit, applying the patch in reverse, then continuing the rebase up to the selected commit, before applying the patch forward and amending the selected commit. The rebase is then continued to completion. If commits between the source and destination commit depend on the patch, you may need to resolve conflicts.", CopyPatchToClipboard: "Copy patch to clipboard", diff --git a/pkg/integration/tests/patch_building/move_to_new_commit.go b/pkg/integration/tests/patch_building/move_to_new_commit.go index 1df3e5dcb..8f1c77376 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit.go @@ -48,7 +48,7 @@ var MoveToNewCommit = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(Contains("Building patch")) - t.Common().SelectPatchOption(Contains("Move patch into new commit")) + t.Common().SelectPatchOption(Contains("Move patch into new commit after the original commit")) t.ExpectPopup().CommitMessagePanel(). InitialText(Equals("")). diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_before.go b/pkg/integration/tests/patch_building/move_to_new_commit_before.go new file mode 100644 index 000000000..79964519e --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_new_commit_before.go @@ -0,0 +1,91 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToNewCommitBefore = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to a new commit before the original one", + ExtraCmdArgs: []string{}, + Skip: false, + GitVersion: AtLeast("2.26.0"), + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("dir") + shell.CreateFileAndAdd("dir/file1", "file1 content") + shell.CreateFileAndAdd("dir/file2", "file2 content") + shell.Commit("first commit") + + shell.UpdateFileAndAdd("dir/file1", "file1 content with old changes") + shell.DeleteFileAndAdd("dir/file2") + shell.CreateFileAndAdd("dir/file3", "file3 content") + shell.Commit("commit to move from") + + shell.UpdateFileAndAdd("dir/file1", "file1 content with new changes") + shell.Commit("third commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("third commit").IsSelected(), + Contains("commit to move from"), + Contains("first commit"), + ). + SelectNextItem(). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), + ). + PressPrimaryAction(). + PressEscape() + + t.Views().Information().Content(Contains("Building patch")) + + t.Common().SelectPatchOption(Contains("Move patch into new commit before the original commit")) + + t.ExpectPopup().CommitMessagePanel(). + InitialText(Equals("")). + Type("new commit").Confirm() + + t.Views().Commits(). + IsFocused(). + Lines( + Contains("third commit"), + Contains("commit to move from").IsSelected(), + Contains("new commit"), + Contains("first commit"), + ). + SelectNextItem(). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), + ). + PressEscape() + + t.Views().Commits(). + IsFocused(). + SelectPreviousItem(). + PressEnter() + + // the original commit has no more files in it + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("(none)"), + ) + }, +}) diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_before_no_keep_empty.go b/pkg/integration/tests/patch_building/move_to_new_commit_before_no_keep_empty.go new file mode 100644 index 000000000..bf4e278ef --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_new_commit_before_no_keep_empty.go @@ -0,0 +1,77 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToNewCommitBeforeNoKeepEmpty = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to a new commit before the original one, for older git versions that don't keep the empty commit", + ExtraCmdArgs: []string{}, + Skip: false, + GitVersion: Before("2.26.0"), + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("dir") + shell.CreateFileAndAdd("dir/file1", "file1 content") + shell.CreateFileAndAdd("dir/file2", "file2 content") + shell.Commit("first commit") + + shell.UpdateFileAndAdd("dir/file1", "file1 content with old changes") + shell.DeleteFileAndAdd("dir/file2") + shell.CreateFileAndAdd("dir/file3", "file3 content") + shell.Commit("commit to move from") + + shell.UpdateFileAndAdd("dir/file1", "file1 content with new changes") + shell.Commit("third commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("third commit").IsSelected(), + Contains("commit to move from"), + Contains("first commit"), + ). + SelectNextItem(). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), + ). + PressPrimaryAction(). + PressEscape() + + t.Views().Information().Content(Contains("Building patch")) + + t.Common().SelectPatchOption(Contains("Move patch into new commit before the original commit")) + + t.ExpectPopup().CommitMessagePanel(). + InitialText(Equals("")). + Type("new commit").Confirm() + + t.Views().Commits(). + IsFocused(). + Lines( + Contains("third commit"), + Contains("new commit").IsSelected(), + Contains("first commit"), + ). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), + ). + PressEscape() + }, +}) diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_from_added_file.go b/pkg/integration/tests/patch_building/move_to_new_commit_from_added_file.go index 97aaea2db..11abe23f4 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit_from_added_file.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit_from_added_file.go @@ -39,7 +39,7 @@ var MoveToNewCommitFromAddedFile = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(Contains("Building patch")) - t.Common().SelectPatchOption(Contains("Move patch into new commit")) + t.Common().SelectPatchOption(Contains("Move patch into new commit after the original commit")) t.ExpectPopup().CommitMessagePanel(). InitialText(Equals("")). diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_from_deleted_file.go b/pkg/integration/tests/patch_building/move_to_new_commit_from_deleted_file.go index 2c51acf85..9edc06fb2 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit_from_deleted_file.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit_from_deleted_file.go @@ -39,7 +39,7 @@ var MoveToNewCommitFromDeletedFile = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(Contains("Building patch")) - t.Common().SelectPatchOption(Contains("Move patch into new commit")) + t.Common().SelectPatchOption(Contains("Move patch into new commit after the original commit")) t.ExpectPopup().CommitMessagePanel(). InitialText(Equals("")). diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go b/pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go index 645d2a120..ed98657d5 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit_in_last_commit_of_stacked_branch.go @@ -52,7 +52,7 @@ var MoveToNewCommitInLastCommitOfStackedBranch = NewIntegrationTest(NewIntegrati t.Views().Information().Content(Contains("Building patch")) - t.Common().SelectPatchOption(Contains("Move patch into new commit")) + t.Common().SelectPatchOption(Contains("Move patch into new commit after the original commit")) t.ExpectPopup().CommitMessagePanel(). InitialText(Equals("")). diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go b/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go index 6a86546c9..4d12e0f90 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go @@ -44,7 +44,7 @@ var MoveToNewCommitPartialHunk = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(Contains("Building patch")) - t.Common().SelectPatchOption(Contains("Move patch into new commit")) + t.Common().SelectPatchOption(Contains("Move patch into new commit after the original commit")) t.ExpectPopup().CommitMessagePanel(). InitialText(Equals("")). diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index f5f2be22a..0a2a4f46c 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -310,6 +310,8 @@ var tests = []*components.IntegrationTest{ patch_building.MoveToLaterCommit, patch_building.MoveToLaterCommitPartialHunk, patch_building.MoveToNewCommit, + patch_building.MoveToNewCommitBefore, + patch_building.MoveToNewCommitBeforeNoKeepEmpty, patch_building.MoveToNewCommitFromAddedFile, patch_building.MoveToNewCommitFromDeletedFile, patch_building.MoveToNewCommitInLastCommitOfStackedBranch,