diff --git a/pkg/commands/patch/hunk.go b/pkg/commands/patch/hunk.go index 98d932126..a2727f2c9 100644 --- a/pkg/commands/patch/hunk.go +++ b/pkg/commands/patch/hunk.go @@ -45,7 +45,7 @@ func headerInfo(header string) (int, int, string) { return oldStart, newStart, heading } -func (hunk *PatchHunk) updatedLines(lineIndices []int, reverse bool) []string { +func (hunk *PatchHunk) updatedLines(lineIndices []int, reverse bool, willBeAppliedReverse bool) []string { skippedNewlineMessageIndex := -1 newLines := []string{} @@ -58,7 +58,7 @@ func (hunk *PatchHunk) updatedLines(lineIndices []int, reverse bool) []string { isLineSelected := lo.Contains(lineIndices, lineIdx) firstChar, content := line[:1], line[1:] - transformedFirstChar := transformedFirstChar(firstChar, reverse, isLineSelected) + transformedFirstChar := transformedFirstChar(firstChar, reverse, willBeAppliedReverse, isLineSelected) if isLineSelected || (transformedFirstChar == "\\" && skippedNewlineMessageIndex != lineIdx) || transformedFirstChar == " " { newLines = append(newLines, transformedFirstChar+content) @@ -74,7 +74,7 @@ func (hunk *PatchHunk) updatedLines(lineIndices []int, reverse bool) []string { return newLines } -func transformedFirstChar(firstChar string, reverse bool, isLineSelected bool) string { +func transformedFirstChar(firstChar string, reverse bool, willBeAppliedReverse bool, isLineSelected bool) string { if reverse { if !isLineSelected && firstChar == "+" { return " " @@ -87,7 +87,11 @@ func transformedFirstChar(firstChar string, reverse bool, isLineSelected bool) s } } - if !isLineSelected && firstChar == "-" { + linesToKeepInPatchContext := "-" + if willBeAppliedReverse { + linesToKeepInPatchContext = "+" + } + if !isLineSelected && firstChar == linesToKeepInPatchContext { return " " } @@ -98,8 +102,8 @@ func (hunk *PatchHunk) formatHeader(oldStart int, oldLength int, newStart int, n return fmt.Sprintf("@@ -%d,%d +%d,%d @@%s\n", oldStart, oldLength, newStart, newLength, heading) } -func (hunk *PatchHunk) formatWithChanges(lineIndices []int, reverse bool, startOffset int) (int, string) { - bodyLines := hunk.updatedLines(lineIndices, reverse) +func (hunk *PatchHunk) formatWithChanges(lineIndices []int, reverse bool, willBeAppliedReverse bool, startOffset int) (int, string) { + bodyLines := hunk.updatedLines(lineIndices, reverse, willBeAppliedReverse) startOffset, header, ok := hunk.updatedHeader(bodyLines, startOffset, reverse) if !ok { return startOffset, "" diff --git a/pkg/commands/patch/patch_modifier.go b/pkg/commands/patch/patch_modifier.go index 5d9da3b60..fa20c7917 100644 --- a/pkg/commands/patch/patch_modifier.go +++ b/pkg/commands/patch/patch_modifier.go @@ -18,6 +18,15 @@ type PatchOptions struct { // generating the patch. Reverse bool + // If true, we're building a patch that we are going to apply using + // "git apply --reverse". In other words, we are not flipping the '+' and + // '-' ourselves while creating the patch, but git is going to do that when + // applying. This has consequences for which lines we need to keep or + // discard when filtering lines from partial hunks. + // + // Currently incompatible with Reverse. + WillBeAppliedReverse bool + // Whether to keep or discard the original diff header including the // "index deadbeef..fa1afe1 100644" line. KeepOriginalHeader bool @@ -105,7 +114,8 @@ outer: formattedHunks := "" var formattedHunk string for _, hunk := range hunksInRange { - startOffset, formattedHunk = hunk.formatWithChanges(lineIndices, opts.Reverse, startOffset) + startOffset, formattedHunk = hunk.formatWithChanges( + lineIndices, opts.Reverse, opts.WillBeAppliedReverse, startOffset) formattedHunks += formattedHunk } diff --git a/pkg/commands/patch/patch_modifier_test.go b/pkg/commands/patch/patch_modifier_test.go index 618473d4b..c97fd89be 100644 --- a/pkg/commands/patch/patch_modifier_test.go +++ b/pkg/commands/patch/patch_modifier_test.go @@ -69,6 +69,20 @@ index e48a11c..b2ab81b 100644 ... ` +const twoChangesInOneHunk = `diff --git a/filename b/filename +index 9320895..6d79956 100644 +--- a/filename ++++ b/filename +@@ -1,5 +1,5 @@ + apple +-grape ++kiwi + orange +-pear ++banana + lemon +` + const newFile = `diff --git a/newfile b/newfile new file mode 100644 index 0000000..4e680cc @@ -101,13 +115,14 @@ const exampleHunk = `@@ -1,5 +1,5 @@ // TestModifyPatchForRange is a function. func TestModifyPatchForRange(t *testing.T) { type scenario struct { - testName string - filename string - diffText string - firstLineIndex int - lastLineIndex int - reverse bool - expected string + testName string + filename string + diffText string + firstLineIndex int + lastLineIndex int + reverse bool + willBeAppliedReverse bool + expected string } scenarios := []scenario{ @@ -506,6 +521,44 @@ func TestModifyPatchForRange(t *testing.T) { @@ -1,1 +0,0 @@ -new line \ No newline at end of file +`, + }, + { + testName: "adding part of a hunk", + filename: "filename", + firstLineIndex: 6, + lastLineIndex: 7, + reverse: false, + willBeAppliedReverse: false, + diffText: twoChangesInOneHunk, + expected: `--- a/filename ++++ b/filename +@@ -1,5 +1,5 @@ + apple +-grape ++kiwi + orange + pear + lemon +`, + }, + { + testName: "adding part of a hunk, will-be-applied-reverse", + filename: "filename", + firstLineIndex: 6, + lastLineIndex: 7, + reverse: false, + willBeAppliedReverse: true, + diffText: twoChangesInOneHunk, + expected: `--- a/filename ++++ b/filename +@@ -1,5 +1,5 @@ + apple +-grape ++kiwi + orange + banana + lemon `, }, } @@ -514,7 +567,11 @@ func TestModifyPatchForRange(t *testing.T) { s := s t.Run(s.testName, func(t *testing.T) { result := ModifiedPatchForRange(nil, s.filename, s.diffText, s.firstLineIndex, s.lastLineIndex, - PatchOptions{Reverse: s.reverse, KeepOriginalHeader: false}) + PatchOptions{ + Reverse: s.reverse, + WillBeAppliedReverse: s.willBeAppliedReverse, + KeepOriginalHeader: false, + }) if !assert.Equal(t, s.expected, result) { fmt.Println(result) }