1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-11-24 08:52:21 +02:00

Merge pull request #2471 from stefanhaller/improve-custom-patch-conflict-handling

This commit is contained in:
Jesse Duffield 2023-03-08 20:45:10 +11:00 committed by GitHub
commit 804a134aa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 275 additions and 204 deletions

View File

@ -75,19 +75,11 @@ func (hunk *PatchHunk) updatedLines(lineIndices []int, reverse bool) []string {
} }
func transformedFirstChar(firstChar string, reverse bool, isLineSelected bool) string { func transformedFirstChar(firstChar string, reverse bool, isLineSelected bool) string {
linesToKeepInPatchContext := "-"
if reverse { if reverse {
if !isLineSelected && firstChar == "+" { linesToKeepInPatchContext = "+"
return " "
} else if firstChar == "-" {
return "+"
} else if firstChar == "+" {
return "-"
} else {
return firstChar
}
} }
if !isLineSelected && firstChar == linesToKeepInPatchContext {
if !isLineSelected && firstChar == "-" {
return " " return " "
} }
@ -100,14 +92,14 @@ func (hunk *PatchHunk) formatHeader(oldStart int, oldLength int, newStart int, n
func (hunk *PatchHunk) formatWithChanges(lineIndices []int, reverse bool, startOffset int) (int, string) { func (hunk *PatchHunk) formatWithChanges(lineIndices []int, reverse bool, startOffset int) (int, string) {
bodyLines := hunk.updatedLines(lineIndices, reverse) bodyLines := hunk.updatedLines(lineIndices, reverse)
startOffset, header, ok := hunk.updatedHeader(bodyLines, startOffset, reverse) startOffset, header, ok := hunk.updatedHeader(bodyLines, startOffset)
if !ok { if !ok {
return startOffset, "" return startOffset, ""
} }
return startOffset, header + strings.Join(bodyLines, "") return startOffset, header + strings.Join(bodyLines, "")
} }
func (hunk *PatchHunk) updatedHeader(newBodyLines []string, startOffset int, reverse bool) (int, string, bool) { func (hunk *PatchHunk) updatedHeader(newBodyLines []string, startOffset int) (int, string, bool) {
changeCount := nLinesWithPrefix(newBodyLines, []string{"+", "-"}) changeCount := nLinesWithPrefix(newBodyLines, []string{"+", "-"})
oldLength := nLinesWithPrefix(newBodyLines, []string{" ", "-"}) oldLength := nLinesWithPrefix(newBodyLines, []string{" ", "-"})
newLength := nLinesWithPrefix(newBodyLines, []string{"+", " "}) newLength := nLinesWithPrefix(newBodyLines, []string{"+", " "})
@ -117,12 +109,7 @@ func (hunk *PatchHunk) updatedHeader(newBodyLines []string, startOffset int, rev
return startOffset, "", false return startOffset, "", false
} }
var oldStart int oldStart := hunk.oldStart
if reverse {
oldStart = hunk.newStart
} else {
oldStart = hunk.oldStart
}
var newStartOffset int var newStartOffset int
// if the hunk went from zero to positive length, we need to increment the starting point by one // if the hunk went from zero to positive length, we need to increment the starting point by one

View File

@ -162,7 +162,7 @@ func (p *PatchManager) RemoveFileLineRange(filename string, firstLineIdx, lastLi
return nil return nil
} }
func (p *PatchManager) renderPlainPatchForFile(filename string, reverse bool, keepOriginalHeader bool) string { func (p *PatchManager) renderPlainPatchForFile(filename string, reverse bool) string {
info, err := p.getFileInfo(filename) info, err := p.getFileInfo(filename)
if err != nil { if err != nil {
p.Log.Error(err) p.Log.Error(err)
@ -176,14 +176,18 @@ func (p *PatchManager) renderPlainPatchForFile(filename string, reverse bool, ke
return info.diff return info.diff
case PART: case PART:
// generate a new diff with just the selected lines // generate a new diff with just the selected lines
return ModifiedPatchForLines(p.Log, filename, info.diff, info.includedLineIndices, reverse, keepOriginalHeader) return ModifiedPatchForLines(p.Log, filename, info.diff, info.includedLineIndices,
PatchOptions{
Reverse: reverse,
KeepOriginalHeader: true,
})
default: default:
return "" return ""
} }
} }
func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse bool, keepOriginalHeader bool) string { func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse bool) string {
patch := p.renderPlainPatchForFile(filename, reverse, keepOriginalHeader) patch := p.renderPlainPatchForFile(filename, reverse)
if plain { if plain {
return patch return patch
} }
@ -199,7 +203,7 @@ func (p *PatchManager) renderEachFilePatch(plain bool) []string {
sort.Strings(filenames) sort.Strings(filenames)
patches := slices.Map(filenames, func(filename string) string { patches := slices.Map(filenames, func(filename string) string {
return p.RenderPatchForFile(filename, plain, false, true) return p.RenderPatchForFile(filename, plain, false)
}) })
output := slices.Filter(patches, func(patch string) bool { output := slices.Filter(patches, func(patch string) bool {
return patch != "" return patch != ""
@ -240,42 +244,22 @@ func (p *PatchManager) GetFileIncLineIndices(filename string) ([]int, error) {
} }
func (p *PatchManager) ApplyPatches(reverse bool) error { func (p *PatchManager) ApplyPatches(reverse bool) error {
// for whole patches we'll apply the patch in reverse patch := ""
// but for part patches we'll apply a reverse patch forwards
applyFlags := []string{"index", "3way"}
if reverse {
applyFlags = append(applyFlags, "reverse")
}
for filename, info := range p.fileInfoMap { for filename, info := range p.fileInfoMap {
if info.mode == UNSELECTED { if info.mode == UNSELECTED {
continue continue
} }
applyFlags := []string{"index", "3way"} patch += p.RenderPatchForFile(filename, true, reverse)
reverseOnGenerate := false
if reverse {
if info.mode == WHOLE {
applyFlags = append(applyFlags, "reverse")
} else {
reverseOnGenerate = true
}
}
var err error
// first run we try with the original header, then without
for _, keepOriginalHeader := range []bool{true, false} {
patch := p.RenderPatchForFile(filename, true, reverseOnGenerate, keepOriginalHeader)
if patch == "" {
continue
}
if err = p.applyPatch(patch, applyFlags...); err != nil {
continue
}
break
}
if err != nil {
return err
}
} }
return nil return p.applyPatch(patch, applyFlags...)
} }
// clears the patch // clears the patch

View File

@ -13,6 +13,19 @@ var (
patchHeaderRegexp = regexp.MustCompile(`(?ms)(^diff.*?)^@@`) patchHeaderRegexp = regexp.MustCompile(`(?ms)(^diff.*?)^@@`)
) )
type PatchOptions struct {
// Create a patch that will applied in reverse with `git apply --reverse`.
// This affects how unselected lines are treated when only parts of a hunk
// are selected: usually, for unselected lines we change '-' lines to
// context lines and remove '+' lines, but when Reverse is true we need to
// turn '+' lines into context lines and remove '-' lines.
Reverse bool
// Whether to keep or discard the original diff header including the
// "index deadbeef..fa1afe1 100644" line.
KeepOriginalHeader bool
}
func GetHeaderFromDiff(diff string) string { func GetHeaderFromDiff(diff string) string {
match := patchHeaderRegexp.FindStringSubmatch(diff) match := patchHeaderRegexp.FindStringSubmatch(diff)
if len(match) <= 1 { if len(match) <= 1 {
@ -76,7 +89,7 @@ func NewPatchModifier(log *logrus.Entry, filename string, diffText string) *Patc
} }
} }
func (d *PatchModifier) ModifiedPatchForLines(lineIndices []int, reverse bool, keepOriginalHeader bool) string { func (d *PatchModifier) ModifiedPatchForLines(lineIndices []int, opts PatchOptions) string {
// step one is getting only those hunks which we care about // step one is getting only those hunks which we care about
hunksInRange := []*PatchHunk{} hunksInRange := []*PatchHunk{}
outer: outer:
@ -95,7 +108,8 @@ outer:
formattedHunks := "" formattedHunks := ""
var formattedHunk string var formattedHunk string
for _, hunk := range hunksInRange { for _, hunk := range hunksInRange {
startOffset, formattedHunk = hunk.formatWithChanges(lineIndices, reverse, startOffset) startOffset, formattedHunk = hunk.formatWithChanges(
lineIndices, opts.Reverse, startOffset)
formattedHunks += formattedHunk formattedHunks += formattedHunk
} }
@ -108,7 +122,7 @@ outer:
// it makes git confused e.g. when dealing with deleted/added files // it makes git confused e.g. when dealing with deleted/added files
// but with building and applying patches the original header gives git // but with building and applying patches the original header gives git
// information it needs to cleanly apply patches // information it needs to cleanly apply patches
if keepOriginalHeader { if opts.KeepOriginalHeader {
fileHeader = d.header fileHeader = d.header
} else { } else {
fileHeader = fmt.Sprintf("--- a/%s\n+++ b/%s\n", d.filename, d.filename) fileHeader = fmt.Sprintf("--- a/%s\n+++ b/%s\n", d.filename, d.filename)
@ -117,13 +131,13 @@ outer:
return fileHeader + formattedHunks return fileHeader + formattedHunks
} }
func (d *PatchModifier) ModifiedPatchForRange(firstLineIdx int, lastLineIdx int, reverse bool, keepOriginalHeader bool) string { func (d *PatchModifier) ModifiedPatchForRange(firstLineIdx int, lastLineIdx int, opts PatchOptions) string {
// generate array of consecutive line indices from our range // generate array of consecutive line indices from our range
selectedLines := []int{} selectedLines := []int{}
for i := firstLineIdx; i <= lastLineIdx; i++ { for i := firstLineIdx; i <= lastLineIdx; i++ {
selectedLines = append(selectedLines, i) selectedLines = append(selectedLines, i)
} }
return d.ModifiedPatchForLines(selectedLines, reverse, keepOriginalHeader) return d.ModifiedPatchForLines(selectedLines, opts)
} }
func (d *PatchModifier) OriginalPatchLength() int { func (d *PatchModifier) OriginalPatchLength() int {
@ -134,14 +148,14 @@ func (d *PatchModifier) OriginalPatchLength() int {
return d.hunks[len(d.hunks)-1].LastLineIdx() return d.hunks[len(d.hunks)-1].LastLineIdx()
} }
func ModifiedPatchForRange(log *logrus.Entry, filename string, diffText string, firstLineIdx int, lastLineIdx int, reverse bool, keepOriginalHeader bool) string { func ModifiedPatchForRange(log *logrus.Entry, filename string, diffText string, firstLineIdx int, lastLineIdx int, opts PatchOptions) string {
p := NewPatchModifier(log, filename, diffText) p := NewPatchModifier(log, filename, diffText)
return p.ModifiedPatchForRange(firstLineIdx, lastLineIdx, reverse, keepOriginalHeader) return p.ModifiedPatchForRange(firstLineIdx, lastLineIdx, opts)
} }
func ModifiedPatchForLines(log *logrus.Entry, filename string, diffText string, includedLineIndices []int, reverse bool, keepOriginalHeader bool) string { func ModifiedPatchForLines(log *logrus.Entry, filename string, diffText string, includedLineIndices []int, opts PatchOptions) string {
p := NewPatchModifier(log, filename, diffText) p := NewPatchModifier(log, filename, diffText)
return p.ModifiedPatchForLines(includedLineIndices, reverse, keepOriginalHeader) return p.ModifiedPatchForLines(includedLineIndices, opts)
} }
// I want to know, given a hunk, what line a given index is on // I want to know, given a hunk, what line a given index is on

View File

@ -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 const newFile = `diff --git a/newfile b/newfile
new file mode 100644 new file mode 100644
index 0000000..4e680cc index 0000000..4e680cc
@ -116,7 +130,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: -1, firstLineIndex: -1,
lastLineIndex: -1, lastLineIndex: -1,
reverse: false,
diffText: simpleDiff, diffText: simpleDiff,
expected: "", expected: "",
}, },
@ -125,7 +138,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: 5, firstLineIndex: 5,
lastLineIndex: 5, lastLineIndex: 5,
reverse: false,
diffText: simpleDiff, diffText: simpleDiff,
expected: "", expected: "",
}, },
@ -134,7 +146,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: 0, firstLineIndex: 0,
lastLineIndex: 11, lastLineIndex: 11,
reverse: false,
diffText: simpleDiff, diffText: simpleDiff,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -152,7 +163,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: 6, firstLineIndex: 6,
lastLineIndex: 6, lastLineIndex: 6,
reverse: false,
diffText: simpleDiff, diffText: simpleDiff,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -169,7 +179,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: 7, firstLineIndex: 7,
lastLineIndex: 7, lastLineIndex: 7,
reverse: false,
diffText: simpleDiff, diffText: simpleDiff,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -187,7 +196,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: -100, firstLineIndex: -100,
lastLineIndex: 100, lastLineIndex: 100,
reverse: false,
diffText: simpleDiff, diffText: simpleDiff,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -198,59 +206,6 @@ func TestModifyPatchForRange(t *testing.T) {
... ...
... ...
... ...
`,
},
{
testName: "whole range reversed",
filename: "filename",
firstLineIndex: 0,
lastLineIndex: 11,
reverse: true,
diffText: simpleDiff,
expected: `--- a/filename
+++ b/filename
@@ -1,5 +1,5 @@
apple
+orange
-grape
...
...
...
`,
},
{
testName: "removal reversed",
filename: "filename",
firstLineIndex: 6,
lastLineIndex: 6,
reverse: true,
diffText: simpleDiff,
expected: `--- a/filename
+++ b/filename
@@ -1,5 +1,6 @@
apple
+orange
grape
...
...
...
`,
},
{
testName: "removal reversed",
filename: "filename",
firstLineIndex: 7,
lastLineIndex: 7,
reverse: true,
diffText: simpleDiff,
expected: `--- a/filename
+++ b/filename
@@ -1,5 +1,4 @@
apple
-grape
...
...
...
`, `,
}, },
{ {
@ -258,7 +213,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: -100, firstLineIndex: -100,
lastLineIndex: 100, lastLineIndex: 100,
reverse: false,
diffText: addNewlineToEndOfFile, diffText: addNewlineToEndOfFile,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -272,37 +226,21 @@ func TestModifyPatchForRange(t *testing.T) {
`, `,
}, },
{ {
testName: "add newline to end of file, addition only", testName: "add newline to end of file, reversed",
filename: "filename", filename: "filename",
firstLineIndex: 8, firstLineIndex: -100,
lastLineIndex: 8, lastLineIndex: 100,
reverse: true, reverse: true,
diffText: addNewlineToEndOfFile, diffText: addNewlineToEndOfFile,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@@ -60,4 +60,5 @@ grape @@ -60,4 +60,4 @@ grape
...
...
...
+last line
\ No newline at end of file
last line
`,
},
{
testName: "add newline to end of file, removal only",
filename: "filename",
firstLineIndex: 10,
lastLineIndex: 10,
reverse: true,
diffText: addNewlineToEndOfFile,
expected: `--- a/filename
+++ b/filename
@@ -60,4 +60,3 @@ grape
... ...
... ...
... ...
-last line -last line
\ No newline at end of file
+last line
`, `,
}, },
{ {
@ -310,7 +248,24 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: -100, firstLineIndex: -100,
lastLineIndex: 100, lastLineIndex: 100,
reverse: false, diffText: removeNewlinefromEndOfFile,
expected: `--- a/filename
+++ b/filename
@@ -60,4 +60,4 @@ grape
...
...
...
-last line
+last line
\ No newline at end of file
`,
},
{
testName: "remove newline from end of file, reversed",
filename: "filename",
firstLineIndex: -100,
lastLineIndex: 100,
reverse: true,
diffText: removeNewlinefromEndOfFile, diffText: removeNewlinefromEndOfFile,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -328,7 +283,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: 8, firstLineIndex: 8,
lastLineIndex: 8, lastLineIndex: 8,
reverse: false,
diffText: removeNewlinefromEndOfFile, diffText: removeNewlinefromEndOfFile,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -337,6 +291,24 @@ func TestModifyPatchForRange(t *testing.T) {
... ...
... ...
-last line -last line
`,
},
{
testName: "remove newline from end of file, removal only, reversed",
filename: "filename",
firstLineIndex: 8,
lastLineIndex: 8,
reverse: true,
diffText: removeNewlinefromEndOfFile,
expected: `--- a/filename
+++ b/filename
@@ -60,5 +60,4 @@ grape
...
...
...
-last line
last line
\ No newline at end of file
`, `,
}, },
{ {
@ -344,7 +316,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: 9, firstLineIndex: 9,
lastLineIndex: 9, lastLineIndex: 9,
reverse: false,
diffText: removeNewlinefromEndOfFile, diffText: removeNewlinefromEndOfFile,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -355,6 +326,23 @@ func TestModifyPatchForRange(t *testing.T) {
last line last line
+last line +last line
\ No newline at end of file \ No newline at end of file
`,
},
{
testName: "remove newline from end of file, addition only, reversed",
filename: "filename",
firstLineIndex: 9,
lastLineIndex: 9,
reverse: true,
diffText: removeNewlinefromEndOfFile,
expected: `--- a/filename
+++ b/filename
@@ -60,3 +60,4 @@ grape
...
...
...
+last line
\ No newline at end of file
`, `,
}, },
{ {
@ -362,7 +350,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: -100, firstLineIndex: -100,
lastLineIndex: 100, lastLineIndex: 100,
reverse: false,
diffText: twoHunks, diffText: twoHunks,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -389,7 +376,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "filename", filename: "filename",
firstLineIndex: 7, firstLineIndex: 7,
lastLineIndex: 15, lastLineIndex: 15,
reverse: false,
diffText: twoHunks, diffText: twoHunks,
expected: `--- a/filename expected: `--- a/filename
+++ b/filename +++ b/filename
@ -408,32 +394,6 @@ func TestModifyPatchForRange(t *testing.T) {
... ...
... ...
... ...
`,
},
{
testName: "staging part of both hunks, reversed",
filename: "filename",
firstLineIndex: 7,
lastLineIndex: 15,
reverse: true,
diffText: twoHunks,
expected: `--- a/filename
+++ b/filename
@@ -1,5 +1,4 @@
apple
-orange
...
...
...
@@ -8,8 +7,7 @@ grape
...
...
...
-pear
lemon
...
...
...
`, `,
}, },
{ {
@ -441,7 +401,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "newfile", filename: "newfile",
firstLineIndex: -100, firstLineIndex: -100,
lastLineIndex: 100, lastLineIndex: 100,
reverse: false,
diffText: newFile, diffText: newFile,
expected: `--- a/newfile expected: `--- a/newfile
+++ b/newfile +++ b/newfile
@ -456,28 +415,12 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "newfile", filename: "newfile",
firstLineIndex: 6, firstLineIndex: 6,
lastLineIndex: 7, lastLineIndex: 7,
reverse: false,
diffText: newFile, diffText: newFile,
expected: `--- a/newfile expected: `--- a/newfile
+++ b/newfile +++ b/newfile
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
+apple +apple
+orange +orange
`,
},
{
testName: "adding a new file, reversed",
filename: "newfile",
firstLineIndex: -100,
lastLineIndex: 100,
reverse: true,
diffText: newFile,
expected: `--- a/newfile
+++ b/newfile
@@ -1,3 +0,0 @@
-apple
-orange
-grape
`, `,
}, },
{ {
@ -485,7 +428,6 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "newfile", filename: "newfile",
firstLineIndex: -100, firstLineIndex: -100,
lastLineIndex: 100, lastLineIndex: 100,
reverse: false,
diffText: addNewlineToPreviouslyEmptyFile, diffText: addNewlineToPreviouslyEmptyFile,
expected: `--- a/newfile expected: `--- a/newfile
+++ b/newfile +++ b/newfile
@ -499,13 +441,49 @@ func TestModifyPatchForRange(t *testing.T) {
filename: "newfile", filename: "newfile",
firstLineIndex: -100, firstLineIndex: -100,
lastLineIndex: 100, lastLineIndex: 100,
reverse: true,
diffText: addNewlineToPreviouslyEmptyFile, diffText: addNewlineToPreviouslyEmptyFile,
reverse: true,
expected: `--- a/newfile expected: `--- a/newfile
+++ b/newfile +++ b/newfile
@@ -1,1 +0,0 @@ @@ -0,0 +1,1 @@
-new line +new line
\ No newline at end of file \ No newline at end of file
`,
},
{
testName: "adding part of a hunk",
filename: "filename",
firstLineIndex: 6,
lastLineIndex: 7,
reverse: false,
diffText: twoChangesInOneHunk,
expected: `--- a/filename
+++ b/filename
@@ -1,5 +1,5 @@
apple
-grape
+kiwi
orange
pear
lemon
`,
},
{
testName: "adding part of a hunk, reverse",
filename: "filename",
firstLineIndex: 6,
lastLineIndex: 7,
reverse: true,
diffText: twoChangesInOneHunk,
expected: `--- a/filename
+++ b/filename
@@ -1,5 +1,5 @@
apple
-grape
+kiwi
orange
banana
lemon
`, `,
}, },
} }
@ -513,7 +491,11 @@ func TestModifyPatchForRange(t *testing.T) {
for _, s := range scenarios { for _, s := range scenarios {
s := s s := s
t.Run(s.testName, func(t *testing.T) { t.Run(s.testName, func(t *testing.T) {
result := ModifiedPatchForRange(nil, s.filename, s.diffText, s.firstLineIndex, s.lastLineIndex, s.reverse, false) result := ModifiedPatchForRange(nil, s.filename, s.diffText, s.firstLineIndex, s.lastLineIndex,
PatchOptions{
Reverse: s.reverse,
KeepOriginalHeader: false,
})
if !assert.Equal(t, s.expected, result) { if !assert.Equal(t, s.expected, result) {
fmt.Println(result) fmt.Println(result)
} }

View File

@ -181,7 +181,8 @@ func (self *StagingController) applySelection(reverse bool) error {
} }
firstLineIdx, lastLineIdx := state.SelectedRange() firstLineIdx, lastLineIdx := state.SelectedRange()
patch := patch.ModifiedPatchForRange(self.c.Log, path, state.GetDiff(), firstLineIdx, lastLineIdx, reverse, false) patch := patch.ModifiedPatchForRange(self.c.Log, path, state.GetDiff(), firstLineIdx, lastLineIdx,
patch.PatchOptions{Reverse: reverse, KeepOriginalHeader: false})
if patch == "" { if patch == "" {
return nil return nil
@ -190,6 +191,9 @@ func (self *StagingController) applySelection(reverse bool) error {
// apply the patch then refresh this panel // apply the patch then refresh this panel
// create a new temp file with the patch, then call git apply with that patch // create a new temp file with the patch, then call git apply with that patch
applyFlags := []string{} applyFlags := []string{}
if reverse {
applyFlags = append(applyFlags, "reverse")
}
if !reverse || self.staged { if !reverse || self.staged {
applyFlags = append(applyFlags, "cached") applyFlags = append(applyFlags, "cached")
} }
@ -227,7 +231,8 @@ func (self *StagingController) editHunk() error {
hunk := state.CurrentHunk() hunk := state.CurrentHunk()
patchText := patch.ModifiedPatchForRange( patchText := patch.ModifiedPatchForRange(
self.c.Log, path, state.GetDiff(), hunk.FirstLineIdx, hunk.LastLineIdx(), self.staged, false, self.c.Log, path, state.GetDiff(), hunk.FirstLineIdx, hunk.LastLineIdx(),
patch.PatchOptions{Reverse: self.staged, KeepOriginalHeader: false},
) )
patchFilepath, err := self.git.WorkingTree.SaveTemporaryPatch(patchText) patchFilepath, err := self.git.WorkingTree.SaveTemporaryPatch(patchText)
if err != nil { if err != nil {
@ -249,9 +254,15 @@ func (self *StagingController) editHunk() error {
lineCount := strings.Count(editedPatchText, "\n") + 1 lineCount := strings.Count(editedPatchText, "\n") + 1
newPatchText := patch.ModifiedPatchForRange( newPatchText := patch.ModifiedPatchForRange(
self.c.Log, path, editedPatchText, 0, lineCount, false, false, self.c.Log, path, editedPatchText, 0, lineCount,
patch.PatchOptions{KeepOriginalHeader: false},
) )
if err := self.git.WorkingTree.ApplyPatch(newPatchText, "cached"); err != nil {
applyFlags := []string{"cached"}
if self.staged {
applyFlags = append(applyFlags, "reverse")
}
if err := self.git.WorkingTree.ApplyPatch(newPatchText, applyFlags...); err != nil {
return self.c.Error(err) return self.c.Error(err)
} }

View File

@ -672,7 +672,7 @@ func (gui *Gui) refreshPatchBuildingPanel(opts types.OnFocusOpts) error {
return err return err
} }
secondaryDiff := gui.git.Patch.PatchManager.RenderPatchForFile(path, false, false, true) secondaryDiff := gui.git.Patch.PatchManager.RenderPatchForFile(path, false, false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -121,7 +121,7 @@ func RunTUI() {
return nil return nil
} }
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code -r pkg/integration/tests/%s", currentTest.Name())) cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code -r pkg/integration/tests/%s.go", currentTest.Name()))
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return err return err
} }

View File

@ -0,0 +1,92 @@
package patch_building
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var ApplyInReverseWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Apply a custom patch in reverse, resulting in a conflict",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "file1 content\n")
shell.CreateFileAndAdd("file2", "file2 content\n")
shell.Commit("first commit")
shell.UpdateFileAndAdd("file1", "file1 content\nmore file1 content\n")
shell.UpdateFileAndAdd("file2", "file2 content\nmore file2 content\n")
shell.Commit("second commit")
shell.UpdateFileAndAdd("file1", "file1 content\nmore file1 content\neven more file1\n")
shell.Commit("third commit")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Lines(
Contains("third commit").IsSelected(),
Contains("second commit"),
Contains("first commit"),
).
NavigateToLine(Contains("second commit")).
PressEnter()
t.Views().CommitFiles().
IsFocused().
Lines(
Contains("M").Contains("file1").IsSelected(),
Contains("M").Contains("file2"),
).
// Add both files to the patch; the first will conflict, the second won't
PressPrimaryAction().
SelectNextItem().
PressPrimaryAction()
t.Views().Information().Content(Contains("building patch"))
t.Views().PatchBuildingSecondary().Content(
Contains("+more file1 content").Contains("+more file2 content"))
t.Common().SelectPatchOption(Contains("apply patch in reverse"))
t.ExpectPopup().Alert().
Title(Equals("Error")).
Content(Contains("Applied patch to 'file1' with conflicts.").
Contains("Applied patch to 'file2' cleanly.")).
Confirm()
t.Views().Files().
Focus().
Lines(
Contains("UU").Contains("file1").IsSelected(),
).
PressPrimaryAction()
t.Views().MergeConflicts().
IsFocused().
ContainsLines(
Contains("file1 content"),
Contains("<<<<<<< ours").IsSelected(),
Contains("more file1 content").IsSelected(),
Contains("even more file1").IsSelected(),
Contains("=======").IsSelected(),
Contains(">>>>>>> theirs"),
).
SelectNextItem().
PressPrimaryAction()
t.Views().Files().
Focus().
Lines(
Contains("M").Contains("file1").IsSelected(),
Contains("M").Contains("file2"),
)
t.Views().Main().
ContainsLines(
Contains(" file1 content"),
Contains("-more file1 content"),
Contains("-even more file1"),
)
},
})

View File

@ -8,7 +8,7 @@ import (
var MoveToIndexWithConflict = NewIntegrationTest(NewIntegrationTestArgs{ var MoveToIndexWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Move a patch from a commit to the index, causing a conflict", Description: "Move a patch from a commit to the index, causing a conflict",
ExtraCmdArgs: "", ExtraCmdArgs: "",
Skip: true, // Skipping until https://github.com/jesseduffield/lazygit/pull/2471 is merged Skip: false,
SetupConfig: func(config *config.AppConfig) {}, SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) { SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "file1 content") shell.CreateFileAndAdd("file1", "file1 content")

View File

@ -99,6 +99,7 @@ var tests = []*components.IntegrationTest{
misc.InitialOpen, misc.InitialOpen,
patch_building.Apply, patch_building.Apply,
patch_building.ApplyInReverse, patch_building.ApplyInReverse,
patch_building.ApplyInReverseWithConflict,
patch_building.CopyPatchToClipboard, patch_building.CopyPatchToClipboard,
patch_building.MoveToIndex, patch_building.MoveToIndex,
patch_building.MoveToIndexPartial, patch_building.MoveToIndexPartial,