Eric Bower
·
2025-12-13
range_diff_test.go
1package git
2
3import (
4 "fmt"
5 "strings"
6 "testing"
7
8 "github.com/picosh/git-pr/fixtures"
9)
10
11func bail(err error) {
12 if err != nil {
13 panic(bail)
14 }
15}
16
17func cmp(afile, bfile string) string {
18 a, err := fixtures.Fixtures.Open(afile)
19 bail(err)
20 b, err := fixtures.Fixtures.Open(bfile)
21 bail(err)
22 aPatches, err := ParsePatchset(a)
23 bail(err)
24 bPatches, err := ParsePatchset(b)
25 bail(err)
26 actual := RangeDiff(aPatches, bPatches)
27 return RangeDiffToStr(actual)
28}
29
30func fail(expected, actual string) string {
31 return fmt.Sprintf("expected:[\n%s] actual:[\n%s]", expected, actual)
32}
33
34// https://git.kernel.org/tree/t/t3206-range-diff.sh?id=d19b6cd2dd72dc811f19df4b32c7ed223256c3ee
35
36// simple A..B A..C (unmodified)
37/*
38 1: $(test_oid t1) = 1: $(test_oid u1) s/5/A/
39 2: $(test_oid t2) = 2: $(test_oid u2) s/4/A/
40 3: $(test_oid t3) = 3: $(test_oid u3) s/11/B/
41 4: $(test_oid t4) = 4: $(test_oid u4) s/12/B/
42*/
43func TestRangeDiffUnmodified(t *testing.T) {
44 actual := cmp("a_b.patch", "a_c.patch")
45 expected := "1: 33c682a = 1: 1668484 chore: add torch and create random tensor\n"
46 if expected != actual {
47 t.Fatal(fail(expected, actual))
48 }
49}
50
51// trivial reordering
52/*
53 1: $(test_oid t1) = 1: $(test_oid r1) s/5/A/
54 3: $(test_oid t3) = 2: $(test_oid r2) s/11/B/
55 4: $(test_oid t4) = 3: $(test_oid r3) s/12/B/
56 2: $(test_oid t2) = 4: $(test_oid r4) s/4/A/
57*/
58func TestRangeDiffTrivialReordering(t *testing.T) {
59 actual := cmp("a_b_reorder.patch", "a_c_reorder.patch")
60 expected := `2: 22dde12 = 1: 7dbb94c docs: readme
611: 33c682a = 2: ad17587 chore: add torch and create random tensor
62`
63 if expected != actual {
64 t.Fatal(fail(expected, actual))
65 }
66}
67
68// removed commit
69/*
70 1: $(test_oid t1) = 1: $(test_oid d1) s/5/A/
71 2: $(test_oid t2) < -: $(test_oid __) s/4/A/
72 3: $(test_oid t3) = 2: $(test_oid d2) s/11/B/
73 4: $(test_oid t4) = 3: $(test_oid d3) s/12/B/
74*/
75func TestRangeDiffRemovedCommit(t *testing.T) {
76 actual := cmp("a_b_reorder.patch", "a_c_rm_commit.patch")
77 if !strings.Contains(actual, "1: 33c682a < -: ------- chore: add torch and create random tensor") {
78 t.Fatal("expected removed commit header not found")
79 }
80 if !strings.Contains(actual, "2: 22dde12 = 1: 7dbb94c docs: readme") {
81 t.Fatal("expected equal commit header not found")
82 }
83 if !strings.Contains(actual, "requirements.txt") {
84 t.Fatal("expected file diff for removed commit")
85 }
86}
87
88// added commit
89/*
90 1: $(test_oid t1) = 1: $(test_oid a1) s/5/A/
91 2: $(test_oid t2) = 2: $(test_oid a2) s/4/A/
92 -: $(test_oid __) > 3: $(test_oid a3) s/6/A/
93 3: $(test_oid t3) = 4: $(test_oid a4) s/11/B/
94 4: $(test_oid t4) = 5: $(test_oid a5) s/12/B/
95*/
96func TestRangeDiffAddedCommit(t *testing.T) {
97 actual := cmp("a_b_reorder.patch", "a_c_added_commit.patch")
98 if !strings.Contains(actual, "1: 33c682a = 1: 33c682a chore: add torch and create random tensor") {
99 t.Fatal("expected first equal commit header not found")
100 }
101 if !strings.Contains(actual, "2: 22dde12 = 2: 22dde12 docs: readme") {
102 t.Fatal("expected second equal commit header not found")
103 }
104 if !strings.Contains(actual, "-: ------- > 3: b248060 chore: make tensor 6x6") {
105 t.Fatal("expected added commit header not found")
106 }
107 if !strings.Contains(actual, "train.py") {
108 t.Fatal("expected file diff for added commit")
109 }
110}
111
112// changed commit
113/*
114 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/
115 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/
116 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/
117 @@ file: A
118 9
119 10
120 -11
121 -+B
122 ++BB
123 12
124 13
125 14
126 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/
127 @@ file
128 @@ file: A
129 9
130 10
131 - B
132 + BB
133 -12
134 +B
135 13
136*/
137func TestRangeDiffChangedCommit(t *testing.T) {
138 actual := cmp("a_b_reorder.patch", "a_c_changed_commit.patch")
139 // os.WriteFile("fixtures/expected_commit_changed.txt", []byte(actual), 0644)
140 fp, err := fixtures.Fixtures.ReadFile("expected_commit_changed.txt")
141 if err != nil {
142 t.Fatal("file not found")
143 }
144 expected := string(fp)
145 if strings.TrimSpace(expected) != strings.TrimSpace(actual) {
146 t.Fatal(fail(expected, actual))
147 }
148}
149
150// renamed file
151/*
152 1: $(test_oid t1) = 1: $(test_oid n1) s/5/A/
153 2: $(test_oid t2) ! 2: $(test_oid n2) s/4/A/
154 @@ Metadata
155 ZAuthor: Thomas Rast <trast@inf.ethz.ch>
156 Z
157 Z ## Commit message ##
158 - s/4/A/
159 + s/4/A/ + rename file
160 Z
161 - ## file ##
162 + ## file => renamed-file ##
163 Z@@
164 Z 1
165 Z 2
166 3: $(test_oid t3) ! 3: $(test_oid n3) s/11/B/
167 @@ Metadata
168 Z ## Commit message ##
169 Z s/11/B/
170 Z
171 - ## file ##
172 -@@ file: A
173 + ## renamed-file ##
174 +@@ renamed-file: A
175 Z 8
176 Z 9
177 Z 10
178 4: $(test_oid t4) ! 4: $(test_oid n4) s/12/B/
179 @@ Metadata
180 Z ## Commit message ##
181 Z s/12/B/
182 Z
183 - ## file ##
184 -@@ file: A
185 + ## renamed-file ##
186 +@@ renamed-file: A
187 Z 9
188 Z 10
189 Z B
190*/
191func TestRangeDiffRenamedFile(t *testing.T) {
192 actual := cmp("a_b_reorder.patch", "a_c_renamed_file.patch")
193 if !strings.Contains(actual, "1: 33c682a = 1: 33c682a") {
194 t.Fatal("expected first commit to be equal")
195 }
196 if !strings.Contains(actual, "2: 22dde12 ! 2: aabbcc1") {
197 t.Fatal("expected second commit to show diff marker")
198 }
199 if !strings.Contains(actual, "DOCS.md") {
200 t.Fatal("expected renamed file DOCS.md in output")
201 }
202}
203
204// file with mode only change
205/*
206 1: $(test_oid t2) ! 1: $(test_oid o1) s/4/A/
207 @@ Metadata
208 ZAuthor: Thomas Rast <trast@inf.ethz.ch>
209 Z
210 Z ## Commit message ##
211 - s/4/A/
212 + s/4/A/ + add other-file
213 Z
214 Z ## file ##
215 Z@@
216 @@ file
217 Z A
218 Z 6
219 Z 7
220 +
221 + ## other-file (new) ##
222 2: $(test_oid t3) ! 2: $(test_oid o2) s/11/B/
223 @@ Metadata
224 ZAuthor: Thomas Rast <trast@inf.ethz.ch>
225 Z
226 Z ## Commit message ##
227 - s/11/B/
228 + s/11/B/ + mode change other-file
229 Z
230 Z ## file ##
231 Z@@ file: A
232 @@ file: A
233 Z 12
234 Z 13
235 Z 14
236 +
237 + ## other-file (mode change 100644 => 100755) ##
238 3: $(test_oid t4) = 3: $(test_oid o3) s/12/B/
239*/
240func TestRangeDiffFileWithModeOnlyChange(t *testing.T) {
241 actual := cmp("a_b_reorder.patch", "a_c_mode_change.patch")
242 if !strings.Contains(actual, "1: 33c682a ! 1: 33c682a") {
243 t.Fatal("expected first commit to show diff marker due to new file")
244 }
245 if !strings.Contains(actual, "run.sh") {
246 t.Fatal("expected run.sh script in output")
247 }
248}
249
250// file added and later removed
251/*
252 1: $(test_oid t1) = 1: $(test_oid s1) s/5/A/
253 2: $(test_oid t2) ! 2: $(test_oid s2) s/4/A/
254 @@ Metadata
255 ZAuthor: Thomas Rast <trast@inf.ethz.ch>
256 Z
257 Z ## Commit message ##
258 - s/4/A/
259 + s/4/A/ + new-file
260 Z
261 Z ## file ##
262 Z@@
263 @@ file
264 Z A
265 Z 6
266 Z 7
267 +
268 + ## new-file (new) ##
269 3: $(test_oid t3) ! 3: $(test_oid s3) s/11/B/
270 @@ Metadata
271 ZAuthor: Thomas Rast <trast@inf.ethz.ch>
272 Z
273 Z ## Commit message ##
274 - s/11/B/
275 + s/11/B/ + remove file
276 Z
277 Z ## file ##
278 Z@@ file: A
279 @@ file: A
280 Z 12
281 Z 13
282 Z 14
283 +
284 + ## new-file (deleted) ##
285 4: $(test_oid t4) = 4: $(test_oid s4) s/12/B/
286*/
287func TestRangeDiffFileAddedThenRemoved(t *testing.T) {
288 actual := cmp("a_b_reorder.patch", "a_c_file_added_removed.patch")
289 if !strings.Contains(actual, "1: 33c682a = 1: 33c682a") {
290 t.Fatal("expected first commit to be equal")
291 }
292 if !strings.Contains(actual, "temp.txt") {
293 t.Fatal("expected temp.txt in output")
294 }
295 if !strings.Contains(actual, "-: ------- > 3: ccddee1") {
296 t.Fatal("expected third commit to be added")
297 }
298}
299
300// changed message
301/*
302 1: $(test_oid t1) = 1: $(test_oid m1) s/5/A/
303 2: $(test_oid t2) ! 2: $(test_oid m2) s/4/A/
304 @@ Metadata
305 Z ## Commit message ##
306 Z s/4/A/
307 Z
308 + Also a silly comment here!
309 +
310 Z ## file ##
311 Z@@
312 Z 1
313 3: $(test_oid t3) = 3: $(test_oid m3) s/11/B/
314 4: $(test_oid t4) = 4: $(test_oid m4) s/12/B/
315*/
316func TestRangeDiffChangedMessage(t *testing.T) {
317 actual := cmp("a_b_reorder.patch", "a_c_changed_message.patch")
318 if !strings.Contains(actual, "1: 33c682a = 1: 33c682a") {
319 t.Fatal("expected first commit to be equal")
320 }
321 if !strings.Contains(actual, "2: 22dde12 ! 2: ddeeff1") {
322 t.Fatal("expected second commit to show diff marker due to message change")
323 }
324}
325
326func TestRangeDiffEmptyPatchset(t *testing.T) {
327 a, err := fixtures.Fixtures.Open("a_b_reorder.patch")
328 bail(err)
329 aPatches, err := ParsePatchset(a)
330 bail(err)
331 bPatches := []*Patch{}
332
333 actual := RangeDiff(aPatches, bPatches)
334 result := RangeDiffToStr(actual)
335
336 if len(aPatches) != 2 {
337 t.Fatalf("expected 2 patches in a, got %d", len(aPatches))
338 }
339 if !strings.Contains(result, "< -:") {
340 t.Fatal("expected removed commit markers when comparing to empty patchset")
341 }
342 if !strings.Contains(result, "1: 33c682a < -:") {
343 t.Fatal("expected first commit to show as removed")
344 }
345 if !strings.Contains(result, "2: 22dde12 < -:") {
346 t.Fatal("expected second commit to show as removed")
347 }
348}
349
350func TestRangeDiffEmptyToNonEmpty(t *testing.T) {
351 b, err := fixtures.Fixtures.Open("a_b_reorder.patch")
352 bail(err)
353 bPatches, err := ParsePatchset(b)
354 bail(err)
355 aPatches := []*Patch{}
356
357 actual := RangeDiff(aPatches, bPatches)
358 result := RangeDiffToStr(actual)
359
360 if len(bPatches) != 2 {
361 t.Fatalf("expected 2 patches in b, got %d", len(bPatches))
362 }
363 if !strings.Contains(result, "> 1:") {
364 t.Fatal("expected added commit markers when comparing from empty patchset")
365 }
366 if !strings.Contains(result, "-: ------- > 1: 33c682a") {
367 t.Fatal("expected first commit to show as added")
368 }
369}
370
371func TestRangeDiffSquashedCommits(t *testing.T) {
372 actual := cmp("a_b_reorder.patch", "a_c_squashed.patch")
373
374 if !strings.Contains(actual, "< -:") {
375 t.Fatal("expected at least one commit to show as removed (squashed away)")
376 }
377 if !strings.Contains(actual, "aabbccd") {
378 t.Fatal("expected squashed commit sha in output")
379 }
380}
381
382func TestRangeDiffSplitCommits(t *testing.T) {
383 actual := cmp("a_b_reorder.patch", "a_c_split.patch")
384
385 if !strings.Contains(actual, "-: ------- >") {
386 t.Fatal("expected added commit marker for split commit")
387 }
388 if !strings.Contains(actual, "aabb112") {
389 t.Fatal("expected new split commit sha (aabb112) in output")
390 }
391 if !strings.Contains(actual, "33c682a") {
392 t.Fatal("expected original commit sha in output")
393 }
394}
395
396func TestRangeDiffDifferentAuthor(t *testing.T) {
397 actual := cmp("a_b_reorder.patch", "a_c_different_author.patch")
398
399 if !strings.Contains(actual, "!") {
400 t.Fatal("expected diff marker (!) for commits with different author")
401 }
402 if !strings.Contains(actual, "33c682a") && !strings.Contains(actual, "22dde12") {
403 t.Fatal("expected commit shas in output")
404 }
405}
406
407func TestRangeDiffMultipleFilesInCommit(t *testing.T) {
408 actual := cmp("a_b_reorder.patch", "a_c_multi_file_change.patch")
409
410 if !strings.Contains(actual, "1: 33c682a = 1: 33c682a") {
411 t.Fatal("expected first commit to be equal")
412 }
413 if !strings.Contains(actual, "2: 22dde12 ! 2: bbccdd1") {
414 t.Fatal("expected second commit to show diff marker")
415 }
416 if !strings.Contains(actual, "CONTRIBUTING.md") {
417 t.Fatal("expected CONTRIBUTING.md in diff output")
418 }
419 if !strings.Contains(actual, "LICENSE.md") {
420 t.Fatal("expected LICENSE.md in diff output")
421 }
422}
423
424func TestRangeDiffIgnoresContextLines(t *testing.T) {
425 actual := cmp("context_lines_v1.patch", "context_lines_v2.patch")
426
427 if !strings.Contains(actual, "=") {
428 t.Fatal("expected equal marker (=) since +/- lines are identical")
429 }
430 if strings.Contains(actual, "!") {
431 t.Fatal("should not show diff marker (!) when only context lines differ")
432 }
433 if strings.Contains(actual, "old_value") || strings.Contains(actual, "new_value") {
434 t.Fatal("should not have file diff output when changes are equal")
435 }
436}
437
438func TestRangeDiffNormalizesHunkHeaders(t *testing.T) {
439 actual := cmp("hunk_header_v1.patch", "hunk_header_v2.patch")
440
441 if !strings.Contains(actual, "=") {
442 t.Fatal("expected equal marker (=) since changes are identical despite different hunk headers")
443 }
444 if strings.Contains(actual, "!") {
445 t.Fatal("should not show diff marker (!) when only hunk header line numbers differ")
446 }
447 if strings.Contains(actual, "@@ server.go") {
448 t.Fatal("should not have file diff output when changes are equal")
449 }
450}