diff --git a/gitdiff/apply.go b/gitdiff/apply.go index c49e944..f4e2b71 100644 --- a/gitdiff/apply.go +++ b/gitdiff/apply.go @@ -122,19 +122,22 @@ func (f *TextFragment) ApplyStrict(dst io.Writer, src LineReader) error { return applyError(err, lineNum(n)) } + used := int64(0) for i, line := range f.Lines { - fromSrc, err := applyTextLine(dst, nextLine, line) - if err != nil { + if err := applyTextLine(dst, nextLine, line); err != nil { return applyError(err, lineNum(n), fragLineNum(i)) } - - if fromSrc && i < len(f.Lines)-1 { + if fromSrc(line) { + used++ + } + // advance reader if the next fragment line appears in src and we're behind + if i < len(f.Lines)-1 && fromSrc(f.Lines[i+1]) && int64(n)-limit < used { nextLine, n, err = src.ReadLine() if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } - return applyError(err, lineNum(n), fragLineNum(i+1)) + return applyError(err, lineNum(n), fragLineNum(i+1)) // report for _next_ line in fragment } } } @@ -142,12 +145,11 @@ func (f *TextFragment) ApplyStrict(dst io.Writer, src LineReader) error { return nil } -func applyTextLine(dst io.Writer, src string, line Line) (fromSrc bool, err error) { +func applyTextLine(dst io.Writer, src string, line Line) (err error) { switch line.Op { case OpContext, OpDelete: - fromSrc = true if src != line.Line { - return fromSrc, conflictError("fragment line does not match src line") + return conflictError("fragment line does not match src line") } } switch line.Op { @@ -157,6 +159,10 @@ func applyTextLine(dst io.Writer, src string, line Line) (fromSrc bool, err erro return } +func fromSrc(line Line) bool { + return line.Op != OpAdd +} + // copyLines copies from src to dst until the line at limit, exclusive. Returns // the line at limit and the line number. The line number may not equal the // limit if and only if a non-EOF error occurs. A negative limit means the diff --git a/gitdiff/apply_test.go b/gitdiff/apply_test.go new file mode 100644 index 0000000..a7a027b --- /dev/null +++ b/gitdiff/apply_test.go @@ -0,0 +1,66 @@ +package gitdiff + +import ( + "bytes" + "io/ioutil" + "path/filepath" + "testing" +) + +func TestTextFragmentApplyStrict(t *testing.T) { + tests := map[string]struct { + File string + Err bool + }{ + "createFile": {File: "new"}, + "deleteFile": {File: "delete_all"}, + "addStart": {File: "add_start"}, + "addMiddle": {File: "add_middle"}, + "addEnd": {File: "add_end"}, + "changeStart": {File: "change_start"}, + "changeMiddle": {File: "change_middle"}, + "changeEnd": {File: "change_end"}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + base := filepath.Join("testdata", "apply", "text_fragment_"+test.File) + + src, err := ioutil.ReadFile(base + ".src") + if err != nil { + t.Fatalf("failed to read source file: %v", err) + } + patch, err := ioutil.ReadFile(base + ".patch") + if err != nil { + t.Fatalf("failed to read patch file: %v", err) + } + result, err := ioutil.ReadFile(base + ".dst") + if err != nil { + t.Fatalf("failed to read result file: %v", err) + } + + files, _, err := Parse(bytes.NewReader(patch)) + if err != nil { + t.Fatalf("failed to parse patch file: %v", err) + } + + frag := files[0].TextFragments[0] + + var dst bytes.Buffer + err = frag.ApplyStrict(&dst, NewLineReader(bytes.NewReader(src), 0)) + if test.Err { + if err == nil { + t.Fatalf("expected error applying fragment, but got nil") + } + return + } + if err != nil { + t.Fatalf("unexpected error applying fragment: %v", err) + } + + if !bytes.Equal(result, dst.Bytes()) { + t.Errorf("incorrect result after apply\nexpected:\n%s\nactual:\n%s", result, dst.Bytes()) + } + }) + } +} diff --git a/gitdiff/testdata/apply/text_fragment_add_end.dst b/gitdiff/testdata/apply/text_fragment_add_end.dst new file mode 100644 index 0000000..648fd44 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_end.dst @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +new line a +new line b diff --git a/gitdiff/testdata/apply/text_fragment_add_end.patch b/gitdiff/testdata/apply/text_fragment_add_end.patch new file mode 100644 index 0000000..de708be --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_end.patch @@ -0,0 +1,9 @@ +diff --git a/gitdiff/testdata/apply/fragment_add_end.src b/gitdiff/testdata/apply/fragment_add_end.src +--- a/gitdiff/testdata/apply/fragment_add_end.src ++++ b/gitdiff/testdata/apply/fragment_add_end.src +@@ -1,3 +1,5 @@ + line 1 + line 2 + line 3 ++new line a ++new line b diff --git a/gitdiff/testdata/apply/text_fragment_add_end.src b/gitdiff/testdata/apply/text_fragment_add_end.src new file mode 100644 index 0000000..a92d664 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_end.src @@ -0,0 +1,3 @@ +line 1 +line 2 +line 3 diff --git a/gitdiff/testdata/apply/text_fragment_add_middle.dst b/gitdiff/testdata/apply/text_fragment_add_middle.dst new file mode 100644 index 0000000..ded20d8 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_middle.dst @@ -0,0 +1,5 @@ +line 1 +line 2 +new line a +new line b +line 3 diff --git a/gitdiff/testdata/apply/text_fragment_add_middle.patch b/gitdiff/testdata/apply/text_fragment_add_middle.patch new file mode 100644 index 0000000..43aee3b --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_middle.patch @@ -0,0 +1,9 @@ +diff --git a/gitdiff/testdata/apply/fragment_add_middle.src b/gitdiff/testdata/apply/fragment_add_middle.src +--- a/gitdiff/testdata/apply/fragment_add_middle.src ++++ b/gitdiff/testdata/apply/fragment_add_middle.src +@@ -1,3 +1,5 @@ + line 1 + line 2 ++new line a ++new line b + line 3 diff --git a/gitdiff/testdata/apply/text_fragment_add_middle.src b/gitdiff/testdata/apply/text_fragment_add_middle.src new file mode 100644 index 0000000..a92d664 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_middle.src @@ -0,0 +1,3 @@ +line 1 +line 2 +line 3 diff --git a/gitdiff/testdata/apply/text_fragment_add_start.dst b/gitdiff/testdata/apply/text_fragment_add_start.dst new file mode 100644 index 0000000..b153f60 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_start.dst @@ -0,0 +1,4 @@ +new line a +line 1 +line 2 +line 3 diff --git a/gitdiff/testdata/apply/text_fragment_add_start.patch b/gitdiff/testdata/apply/text_fragment_add_start.patch new file mode 100644 index 0000000..5218764 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_start.patch @@ -0,0 +1,8 @@ +diff --git a/gitdiff/testdata/apply/fragment_add_start.src b/gitdiff/testdata/apply/fragment_add_start.src +--- a/gitdiff/testdata/apply/fragment_add_start.src ++++ b/gitdiff/testdata/apply/fragment_add_start.src +@@ -1,3 +1,4 @@ ++new line a + line 1 + line 2 + line 3 diff --git a/gitdiff/testdata/apply/text_fragment_add_start.src b/gitdiff/testdata/apply/text_fragment_add_start.src new file mode 100644 index 0000000..a92d664 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_add_start.src @@ -0,0 +1,3 @@ +line 1 +line 2 +line 3 diff --git a/gitdiff/testdata/apply/text_fragment_change_end.dst b/gitdiff/testdata/apply/text_fragment_change_end.dst new file mode 100644 index 0000000..e3cbece --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_end.dst @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6 +line 7 +line 8 +line 9 +new line a diff --git a/gitdiff/testdata/apply/text_fragment_change_end.patch b/gitdiff/testdata/apply/text_fragment_change_end.patch new file mode 100644 index 0000000..5655880 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_end.patch @@ -0,0 +1,9 @@ +diff --git a/gitdiff/testdata/apply/text_fragment_change_end.src b/gitdiff/testdata/apply/text_fragment_change_end.src +--- a/gitdiff/testdata/apply/text_fragment_change_end.src ++++ b/gitdiff/testdata/apply/text_fragment_change_end.src +@@ -7,4 +7,4 @@ line 6 + line 7 + line 8 + line 9 +-line 10 ++new line a diff --git a/gitdiff/testdata/apply/text_fragment_change_end.src b/gitdiff/testdata/apply/text_fragment_change_end.src new file mode 100644 index 0000000..fa2da6e --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_end.src @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6 +line 7 +line 8 +line 9 +line 10 diff --git a/gitdiff/testdata/apply/text_fragment_change_middle.dst b/gitdiff/testdata/apply/text_fragment_change_middle.dst new file mode 100644 index 0000000..fd0a9ad --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_middle.dst @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 4 +line 5 +new line a +line 7 +line 8 +line 9 diff --git a/gitdiff/testdata/apply/text_fragment_change_middle.patch b/gitdiff/testdata/apply/text_fragment_change_middle.patch new file mode 100644 index 0000000..139a0fe --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_middle.patch @@ -0,0 +1,12 @@ +diff --git a/gitdiff/testdata/apply/text_fragment_change_middle.src b/gitdiff/testdata/apply/text_fragment_change_middle.src +--- a/gitdiff/testdata/apply/text_fragment_change_middle.src ++++ b/gitdiff/testdata/apply/text_fragment_change_middle.src +@@ -3,7 +3,7 @@ line 2 + line 3 + line 4 + line 5 +-line 6 ++new line a + line 7 + line 8 + line 9 diff --git a/gitdiff/testdata/apply/text_fragment_change_middle.src b/gitdiff/testdata/apply/text_fragment_change_middle.src new file mode 100644 index 0000000..fa2da6e --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_middle.src @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6 +line 7 +line 8 +line 9 +line 10 diff --git a/gitdiff/testdata/apply/text_fragment_change_start.dst b/gitdiff/testdata/apply/text_fragment_change_start.dst new file mode 100644 index 0000000..5156941 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_start.dst @@ -0,0 +1,4 @@ +new line a +line 2 +line 3 +line 4 diff --git a/gitdiff/testdata/apply/text_fragment_change_start.patch b/gitdiff/testdata/apply/text_fragment_change_start.patch new file mode 100644 index 0000000..d0a6653 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_start.patch @@ -0,0 +1,9 @@ +diff --git a/gitdiff/testdata/apply/text_fragment_change_start.src b/gitdiff/testdata/apply/text_fragment_change_start.src +--- a/gitdiff/testdata/apply/text_fragment_change_start.src ++++ b/gitdiff/testdata/apply/text_fragment_change_start.src +@@ -1,4 +1,4 @@ +-line 1 ++new line a + line 2 + line 3 + line 4 diff --git a/gitdiff/testdata/apply/text_fragment_change_start.src b/gitdiff/testdata/apply/text_fragment_change_start.src new file mode 100644 index 0000000..fa2da6e --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_change_start.src @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6 +line 7 +line 8 +line 9 +line 10 diff --git a/gitdiff/testdata/apply/text_fragment_delete_all.dst b/gitdiff/testdata/apply/text_fragment_delete_all.dst new file mode 100644 index 0000000..e69de29 diff --git a/gitdiff/testdata/apply/text_fragment_delete_all.patch b/gitdiff/testdata/apply/text_fragment_delete_all.patch new file mode 100644 index 0000000..8a2fb9c --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_delete_all.patch @@ -0,0 +1,8 @@ +diff --git a/gitdiff/testdata/apply/fragment_delete_all.src b/gitdiff/testdata/apply/fragment_delete_all.src +--- a/gitdiff/testdata/apply/fragment_delete_all.src ++++ b/gitdiff/testdata/apply/fragment_delete_all.src +@@ -1,4 +0,0 @@ +-line a +-line b +-line c +-line d diff --git a/gitdiff/testdata/apply/text_fragment_delete_all.src b/gitdiff/testdata/apply/text_fragment_delete_all.src new file mode 100644 index 0000000..47d03ac --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_delete_all.src @@ -0,0 +1,4 @@ +line a +line b +line c +line d diff --git a/gitdiff/testdata/apply/text_fragment_new.dst b/gitdiff/testdata/apply/text_fragment_new.dst new file mode 100644 index 0000000..a92d664 --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_new.dst @@ -0,0 +1,3 @@ +line 1 +line 2 +line 3 diff --git a/gitdiff/testdata/apply/text_fragment_new.patch b/gitdiff/testdata/apply/text_fragment_new.patch new file mode 100644 index 0000000..c87487b --- /dev/null +++ b/gitdiff/testdata/apply/text_fragment_new.patch @@ -0,0 +1,7 @@ +diff --git a/gitdiff/testdata/apply/fragment_new.src b/gitdiff/testdata/apply/fragment_new.src +--- a/gitdiff/testdata/apply/fragment_new.src ++++ b/gitdiff/testdata/apply/fragment_new.src +@@ -0,0 +1,3 @@ ++line 1 ++line 2 ++line 3 diff --git a/gitdiff/testdata/apply/text_fragment_new.src b/gitdiff/testdata/apply/text_fragment_new.src new file mode 100644 index 0000000..e69de29