From 4b4dccfd7d01bf24af80223efca224cb76e2f51e Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 18 Mar 2023 08:17:47 +0100 Subject: [PATCH] Fix "move patch into new commit" for partial hunk (#2507) --- pkg/commands/git_commands/patch.go | 71 ++++++++------ pkg/commands/git_commands/rebase.go | 2 +- pkg/commands/git_commands/rebase_test.go | 6 +- pkg/commands/patch/patch_manager.go | 10 ++ pkg/integration/components/shell.go | 17 ++++ .../patch_building/move_to_earlier_commit.go | 88 +++++++++++++++++ ...e_to_index_part_of_adjacent_added_lines.go | 70 ++++++++++++++ .../patch_building/move_to_later_commit.go | 89 +++++++++++++++++ .../move_to_later_commit_partial_hunk.go | 96 +++++++++++++++++++ .../patch_building/move_to_new_commit.go | 41 ++++++-- .../move_to_new_commit_partial_hunk.go | 83 ++++++++++++++++ pkg/integration/tests/test_list.go | 5 + 12 files changed, 534 insertions(+), 44 deletions(-) create mode 100644 pkg/integration/tests/patch_building/move_to_earlier_commit.go create mode 100644 pkg/integration/tests/patch_building/move_to_index_part_of_adjacent_added_lines.go create mode 100644 pkg/integration/tests/patch_building/move_to_later_commit.go create mode 100644 pkg/integration/tests/patch_building/move_to_later_commit_partial_hunk.go create mode 100644 pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go index a805c7f21..643091f63 100644 --- a/pkg/commands/git_commands/patch.go +++ b/pkg/commands/git_commands/patch.go @@ -45,9 +45,7 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com // apply each patch in reverse if err := self.PatchManager.ApplyPatches(true); err != nil { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + _ = self.rebase.AbortRebase() return err } @@ -73,9 +71,8 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s // apply each patch forward if err := self.PatchManager.ApplyPatches(false); err != nil { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + // Don't abort the rebase here; this might cause conflicts, so give + // the user a chance to resolve them return err } @@ -121,9 +118,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s // apply each patch in reverse if err := self.PatchManager.ApplyPatches(true); err != nil { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + _ = self.rebase.AbortRebase() return err } @@ -132,6 +127,12 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s return err } + patch, err := self.diffHeadAgainstCommit(commits[sourceCommitIdx]) + if err != nil { + _ = self.rebase.AbortRebase() + return err + } + if self.rebase.onSuccessfulContinue != nil { return errors.New("You are midway through another rebase operation. Please abort to start again") } @@ -139,10 +140,9 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s self.rebase.onSuccessfulContinue = func() error { // now we should be up to the destination, so let's apply forward these patches to that. // ideally we would ensure we're on the right commit but I'm not sure if that check is necessary - if err := self.PatchManager.ApplyPatches(false); err != nil { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil { + // Don't abort the rebase here; this might cause conflicts, so give + // the user a chance to resolve them return err } @@ -175,9 +175,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId if err := self.PatchManager.ApplyPatches(true); err != nil { if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + _ = self.rebase.AbortRebase() } return err } @@ -187,17 +185,21 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId return err } + patch, err := self.diffHeadAgainstCommit(commits[commitIdx]) + if err != nil { + _ = self.rebase.AbortRebase() + return err + } + if self.rebase.onSuccessfulContinue != nil { return errors.New("You are midway through another rebase operation. Please abort to start again") } self.rebase.onSuccessfulContinue = func() error { // add patches to index - if err := self.PatchManager.ApplyPatches(false); err != nil { + if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil { if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + _ = self.rebase.AbortRebase() } return err } @@ -221,9 +223,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm } if err := self.PatchManager.ApplyPatches(true); err != nil { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + _ = self.rebase.AbortRebase() return err } @@ -232,18 +232,20 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm return err } - // add patches to index - if err := self.PatchManager.ApplyPatches(false); err != nil { - if err := self.rebase.AbortRebase(); err != nil { - return err - } + patch, err := self.diffHeadAgainstCommit(commits[commitIdx]) + if err != nil { + _ = self.rebase.AbortRebase() + return err + } + + if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil { + _ = self.rebase.AbortRebase() return err } head_message, _ := self.commit.GetHeadCommitMessage() new_message := fmt.Sprintf("Split from \"%s\"", head_message) - err := self.commit.CommitCmdObj(new_message).Run() - if err != nil { + if err := self.commit.CommitCmdObj(new_message).Run(); err != nil { return err } @@ -254,3 +256,12 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm self.PatchManager.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 +// only some lines of a range of adjacent added lines. To solve this, we +// get the diff of HEAD and the original commit and then apply that. +func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string, error) { + return self.cmd.New(fmt.Sprintf("git diff HEAD..%s", commit.Sha)).RunWithOutput() +} diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index cff9b961a..b0fa939c3 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -137,7 +137,7 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(baseShaOrRoot string debug = "TRUE" } - cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty --no-autosquash %s", baseShaOrRoot) + cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash %s", baseShaOrRoot) self.Log.WithField("command", cmdStr).Debug("RunCommand") cmdObj := self.cmd.New(cmdStr) diff --git a/pkg/commands/git_commands/rebase_test.go b/pkg/commands/git_commands/rebase_test.go index ed731ed04..b2cd88323 100644 --- a/pkg/commands/git_commands/rebase_test.go +++ b/pkg/commands/git_commands/rebase_test.go @@ -26,7 +26,7 @@ func TestRebaseRebaseBranch(t *testing.T) { testName: "successful rebase", arg: "master", runner: oscommands.NewFakeRunner(t). - Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash master`, "", nil), + Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash master`, "", nil), test: func(err error) { assert.NoError(t, err) }, @@ -35,7 +35,7 @@ func TestRebaseRebaseBranch(t *testing.T) { testName: "unsuccessful rebase", arg: "master", runner: oscommands.NewFakeRunner(t). - Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash master`, "", errors.New("error")), + Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash master`, "", errors.New("error")), test: func(err error) { assert.Error(t, err) }, @@ -125,7 +125,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) { commitIndex: 0, fileName: "test999.txt", runner: oscommands.NewFakeRunner(t). - Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash abcdef`, "", nil). + Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash abcdef`, "", nil). Expect(`git cat-file -e HEAD^:"test999.txt"`, "", nil). Expect(`git checkout HEAD^ -- "test999.txt"`, "", nil). Expect(`git commit --amend --no-edit --allow-empty`, "", nil). diff --git a/pkg/commands/patch/patch_manager.go b/pkg/commands/patch/patch_manager.go index 3a379b97e..84db20be6 100644 --- a/pkg/commands/patch/patch_manager.go +++ b/pkg/commands/patch/patch_manager.go @@ -286,3 +286,13 @@ func (p *PatchManager) IsEmpty() bool { func (p *PatchManager) NewPatchRequired(from string, to string, reverse bool) bool { return from != p.From || to != p.To || reverse != p.reverse } + +func (p *PatchManager) AllFilesInPatch() []string { + files := make([]string, 0, len(p.fileInfoMap)) + + for filename := range p.fileInfoMap { + files = append(files, filename) + } + + return files +} diff --git a/pkg/integration/components/shell.go b/pkg/integration/components/shell.go index 3c5177949..d55fa8303 100644 --- a/pkg/integration/components/shell.go +++ b/pkg/integration/components/shell.go @@ -94,6 +94,16 @@ func (self *Shell) CreateFile(path string, content string) *Shell { return self } +func (self *Shell) DeleteFile(path string) *Shell { + fullPath := filepath.Join(self.dir, path) + err := os.Remove(fullPath) + if err != nil { + self.fail(fmt.Sprintf("error deleting file: %s\n%s", fullPath, err)) + } + + return self +} + func (self *Shell) CreateDir(path string) *Shell { fullPath := filepath.Join(self.dir, path) if err := os.MkdirAll(fullPath, 0o755); err != nil { @@ -171,6 +181,13 @@ func (self *Shell) UpdateFileAndAdd(fileName string, fileContents string) *Shell GitAdd(fileName) } +// convenience method for deleting a file and adding it +func (self *Shell) DeleteFileAndAdd(fileName string) *Shell { + return self. + DeleteFile(fileName). + GitAdd(fileName) +} + // creates commits 01, 02, 03, ..., n with a new file in each // The reason for padding with zeroes is so that it's easier to do string // matches on the commit messages when there are many of them diff --git a/pkg/integration/tests/patch_building/move_to_earlier_commit.go b/pkg/integration/tests/patch_building/move_to_earlier_commit.go new file mode 100644 index 000000000..5803737f0 --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_earlier_commit.go @@ -0,0 +1,88 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToEarlierCommit = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to an earlier commit", + ExtraCmdArgs: "", + Skip: false, + 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.CreateFileAndAdd("unrelated-file", "") + shell.Commit("destination 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") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("commit to move from").IsSelected(), + Contains("destination commit"), + Contains("first commit"), + ). + 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.Views().Commits(). + IsFocused(). + SelectNextItem() + + t.Common().SelectPatchOption(Contains("move patch to selected commit")) + + t.Views().Commits(). + IsFocused(). + Lines( + Contains("commit to move from"), + Contains("destination commit").IsSelected(), + Contains("first commit"), + ). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), + Contains("A unrelated-file"), + ). + 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_index_part_of_adjacent_added_lines.go b/pkg/integration/tests/patch_building/move_to_index_part_of_adjacent_added_lines.go new file mode 100644 index 000000000..c307ae432 --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_index_part_of_adjacent_added_lines.go @@ -0,0 +1,70 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToIndexPartOfAdjacentAddedLines = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to the index, with only some lines of a range of adjacent added lines in the patch", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("file1", "") + shell.Commit("first commit") + + shell.UpdateFileAndAdd("file1", "1st line\n2nd line\n") + shell.Commit("commit to move from") + + shell.UpdateFileAndAdd("unrelated-file", "") + 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("file1").IsSelected(), + ). + PressEnter() + + t.Views().PatchBuilding(). + IsFocused(). + PressEnter(). + PressPrimaryAction() + + t.Views().Information().Content(Contains("building patch")) + + t.Common().SelectPatchOption(Contains("move patch out into index")) + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("file1").IsSelected(), + ). + Tap(func() { + t.Views().Main(). + Content(Contains("+2nd line"). + DoesNotContain("1st line")) + }) + + t.Views().Files(). + Focus(). + ContainsLines( + Contains("M").Contains("file1"), + ) + + t.Views().Main(). + Content(Contains("+1st line\n 2nd line\n")) + }, +}) diff --git a/pkg/integration/tests/patch_building/move_to_later_commit.go b/pkg/integration/tests/patch_building/move_to_later_commit.go new file mode 100644 index 000000000..f80293e72 --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_later_commit.go @@ -0,0 +1,89 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToLaterCommit = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to a later commit", + ExtraCmdArgs: "", + Skip: false, + 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.CreateFileAndAdd("unrelated-file", "") + shell.Commit("destination commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("destination 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.Views().Commits(). + IsFocused(). + SelectPreviousItem() + + t.Common().SelectPatchOption(Contains("move patch to selected commit")) + + t.Views().Commits(). + IsFocused(). + Lines( + Contains("destination commit").IsSelected(), + Contains("commit to move from"), + Contains("first commit"), + ). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), + Contains("A unrelated-file"), + ). + PressEscape() + + t.Views().Commits(). + IsFocused(). + SelectNextItem(). + 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_later_commit_partial_hunk.go b/pkg/integration/tests/patch_building/move_to_later_commit_partial_hunk.go new file mode 100644 index 000000000..3ebf4a886 --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_later_commit_partial_hunk.go @@ -0,0 +1,96 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToLaterCommitPartialHunk = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to a later commit, with only parts of a hunk in the patch", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("file1", "") + shell.Commit("first commit") + + shell.UpdateFileAndAdd("file1", "1st line\n2nd line\n") + shell.Commit("commit to move from") + + shell.UpdateFileAndAdd("unrelated-file", "") + shell.Commit("destination commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("destination commit").IsSelected(), + Contains("commit to move from"), + Contains("first commit"), + ). + SelectNextItem(). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("file1").IsSelected(), + ). + PressEnter() + + t.Views().PatchBuilding(). + IsFocused(). + PressEnter(). + PressPrimaryAction(). + PressEscape() + + t.Views().Information().Content(Contains("building patch")) + + t.Views().CommitFiles(). + IsFocused(). + PressEscape() + + t.Views().Commits(). + IsFocused(). + SelectPreviousItem() + + t.Common().SelectPatchOption(Contains("move patch to selected commit")) + + t.Views().Commits(). + IsFocused(). + Lines( + Contains("destination commit").IsSelected(), + Contains("commit to move from"), + Contains("first commit"), + ). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("file1").IsSelected(), + Contains("unrelated-file"), + ). + Tap(func() { + t.Views().Main(). + Content(Contains("+1st line\n 2nd line")) + }). + PressEscape() + + t.Views().Commits(). + IsFocused(). + SelectNextItem(). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("file1").IsSelected(), + ). + Tap(func() { + t.Views().Main(). + Content(Contains("+2nd line"). + DoesNotContain("1st line")) + }) + }, +}) 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 483a1ab53..491a8c9c5 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit.go @@ -11,13 +11,17 @@ var MoveToNewCommit = NewIntegrationTest(NewIntegrationTestArgs{ Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { - shell.CreateFileAndAdd("file1", "file1 content") + shell.CreateDir("dir") + shell.CreateFileAndAdd("dir/file1", "file1 content") + shell.CreateFileAndAdd("dir/file2", "file2 content") shell.Commit("first commit") - shell.UpdateFileAndAdd("file1", "file1 content with old changes") - shell.Commit("second 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("file1", "file1 content with new changes") + shell.UpdateFileAndAdd("dir/file1", "file1 content with new changes") shell.Commit("third commit") }, Run: func(t *TestDriver, keys config.KeybindingConfig) { @@ -25,7 +29,7 @@ var MoveToNewCommit = NewIntegrationTest(NewIntegrationTestArgs{ Focus(). Lines( Contains("third commit").IsSelected(), - Contains("second commit"), + Contains("commit to move from"), Contains("first commit"), ). SelectNextItem(). @@ -34,18 +38,35 @@ var MoveToNewCommit = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().CommitFiles(). IsFocused(). Lines( - Contains("file1").IsSelected(), + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), ). - PressPrimaryAction() + PressPrimaryAction(). + PressEscape() t.Views().Information().Content(Contains("building patch")) t.Common().SelectPatchOption(Contains("move patch into new commit")) + t.Views().Commits(). + IsFocused(). + Lines( + Contains("third commit"), + Contains(`Split from "commit to move from"`).IsSelected(), + Contains("commit to move from"), + Contains("first commit"), + ). + PressEnter() + t.Views().CommitFiles(). IsFocused(). Lines( - Contains("file1").IsSelected(), + Contains("dir").IsSelected(), + Contains(" M file1"), + Contains(" D file2"), + Contains(" A file3"), ). PressEscape() @@ -53,8 +74,8 @@ var MoveToNewCommit = NewIntegrationTest(NewIntegrationTestArgs{ IsFocused(). Lines( Contains("third commit"), - Contains(`Split from "second commit"`).IsSelected(), - Contains("second commit"), + Contains(`Split from "commit to move from"`).IsSelected(), + Contains("commit to move from"), Contains("first commit"), ). SelectNextItem(). 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 new file mode 100644 index 000000000..2e3c59f77 --- /dev/null +++ b/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go @@ -0,0 +1,83 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveToNewCommitPartialHunk = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a patch from a commit to a new commit, with only parts of a hunk in the patch", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("file1", "") + shell.Commit("first commit") + + shell.UpdateFileAndAdd("file1", "1st line\n2nd line\n") + shell.Commit("commit to move from") + + shell.UpdateFileAndAdd("file1", "1st line\n2nd line\n3rd line\n") + 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("file1").IsSelected(), + ). + PressEnter() + + t.Views().PatchBuilding(). + IsFocused(). + PressEnter(). + PressPrimaryAction() + + t.Views().Information().Content(Contains("building patch")) + + t.Common().SelectPatchOption(Contains("move patch into new commit")) + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("file1").IsSelected(), + ). + Tap(func() { + t.Views().Main(). + Content(Contains("+1st line\n 2nd line")) + }). + PressEscape() + + t.Views().Commits(). + IsFocused(). + Lines( + Contains("third commit"), + Contains(`Split from "commit to move from"`).IsSelected(), + Contains("commit to move from"), + Contains("first commit"), + ). + SelectNextItem(). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("file1").IsSelected(), + ). + Tap(func() { + t.Views().Main(). + Content(Contains("+2nd line"). + DoesNotContain("1st line")) + }) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 3a0fa8269..807c5810d 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -101,10 +101,15 @@ var tests = []*components.IntegrationTest{ patch_building.ApplyInReverse, patch_building.ApplyInReverseWithConflict, patch_building.CopyPatchToClipboard, + patch_building.MoveToEarlierCommit, patch_building.MoveToIndex, + patch_building.MoveToIndexPartOfAdjacentAddedLines, patch_building.MoveToIndexPartial, patch_building.MoveToIndexWithConflict, + patch_building.MoveToLaterCommit, + patch_building.MoveToLaterCommitPartialHunk, patch_building.MoveToNewCommit, + patch_building.MoveToNewCommitPartialHunk, patch_building.RemoveFromCommit, patch_building.ResetWithEscape, patch_building.SelectAllFiles,