fork of go-gitdiff with jj support
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix EOF handling in TextFragment#ApplyStrict

io.EOF was not properly accounted for when dealing with patches that are
missing trailing newline characters.

+70 -23
+14 -15
gitdiff/apply.go
··· 53 53 54 54 e, ok := err.(*ApplyError) 55 55 if !ok { 56 + if err == io.EOF { 57 + err = io.ErrUnexpectedEOF 58 + } 56 59 e = &ApplyError{err: err} 57 60 } 58 61 for _, arg := range args { ··· 115 118 // line numbers are zero-indexed, positions are one-indexed 116 119 limit := f.OldPosition - 1 117 120 118 - // an EOF is allowed here: the fragment applies to the last line of the 119 - // source but it does not have a newline character 121 + // io.EOF is acceptable here: the first line of the patch is the last of 122 + // the source and it has no newline character 120 123 nextLine, n, err := copyLines(dst, src, limit) 121 124 if err != nil && err != io.EOF { 122 125 return applyError(err, lineNum(n)) ··· 127 130 if err := applyTextLine(dst, nextLine, line); err != nil { 128 131 return applyError(err, lineNum(n), fragLineNum(i)) 129 132 } 130 - if fromSrc(line) { 133 + if line.Old() { 131 134 used++ 132 135 } 133 136 // advance reader if the next fragment line appears in src and we're behind 134 - if i < len(f.Lines)-1 && fromSrc(f.Lines[i+1]) && int64(n)-limit < used { 137 + if i < len(f.Lines)-1 && f.Lines[i+1].Old() && int64(n)-limit < used { 135 138 nextLine, n, err = src.ReadLine() 136 - if err != nil { 137 - if err == io.EOF { 138 - err = io.ErrUnexpectedEOF 139 - } 139 + switch { 140 + case err == io.EOF && f.Lines[i+1].NoEOL(): 141 + continue 142 + case err != nil: 140 143 return applyError(err, lineNum(n), fragLineNum(i+1)) // report for _next_ line in fragment 141 144 } 142 145 } ··· 159 162 return 160 163 } 161 164 162 - func fromSrc(line Line) bool { 163 - return line.Op != OpAdd 164 - } 165 - 166 165 // copyLines copies from src to dst until the line at limit, exclusive. Returns 167 - // the line at limit and the line number. The line number may not equal the 168 - // limit if and only if a non-EOF error occurs. A negative limit means the 169 - // first read should return io.EOF and no data. 166 + // the line at limit and the line number. If the error is nil or io.EOF, the 167 + // line number equals limit. A negative limit checks that the source has no 168 + // more lines to read. 170 169 func copyLines(dst io.Writer, src LineReader, limit int64) (string, int, error) { 171 170 // TODO(bkeyes): fix int vs int64 for limit and return value 172 171 for {
+12 -8
gitdiff/apply_test.go
··· 12 12 File string 13 13 Err bool 14 14 }{ 15 - "createFile": {File: "new"}, 16 - "deleteFile": {File: "delete_all"}, 17 - "addStart": {File: "add_start"}, 18 - "addMiddle": {File: "add_middle"}, 19 - "addEnd": {File: "add_end"}, 20 - "changeStart": {File: "change_start"}, 21 - "changeMiddle": {File: "change_middle"}, 22 - "changeEnd": {File: "change_end"}, 15 + "createFile": {File: "new"}, 16 + "deleteFile": {File: "delete_all"}, 17 + 18 + "addStart": {File: "add_start"}, 19 + "addMiddle": {File: "add_middle"}, 20 + "addEnd": {File: "add_end"}, 21 + "addEndNoEOL": {File: "add_end_noeol"}, 22 + 23 + "changeStart": {File: "change_start"}, 24 + "changeMiddle": {File: "change_middle"}, 25 + "changeEnd": {File: "change_end"}, 26 + "changeSingleNoEOL": {File: "change_single_noeol"}, 23 27 } 24 28 25 29 for name, test := range tests {
+15
gitdiff/gitdiff.go
··· 137 137 return fl.Op.String() + fl.Line 138 138 } 139 139 140 + // Old returns true if the line appears in the old content of the fragment. 141 + func (fl Line) Old() bool { 142 + return fl.Op != OpAdd 143 + } 144 + 145 + // New returns true if the line appears in the new content of the fragment. 146 + func (fl Line) New() bool { 147 + return fl.Op == OpAdd 148 + } 149 + 150 + // NoEOL returns true if the line is missing a trailing newline character. 151 + func (fl Line) NoEOL() bool { 152 + return len(fl.Line) == 0 || fl.Line[len(fl.Line)-1] != '\n' 153 + } 154 + 140 155 // LineOp describes the type of a text fragment line: context, added, or removed. 141 156 type LineOp int 142 157
+5
gitdiff/testdata/apply/text_fragment_add_end_noeol.dst
··· 1 + line 1 2 + line 2 3 + line 3 4 + line 4 5 + line 5
+11
gitdiff/testdata/apply/text_fragment_add_end_noeol.patch
··· 1 + diff --git a/gitdiff/testdata/apply/text_fragment_add_end_noeol.src b/gitdiff/testdata/apply/text_fragment_add_end_noeol.src 2 + --- a/gitdiff/testdata/apply/text_fragment_add_end_noeol.src 3 + +++ b/gitdiff/testdata/apply/text_fragment_add_end_noeol.src 4 + @@ -1,3 +1,5 @@ 5 + line 1 6 + line 2 7 + -line 3 8 + \ No newline at end of file 9 + +line 3 10 + +line 4 11 + +line 5
+3
gitdiff/testdata/apply/text_fragment_add_end_noeol.src
··· 1 + line 1 2 + line 2 3 + line 3
+1
gitdiff/testdata/apply/text_fragment_change_single_noeol.dst
··· 1 + new line a
+8
gitdiff/testdata/apply/text_fragment_change_single_noeol.patch
··· 1 + diff --git a/gitdiff/testdata/apply/text_fragment_change_single_noeol.src b/gitdiff/testdata/apply/text_fragment_change_single_noeol.src 2 + --- a/gitdiff/testdata/apply/text_fragment_change_single_noeol.src 3 + +++ b/gitdiff/testdata/apply/text_fragment_change_single_noeol.src 4 + @@ -1 +1 @@ 5 + -line 1 6 + \ No newline at end of file 7 + +new line a 8 + \ No newline at end of file
+1
gitdiff/testdata/apply/text_fragment_change_single_noeol.src
··· 1 + line 1