Skip to content

Commit 91d5c8c

Browse files
committed
format: diff, Handle no newline at end of file. Fixes go-git#936
Signed-off-by: Stuart Jansen <[email protected]>
1 parent 47f9a70 commit 91d5c8c

File tree

3 files changed

+114
-30
lines changed

3 files changed

+114
-30
lines changed

plumbing/format/diff/unified_encoder.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"fmt"
66
"io"
7+
"regexp"
78
"strings"
89

910
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -25,9 +26,10 @@ const (
2526
tPath = "+++ %s\n"
2627
binary = "Binary files %s and %s differ\n"
2728

28-
addLine = "+%s\n"
29-
deleteLine = "-%s\n"
30-
equalLine = " %s\n"
29+
addLine = "+%s%s"
30+
deleteLine = "-%s%s"
31+
equalLine = " %s%s"
32+
noNewLine = "\n\\ No newline at end of file\n"
3133

3234
oldMode = "old mode %o\n"
3335
newMode = "new mode %o\n"
@@ -216,7 +218,7 @@ func (c *hunksGenerator) processHunk(i int, op Operation) {
216218
linesBefore = c.ctxLines
217219
}
218220

219-
c.current = &hunk{ctxPrefix: ctxPrefix}
221+
c.current = &hunk{ctxPrefix: strings.TrimSuffix(ctxPrefix, "\n")}
220222
c.current.AddOp(Equal, c.beforeContext...)
221223

222224
switch op {
@@ -279,12 +281,13 @@ func (c *hunksGenerator) processEqualsLines(ls []string, i int) {
279281
}
280282
}
281283

284+
var splitLinesRE = regexp.MustCompile(`[^\n]*(\n|$)`)
285+
282286
func splitLines(s string) []string {
283-
out := strings.Split(s, "\n")
287+
out := splitLinesRE.FindAllString(s, -1)
284288
if out[len(out)-1] == "" {
285289
out = out[:len(out)-1]
286290
}
287-
288291
return out
289292
}
290293

@@ -346,7 +349,7 @@ type op struct {
346349
}
347350

348351
func (o *op) String() string {
349-
var prefix string
352+
var prefix, suffix string
350353
switch o.t {
351354
case Add:
352355
prefix = addLine
@@ -355,6 +358,10 @@ func (o *op) String() string {
355358
case Equal:
356359
prefix = equalLine
357360
}
361+
n := len(o.text)
362+
if n > 0 && o.text[n-1] != '\n' {
363+
suffix = noNewLine
364+
}
358365

359-
return fmt.Sprintf(prefix, o.text)
366+
return fmt.Sprintf(prefix, o.text, suffix)
360367
}

plumbing/format/diff/unified_encoder_test.go

+68-22
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ var oneChunkPatch Patch = testPatch{
8383
content: "A\n",
8484
op: Delete,
8585
}, {
86-
content: "B\nC\nD\nE\nF\nG",
86+
content: "B\nC\nD\nE\nF\nG\n",
8787
op: Equal,
8888
}, {
8989
content: "H\n",
@@ -125,7 +125,7 @@ var oneChunkPatchInverted Patch = testPatch{
125125
content: "A\n",
126126
op: Add,
127127
}, {
128-
content: "B\nC\nD\nE\nF\nG",
128+
content: "B\nC\nD\nE\nF\nG\n",
129129
op: Equal,
130130
}, {
131131
content: "H\n",
@@ -164,13 +164,13 @@ var fixtures []*fixture = []*fixture{{
164164
seed: "hello\nbug\n",
165165
},
166166
chunks: []testChunk{{
167-
content: "hello",
167+
content: "hello\n",
168168
op: Equal,
169169
}, {
170-
content: "world",
170+
content: "world\n",
171171
op: Delete,
172172
}, {
173-
content: "bug",
173+
content: "bug\n",
174174
op: Add,
175175
}},
176176
}},
@@ -239,18 +239,18 @@ rename to test1.txt
239239
from: &testFile{
240240
mode: filemode.Regular,
241241
path: "test.txt",
242-
seed: "test",
242+
seed: "test\n",
243243
},
244244
to: &testFile{
245245
mode: filemode.Regular,
246246
path: "test1.txt",
247-
seed: "test1",
247+
seed: "test1\n",
248248
},
249249
chunks: []testChunk{{
250-
content: "test",
250+
content: "test\n",
251251
op: Delete,
252252
}, {
253-
content: "test1",
253+
content: "test1\n",
254254
op: Add,
255255
}},
256256
}},
@@ -260,7 +260,7 @@ rename to test1.txt
260260
diff: `diff --git a/test.txt b/test1.txt
261261
rename from test.txt
262262
rename to test1.txt
263-
index 30d74d258442c7c65512eafab474568dd706c430..f079749c42ffdcc5f52ed2d3a6f15b09307e975e 100644
263+
index 9daeafb9864cf43055ae93beb0afd6c7d144bfa4..a5bce3fd2565d8f458555a0c6f42d0504a848bd5 100644
264264
--- a/test.txt
265265
+++ b/test1.txt
266266
@@ -1 +1 @@
@@ -299,19 +299,19 @@ rename to test1.txt
299299
from: &testFile{
300300
mode: filemode.Regular,
301301
path: "test.txt",
302-
seed: "test",
302+
seed: "test\n",
303303
},
304304
to: &testFile{
305305
mode: filemode.Regular,
306306
path: "test.txt",
307-
seed: "test2",
307+
seed: "test2\n",
308308
},
309309

310310
chunks: []testChunk{{
311-
content: "test",
311+
content: "test\n",
312312
op: Delete,
313313
}, {
314-
content: "test2",
314+
content: "test2\n",
315315
op: Add,
316316
}},
317317
}},
@@ -320,7 +320,7 @@ rename to test1.txt
320320
desc: "one line change",
321321
context: 1,
322322
diff: `diff --git a/test.txt b/test.txt
323-
index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d55b2ae9d 100644
323+
index 9daeafb9864cf43055ae93beb0afd6c7d144bfa4..180cf8328022becee9aaa2577a8f84ea2b9f3827 100644
324324
--- a/test.txt
325325
+++ b/test.txt
326326
@@ -1 +1 @@
@@ -334,19 +334,19 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d
334334
from: &testFile{
335335
mode: filemode.Regular,
336336
path: "test.txt",
337-
seed: "test",
337+
seed: "test\n",
338338
},
339339
to: &testFile{
340340
mode: filemode.Regular,
341341
path: "test.txt",
342-
seed: "test2",
342+
seed: "test2\n",
343343
},
344344

345345
chunks: []testChunk{{
346-
content: "test",
346+
content: "test\n",
347347
op: Delete,
348348
}, {
349-
content: "test2",
349+
content: "test2\n",
350350
op: Add,
351351
}},
352352
}},
@@ -356,7 +356,7 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d
356356
context: 1,
357357
diff: `this is the message
358358
diff --git a/test.txt b/test.txt
359-
index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d55b2ae9d 100644
359+
index 9daeafb9864cf43055ae93beb0afd6c7d144bfa4..180cf8328022becee9aaa2577a8f84ea2b9f3827 100644
360360
--- a/test.txt
361361
+++ b/test.txt
362362
@@ -1 +1 @@
@@ -397,7 +397,9 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d
397397
+++ b/test.txt
398398
@@ -1 +1 @@
399399
-test
400+
\ No newline at end of file
400401
+test2
402+
\ No newline at end of file
401403
`,
402404
}, {
403405
patch: testPatch{
@@ -407,7 +409,7 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d
407409
to: &testFile{
408410
mode: filemode.Regular,
409411
path: "new.txt",
410-
seed: "test\ntest2\test3",
412+
seed: "test\ntest2\ntest3",
411413
},
412414

413415
chunks: []testChunk{{
@@ -421,13 +423,14 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d
421423
context: 1,
422424
diff: `diff --git a/new.txt b/new.txt
423425
new file mode 100644
424-
index 0000000000000000000000000000000000000000..65c8dd02a42273038658a22b1cb29c8d9457ca12
426+
index 0000000000000000000000000000000000000000..3ceaab5442b64a0c2b33dd25fae67ccdb4fd1ea8
425427
--- /dev/null
426428
+++ b/new.txt
427429
@@ -0,0 +1,3 @@
428430
+test
429431
+test2
430432
+test3
433+
\ No newline at end of file
431434
`,
432435
}, {
433436
patch: testPatch{
@@ -456,6 +459,7 @@ index 30d74d258442c7c65512eafab474568dd706c430..00000000000000000000000000000000
456459
+++ /dev/null
457460
@@ -1 +0,0 @@
458461
-test
462+
\ No newline at end of file
459463
`,
460464
}, {
461465
patch: oneChunkPatch,
@@ -548,6 +552,7 @@ index ab5eed5d4a2c33aeef67e0188ee79bed666bde6f..0adddcde4fd38042c354518351820eb0
548552
X
549553
Y
550554
Z
555+
\ No newline at end of file
551556
`,
552557
}, {
553558
patch: oneChunkPatch,
@@ -813,6 +818,47 @@ index 0adddcde4fd38042c354518351820eb06c417c82..553ae669c7a9303cf848fcc749a25692
813818
+++ b/onechunk.txt
814819
@@ -23 +22,0 @@ Y
815820
-Z
821+
\ No newline at end of file
822+
`,
823+
}, {
824+
patch: testPatch{
825+
message: "",
826+
filePatches: []testFilePatch{{
827+
from: &testFile{
828+
mode: filemode.Regular,
829+
path: "onechunk.txt",
830+
seed: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY\nZ",
831+
},
832+
to: &testFile{
833+
mode: filemode.Regular,
834+
path: "onechunk.txt",
835+
seed: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY",
836+
},
837+
838+
chunks: []testChunk{{
839+
content: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\n",
840+
op: Equal,
841+
}, {
842+
content: "Y\nZ",
843+
op: Delete,
844+
}, {
845+
content: "Y",
846+
op: Add,
847+
}},
848+
}},
849+
},
850+
desc: "remove last letter and no newline at end of file",
851+
context: 0,
852+
diff: `diff --git a/onechunk.txt b/onechunk.txt
853+
index 0adddcde4fd38042c354518351820eb06c417c82..d39ae38aad7ba9447b5e7998b2e4714f26c9218d 100644
854+
--- a/onechunk.txt
855+
+++ b/onechunk.txt
856+
@@ -22,2 +21 @@ X
857+
-Y
858+
-Z
859+
\ No newline at end of file
860+
+Y
861+
\ No newline at end of file
816862
`,
817863
}}
818864

utils/diff/diff_ext_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,37 @@ var doTests = [...]struct {
9999
{Type: 1, Text: "111\nBCD\n"},
100100
},
101101
},
102+
{
103+
src: "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nÑ\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ",
104+
dst: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY\nZ",
105+
exp: []diffmatchpatch.Diff{
106+
{Type: -1, Text: "A\n"},
107+
{Type: 0, Text: "B\nC\nD\nE\nF\nG\n"},
108+
{Type: -1, Text: "H\n"},
109+
{Type: 0, Text: "I\nJ\nK\nL\nM\nN\n"},
110+
{Type: -1, Text: \n"},
111+
{Type: 0, Text: "O\nP\nQ\nR\nS\nT\n"},
112+
{Type: -1, Text: "U\n"},
113+
{Type: 0, Text: "V\nW\nX\nY\nZ"},
114+
},
115+
},
116+
{
117+
src: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY\nZ",
118+
dst: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY\n",
119+
exp: []diffmatchpatch.Diff{
120+
{Type: 0, Text: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY\n"},
121+
{Type: -1, Text: "Z"},
122+
},
123+
},
124+
{
125+
src: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY\nZ",
126+
dst: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY",
127+
exp: []diffmatchpatch.Diff{
128+
{Type: 0, Text: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\n"},
129+
{Type: -1, Text: "Y\nZ"},
130+
{Type: 1, Text: "Y"},
131+
},
132+
},
102133
}
103134

104135
func (s *suiteCommon) TestDo(c *C) {

0 commit comments

Comments
 (0)