git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v6 8/9] diff: consult diff process for --stat counts

From
Michael Montalbo via GitGitGadget <gitgitgadget@gmail.com>
Date
Jul 26, 2026, 18:51 UTC
Message-ID
<7e3ba569679401c98d78d0a0eff42c19c2886edb.1785091889.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.2120.v6.git.1785091889.gitgitgadget@gmail.com>
From: Michael Montalbo <mmontalbo@gmail.com>

builtin_diff() already consults a configured diff.<driver>.process: a file the tool reports as equivalent emits no patch, and otherwise the tool's hunks drive the output. builtin_diffstat() ran its own xdiff and ignored the process, so "git diff --stat" still counted a byte-level change for a file that "git diff" showed as unchanged.

Consult diff_process_fill_hunks() before the stat xdiff, as builtin_diff() does. On DIFF_PROCESS_EQUIVALENT, skip the xdiff so the file keeps its zero inserted and deleted counts and the existing "nothing changed" pruning drops it, matching the empty patch. Otherwise the tool's hunks, or the builtin fallback, feed the counts through the shared xpparam_t.

Under -L, route the surviving hunks through the same line-range filter builtin_diffstat() already uses for a tracked range, so a process-provided diff is scoped to that range: "git log -L<range> --stat" counts the tool's changed lines within the range rather than the builtin line diff's.

Like the builtin summary path, builtin_diffstat() does not apply textconv, so the process is consulted on the raw blob content here, unlike builtin_diff() which sends textconv'd content. This keeps "git diff --stat" counting raw lines as it does today; the asymmetry between patch output and summary counts under textconv predates this change. Because the content is the raw blob, the stat path sends the blob object names to the tool (old-oid/new-oid) for any stored blob, where the patch path omits the oid under textconv.

Move the summary formats out of the "not yet wired" group of the "Which features consult the diff process" documentation and into the list of features that use the tool's hunks, noting the raw, non-textconv content they receive. Document that the line-counting --dirstat=lines follows these counts while the default --dirstat does not, and that summary formats and blame (only under --textconv) differ from patch output in whether they textconv the content the tool sees.

Add tests covering counts from the tool's hunks (--numstat, --shortstat), an equivalent file producing no stat line, --stat --exit-code, the raw non-textconv content the tool receives, a multi-file mix of equivalent and changed files, a mode-only change, and a range-scoped --stat under "git log -L" that reflects the tool's hunks.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
---
 Documentation/gitattributes.adoc |  33 ++++--
 diff.c                           |  47 ++++++---
 t/t4080-diff-process.sh          | 176 +++++++++++++++++++++++++++++++
 3 files changed, 234 insertions(+), 22 deletions(-)
diff --git a/Documentation/gitattributes.adoc b/Documentation/gitattributes.adoc
index a03fb9deb1..7cdede6b21 100644
--- a/Documentation/gitattributes.adoc
+++ b/Documentation/gitattributes.adoc
@@ -874,7 +874,10 @@ a flush packet, followed by the old and new file content as packetized
 data, each terminated with a flush packet.  The pathname is relative
 to the repository root.  When `diff.<name>.textconv` is also set,
 the tool receives the textconv-transformed content rather than the
-raw blob.  Git does not send binary files to the diff process.
+raw blob, matching what the consuming feature itself diffs: patch
+output is textconv'd, the summary formats (noted below) are not, and
+`git blame` applies textconv only under `--textconv`.  Git does not
+send binary files to the diff process.
 
 -----------------------
 packet:          git> command=hunks
@@ -960,8 +963,8 @@ still slide or regroup those changes against matching context for
 display, exactly as it compacts its own diffs, so the tool controls
 which lines are reported as changed, not the precise hunk boundaries.
 Patch output features (word diff, function context, color) work
-normally.  Summary formats such as `--stat` still compute their counts
-with the builtin diff for now; see "Which features consult the diff
+normally, as do summary formats like `--stat`.  Not every feature
+consults the process, though; see "Which features consult the diff
 process" below for the full picture and the reasoning behind it.
 
 If no hunk lines precede the flush, followed by "success", Git
@@ -1040,6 +1043,17 @@ of the builtin algorithm:
   hunks without any further negotiation.
 - `git blame`: a commit whose change the tool reports as equivalent is
   skipped, and its lines are attributed to an earlier commit.
+- `--stat`, `--numstat`, and `--shortstat`: the inserted and deleted
+  counts come from the tool's hunks, so a file the tool calls
+  equivalent contributes no stat line, matching the empty patch that
+  `git diff` produces for it.  These summary formats do not apply
+  textconv (just as the builtin summary path does not), so the tool
+  is consulted on the raw blob content even when a `textconv` is also
+  configured for patch output; this mirrors how builtin `--stat`
+  already counts raw lines rather than the textconv'd view.  The
+  line-counting `--dirstat=lines` uses these same counts; the default
+  `--dirstat`, which weighs byte changes, is computed on its own and
+  does not consult the tool.
 
 Features that ask a different question do not consult the process, by
 design:
@@ -1065,13 +1079,12 @@ design:
 - `--raw`, `--name-only`, and `--name-status` compare object ids at
   the tree level and never run a line-level diff at all.
 
-Some features ask "which lines changed" but still use the builtin
-algorithm for now, and may consult the process in a later change: the
-summary formats (`--stat`, `--numstat`, `--shortstat`); `git log -L`'s
-commit selection and parent range propagation (as distinct from its
-display, which is covered above); and combined diffs (`--cc` and merge
-diffs), whose protocol would have to be extended from a single old/new
-pair to one comparison per merge parent.
+Two cases ask "which lines changed" but still use the builtin
+algorithm, and may consult the process in a later change: `git log
+-L`'s commit selection and parent range propagation (as distinct from
+its display, which is covered above), and combined diffs (`--cc` and
+merge diffs), whose protocol would have to be extended from a single
+old/new pair to one comparison per merge parent.
 
 `--no-ext-diff` and `--diff-algorithm` bypass the process entirely,
 for every feature listed above.  The whitespace-ignoring options
diff --git a/diff.c b/diff.c
index 1a487bb353..241c07fc63 100644
--- a/diff.c
+++ b/diff.c
@@ -4342,20 +4342,43 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 		xecfg.ctxlen = o->context;
 		xecfg.interhunkctxlen = o->interhunkcontext;
 		xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
-
-		if (p->line_ranges) {
-			struct line_range_filter lr_filter;
-
-			line_range_filter_init(&lr_filter, p->line_ranges,
-					       diffstat_consume, diffstat);
-
-			if (line_range_filter_diff(&lr_filter, &mf1, &mf2,
-						   &xpp, &xecfg))
+		/*
+		 * Consult the diff process so --stat reflects the
+		 * tool's view of which lines changed rather than the
+		 * builtin line diff.  --stat never applies textconv, so
+		 * the tool is fed the same raw mmfiles the stat itself
+		 * diffs (unlike builtin_diff, which consults the process
+		 * on textconv'd content).
+		 * When the tool reports the files as equivalent we skip
+		 * xdiff entirely, leaving added and deleted at zero so
+		 * the file is pruned below, just as builtin_diff() emits
+		 * no patch for an equivalent file.
+		 *
+		 * Under -L, feed the tool's hunks through the same
+		 * line-range filter the builtin stat uses, so a
+		 * process-provided diff is scoped to the tracked range.
+		 */
+		if (diff_process_fill_hunks(o, name_a, &mf1, &mf2,
+					    one->oid_valid ? &one->oid : NULL,
+					    two->oid_valid ? &two->oid : NULL,
+					    &xpp)
+		    != DIFF_PROCESS_EQUIVALENT) {
+			if (p->line_ranges) {
+				struct line_range_filter lr_filter;
+
+				line_range_filter_init(&lr_filter, p->line_ranges,
+						       diffstat_consume, diffstat);
+
+				if (line_range_filter_diff(&lr_filter, &mf1, &mf2,
+							   &xpp, &xecfg))
+					die("unable to generate diffstat for %s",
+					    one->path);
+			} else if (xdi_diff_outf(&mf1, &mf2, NULL, diffstat_consume,
+						 diffstat, &xpp, &xecfg))
 				die("unable to generate diffstat for %s",
 				    one->path);
-		} else if (xdi_diff_outf(&mf1, &mf2, NULL,
-				  diffstat_consume, diffstat, &xpp, &xecfg))
-			die("unable to generate diffstat for %s", one->path);
+		}
+		free(xpp.external_hunks);
 
 		if (DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two)) {
 			struct diffstat_file *file =
diff --git a/t/t4080-diff-process.sh b/t/t4080-diff-process.sh
index 694c94edb2..118d0f9464 100755
--- a/t/t4080-diff-process.sh
+++ b/t/t4080-diff-process.sh
@@ -282,6 +282,21 @@ test_expect_success 'diff process works alongside textconv' '
 	test_must_be_empty stderr
 '
 
+test_expect_success 'diff process --stat is fed raw, not textconv, content' '
+	# Reuses textconv.c from the previous test (committed "hello
+	# world", modified to "goodbye world").  Unlike patch output,
+	# --stat does not apply textconv, so the tool sees raw lowercase
+	# content here even with a textconv configured.
+	test_when_finished "rm -f backend.log" &&
+	git -c diff.cdiff.textconv="./uppercase-filter" \
+	    -c diff.cdiff.process="$BACKEND --log=backend.log" \
+		diff --stat -- textconv.c >actual 2>stderr &&
+	test_grep "pathname=textconv.c" backend.log &&
+	test_grep "old=hello world" backend.log &&
+	test_grep "new=goodbye world" backend.log &&
+	test_must_be_empty stderr
+'
+
 #
 # Downstream features: word diff, log, equivalent files, exit code.
 #
@@ -386,6 +401,167 @@ test_expect_success 'diff process with --exit-code and hunks returns failure' '
 		diff --exit-code newfile.c
 '
 
+test_expect_success 'diff process feeds --numstat counts' '
+	# fixed-hunk reports only lines 5-6 as changed, so the stat
+	# counts come from the tool (2/2), not the builtin diff (4/4).
+	test_when_finished "rm -f backend.log" &&
+	git -c diff.cdiff.process="$BACKEND --mode=fixed-hunk --log=backend.log" \
+		diff --numstat boundary.c >actual 2>stderr &&
+	printf "2\t2\tboundary.c\n" >expect &&
+	test_cmp expect actual &&
+	test_grep "command=hunks pathname=boundary.c" backend.log &&
+	test_must_be_empty stderr
+'
+
+test_expect_success 'diff process --numstat sums multi-hunk counts' '
+	# multi-hunk reports both 2-line regions (5-6 and 9-10), so the
+	# counts add up across both hunks: 4 inserted, 4 deleted.  This
+	# exercises the two-region hunk path through builtin_diffstat.
+	git -c diff.cdiff.process="$BACKEND --mode=multi-hunk" \
+		diff --numstat boundary.c >actual &&
+	printf "4\t4\tboundary.c\n" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'diff process equivalent files produce no --stat line' '
+	# A file the tool calls equivalent contributes no stat line,
+	# matching the empty patch that git diff produces for it.
+	test_when_finished "rm -f backend.log" &&
+	git -c diff.cdiff.process="$BACKEND --mode=no-hunks --log=backend.log" \
+		diff --stat worddiff.c >actual 2>stderr &&
+	test_must_be_empty actual &&
+	test_grep "command=hunks pathname=worddiff.c" backend.log &&
+	test_must_be_empty stderr
+'
+
+test_expect_success 'diff process feeds --shortstat counts' '
+	# fixed-hunk reports lines 5-6 only, so the summary counts come
+	# from the tool (2 insertions, 2 deletions), not builtin (4/4).
+	git -c diff.cdiff.process="$BACKEND --mode=fixed-hunk" \
+		diff --shortstat boundary.c >actual &&
+	test_grep "2 insertions" actual &&
+	test_grep "2 deletions" actual
+'
+
+test_expect_success 'diff process scopes --stat to the tracked range under log -L' '
+	test_when_finished "rm -f backend.log" &&
+	cat >rangestat.c <<-\EOF &&
+	line1
+	line2
+	line3
+	line4
+	OLD5
+	OLD6
+	line7
+	line8
+	OLD9
+	OLD10
+	EOF
+	git add rangestat.c &&
+	git commit -m "add rangestat.c" &&
+
+	cat >rangestat.c <<-\EOF &&
+	line1
+	line2
+	line3
+	line4
+	NEW5
+	NEW6
+	line7
+	line8
+	NEW9
+	NEW10
+	EOF
+	git add rangestat.c &&
+	git commit -m "change rangestat.c" &&
+
+	# The file changes at lines 5-6 and 9-10, but fixed-hunk reports
+	# only 5-6.  The builtin line diff counts both regions (4/4); the
+	# tool hunks flow through the same line-range filter the stat uses,
+	# so the range-scoped stat reflects the tool view instead (2/2).
+	git log --no-ext-diff -L1,10:rangestat.c --oneline --stat >builtin &&
+	test_grep "4 insertions(+), 4 deletions(-)" builtin &&
+
+	git -c diff.cdiff.process="$BACKEND --mode=fixed-hunk --log=backend.log" \
+		log -L1,10:rangestat.c --oneline --stat >actual &&
+	test_grep "2 insertions(+), 2 deletions(-)" actual &&
+	test_grep ! "4 insertions" actual &&
+	test_grep "command=hunks pathname=rangestat.c" backend.log
+'
+
+test_expect_success 'diff process equivalent file makes --stat --exit-code succeed' '
+	# The tool reports worddiff.c equivalent, so --exit-code reports
+	# no change (0); the builtin diff would report a change (1).
+	git -c diff.cdiff.process="$BACKEND --mode=no-hunks" \
+		diff --stat --exit-code worddiff.c &&
+	test_expect_code 1 git diff --no-ext-diff --stat --exit-code worddiff.c
+'
+
+test_expect_success 'diff process --numstat with mixed equivalent and changed files' '
+	test_when_finished "rm -f c.log h.log" &&
+	# Self-contained fixtures: *.c uses whole-file (changed); *.mh
+	# uses no-hunks (equivalent).
+	echo "*.mh diff=hdiff" >>.gitattributes &&
+	git add .gitattributes &&
+	printf "int a(void) { return 1; }\n" >mixed.c &&
+	printf "int b(void) { return 1; }\n" >mixed.mh &&
+	git add mixed.c mixed.mh &&
+	git commit -m "add mixed fixtures" &&
+	printf "int a(void) { return 2; }\n" >mixed.c &&
+	printf "int b(void) { return 2; }\n" >mixed.mh &&
+	git -c diff.cdiff.process="$BACKEND --mode=whole-file --log=c.log" \
+	    -c diff.hdiff.process="$BACKEND --mode=no-hunks --log=h.log" \
+		diff --numstat mixed.c mixed.mh >actual 2>stderr &&
+	test_grep "mixed.c" actual &&
+	test_grep ! "mixed.mh" actual &&
+	test_grep "pathname=mixed.c" c.log &&
+	test_grep "pathname=mixed.mh" h.log &&
+	test_must_be_empty stderr
+'
+
+test_expect_success POSIXPERM 'diff process keeps mode-only change in --stat' '
+	test_when_finished "rm -f backend.log" &&
+	cat >modeonly.c <<-\EOF &&
+	int m(void) { return 1; }
+	EOF
+	git add modeonly.c &&
+	git commit -m "add modeonly.c" &&
+	cat >modeonly.c <<-\EOF &&
+	int m(void) { return 2; }
+	EOF
+	git add modeonly.c &&
+	test_chmod +x modeonly.c &&
+	git commit -m "edit and chmod modeonly.c" &&
+	# Content and mode both changed, but no-hunks reports the content
+	# equivalent.  The tool is consulted (counts are zero, not the
+	# builtin 1/1), yet the mode change keeps the file from being
+	# pruned.
+	git -c diff.cdiff.process="$BACKEND --mode=no-hunks --log=backend.log" \
+		diff --stat HEAD^ HEAD >actual 2>stderr &&
+	test_grep "modeonly.c" actual &&
+	test_grep "command=hunks pathname=modeonly.c" backend.log &&
+	test_grep ! "1 insertion" actual &&
+	test_must_be_empty stderr
+'
+
+test_expect_success 'diff process not consulted for default --dirstat' '
+	# The default (change-based) --dirstat algorithm counts via its
+	# own path and never contacts the tool (here --dirstat=0 just
+	# sets a 0% threshold), so the change is still reported even
+	# though no-hunks would call it equivalent.  --dirstat=lines
+	# instead uses the process-aware stat path.
+	test_when_finished "rm -f backend.log" &&
+	mkdir -p dsub &&
+	printf "a\nb\nc\n" >dsub/d.c &&
+	git add dsub/d.c &&
+	git commit -m "add dsub/d.c" &&
+	printf "a\nB\nc\n" >dsub/d.c &&
+	git -c diff.cdiff.process="$BACKEND --mode=no-hunks --log=backend.log" \
+		diff --dirstat=0 dsub/d.c >actual &&
+	test_grep "dsub" actual &&
+	test_path_is_missing backend.log
+'
+
 #
 # Bypass mechanisms: flags and commands that skip the diff process.
 #
-- 
gitgitgadget
Previous: Michael Montalbo via GitGitGadgetNext: Michael Montalbo via GitGitGadget
Message 64 of 81 in “[RFC] diff: add diff.<driver>.process for external hunk providers”
  1. 0/5 [RFC] diff: add diff.<driver>.process for external hunk providersMichael Montalbo via GitGitGadget, May 22, 2026
  2. 1/5 xdiff: support external hunks via xpparam_tMichael Montalbo via GitGitGadget, May 22, 2026
  3. Junio C HamanoMay 22, 2026
  4. Michael MontalboMay 22, 2026
  5. Junio C HamanoMay 24, 2026
  6. Michael MontalboMay 24, 2026
  7. 2/5 userdiff: add diff.<driver>.process configMichael Montalbo via GitGitGadget, May 22, 2026
  8. 3/5 diff: add long-running diff process via diff.<driver>.processMichael Montalbo via GitGitGadget, May 22, 2026
  9. 4/5 blame: consult diff process for zero-hunk detectionMichael Montalbo via GitGitGadget, May 22, 2026
  10. 5/5 diff-process-normalize: add built-in whitespace normalizerMichael Montalbo via GitGitGadget, May 22, 2026
  11. Junio C HamanoMay 22, 2026
  12. Michael MontalboMay 22, 2026
  13. 0/4 [RFC] diff: add diff.<driver>.process for external hunk providersMichael Montalbo via GitGitGadget, May 25, 2026
  14. 1/4 xdiff: support external hunks via xpparam_tMichael Montalbo via GitGitGadget, May 25, 2026
  15. 2/4 userdiff: add diff.<driver>.process configMichael Montalbo via GitGitGadget, May 25, 2026
  16. 3/4 diff: add long-running diff process via diff.<driver>.processMichael Montalbo via GitGitGadget, May 25, 2026
  17. Junio C HamanoMay 26, 2026
  18. Michael MontalboMay 29, 2026
  19. Junio C HamanoMay 26, 2026
  20. Michael MontalboMay 29, 2026
  21. 4/4 blame: consult diff process for zero-hunk detectionMichael Montalbo via GitGitGadget, May 25, 2026
  22. 0/6 [RFC] diff: add diff.<driver>.process for external hunk providersMichael Montalbo via GitGitGadget, May 29, 2026
  23. 1/6 xdiff: support external hunks via xpparam_tMichael Montalbo via GitGitGadget, May 29, 2026
  24. 2/6 userdiff: add diff.<driver>.process configMichael Montalbo via GitGitGadget, May 29, 2026
  25. 3/6 sub-process: separate process lifecycle from hashmap managementMichael Montalbo via GitGitGadget, May 29, 2026
  26. 4/6 diff: add long-running diff process via diff.<driver>.processMichael Montalbo via GitGitGadget, May 29, 2026
  27. Johannes SchindelinJun 7, 2026
  28. Michael MontalboJun 7, 2026
  29. Junio C HamanoJun 8, 2026
  30. Michael MontalboJun 7, 2026
  31. Junio C HamanoJun 8, 2026
  32. Junio C HamanoJun 8, 2026
  33. 5/6 diff: bypass diff process with --no-ext-diff and in format-patchMichael Montalbo via GitGitGadget, May 29, 2026
  34. 6/6 blame: consult diff process for no-hunk detectionMichael Montalbo via GitGitGadget, May 29, 2026
  35. Junio C HamanoMay 31, 2026
  36. Michael MontalboJun 1, 2026
  37. 0/6 [RFC] diff: add diff.<driver>.process for external hunk providersMichael Montalbo via GitGitGadget, Jun 14, 2026
  38. 1/6 xdiff: support external hunks via xpparam_tMichael Montalbo via GitGitGadget, Jun 14, 2026
  39. 2/6 userdiff: add diff.<driver>.process configMichael Montalbo via GitGitGadget, Jun 14, 2026
  40. 3/6 sub-process: separate process lifecycle from hashmap managementMichael Montalbo via GitGitGadget, Jun 14, 2026
  41. 4/6 diff: add long-running diff process via diff.<driver>.processMichael Montalbo via GitGitGadget, Jun 14, 2026
  42. 5/6 diff: bypass diff process with --no-ext-diff and in format-patchMichael Montalbo via GitGitGadget, Jun 14, 2026
  43. 6/6 blame: consult diff process for no-hunk detectionMichael Montalbo via GitGitGadget, Jun 14, 2026
  44. 0/9 [RFC] diff: add diff.<driver>.process for external hunk providersMichael Montalbo via GitGitGadget, Jul 15, 2026
  45. 1/9 gitattributes: document how external diff drivers relate to diff featuresMichael Montalbo via GitGitGadget, Jul 15, 2026
  46. 2/9 xdiff: support external hunks via xpparam_tMichael Montalbo via GitGitGadget, Jul 15, 2026
  47. 3/9 userdiff: add diff.<driver>.process configMichael Montalbo via GitGitGadget, Jul 15, 2026
  48. 4/9 sub-process: separate process lifecycle from hashmap managementMichael Montalbo via GitGitGadget, Jul 15, 2026
  49. 5/9 diff: add long-running diff process via diff.<driver>.processMichael Montalbo via GitGitGadget, Jul 15, 2026
  50. 6/9 diff: bypass diff process with --no-ext-diff and in format-patchMichael Montalbo via GitGitGadget, Jul 15, 2026
  51. 7/9 blame: consult diff process for no-hunk detectionMichael Montalbo via GitGitGadget, Jul 15, 2026
  52. 8/9 diff: consult diff process for --stat countsMichael Montalbo via GitGitGadget, Jul 15, 2026
  53. 9/9 line-log: consult diff process for range trackingMichael Montalbo via GitGitGadget, Jul 15, 2026
  54. Junio C HamanoJul 16, 2026
  55. Michael MontalboJul 16, 2026
  56. 0/9 [RFC] diff: add diff.<driver>.process for external hunk providersMichael Montalbo via GitGitGadget, Jul 26, 2026
  57. 1/9 gitattributes: document how external diff drivers relate to diff featuresMichael Montalbo via GitGitGadget, Jul 26, 2026
  58. 2/9 xdiff: support external hunks via xpparam_tMichael Montalbo via GitGitGadget, Jul 26, 2026
  59. 3/9 userdiff: add diff.<driver>.process configMichael Montalbo via GitGitGadget, Jul 26, 2026
  60. 4/9 sub-process: separate process lifecycle from hashmap managementMichael Montalbo via GitGitGadget, Jul 26, 2026
  61. 5/9 diff: add long-running diff process via diff.<driver>.processMichael Montalbo via GitGitGadget, Jul 26, 2026
  62. 6/9 diff: bypass diff process with --no-ext-diff and in format-patchMichael Montalbo via GitGitGadget, Jul 26, 2026
  63. 7/9 blame: consult diff process for no-hunk detectionMichael Montalbo via GitGitGadget, Jul 26, 2026
  64. 8/9 diff: consult diff process for --stat countsMichael Montalbo via GitGitGadget, Jul 26, 2026
  65. 9/9 line-log: consult diff process for range trackingMichael Montalbo via GitGitGadget, Jul 26, 2026
  66. 0/10 diff: add provider interface and initial providersMichael Montalbo, Aug 1, 2026
  67. 01/10 gitattributes: document how external diff drivers relate to diff featuresMichael Montalbo, Aug 1, 2026
  68. 02/10 diff: introduce a hunk provider interfaceMichael Montalbo, Aug 1, 2026
  69. 03/10 diff-hunks: add the store format, library, and commandMichael Montalbo, Aug 1, 2026
  70. 04/10 diff: record precomputed hunks during stat outputMichael Montalbo, Aug 1, 2026
  71. 05/10 diff: read precomputed hunks for stat outputMichael Montalbo, Aug 1, 2026
  72. 06/10 blame: read precomputed hunksMichael Montalbo, Aug 1, 2026
  73. 07/10 sub-process: separate process lifecycle from hashmap managementMichael Montalbo, Aug 1, 2026
  74. 08/10 sub-process: add a gentle status readMichael Montalbo, Aug 1, 2026
  75. 09/10 userdiff: add diff.<driver>.process configMichael Montalbo, Aug 1, 2026
  76. 10/10 diff: consult oid-only hunk providers via diff.<driver>.processMichael Montalbo, Aug 1, 2026
  77. Michael MontalboAug 4, 2026
  78. Junio C HamanoAug 13, 2026
  79. Phillip WoodAug 4, 2026
  80. Michael MontalboAug 23, 2026
  81. Michael MontalboJun 15, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.