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