1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
|
#!/bin/sh
test_description='diff.<driver>.process: oid-only hunk requests'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# See t/helper/test-diff-process-backend.c for the process implementation
# and available --mode= options.
BACKEND="test-tool diff-process-backend"
test_expect_success 'setup' '
echo "*.c diff=cdiff" >.gitattributes &&
git add .gitattributes &&
# 10 lines, changes at 5-6 and 9-10 between the two commits.
cat >pair.c <<-\EOF &&
line1
line2
line3
line4
original5
original6
line7
line8
line9
line10
EOF
git add pair.c &&
git commit -m "add pair.c" &&
cat >pair.c <<-\EOF &&
line1
line2
line3
line4
changed5
changed6
line7
line8
changed9
changed10
EOF
git add pair.c &&
git commit -m "change pair.c"
'
test_expect_success 'an oid-capable process answers blame by object names alone' '
test_when_finished "rm -f backend.log" &&
ORIG=$(git rev-parse --short HEAD~1) &&
CHANGE=$(git rev-parse --short HEAD) &&
# The process reports only lines 5-6 as changed, so blame attributes
# lines 9-10 to the original commit even though the builtin diff
# would show them as changed.
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
blame pair.c >actual &&
sed -n "9p" actual >line9 &&
sed -n "10p" actual >line10 &&
test_grep "$ORIG" line9 &&
test_grep "$ORIG" line10 &&
sed -n "5p" actual >line5 &&
test_grep "$CHANGE" line5 &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'an oid-capable process answers --numstat by object names alone' '
test_when_finished "rm -f backend.log" &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
log -1 --format= --numstat -- pair.c >actual &&
printf "2\t2\tpair.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'need-content falls through to the builtin diff' '
test_when_finished "rm -f backend.log" &&
git -c diff.cdiff.process="$BACKEND --mode=oid-need-content --log=backend.log" \
log -1 --format= --numstat -- pair.c >actual &&
printf "4\t4\tpair.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'a warmed hunk store does not override process hunks' '
test_when_finished "git diff-hunks clear" &&
ORIG=$(git rev-parse --short HEAD~1) &&
GIT_DIFF_HUNKS_WRITE=1 git log -2 --stat -- pair.c >/dev/null &&
# Control: without a process, blame is served from the store.
git blame --show-stats pair.c >stats &&
test_grep "num precomputed hits: 1" stats &&
# The store holds the builtin hunks, but a process-capable driver
# makes the process authoritative, so blame must reflect the
# process hunks (only lines 5-6), not a store hit.
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed" \
blame pair.c >actual &&
sed -n "9p" actual >line9 &&
test_grep "$ORIG" line9
'
test_expect_success 'a worktree side is not asked by object names' '
test_when_finished "rm -f backend.log && git checkout -- pair.c" &&
echo "worktree edit" >>pair.c &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
diff --numstat -- pair.c >actual &&
printf "1\t0\tpair.c\n" >expect &&
test_cmp expect actual &&
test_path_is_missing backend.log
'
test_expect_success 'diff process bypassed by --no-ext-diff' '
test_when_finished "rm -f backend.log" &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
log -1 --format= --numstat --no-ext-diff -- pair.c >actual &&
printf "4\t4\tpair.c\n" >expect &&
test_cmp expect actual &&
test_path_is_missing backend.log
'
test_expect_success 'format-patch keeps its diffstat off the process' '
test_when_finished "rm -f backend.log" &&
# format-patch emits a diffstat, and a diffstat consults the
# process, but the gate keeps it builtin so a generated patch
# applies for recipients without the process.
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
format-patch -1 --stdout --stat -- pair.c >actual &&
test_grep "^+changed9" actual &&
test_path_is_missing backend.log
'
test_expect_success 'format-patch --ext-diff keeps its diffstat off the process' '
test_when_finished "rm -f backend.log" &&
# The gate holds even when --ext-diff enables the external command.
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
format-patch -1 --stdout --ext-diff --stat -- pair.c >actual &&
test_grep "^+changed9" actual &&
test_path_is_missing backend.log
'
test_expect_success 'diff process not consulted by plumbing diff commands' '
test_when_finished "rm -f backend.log && git checkout -f HEAD -- pair.c" &&
# diff-tree diffs the two commits, a real pair a defeated gate would
# consult the process for.
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
diff-tree --numstat HEAD >actual &&
test_grep "pair.c" actual &&
# diff-index needs a change to diff, or there is no pair and a
# missing log proves nothing; stage one and diff it against HEAD.
echo "staged change" >>pair.c &&
git add pair.c &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
diff-index --cached --numstat HEAD -- pair.c >actual &&
test_grep "pair.c" actual &&
test_path_is_missing backend.log
'
test_expect_success 'add -p stages from the builtin diff with a process configured' '
test_when_finished "rm -f backend.log" &&
cat >gate.c <<-\EOF &&
int gate(void) { return 1; }
EOF
git add gate.c &&
git commit -m "add gate.c" &&
cat >gate.c <<-\EOF &&
int gate(void) { return 2; }
EOF
# add -p builds its hunks from patch text, which is not a provider
# consumer today, so a configured process cannot shape what it
# offers. This pins that interactive patch stays builtin for the
# current consumers, rather than exercising the plumbing gate.
test_write_lines y |
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
add -p gate.c &&
git diff --cached -- gate.c >staged &&
test_grep "return 2" staged &&
test_path_is_missing backend.log &&
git commit -m "gate.c v2"
'
test_expect_success 'blame withholds identity for the working-tree pair' '
test_when_finished "rm -f backend.log && git checkout -- pair.c" &&
echo "uncommitted" >>pair.c &&
wt=$(git hash-object pair.c) &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
blame pair.c >actual &&
# The dirty working-tree side is not a stored blob: no request
# may name its bytes by object id.
test_grep ! "new-oid=$wt" backend.log
'
test_expect_success 'a replaced blob makes the process step aside' '
new_blob=$(git rev-parse HEAD:pair.c) &&
test_when_finished "rm -f backend.log && git replace -d $new_blob" &&
# Replacing the new-side blob redirects the content the diff reads
# under the id the process would be sent, so a raw-id request would
# name bytes other than the ones diffed. Identity is withheld: the
# process is not consulted and the builtin computes the pair from
# the replaced content.
repl=$(printf "just one line\n" | git hash-object -w --stdin) &&
git replace "$new_blob" "$repl" &&
git log -1 --format= --numstat -- pair.c >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
log -1 --format= --numstat -- pair.c >actual &&
test_cmp expect actual &&
test_path_is_missing backend.log
'
test_expect_success 'an equivalence answer omits the pair from the stat' '
test_when_finished "rm -f backend.log" &&
git -c diff.cdiff.process="$BACKEND --mode=oid-equal --log=backend.log" \
log -1 --format= --numstat -- pair.c >actual &&
# An equivalent pair sums to a zero-count entry, and the stat
# code omits zero-count modified entries, the same way a
# whitespace-only pair prints nothing under -w. The builtin
# diff would print nonzero counts here, and the log proves the
# process was consulted.
test_must_be_empty actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'blame passes equivalent pairs through to the boundary' '
ORIG=$(git rev-parse --short ":/add pair.c") &&
# The process asserts every consulted pair equal, so no line is
# ever treated as changed: every line passes through to the
# commit that added the file, marked as the blame boundary.
git -c diff.cdiff.process="$BACKEND --mode=oid-equal" \
blame pair.c >actual &&
sed -n "5p" actual >line5 &&
test_grep "^\^$ORIG" line5 &&
sed -n "10p" actual >line10 &&
test_grep "^\^$ORIG" line10
'
test_expect_success 'a warming run records a pair the process defers' '
test_when_finished "git diff-hunks clear" &&
git diff-hunks clear &&
# The process owns the path but defers this pair with
# need-content, so the pair gets the builtin diff; that is the
# result the store holds, so the warming run records it and a
# later read is served from the store.
GIT_DIFF_HUNKS_WRITE=1 git -c core.diffHunks=true \
-c diff.cdiff.process="$BACKEND --mode=oid-need-content" \
log -1 --format= --stat -- pair.c >/dev/null &&
git -c core.diffHunks=true blame --show-stats pair.c >stats 2>&1 &&
test_grep "num precomputed hits: 1" stats
'
# The protocol error paths: each adversarial response shape must warn,
# fall back to the builtin output, and either keep the process alive
# (a per-pair rejection) or disable it for the rest of the command (a
# protocol error). The request log tells the two apart: the log walk
# below consults two pairs (gate.c first, then pair.c), so a disabled
# process shows one logged request and a live one shows two.
test_expect_success 'a malformed hunk line disables the process for the command' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-malformed --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "disabling it for the remainder" err &&
test_line_count = 1 backend.log
'
test_expect_success 'coordinates past the blob size skip the pair, process stays alive' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-huge --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "past the end" err &&
test_line_count = 2 backend.log
'
test_expect_success 'a count that overflows long skips the pair, process stays alive' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-erange --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "out-of-range coordinates" err &&
test_line_count = 2 backend.log
'
test_expect_success 'overlapping hunks are rejected per pair' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-overlap --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "overlapping hunks" err &&
test_line_count = 2 backend.log
'
test_expect_success 'misaligned hunks are rejected per pair' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-misaligned --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "misaligned" err &&
test_line_count = 2 backend.log
'
test_expect_success 'a start of zero with a nonzero count is rejected per pair' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
# A start of 0 names an empty side, so a nonzero count beside it
# names no line; the coordinate is rejected and the pair falls back
# to the builtin diff while the process stays alive.
git -c diff.cdiff.process="$BACKEND --mode=oid-badstart --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "out-of-range coordinates" err &&
test_line_count = 2 backend.log
'
test_expect_success 'an unrecognized status disables the process for the command' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-unknown-status --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "unrecognized status .frobnicate." err &&
test_line_count = 1 backend.log
'
test_expect_success 'status=abort withdraws the capability without a warning' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-abort --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep ! "disabling" err &&
test_line_count = 1 backend.log
'
test_expect_success 'a bare status without the hunk-section flush is a protocol error' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-bare-status --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "disabling it for the remainder" err &&
test_line_count = 1 backend.log
'
test_expect_success 'an empty packet in the hunk section is a protocol error' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-empty-packet --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "disabling it for the remainder" err &&
test_line_count = 1 backend.log
'
test_expect_success 'a process that dies mid-response fails the command over to builtin' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-crash --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "disabling it for the remainder" err &&
test_line_count = 1 backend.log
'
test_expect_success 'garbage bytes on stdout fail the command over to builtin' '
test_when_finished "rm -f backend.log err" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=oid-garbage --log=backend.log" \
log --format= --numstat -- "*.c" >actual 2>err &&
test_cmp expect actual &&
test_grep "disabling it for the remainder" err &&
test_line_count = 1 backend.log
'
test_expect_success 'a process announcing no capability is never asked' '
test_when_finished "rm -f backend.log" &&
git log --format= --numstat -- "*.c" >expect &&
git -c diff.cdiff.process="$BACKEND --mode=cap-none --log=backend.log" \
log --format= --numstat -- "*.c" >actual &&
test_cmp expect actual &&
test_must_be_empty backend.log
'
test_expect_success 'a trailing token on a hunk line is ignored' '
test_when_finished "rm -f backend.log" &&
git -c diff.cdiff.process="$BACKEND --mode=oid-trailing --log=backend.log" \
log -1 --format= --numstat -- pair.c >actual &&
printf "2\t2\tpair.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'a failed start warns once and the store may serve the path' '
git init failrepo &&
(
cd failrepo &&
echo "*.c diff=cdiff" >.gitattributes &&
git add .gitattributes &&
test_commit f1 f.c "one" &&
test_commit f2 f.c "one
two" &&
test_commit f3 f.c "one
two
three" &&
GIT_DIFF_HUNKS_WRITE=1 git log --format= --stat -- f.c >/dev/null &&
# The command has no shell metacharacters, so it fails at
# exec time; a shell-wrapped command would fail at the
# handshake, which the gentle handshake in the base
# (061a68e443) likewise degrades to the builtin diff.
git blame f.c >expect &&
git -c diff.cdiff.process=/does-not-exist-diff-backend \
blame f.c >actual 2>err &&
test_cmp expect actual &&
# One warning even though the blame consults two pairs.
test $(grep -c "failed to start" err) = 1 &&
# A failure is a non-answer like any other: the request
# that observes it and every later request pass to the
# store, so the warmed store serves both pairs.
git -c diff.cdiff.process=/does-not-exist-diff-backend \
blame --show-stats f.c >stats 2>&1 &&
test_grep "num precomputed hits: 2" stats
)
'
test_expect_success 'the store serves a pair the process defers' '
test_when_finished "git diff-hunks clear" &&
git diff-hunks clear &&
GIT_DIFF_HUNKS_WRITE=1 git log --format= --stat -- pair.c >/dev/null &&
git blame pair.c >expect &&
# need-content defers the pair to the builtin diff, which is
# what the store holds, so the walk continues past the process
# and the store serves the pair.
git -c diff.cdiff.process="$BACKEND --mode=oid-need-content" \
blame pair.c >actual &&
test_cmp expect actual &&
git -c diff.cdiff.process="$BACKEND --mode=oid-need-content" \
blame --show-stats pair.c >stats 2>&1 &&
test_grep "num precomputed hits: 1" stats
'
test_expect_success 'git diff between commits consults the process' '
test_when_finished "rm -f backend.log" &&
ORIG=$(git rev-parse ":/add pair.c") &&
CHANGE=$(git rev-parse ":/change pair.c") &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
diff --numstat $ORIG $CHANGE -- pair.c >actual &&
printf "2\t2\tpair.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'git show consults the process' '
test_when_finished "rm -f backend.log" &&
CHANGE=$(git rev-parse ":/change pair.c") &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
show --format= --numstat $CHANGE -- pair.c >actual &&
printf "2\t2\tpair.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'diff-tree --ext-diff consults the process' '
test_when_finished "rm -f backend.log" &&
CHANGE=$(git rev-parse ":/change pair.c") &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
diff-tree --ext-diff --no-commit-id --numstat $CHANGE >actual &&
printf "2\t2\tpair.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success '--no-diff-process forbids consulting alone' '
test_when_finished "rm -f backend.log" &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
log -1 --no-diff-process --format= --numstat -- pair.c >actual &&
printf "4\t4\tpair.c\n" >expect &&
test_cmp expect actual &&
test_path_is_missing backend.log
'
test_expect_success '--diff-process allows plumbing to consult' '
test_when_finished "rm -f backend.log" &&
CHANGE=$(git rev-parse ":/change pair.c") &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
diff-tree --diff-process --no-commit-id --numstat $CHANGE >actual &&
printf "2\t2\tpair.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=pair.c" backend.log
'
test_expect_success 'a forced blame diff algorithm bypasses the process' '
test_when_finished "rm -f backend.log" &&
CHANGE=$(git rev-parse --short ":/change pair.c") &&
# The process would attribute lines 9-10 to the original commit
# (see the oid-fixed blame test above); a forced builtin
# algorithm must produce the builtin attribution and never start
# the process.
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
blame --histogram pair.c >actual &&
sed -n "9p" actual >line9 &&
test_grep "$CHANGE" line9 &&
test_path_is_missing backend.log
'
test_expect_success 'textconv output is never identified to the process' '
test_when_finished "rm -f backend.log" &&
echo "*.tcv diff=tcv" >>.gitattributes &&
git add .gitattributes &&
git commit -m tcv-attr &&
test_config diff.tcv.textconv cat &&
test_commit tcv1 file.tcv "alpha" &&
test_commit tcv2 file.tcv "alpha
beta" &&
git blame file.tcv >expect &&
git -c diff.tcv.process="$BACKEND --mode=oid-fixed --log=backend.log" \
blame file.tcv >actual &&
test_cmp expect actual &&
test_path_is_missing backend.log
'
test_expect_success 'a gitlink side is never identified to the process' '
test_when_finished "rm -f backend.log" &&
echo "sub diff=cdiff" >>.gitattributes &&
git add .gitattributes &&
git commit -m sub-attr &&
C1=$(git rev-parse HEAD) &&
C2=$(git rev-parse HEAD~1) &&
git update-index --add --cacheinfo 160000,$C1,sub &&
git commit -m sub-1 &&
git update-index --add --cacheinfo 160000,$C2,sub &&
git commit -m sub-2 &&
git -c diff.cdiff.process="$BACKEND --mode=oid-fixed --log=backend.log" \
log -1 --format= --numstat -- sub >actual &&
printf "1\t1\tsub\n" >expect &&
test_cmp expect actual &&
test_path_is_missing backend.log
'
test_expect_success 'a relative diff consults by the repo-relative path' '
test_when_finished "rm -f backend.log" &&
echo "reldir/*.rel diff=rdrv" >>.gitattributes &&
git add .gitattributes &&
git commit -m rel-attr &&
mkdir reldir &&
test_write_lines line1 line2 line3 line4 original5 original6 \
line7 line8 line9 line10 >reldir/x.rel &&
git add reldir/x.rel &&
git commit -m "add x.rel" &&
test_write_lines line1 line2 line3 line4 changed5 changed6 \
line7 line8 changed9 changed10 >reldir/x.rel &&
git add reldir/x.rel &&
git commit -m "change x.rel" &&
# diff.relative strips the prefix from the displayed name; the
# driver lookup and the request pathname must still use the
# repo-relative path, or the directory-scoped attribute above
# would not match and the process would never be consulted.
# The process runs at the repository root, so its log lands there.
(
cd reldir &&
git -c diff.relative=true \
-c diff.rdrv.process="$BACKEND --mode=oid-fixed --log=backend.log" \
log -1 --format= --numstat -- x.rel
) >actual &&
printf "2\t2\tx.rel\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=reldir/x.rel" backend.log
'
test_expect_success 'an empty file side is answered with a start of zero' '
test_when_finished "rm -f backend.log" &&
>empty.c &&
git add empty.c &&
git commit -m "add empty.c" &&
printf "x\ny\nz\n" >empty.c &&
git add empty.c &&
git commit -m "fill empty.c" &&
# The process addresses the empty old side with a start of 0 and a
# count of 0, and claims two of the three added lines. The answer is
# used as sent, so the stat shows the two lines it named, not the
# three the builtin would.
git -c diff.cdiff.process="$BACKEND --mode=oid-empty --log=backend.log" \
log -1 --format= --numstat -- empty.c >actual &&
printf "2\t0\tempty.c\n" >expect &&
test_cmp expect actual &&
test_grep "command=hunks-by-oid pathname=empty.c" backend.log
'
test_done
|