81
test_cmp expect actual
82
'
83
84
+test_expect_success 'log --stat matches with and without the store' '
85
+ no_store log --stat >expect &&
86
+ warm &&
87
+ git log --stat >actual &&
88
+ test_cmp expect actual
89
+'
90
+
91
+test_expect_success 'log --numstat and --shortstat match' '
92
+ no_store log --numstat >expect_num &&
93
+ no_store log --shortstat >expect_short &&
94
+ warm &&
95
+ git log --numstat >actual_num &&
96
+ git log --shortstat >actual_short &&
97
+ test_cmp expect_num actual_num &&
98
+ test_cmp expect_short actual_short
99
+'
100
+
101
+# A built store must reproduce diffstat output at every context
102
+# length. Only trim-stable pairs are recorded, so one entry serves
103
+# every context; a trim-divergent pair is never recorded and always
104
+# computed. Zero context is where trim_common_tail runs, which is
105
+# what makes the two diffs differ.
106
+test_expect_success 'diffstat matches at several context lengths' '
107
+ no_store log --stat >expect_def &&
108
+ no_store log -U0 --stat >expect_u0 &&
109
+ no_store log -U7 --stat >expect_u7 &&
110
+ warm &&
111
+ git log --stat >got_def &&
112
+ git log -U0 --stat >got_u0 &&
113
+ git log -U7 --stat >got_u7 &&
114
+ test_cmp expect_def got_def &&
115
+ test_cmp expect_u0 got_u0 &&
116
+ test_cmp expect_u7 got_u7
117
+'
118
+
119
+test_expect_success 'store built at a nonzero context stays correct at that context' '
120
+ no_store -c diff.context=5 log --stat >expect &&
121
+ warm -c diff.context=5 &&
122
+ git -c diff.context=5 log --stat >actual &&
123
+ test_cmp expect actual
124
+'
125
+
126
+# This blob pair (a real git test file being modernized) has different
127
+# valid diffs at different contexts: at zero context, where
128
+# trim_common_tail runs, "diff -U0" reports 9/6, while "diff -U3"
129
+# reports 10/7. Such a trim-divergent pair is exactly what the writer
130
+# must never record, since no single entry could serve both readers.
131
+# A compact synthetic pair cannot show this count split: on small
132
+# inputs xdiff produces minimal diffs, minimal diffs of one pair all
133
+# add and delete the same number of lines, and trimming the common
134
+# tail preserves minimality, so the counts agree by construction (a
135
+# search over thousands of synthetic pairs up to 8 lines found no
136
+# split). The split needs the cost-capping heuristics that only larger
137
+# inputs trigger, so the pair is shipped as a fixture under t4220/.
138
+test_expect_success 'a trim-divergent file is correct at each context' '
139
+ cp "$TEST_DIRECTORY/t4220/trim-divergent-old" div.sh &&
140
+ git add div.sh &&
141
+ git commit -m divergent-old &&
142
+ cp "$TEST_DIRECTORY/t4220/trim-divergent-new" div.sh &&
143
+ git add div.sh &&
144
+ git commit -m divergent-new &&
145
+ no_store log -1 --format= --stat -- div.sh >expect_def &&
146
+ no_store log -1 --format= -U0 --stat -- div.sh >expect_u0 &&
147
+ warm &&
148
+ git log -1 --format= --stat -- div.sh >got_def &&
149
+ git log -1 --format= -U0 --stat -- div.sh >got_u0 &&
150
+ test_cmp expect_def got_def &&
151
+ test_cmp expect_u0 got_u0 &&
152
+ # The fixture must actually diverge, or the test would pass without
153
+ # exercising the split; fail loudly if a diff change ever levels it.
154
+ ! test_cmp expect_def expect_u0
155
+'
156
+
157
# A warming run displays the diffstat it computes. At zero context xdi_diff
158
# trims, so the displayed counts must be the trimmed ones (what a store-less
159
# run shows), not the untrimmed ones the writer compares against when it
172
)
173
'
174
175
+test_expect_success 'diff --stat matches with and without the store, both directions' '
176
+ no_store diff --stat second fourth >expect_fwd &&
177
+ no_store diff --stat fourth second >expect_rev &&
178
+ warm &&
179
+ git diff --stat second fourth >got_fwd &&
180
+ git diff --stat fourth second >got_rev &&
181
+ test_cmp expect_fwd got_fwd &&
182
+ test_cmp expect_rev got_rev
183
+'
184
+
185
test_expect_success 'show and diff-tree --stat use the store' '
186
test_when_finished "git diff-hunks clear" &&
187
# diff_hunks_attach() runs for show and diff-tree: a write-enabled
204
test_cmp expect_dt got_dt
205
'
206
207
+test_expect_success 'log -R --stat matches (reversed pairs keyed apart)' '
208
+ no_store log -R --stat >expect &&
209
+ warm &&
210
+ git log -R --stat >actual &&
211
+ test_cmp expect actual
212
+'
213
+
214
+# The diffstat read path produces identical output on a hit or a miss, so
215
+# it emits a trace2 "read-hits" count to prove it consulted the store.
216
+test_expect_success 'diffstat consults the store (trace shows read hits)' '
217
+ warm &&
218
+ GIT_TRACE2_EVENT="$PWD/trace_on.json" git log --stat >/dev/null &&
219
+ test_grep read-hits trace_on.json &&
220
+ test_env GIT_TRACE2_EVENT="$PWD/trace_off.json" no_store log --stat >/dev/null &&
221
+ test_grep ! read-hits trace_off.json
222
+'
223
+
224
+# Diff settings that change hunks but are not part of the store key must
225
+# bypass it in both directions, so output stays byte-identical to a
226
+# store-less run.
227
+test_expect_success 'setup ignore fixture' '
228
+ git init ignore-repo &&
229
+ (
230
+ cd ignore-repo &&
231
+ test_write_lines code keep "# c" >f &&
232
+ git add f &&
233
+ git commit -m c1 &&
234
+ test_write_lines codeCH keep "# cX" >f &&
235
+ git add f &&
236
+ git commit -m c2 &&
237
+ warm
238
+ )
239
+'
240
+
241
+# Output parity alone cannot prove the guard: served counts can
242
+# coincide with computed ones, so each bypass below also asserts the
243
+# consultation itself (no read hit with the option, a hit without it)
244
+# and that a warming run under the option records nothing.
245
+test_expect_success '-I bypasses the store in both directions' '
246
+ (
247
+ cd ignore-repo &&
248
+ no_store diff -I"^#" --numstat HEAD~ HEAD >expect &&
249
+ git diff -I"^#" --numstat HEAD~ HEAD >actual &&
250
+ test_cmp expect actual &&
251
+ # -I does not change the key, so only the ignore_regex
252
+ # guard keeps the warmed entry from serving here.
253
+ GIT_TRACE2_EVENT="$PWD/trace_i.json" \
254
+ git diff -I"^#" --numstat HEAD~ HEAD >/dev/null &&
255
+ test_grep ! read-hits trace_i.json &&
256
+ GIT_TRACE2_EVENT="$PWD/trace_i_ctl.json" \
257
+ git diff --numstat HEAD~ HEAD >/dev/null &&
258
+ test_grep read-hits trace_i_ctl.json &&
259
+ git diff-hunks clear &&
260
+ GIT_DIFF_HUNKS_WRITE=1 git diff -I"^#" --numstat HEAD~ HEAD >/dev/null &&
261
+ test_path_is_missing .git/objects/info/diff-hunks &&
262
+ # Restore the warmed fixture for the tests below.
263
+ warm
264
+ )
265
+'
266
+
267
+test_expect_success '-B bypasses the store in both directions' '
268
+ git init break-repo &&
269
+ (
270
+ cd break-repo &&
271
+ test_write_lines a b c d e f g h >f &&
272
+ git add f &&
273
+ git commit -m orig &&
274
+ test_write_lines 1 2 3 4 5 6 7 8 >f &&
275
+ git add f &&
276
+ git commit -m rewrite &&
277
+ warm &&
278
+ no_store diff -B --stat HEAD~ HEAD >expect &&
279
+ git diff -B --stat HEAD~ HEAD >actual &&
280
+ test_cmp expect actual &&
281
+ GIT_TRACE2_EVENT="$PWD/trace_b.json" \
282
+ git diff -B --stat HEAD~ HEAD >/dev/null &&
283
+ test_grep ! read-hits trace_b.json &&
284
+ GIT_TRACE2_EVENT="$PWD/trace_ctl.json" \
285
+ git diff --stat HEAD~ HEAD >/dev/null &&
286
+ test_grep read-hits trace_ctl.json &&
287
+ git diff-hunks clear &&
288
+ GIT_DIFF_HUNKS_WRITE=1 git diff -B --stat HEAD~ HEAD >/dev/null &&
289
+ test_path_is_missing .git/objects/info/diff-hunks
290
+ )
291
+'
292
+
293
+test_expect_success '--anchored bypasses the store in both directions' '
294
+ (
295
+ cd ignore-repo &&
296
+ no_store diff --stat --anchored=keep HEAD~ HEAD >expect &&
297
+ git diff --stat --anchored=keep HEAD~ HEAD >actual &&
298
+ test_cmp expect actual &&
299
+ # Anchors do not change the key, so only the anchors guard
300
+ # keeps the warmed entry from serving here.
301
+ GIT_TRACE2_EVENT="$PWD/trace_anchor.json" \
302
+ git diff --stat --anchored=keep HEAD~ HEAD >/dev/null &&
303
+ test_grep ! read-hits trace_anchor.json &&
304
+ GIT_TRACE2_EVENT="$PWD/trace_plain.json" \
305
+ git diff --stat HEAD~ HEAD >/dev/null &&
306
+ test_grep read-hits trace_plain.json &&
307
+ git diff-hunks clear &&
308
+ GIT_DIFF_HUNKS_WRITE=1 \
309
+ git diff --stat --anchored=keep HEAD~ HEAD >/dev/null &&
310
+ test_path_is_missing .git/objects/info/diff-hunks
311
+ )
312
+'
313
+
314
+test_expect_success '--ignore-blank-lines bypasses the store in both directions' '
315
+ git init ibl-repo &&
316
+ (
317
+ cd ibl-repo &&
318
+ printf "a\n\nx\ny\nb\n" >f &&
319
+ git add f &&
320
+ git commit -m v1 &&
321
+ printf "a\nx\ny\nB\n" >f &&
322
+ git add f &&
323
+ git commit -m v2 &&
324
+ warm &&
325
+ no_store diff --stat --ignore-blank-lines HEAD~ HEAD >expect &&
326
+ git diff --stat --ignore-blank-lines HEAD~ HEAD >actual &&
327
+ test_cmp expect actual &&
328
+ # The flag is an xdl_opts bit and thus part of the key; the
329
+ # stat consumer excludes it before consulting at all.
330
+ GIT_TRACE2_EVENT="$PWD/trace_ibl.json" \
331
+ git diff --stat --ignore-blank-lines HEAD~ HEAD >/dev/null &&
332
+ test_grep ! read-hits trace_ibl.json &&
333
+ GIT_TRACE2_EVENT="$PWD/trace_plain.json" \
334
+ git diff --stat HEAD~ HEAD >/dev/null &&
335
+ test_grep read-hits trace_plain.json &&
336
+ git diff-hunks clear &&
337
+ GIT_DIFF_HUNKS_WRITE=1 \
338
+ git diff --stat --ignore-blank-lines HEAD~ HEAD >/dev/null &&
339
+ test_path_is_missing .git/objects/info/diff-hunks
340
+ )
341
+'
342
+
343
+test_expect_success 'a whitespace-ignoring diff is not served default entries' '
344
+ git init ws-repo &&
345
+ (
346
+ cd ws-repo &&
347
+ test_write_lines alpha beta gamma >f &&
348
+ git add f &&
349
+ git commit -m c1 &&
350
+ test_write_lines " alpha" beta gamma delta >f &&
351
+ git add f &&
352
+ git commit -m c2 &&
353
+ warm &&
354
+ no_store diff -w --numstat HEAD~ HEAD >expect &&
355
+ git diff -w --numstat HEAD~ HEAD >actual &&
356
+ test_cmp expect actual
357
+ )
358
+'
359
+
360
+test_expect_success 'a driver algorithm override keeps output correct and keys apart' '
361
+ git init driver-algo &&
362
+ (
363
+ cd driver-algo &&
364
+ echo "file.foo diff=foo" >.gitattributes &&
365
+ git add .gitattributes &&
366
+ git commit -m attributes &&
367
+ test_write_lines 1 2 3 4 5 >file.foo &&
368
+ git add file.foo &&
369
+ git commit -m one &&
370
+ test_write_lines 1 2 X 4 5 6 >file.foo &&
371
+ git add file.foo &&
372
+ git commit -m two &&
373
+ warm -c diff.foo.algorithm=histogram &&
374
+ no_store -c diff.foo.algorithm=histogram log --stat >expect &&
375
+ git -c diff.foo.algorithm=histogram log --stat >actual &&
376
+ test_cmp expect actual &&
377
+ # The driver algorithm is an xdl_opts key bit: entries
378
+ # warmed at the default settings must not serve a
379
+ # driver-forced histogram read, and output stays correct.
380
+ git diff-hunks clear &&
381
+ warm &&
382
+ no_store -c diff.foo.algorithm=histogram log --stat >expect2 &&
383
+ git -c diff.foo.algorithm=histogram log --stat >actual2 &&
384
+ test_cmp expect2 actual2 &&
385
+ GIT_TRACE2_EVENT="$PWD/trace_algo.json" \
386
+ git -c diff.foo.algorithm=histogram log --stat >/dev/null &&
387
+ test_grep ! read-hits trace_algo.json &&
388
+ GIT_TRACE2_EVENT="$PWD/trace_algo_ctl.json" \
389
+ git log --stat >/dev/null &&
390
+ test_grep read-hits trace_algo_ctl.json
391
+ )
392
+'
393
+
394
# Cover the pair shapes an object walk encounters: binary and
395
# mode-only changes produce no text hunks to record.
396
test_expect_success 'binary and mode-only changes do not break the writer' '
411
test_cmp expect actual
412
'
413
414
+test_expect_success 'log -L --stat neither reads nor records' '
415
+ warm &&
416
+ GIT_TRACE2_EVENT="$PWD/trace_linelog.json" \
417
+ git log -L1,1:file.txt --stat >/dev/null &&
418
+ test_grep ! read-hits trace_linelog.json &&
419
+ git diff-hunks clear &&
420
+ GIT_DIFF_HUNKS_WRITE=1 git log -L1,1:file.txt --stat >/dev/null &&
421
+ test_path_is_missing $STORE
422
+'
423
+
424
test_expect_success 'verify succeeds on a valid store and on an absent one' '
425
warm &&
426
git diff-hunks verify &&