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