t4211: add tests for -L with standard diff options
Now that -L output flows through the standard diff pipeline, verify that previously-ignored diff options work: formatting (--word-diff, --word-diff-regex, --no-prefix, --src/dst-prefix, --full-index, --abbrev), whitespace handling (-w, -b), output indicators (--output-indicator-new/old/context), direction reversal (-R), --color-moved, and pickaxe options (-S, -G). Signed-off-by: Michael Montalbo <mmontalbo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Michael Montalbo committed
Mar 17, 2026 at 02:21 UTC
0e51f7a7faae18d9e6819c38cc822d6232deacae
1 file changed
+281
t/t4211-line-log.sh
+281
@@ -339,6 +339,92 @@ test_expect_success 'zero-width regex .* matches any function name' '
339
test_cmp expect actual
340
'
341
342
+test_expect_success 'setup for diff pipeline tests' '
343
+ git checkout parent-oids &&
344
+
345
+ head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
346
+ head_blob_new=$(git rev-parse --short HEAD:file.c) &&
347
+ root_blob=$(git rev-parse --short HEAD~4:file.c) &&
348
+ null_blob=$(test_oid zero | cut -c1-7) &&
349
+ head_blob_old_full=$(git rev-parse HEAD^:file.c) &&
350
+ head_blob_new_full=$(git rev-parse HEAD:file.c) &&
351
+ root_blob_full=$(git rev-parse HEAD~4:file.c) &&
352
+ null_blob_full=$(test_oid zero)
353
+'
354
+
355
+test_expect_success '-L diff output includes index and new file mode' '
356
+ git log -L:func2:file.c --format= >actual &&
357
+
358
+ # Output should contain index headers (not present in old code path)
359
+ grep "^index $head_blob_old\.\.$head_blob_new 100644" actual &&
360
+
361
+ # Root commit should show new file mode and null index
362
+ grep "^new file mode 100644" actual &&
363
+ grep "^index $null_blob\.\.$root_blob$" actual &&
364
+
365
+ # Hunk headers should include funcname context
366
+ grep "^@@ .* @@ int func1()" actual
367
+'
368
+
369
+test_expect_success '-L with --word-diff' '
370
+ cat >expect <<-\EOF &&
371
+
372
+ diff --git a/file.c b/file.c
373
+ --- a/file.c
374
+ +++ b/file.c
375
+ @@ -6,4 +6,4 @@ int func1()
376
+ int func2()
377
+ {
378
+ return [-F2;-]{+F2 + 2;+}
379
+ }
380
+
381
+ diff --git a/file.c b/file.c
382
+ new file mode 100644
383
+ --- /dev/null
384
+ +++ b/file.c
385
+ @@ -0,0 +6,4 @@
386
+ {+int func2()+}
387
+ {+{+}
388
+ {+ return F2;+}
389
+ {+}+}
390
+ EOF
391
+ git log -L:func2:file.c --word-diff --format= >actual &&
392
+ grep -v "^index " actual >actual.filtered &&
393
+ grep -v "^index " expect >expect.filtered &&
394
+ test_cmp expect.filtered actual.filtered
395
+'
396
+
397
+test_expect_success '-L with --no-prefix' '
398
+ git log -L:func2:file.c --no-prefix --format= >actual &&
399
+ grep "^diff --git file.c file.c" actual &&
400
+ grep "^--- file.c" actual &&
401
+ ! grep "^--- a/" actual
402
+'
403
+
404
+test_expect_success '-L with --full-index' '
405
+ git log -L:func2:file.c --full-index --format= >actual &&
406
+ grep "^index $head_blob_old_full\.\.$head_blob_new_full 100644" actual &&
407
+ grep "^index $null_blob_full\.\.$root_blob_full$" actual
408
+'
409
+
410
+test_expect_success 'setup -L with whitespace change' '
411
+ git checkout -b ws-change parent-oids &&
412
+ sed "s/ return F2 + 2;/ return F2 + 2;/" file.c >tmp &&
413
+ mv tmp file.c &&
414
+ git commit -a -m "Whitespace change in func2()"
415
+'
416
+
417
+test_expect_success '-L with --ignore-all-space suppresses whitespace-only diff' '
418
+ git log -L:func2:file.c --format= >without_w &&
419
+ git log -L:func2:file.c --format= -w >with_w &&
420
+
421
+ # Without -w: three commits produce diffs (whitespace, modify, root)
422
+ test $(grep -c "^diff --git" without_w) = 3 &&
423
+
424
+ # With -w: whitespace-only commit produces no hunk, so only two diffs
425
+ test $(grep -c "^diff --git" with_w) = 2
426
+'
427
+
428
test_expect_success 'show line-log with graph' '
429
git checkout parent-oids &&
430
head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
@@ -430,4 +516,199 @@ test_expect_failure '-L --find-object should filter commits by object' '
516
test_must_be_empty actual
517
'
518
519
+test_expect_success '-L with --word-diff-regex' '
520
+ git checkout parent-oids &&
521
+ git log -L:func2:file.c --word-diff \
522
+ --word-diff-regex="[a-zA-Z0-9_]+" --format= >actual &&
523
+ # Word-diff markers must be present
524
+ grep "{+" actual &&
525
+ grep "+}" actual &&
526
+ # No line-level +/- markers (word-diff replaces them);
527
+ # exclude --- header lines from the check
528
+ ! grep "^+[^+]" actual &&
529
+ ! grep "^-[^-]" actual
530
+'
531
+
532
+test_expect_success '-L with --src-prefix and --dst-prefix' '
533
+ git checkout parent-oids &&
534
+ git log -L:func2:file.c --src-prefix=old/ --dst-prefix=new/ \
535
+ --format= >actual &&
536
+ grep "^diff --git old/file.c new/file.c" actual &&
537
+ grep "^--- old/file.c" actual &&
538
+ grep "^+++ new/file.c" actual &&
539
+ ! grep "^--- a/" actual
540
+'
541
+
542
+test_expect_success '-L with --abbrev' '
543
+ git checkout parent-oids &&
544
+ git log -L:func2:file.c --abbrev=4 --format= -1 >actual &&
545
+ # 4-char abbreviated hashes on index line
546
+ grep "^index [0-9a-f]\{4\}\.\.[0-9a-f]\{4\}" actual
547
+'
548
+
549
+test_expect_success '-L with -b suppresses whitespace-only diff' '
550
+ git checkout ws-change &&
551
+ git log -L:func2:file.c --format= >without_b &&
552
+ git log -L:func2:file.c --format= -b >with_b &&
553
+ test $(grep -c "^diff --git" without_b) = 3 &&
554
+ test $(grep -c "^diff --git" with_b) = 2
555
+'
556
+
557
+test_expect_success '-L with --output-indicator-*' '
558
+ git checkout parent-oids &&
559
+ git log -L:func2:file.c --output-indicator-new=">" \
560
+ --output-indicator-old="<" --output-indicator-context="|" \
561
+ --format= -1 >actual &&
562
+ grep "^>" actual &&
563
+ grep "^<" actual &&
564
+ grep "^|" actual &&
565
+ # No standard +/-/space content markers; exclude ---/+++ headers
566
+ ! grep "^+[^+]" actual &&
567
+ ! grep "^-[^-]" actual &&
568
+ ! grep "^ " actual
569
+'
570
+
571
+test_expect_success '-L with -R reverses diff' '
572
+ git checkout parent-oids &&
573
+ git log -L:func2:file.c -R --format= -1 >actual &&
574
+ grep "^diff --git b/file.c a/file.c" actual &&
575
+ grep "^--- b/file.c" actual &&
576
+ grep "^+++ a/file.c" actual &&
577
+ # The modification added "F2 + 2", so reversed it is removed
578
+ grep "^-.*F2 + 2" actual &&
579
+ grep "^+.*return F2;" actual
580
+'
581
+
582
+test_expect_success 'setup for color-moved test' '
583
+ git checkout -b color-moved-test parent-oids &&
584
+ cat >big.c <<-\EOF &&
585
+ int bigfunc()
586
+ {
587
+ int a = 1;
588
+ int b = 2;
589
+ int c = 3;
590
+ return a + b + c;
591
+ }
592
+ EOF
593
+ git add big.c &&
594
+ git commit -m "add bigfunc" &&
595
+ sed "s/ / /" big.c >tmp && mv tmp big.c &&
596
+ git commit -a -m "reindent bigfunc"
597
+'
598
+
599
+test_expect_success '-L with --color-moved' '
600
+ git log -L:bigfunc:big.c --color-moved=zebra \
601
+ --color-moved-ws=ignore-all-space \
602
+ --color=always --format= -1 >actual.raw &&
603
+ test_decode_color <actual.raw >actual &&
604
+ # Old moved lines: bold magenta; new moved lines: bold cyan
605
+ grep "BOLD;MAGENTA" actual &&
606
+ grep "BOLD;CYAN" actual
607
+'
608
+
609
+test_expect_success 'setup for no-newline-at-eof tests' '
610
+ git checkout --orphan no-newline &&
611
+ git reset --hard &&
612
+ printf "int top()\n{\n return 1;\n}\n\nint bot()\n{\n return 2;\n}" >noeol.c &&
613
+ git add noeol.c &&
614
+ test_tick &&
615
+ git commit -m "add noeol.c (no trailing newline)" &&
616
+ sed "s/return 2/return 22/" noeol.c >tmp && mv tmp noeol.c &&
617
+ git commit -a -m "modify bot()" &&
618
+ printf "int top()\n{\n return 1;\n}\n\nint bot()\n{\n return 33;\n}\n" >noeol.c &&
619
+ git commit -a -m "modify bot() and add trailing newline"
620
+'
621
+
622
+# When the tracked function is at the end of a file with no trailing
623
+# newline, the "\ No newline at end of file" marker should appear.
624
+test_expect_success '-L no-newline-at-eof appears in tracked range' '
625
+ git log -L:bot:noeol.c --format= -1 HEAD~1 >actual &&
626
+ grep "No newline at end of file" actual
627
+'
628
+
629
+# When tracking a function that ends before the no-newline content,
630
+# the marker should not appear in the output.
631
+test_expect_success '-L no-newline-at-eof suppressed outside range' '
632
+ git log -L:top:noeol.c --format= >actual &&
633
+ ! grep "No newline at end of file" actual
634
+'
635
+
636
+# When a commit removes a no-newline last line and replaces it with
637
+# a newline-terminated line, the marker should still appear (on the
638
+# old side of the diff).
639
+test_expect_success '-L no-newline-at-eof marker with deleted line' '
640
+ git log -L:bot:noeol.c --format= -1 >actual &&
641
+ grep "No newline at end of file" actual
642
+'
643
+
644
+test_expect_success 'setup for range boundary deletion test' '
645
+ git checkout --orphan range-boundary &&
646
+ git reset --hard &&
647
+ cat >boundary.c <<-\EOF &&
648
+ void above()
649
+ {
650
+ return;
651
+ }
652
+
653
+ void tracked()
654
+ {
655
+ int x = 1;
656
+ int y = 2;
657
+ }
658
+
659
+ void below()
660
+ {
661
+ return;
662
+ }
663
+ EOF
664
+ git add boundary.c &&
665
+ test_tick &&
666
+ git commit -m "add boundary.c" &&
667
+ cat >boundary.c <<-\EOF &&
668
+ void above()
669
+ {
670
+ return;
671
+ }
672
+
673
+ void tracked()
674
+ {
675
+ int x = 1;
676
+ int y = 2;
677
+ }
678
+
679
+ void below_renamed()
680
+ {
681
+ return 0;
682
+ }
683
+ EOF
684
+ git commit -a -m "modify below() only"
685
+'
686
+
687
+# When only a function below the tracked range is modified, the
688
+# tracked function should not produce a diff.
689
+test_expect_success '-L suppresses deletions outside tracked range' '
690
+ git log -L:tracked:boundary.c --format= >actual &&
691
+ test $(grep -c "^diff --git" actual) = 1
692
+'
693
+
694
+test_expect_success '-L with -S filters to string-count changes' '
695
+ git checkout parent-oids &&
696
+ git log -L:func2:file.c -S "F2 + 2" --format= >actual &&
697
+ # -S searches the whole file, not just the tracked range;
698
+ # combined with the -L range walk, this selects commits that
699
+ # both touch func2 and change the count of "F2 + 2" in the file.
700
+ test $(grep -c "^diff --git" actual) = 1 &&
701
+ grep "F2 + 2" actual
702
+'
703
+
704
+test_expect_success '-L with -G filters to diff-text matches' '
705
+ git checkout parent-oids &&
706
+ git log -L:func2:file.c -G "F2 [+] 2" --format= >actual &&
707
+ # -G greps the whole-file diff text, not just the tracked range;
708
+ # combined with -L, this selects commits that both touch func2
709
+ # and have "F2 + 2" in their diff.
710
+ test $(grep -c "^diff --git" actual) = 1 &&
711
+ grep "F2 + 2" actual
712
+'
713
+
714
test_done