summaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/commands/git_commands/patch.go14
-rw-r--r--pkg/commands/patch/patch_builder.go28
-rw-r--r--pkg/commands/patch/transform.go25
-rw-r--r--pkg/gui/controllers/custom_patch_options_menu_action.go2
-rw-r--r--pkg/gui/controllers/helpers/patch_building_helper.go7
-rw-r--r--pkg/integration/tests/patch_building/move_to_index_from_added_file_with_conflict.go96
-rw-r--r--pkg/integration/tests/patch_building/move_to_new_commit_from_added_file.go88
-rw-r--r--pkg/integration/tests/patch_building/remove_parts_of_added_file.go56
-rw-r--r--pkg/integration/tests/patch_building/specific_selection.go6
-rw-r--r--pkg/integration/tests/test_list.go3
10 files changed, 297 insertions, 28 deletions
diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go
index 3d18bf3e2..bceaf9943 100644
--- a/pkg/commands/git_commands/patch.go
+++ b/pkg/commands/git_commands/patch.go
@@ -47,8 +47,8 @@ type ApplyPatchOpts struct {
Reverse bool
}
-func (self *PatchCommands) ApplyCustomPatch(reverse bool) error {
- patch := self.PatchBuilder.PatchToApply(reverse)
+func (self *PatchCommands) ApplyCustomPatch(reverse bool, turnAddedFilesIntoDiffAgainstEmptyFile bool) error {
+ patch := self.PatchBuilder.PatchToApply(reverse, turnAddedFilesIntoDiffAgainstEmptyFile)
return self.ApplyPatch(patch, ApplyPatchOpts{
Index: true,
@@ -94,7 +94,7 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com
}
// apply each patch in reverse
- if err := self.ApplyCustomPatch(true); err != nil {
+ if err := self.ApplyCustomPatch(true, true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -123,7 +123,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
}
// apply each patch forward
- if err := self.ApplyCustomPatch(false); err != nil {
+ if err := self.ApplyCustomPatch(false, false); err != nil {
// Don't abort the rebase here; this might cause conflicts, so give
// the user a chance to resolve them
return err
@@ -172,7 +172,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
}
// apply each patch in reverse
- if err := self.ApplyCustomPatch(true); err != nil {
+ if err := self.ApplyCustomPatch(true, true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -228,7 +228,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
return err
}
- if err := self.ApplyCustomPatch(true); err != nil {
+ if err := self.ApplyCustomPatch(true, true); err != nil {
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
_ = self.rebase.AbortRebase()
}
@@ -282,7 +282,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(
return err
}
- if err := self.ApplyCustomPatch(true); err != nil {
+ if err := self.ApplyCustomPatch(true, true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
diff --git a/pkg/commands/patch/patch_builder.go b/pkg/commands/patch/patch_builder.go
index e18c7d1bb..5ef81c72e 100644
--- a/pkg/commands/patch/patch_builder.go
+++ b/pkg/commands/patch/patch_builder.go
@@ -65,7 +65,7 @@ func (p *PatchBuilder) Start(from, to string, reverse bool, canRebase bool) {
p.fileInfoMap = map[string]*fileInfo{}
}
-func (p *PatchBuilder) PatchToApply(reverse bool) string {
+func (p *PatchBuilder) PatchToApply(reverse bool, turnAddedFilesIntoDiffAgainstEmptyFile bool) string {
patch := ""
for filename, info := range p.fileInfoMap {
@@ -74,9 +74,10 @@ func (p *PatchBuilder) PatchToApply(reverse bool) string {
}
patch += p.RenderPatchForFile(RenderPatchForFileOpts{
- Filename: filename,
- Plain: true,
- Reverse: reverse,
+ Filename: filename,
+ Plain: true,
+ Reverse: reverse,
+ TurnAddedFilesIntoDiffAgainstEmptyFile: turnAddedFilesIntoDiffAgainstEmptyFile,
})
}
@@ -177,9 +178,10 @@ func (p *PatchBuilder) RemoveFileLineRange(filename string, firstLineIdx, lastLi
}
type RenderPatchForFileOpts struct {
- Filename string
- Plain bool
- Reverse bool
+ Filename string
+ Plain bool
+ Reverse bool
+ TurnAddedFilesIntoDiffAgainstEmptyFile bool
}
func (p *PatchBuilder) RenderPatchForFile(opts RenderPatchForFileOpts) string {
@@ -202,8 +204,9 @@ func (p *PatchBuilder) RenderPatchForFile(opts RenderPatchForFileOpts) string {
patch := Parse(info.diff).
Transform(TransformOpts{
- Reverse: opts.Reverse,
- IncludedLineIndices: info.includedLineIndices,
+ Reverse: opts.Reverse,
+ TurnAddedFilesIntoDiffAgainstEmptyFile: opts.TurnAddedFilesIntoDiffAgainstEmptyFile,
+ IncludedLineIndices: info.includedLineIndices,
})
if opts.Plain {
@@ -220,9 +223,10 @@ func (p *PatchBuilder) renderEachFilePatch(plain bool) []string {
sort.Strings(filenames)
patches := lo.Map(filenames, func(filename string, _ int) string {
return p.RenderPatchForFile(RenderPatchForFileOpts{
- Filename: filename,
- Plain: plain,
- Reverse: false,
+ Filename: filename,
+ Plain: plain,
+ Reverse: false,
+ TurnAddedFilesIntoDiffAgainstEmptyFile: true,
})
})
output := lo.Filter(patches, func(patch string, _ int) bool {
diff --git a/pkg/commands/patch/transform.go b/pkg/commands/patch/transform.go
index f861a6540..db35bb4a1 100644
--- a/pkg/commands/patch/transform.go
+++ b/pkg/commands/patch/transform.go
@@ -1,6 +1,10 @@
package patch
-import "github.com/samber/lo"
+import (
+ "strings"
+
+ "github.com/samber/lo"
+)
type patchTransformer struct {
patch *Patch
@@ -22,6 +26,13 @@ type TransformOpts struct {
// information it needs to cleanly apply patches
FileNameOverride string
+ // Custom patches tend to work better when treating new files as diffs
+ // against an empty file. The only case where we need this to be false is
+ // when moving a custom patch to an earlier commit; in that case the patch
+ // command would fail with the error "file does not exist in index" if we
+ // treat it as a diff against an empty file.
+ TurnAddedFilesIntoDiffAgainstEmptyFile bool
+
// The indices of lines that should be included in the patch.
IncludedLineIndices []int
}
@@ -61,6 +72,18 @@ func (self *patchTransformer) transformHeader() []string {
"--- a/" + self.opts.FileNameOverride,
"+++ b/" + self.opts.FileNameOverride,
}
+ } else if self.opts.TurnAddedFilesIntoDiffAgainstEmptyFile {
+ result := make([]string, 0, len(self.patch.header))
+ for idx, line := range self.patch.header {
+ if strings.HasPrefix(line, "new file mode") {
+ continue
+ }
+ if line == "--- /dev/null" && strings.HasPrefix(self.patch.header[idx+1], "+++ b/") {
+ line = "--- a/" + self.patch.header[idx+1][6:]
+ }
+ result = append(result, line)
+ }
+ return result
} else {
return self.patch.header
}
diff --git a/pkg/gui/controllers/custom_patch_options_menu_action.go b/pkg/gui/controllers/custom_patch_options_menu_action.go
index f5099ae2e..f24607597 100644
--- a/pkg/gui/controllers/custom_patch_options_menu_action.go
+++ b/pkg/gui/controllers/custom_patch_options_menu_action.go
@@ -237,7 +237,7 @@ func (self *CustomPatchOptionsMenuAction) handleApplyPatch(reverse bool) error {
action = "Apply patch in reverse"
}
self.c.LogAction(action)
- if err := self.c.Git().Patch.ApplyCustomPatch(reverse); err != nil {
+ if err := self.c.Git().Patch.ApplyCustomPatch(reverse, true); err != nil {
return err
}
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
diff --git a/pkg/gui/controllers/helpers/patch_building_helper.go b/pkg/gui/controllers/helpers/patch_building_helper.go
index 423c9f814..dd4c3515a 100644
--- a/pkg/gui/controllers/helpers/patch_building_helper.go
+++ b/pkg/gui/controllers/helpers/patch_building_helper.go
@@ -82,9 +82,10 @@ func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpt
}
secondaryDiff := self.c.Git().Patch.PatchBuilder.RenderPatchForFile(patch.RenderPatchForFileOpts{
- Filename: path,
- Plain: false,
- Reverse: false,
+ Filename: path,
+ Plain: false,
+ Reverse: false,
+ TurnAddedFilesIntoDiffAgainstEmptyFile: true,
})
context := self.c.Contexts().CustomPatchBuilder
diff --git a/pkg/integration/tests/patch_building/move_to_index_from_added_file_with_conflict.go b/pkg/integration/tests/patch_building/move_to_index_from_added_file_with_conflict.go
new file mode 100644
index 000000000..c7acfcd50
--- /dev/null
+++ b/pkg/integration/tests/patch_building/move_to_index_from_added_file_with_conflict.go
@@ -0,0 +1,96 @@
+package patch_building
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var MoveToIndexFromAddedFileWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Move a patch from a file that was added in a commit to the index, causing a conflict",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.EmptyCommit("first commit")
+
+ shell.CreateFileAndAdd("file1", "1st line\n2nd line\n3rd line\n")
+ shell.Commit("commit to move from")
+ shell.UpdateFileAndAdd("file1", "1st line\n2nd line changed\n3rd line\n")
+ shell.Commit("conflicting change")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ t.Views().Commits().
+ Focus().
+ Lines(
+ Contains("conflicting change").IsSelected(),
+ Contains("commit to move from"),
+ Contains("first commit"),
+ ).
+ SelectNextItem().
+ PressEnter()
+
+ t.Views().CommitFiles().
+ IsFocused().
+ Lines(
+ Contains("file1").IsSelected(),
+ ).
+ PressEnter()
+
+ t.Views().PatchBuilding().
+ IsFocused().
+ SelectNextItem().
+ PressPrimaryAction()
+
+ t.Views().Information().Content(Contains("Building patch"))
+
+ t.Common().SelectPatchOption(Contains("Move patch out into index"))
+
+ t.Common().AcknowledgeConflicts()
+
+ t.Views().Files().
+ IsFocused().
+ Lines(
+ Contains("UU").Contains("file1"),
+ ).
+ PressEnter()
+
+ t.Views().MergeConflicts().
+ IsFocused().
+ ContainsLines(
+ Contains("1st line"),
+ Contains("<<<<<<< HEAD"),
+ Contains("======="),
+ Contains("2nd line changed"),
+ Contains(">>>>>>>"),
+ Contains("3rd line"),
+ ).
+ SelectNextItem().
+ PressPrimaryAction()
+
+ t.Common().ContinueOnConflictsResolved()
+
+ t.ExpectPopup().Alert().
+ Title(Equals("Error")).
+ Content(Contains("Applied patch to 'file1' with conflicts")).
+ Confirm()
+
+ t.Views().Files().
+ IsFocused().
+ Lines(
+ Contains("UU").Contains("file1"),
+ ).
+ PressEnter()
+
+ t.Views().MergeConflicts().
+ TopLines(
+ Contains("1st line"),
+ Contains("<<<<<<< ours"),
+ Contains("2nd line changed"),
+ Contains("======="),
+ Contains("2nd line"),
+ Contains(">>>>>>> theirs"),
+ Contains("3rd line"),
+ ).
+ IsFocused()
+ },
+})
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
new file mode 100644
index 000000000..97aaea2db
--- /dev/null
+++ b/pkg/integration/tests/patch_building/move_to_new_commit_from_added_file.go
@@ -0,0 +1,88 @@
+package patch_building
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var MoveToNewCommitFromAddedFile = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Move a patch from a file that was added in a commit to a new commit",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.EmptyCommit("first commit")
+
+ shell.CreateFileAndAdd("file1", "1st line\n2nd line\n3rd line\n")
+ 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("first commit"),
+ ).
+ PressEnter()
+
+ t.Views().CommitFiles().
+ IsFocused().
+ Lines(
+ Contains("file1").IsSelected(),
+ ).
+ PressEnter()
+
+ t.Views().PatchBuilding().
+ IsFocused().
+ SelectNextItem().
+ PressPrimaryAction()
+
+ 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("new commit").IsSelected(),
+ Contains("commit to move from"),
+ Contains("first commit"),
+ ).
+ PressEnter()
+
+ t.Views().CommitFiles().
+ IsFocused().
+ Lines(
+ Contains("M file1").IsSelected(),
+ ).
+ Tap(func() {
+ t.Views().Main().ContainsLines(
+ Equals(" 1st line"),
+ Equals("+2nd line"),
+ Equals(" 3rd line"),
+ )
+ }).
+ PressEscape()
+
+ t.Views().Commits().
+ IsFocused().
+ NavigateToLine(Contains("commit to move from")).
+ PressEnter()
+
+ t.Views().CommitFiles().
+ IsFocused().
+ Lines(
+ Contains("A file1").IsSelected(),
+ ).
+ Tap(func() {
+ t.Views().Main().ContainsLines(
+ Equals("+1st line"),
+ Equals("+3rd line"),
+ )
+ })
+ },
+})
diff --git a/pkg/integration/tests/patch_building/remove_parts_of_added_file.go b/pkg/integration/tests/patch_building/remove_parts_of_added_file.go
new file mode 100644
index 000000000..9a0b9a951
--- /dev/null
+++ b/pkg/integration/tests/patch_building/remove_parts_of_added_file.go
@@ -0,0 +1,56 @@
+package patch_building
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var RemovePartsOfAddedFile = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Remove a custom patch from a file that was added in a commit",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.EmptyCommit("first commit")
+
+ shell.CreateFileAndAdd("file1", "1st line\n2nd line\n3rd line\n")
+ shell.Commit("commit to remove from")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ t.Views().Commits().
+ Focus().
+ Lines(
+ Contains("commit to remove from").IsSelected(),
+ Contains("first commit"),
+ ).
+ PressEnter()
+
+ t.Views().CommitFiles().
+ IsFocused().
+ Lines(
+ Contains("A file1").IsSelected(),
+ ).
+ PressEnter()
+
+ t.Views().PatchBuilding().
+ IsFocused().
+ SelectNextItem().
+ PressPrimaryAction()
+
+ t.Views().Information().Content(Contains("Building patch"))
+
+ t.Common().SelectPatchOption(Contains("Remove patch from original commit"))
+
+ t.Views().CommitFiles().
+ IsFocused().
+ Lines(
+ Contains("A file1").IsSelected(),
+ ).
+ PressEscape()
+
+ t.Views().Main().ContainsLines(
+ Equals("+1st line"),
+ Equals("+3rd line"),
+ )
+ },
+})
diff --git a/pkg/integration/tests/patch_building/specific_selection.go b/pkg/integration/tests/patch_building/specific_selection.go
index a9dbf9f11..b59b62ccb 100644
--- a/pkg/integration/tests/patch_building/specific_selection.go
+++ b/pkg/integration/tests/patch_building/specific_selection.go
@@ -126,9 +126,8 @@ var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Secondary().ContainsLines(
// direct-file patch
Contains(`diff --git a/direct-file b/direct-file`),
- Contains(`new file mode 100644`),
Contains(`index`),
- Contains(`--- /dev/null`),
+ Contains(`--- a/direct-file`),
Contains(`+++ b/direct-file`),
Contains(`@@ -0,0 +1 @@`),
Contains(`+direct file content`),
@@ -149,9 +148,8 @@ var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{
Contains(` 1f`),
// line-file patch
Contains(`diff --git a/line-file b/line-file`),
- Contains(`new file mode 100644`),
Contains(`index`),
- Contains(`--- /dev/null`),
+ Contains(`--- a/line-file`),
Contains(`+++ b/line-file`),
Contains(`@@ -0,0 +1,5 @@`),
Contains(`+2a`),
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index e2ca3721f..c879b8638 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -232,6 +232,7 @@ var tests = []*components.IntegrationTest{
patch_building.MoveToEarlierCommitFromAddedFile,
patch_building.MoveToEarlierCommitNoKeepEmpty,
patch_building.MoveToIndex,
+ patch_building.MoveToIndexFromAddedFileWithConflict,
patch_building.MoveToIndexPartOfAdjacentAddedLines,
patch_building.MoveToIndexPartial,
patch_building.MoveToIndexWithConflict,
@@ -239,9 +240,11 @@ var tests = []*components.IntegrationTest{
patch_building.MoveToLaterCommit,
patch_building.MoveToLaterCommitPartialHunk,
patch_building.MoveToNewCommit,
+ patch_building.MoveToNewCommitFromAddedFile,
patch_building.MoveToNewCommitFromDeletedFile,
patch_building.MoveToNewCommitPartialHunk,
patch_building.RemoveFromCommit,
+ patch_building.RemovePartsOfAddedFile,
patch_building.ResetWithEscape,
patch_building.SelectAllFiles,
patch_building.SpecificSelection,