3 test_description='blob conversion via gitattributes'
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
13 write_script <<\EOF "$TEST_ROOT/rot13.sh"
15 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
16 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
19 write_script rot13-filter.pl "$PERL_PATH" \
20 <"$TEST_DIRECTORY"/t0021/rot13-filter.pl
22 generate_random_characters () {
25 test-tool genrandom some-seed $LEN |
26 perl -pe "s/./chr((ord($&) % 26) + ord('a'))/sge" >"$TEST_ROOT/$NAME"
34 # Compare two files and ensure that `clean` and `smudge` respectively are
35 # called at least once if specified in the `expect` file. The actual
36 # invocation count is not relevant because their number can vary.
37 # c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
41 for FILE in "$expect" "$actual"
43 sort "$FILE" | uniq -c |
44 sed -e "s/^ *[0-9][0-9]*[ ]*IN: /x IN: /" >"$FILE.tmp"
46 test_cmp "$expect.tmp" "$actual.tmp" &&
47 rm "$expect.tmp" "$actual.tmp"
50 # Compare two files but exclude all `clean` invocations because Git can
51 # call `clean` zero or more times.
52 # c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
53 test_cmp_exclude_clean () {
56 for FILE in "$expect" "$actual"
58 grep -v "IN: clean" "$FILE" >"$FILE.tmp"
60 test_cmp "$expect.tmp" "$actual.tmp" &&
61 rm "$expect.tmp" "$actual.tmp"
64 # Check that the contents of two files are equal and that their rot13 version
65 # is equal to the committed content.
66 test_cmp_committed_rot13 () {
68 rot13.sh <"$1" >expected &&
69 git cat-file blob :"$2" >actual &&
70 test_cmp expected actual
73 test_expect_success setup '
74 git config filter.rot13.smudge ./rot13.sh &&
75 git config filter.rot13.clean ./rot13.sh &&
78 echo "*.t filter=rot13"
83 echo a b c d e f g h i j k l m
84 echo n o p q r s t u v w x y z
90 git add test test.t test.i &&
91 rm -f test test.t test.i &&
92 git checkout -- test test.t test.i &&
94 echo "content-test2" >test2.o &&
95 echo "content-test3 - filename with special characters" >"test3 '\''sq'\'',\$x=.o"
98 script='s/^\$Id: \([0-9a-f]*\) \$/\1/p'
100 test_expect_success check '
102 test_cmp test.o test &&
103 test_cmp test.o test.t &&
105 # ident should be stripped in the repository
106 git diff --raw --exit-code :test :test.i &&
107 id=$(git rev-parse --verify :test) &&
108 embedded=$(sed -ne "$script" test.i) &&
109 test "z$id" = "z$embedded" &&
111 git cat-file blob :test.t >test.r &&
113 ./rot13.sh <test.o >test.t &&
114 test_cmp test.r test.t
117 # If an expanded ident ever gets into the repository, we want to make sure that
118 # it is collapsed before being expanded again on checkout
119 test_expect_success expanded_in_repo '
121 echo "File with expanded keywords"
124 echo "\$Id: 0000000000000000000000000000000000000000 \$"
125 echo "\$Id: NoSpaceAtEnd\$"
126 echo "\$Id:NoSpaceAtFront \$"
127 echo "\$Id:NoSpaceAtEitherEnd\$"
128 echo "\$Id: NoTerminatingSymbol"
129 echo "\$Id: Foreign Commit With Spaces \$"
130 } >expanded-keywords.0 &&
133 cat expanded-keywords.0 &&
134 printf "\$Id: NoTerminatingSymbolAtEOF"
135 } >expanded-keywords &&
136 cat expanded-keywords >expanded-keywords-crlf &&
137 git add expanded-keywords expanded-keywords-crlf &&
138 git commit -m "File with keywords expanded" &&
139 id=$(git rev-parse --verify :expanded-keywords) &&
142 echo "File with expanded keywords"
149 echo "\$Id: NoTerminatingSymbol"
150 echo "\$Id: Foreign Commit With Spaces \$"
151 } >expected-output.0 &&
153 cat expected-output.0 &&
154 printf "\$Id: NoTerminatingSymbolAtEOF"
155 } >expected-output &&
157 append_cr <expected-output.0 &&
158 printf "\$Id: NoTerminatingSymbolAtEOF"
159 } >expected-output-crlf &&
161 echo "expanded-keywords ident"
162 echo "expanded-keywords-crlf ident text eol=crlf"
163 } >>.gitattributes &&
165 rm -f expanded-keywords expanded-keywords-crlf &&
167 git checkout -- expanded-keywords &&
168 test_cmp expected-output expanded-keywords &&
170 git checkout -- expanded-keywords-crlf &&
171 test_cmp expected-output-crlf expanded-keywords-crlf
174 # The use of %f in a filter definition is expanded to the path to
175 # the filename being smudged or cleaned. It must be shell escaped.
176 # First, set up some interesting file names and pet them in
178 test_expect_success 'filter shell-escaped filenames' '
179 cat >argc.sh <<-EOF &&
184 normal=name-no-magic &&
185 special="name with '\''sq'\'' and \$x" &&
186 echo some test text >"$normal" &&
187 echo some test text >"$special" &&
188 git add "$normal" "$special" &&
189 git commit -q -m "add files" &&
190 echo "name* filter=argc" >.gitattributes &&
192 # delete the files and check them out again, using a smudge filter
193 # that will count the args and echo the command-line back to us
194 test_config filter.argc.smudge "sh ./argc.sh %f" &&
195 rm "$normal" "$special" &&
196 git checkout -- "$normal" "$special" &&
198 # make sure argc.sh counted the right number of args
199 echo "argc: 1 $normal" >expect &&
200 test_cmp expect "$normal" &&
201 echo "argc: 1 $special" >expect &&
202 test_cmp expect "$special" &&
204 # do the same thing, but with more args in the filter expression
205 test_config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
206 rm "$normal" "$special" &&
207 git checkout -- "$normal" "$special" &&
209 # make sure argc.sh counted the right number of args
210 echo "argc: 2 $normal --my-extra-arg" >expect &&
211 test_cmp expect "$normal" &&
212 echo "argc: 2 $special --my-extra-arg" >expect &&
213 test_cmp expect "$special" &&
217 test_expect_success 'required filter should filter data' '
218 test_config filter.required.smudge ./rot13.sh &&
219 test_config filter.required.clean ./rot13.sh &&
220 test_config filter.required.required true &&
222 echo "*.r filter=required" >.gitattributes &&
224 cat test.o >test.r &&
228 git checkout -- test.r &&
229 test_cmp test.o test.r &&
231 ./rot13.sh <test.o >expected &&
232 git cat-file blob :test.r >actual &&
233 test_cmp expected actual
236 test_expect_success 'required filter smudge failure' '
237 test_config filter.failsmudge.smudge false &&
238 test_config filter.failsmudge.clean cat &&
239 test_config filter.failsmudge.required true &&
241 echo "*.fs filter=failsmudge" >.gitattributes &&
243 echo test >test.fs &&
246 test_must_fail git checkout -- test.fs
249 test_expect_success 'required filter clean failure' '
250 test_config filter.failclean.smudge cat &&
251 test_config filter.failclean.clean false &&
252 test_config filter.failclean.required true &&
254 echo "*.fc filter=failclean" >.gitattributes &&
256 echo test >test.fc &&
257 test_must_fail git add test.fc
260 test_expect_success 'filtering large input to small output should use little memory' '
261 test_config filter.devnull.clean "cat >/dev/null" &&
262 test_config filter.devnull.required true &&
263 for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
264 echo "30MB filter=devnull" >.gitattributes &&
265 GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
268 test_expect_success 'filter that does not read is fine' '
269 test-tool genrandom foo $((128 * 1024 + 1)) >big &&
270 echo "big filter=epipe" >.gitattributes &&
271 test_config filter.epipe.clean "echo xyzzy" &&
273 git cat-file blob :big >actual &&
274 echo xyzzy >expect &&
275 test_cmp expect actual
278 test_expect_success EXPENSIVE 'filter large file' '
279 test_config filter.largefile.smudge cat &&
280 test_config filter.largefile.clean cat &&
281 for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
282 echo "2GB filter=largefile" >.gitattributes &&
284 test_must_be_empty err &&
286 git checkout -- 2GB 2>err &&
287 test_must_be_empty err
290 test_expect_success "filter: clean empty file" '
291 test_config filter.in-repo-header.clean "echo cleaned && cat" &&
292 test_config filter.in-repo-header.smudge "sed 1d" &&
294 echo "empty-in-worktree filter=in-repo-header" >>.gitattributes &&
295 >empty-in-worktree &&
297 echo cleaned >expected &&
298 git add empty-in-worktree &&
299 git show :empty-in-worktree >actual &&
300 test_cmp expected actual
303 test_expect_success "filter: smudge empty file" '
304 test_config filter.empty-in-repo.clean "cat >/dev/null" &&
305 test_config filter.empty-in-repo.smudge "echo smudged && cat" &&
307 echo "empty-in-repo filter=empty-in-repo" >>.gitattributes &&
308 echo dead data walking >empty-in-repo &&
309 git add empty-in-repo &&
311 echo smudged >expected &&
312 git checkout-index --prefix=filtered- empty-in-repo &&
313 test_cmp expected filtered-empty-in-repo
316 test_expect_success 'disable filter with empty override' '
317 test_config_global filter.disable.smudge false &&
318 test_config_global filter.disable.clean false &&
319 test_config filter.disable.smudge false &&
320 test_config filter.disable.clean false &&
322 echo "*.disable filter=disable" >.gitattributes &&
324 echo test >test.disable &&
325 git -c filter.disable.clean= add test.disable 2>err &&
326 test_must_be_empty err &&
327 rm -f test.disable &&
328 git -c filter.disable.smudge= checkout -- test.disable 2>err &&
329 test_must_be_empty err
332 test_expect_success 'diff does not reuse worktree files that need cleaning' '
333 test_config filter.counter.clean "echo . >>count; sed s/^/clean:/" &&
334 echo "file filter=counter" >.gitattributes &&
335 test_commit one file &&
336 test_commit two file &&
339 git diff-tree -p HEAD &&
340 test_line_count = 0 count
343 test_expect_success PERL 'required process filter should filter data' '
344 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
345 test_config_global filter.protocol.required true &&
352 echo "*.r filter=protocol" >.gitattributes &&
354 git commit -m "test commit 1" &&
355 git branch empty-branch &&
357 cp "$TEST_ROOT/test.o" test.r &&
358 cp "$TEST_ROOT/test2.o" test2.r &&
360 cp "$TEST_ROOT/test3 '\''sq'\'',\$x=.o" "testsubdir/test3 '\''sq'\'',\$x=.r" &&
363 S=$(test_file_size test.r) &&
364 S2=$(test_file_size test2.r) &&
365 S3=$(test_file_size "testsubdir/test3 '\''sq'\'',\$x=.r") &&
366 M=$(git hash-object test.r) &&
367 M2=$(git hash-object test2.r) &&
368 M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
369 EMPTY=$(git hash-object /dev/null) &&
372 cat >expected.log <<-EOF &&
374 init handshake complete
375 IN: clean test.r $S [OK] -- OUT: $S . [OK]
376 IN: clean test2.r $S2 [OK] -- OUT: $S2 . [OK]
377 IN: clean test4-empty.r 0 [OK] -- OUT: 0 [OK]
378 IN: clean testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
381 test_cmp_count expected.log debug.log &&
383 git commit -m "test commit 2" &&
384 MAIN=$(git rev-parse --verify main) &&
385 META="ref=refs/heads/main treeish=$MAIN" &&
386 rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" &&
388 filter_git checkout --quiet --no-progress . &&
389 cat >expected.log <<-EOF &&
391 init handshake complete
392 IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
393 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
396 test_cmp_exclude_clean expected.log debug.log &&
398 # Make sure that the file appears dirty, so checkout below has to
399 # run the configured filter.
401 filter_git checkout --quiet --no-progress empty-branch &&
402 cat >expected.log <<-EOF &&
404 init handshake complete
405 IN: clean test.r $S [OK] -- OUT: $S . [OK]
408 test_cmp_exclude_clean expected.log debug.log &&
410 filter_git checkout --quiet --no-progress main &&
411 cat >expected.log <<-EOF &&
413 init handshake complete
414 IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
415 IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
416 IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
417 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
420 test_cmp_exclude_clean expected.log debug.log &&
422 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
423 test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
424 test_cmp_committed_rot13 "$TEST_ROOT/test3 '\''sq'\'',\$x=.o" "testsubdir/test3 '\''sq'\'',\$x=.r"
428 test_expect_success PERL 'required process filter should filter data for various subcommands' '
429 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
430 test_config_global filter.protocol.required true &&
434 S=$(test_file_size test.r) &&
435 S2=$(test_file_size test2.r) &&
436 S3=$(test_file_size "testsubdir/test3 '\''sq'\'',\$x=.r") &&
437 M=$(git hash-object test.r) &&
438 M2=$(git hash-object test2.r) &&
439 M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
440 EMPTY=$(git hash-object /dev/null) &&
442 MAIN=$(git rev-parse --verify main) &&
444 cp "$TEST_ROOT/test.o" test5.r &&
446 git commit -m "test commit 3" &&
447 git checkout empty-branch &&
448 filter_git rebase --onto empty-branch main^^ main &&
449 MAIN2=$(git rev-parse --verify main) &&
450 META="ref=refs/heads/main treeish=$MAIN2" &&
451 cat >expected.log <<-EOF &&
453 init handshake complete
454 IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
455 IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
456 IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
457 IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
458 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
461 test_cmp_exclude_clean expected.log debug.log &&
463 git reset --hard empty-branch &&
464 filter_git reset --hard $MAIN &&
465 META="treeish=$MAIN" &&
466 cat >expected.log <<-EOF &&
468 init handshake complete
469 IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
470 IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
471 IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
472 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
475 test_cmp_exclude_clean expected.log debug.log &&
477 git branch old-main $MAIN &&
478 git reset --hard empty-branch &&
479 filter_git reset --hard old-main &&
480 META="ref=refs/heads/old-main treeish=$MAIN" &&
481 cat >expected.log <<-EOF &&
483 init handshake complete
484 IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
485 IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
486 IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
487 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
490 test_cmp_exclude_clean expected.log debug.log &&
492 git checkout -b merge empty-branch &&
493 git branch -f main $MAIN2 &&
494 filter_git merge main &&
495 META="treeish=$MAIN2" &&
496 cat >expected.log <<-EOF &&
498 init handshake complete
499 IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
500 IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
501 IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
502 IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
503 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
506 test_cmp_exclude_clean expected.log debug.log &&
508 filter_git archive main >/dev/null &&
509 META="ref=refs/heads/main treeish=$MAIN2" &&
510 cat >expected.log <<-EOF &&
512 init handshake complete
513 IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
514 IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
515 IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
516 IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
517 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
520 test_cmp_exclude_clean expected.log debug.log &&
522 TREE="$(git rev-parse $MAIN2^{tree})" &&
523 filter_git archive $TREE >/dev/null &&
524 META="treeish=$TREE" &&
525 cat >expected.log <<-EOF &&
527 init handshake complete
528 IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
529 IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
530 IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
531 IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
532 IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
535 test_cmp_exclude_clean expected.log debug.log
539 test_expect_success PERL 'required process filter takes precedence' '
540 test_config_global filter.protocol.clean false &&
541 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
542 test_config_global filter.protocol.required true &&
549 echo "*.r filter=protocol" >.gitattributes &&
550 cp "$TEST_ROOT/test.o" test.r &&
551 S=$(test_file_size test.r) &&
553 # Check that the process filter is invoked here
555 cat >expected.log <<-EOF &&
557 init handshake complete
558 IN: clean test.r $S [OK] -- OUT: $S . [OK]
561 test_cmp_count expected.log debug.log
565 test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
566 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
573 echo "*.r filter=protocol" >.gitattributes &&
574 cp "$TEST_ROOT/test.o" test.r &&
575 S=$(test_file_size test.r) &&
578 cat >expected.log <<-EOF &&
580 init handshake complete
581 IN: clean test.r $S [OK] -- OUT: $S . [OK]
584 test_cmp_count expected.log debug.log &&
588 filter_git checkout --quiet --no-progress . &&
589 # If the filter would be used for "smudge", too, we would see
590 # "IN: smudge test.r 57 [OK] -- OUT: 57 . [OK]" here
591 cat >expected.log <<-EOF &&
593 init handshake complete
596 test_cmp_exclude_clean expected.log debug.log
600 test_expect_success PERL 'required process filter should process multiple packets' '
601 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
602 test_config_global filter.protocol.required true &&
610 # Generate data requiring 1, 2, 3 packets
611 S=65516 && # PKTLINE_DATA_MAXLEN -> Maximal size of a packet
612 generate_random_characters $(($S )) 1pkt_1__.file &&
613 generate_random_characters $(($S +1)) 2pkt_1+1.file &&
614 generate_random_characters $(($S*2-1)) 2pkt_2-1.file &&
615 generate_random_characters $(($S*2 )) 2pkt_2__.file &&
616 generate_random_characters $(($S*2+1)) 3pkt_2+1.file &&
618 for FILE in "$TEST_ROOT"/*.file
621 rot13.sh <"$FILE" >"$FILE.rot13"
624 echo "*.file filter=protocol" >.gitattributes &&
625 filter_git add *.file .gitattributes &&
626 cat >expected.log <<-EOF &&
628 init handshake complete
629 IN: clean 1pkt_1__.file $(($S )) [OK] -- OUT: $(($S )) . [OK]
630 IN: clean 2pkt_1+1.file $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK]
631 IN: clean 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
632 IN: clean 2pkt_2__.file $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK]
633 IN: clean 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
636 test_cmp_count expected.log debug.log &&
638 M1="blob=$(git hash-object 1pkt_1__.file)" &&
639 M2="blob=$(git hash-object 2pkt_1+1.file)" &&
640 M3="blob=$(git hash-object 2pkt_2-1.file)" &&
641 M4="blob=$(git hash-object 2pkt_2__.file)" &&
642 M5="blob=$(git hash-object 3pkt_2+1.file)" &&
643 rm -f *.file debug.log &&
645 filter_git checkout --quiet --no-progress -- *.file &&
646 cat >expected.log <<-EOF &&
648 init handshake complete
649 IN: smudge 1pkt_1__.file $M1 $(($S )) [OK] -- OUT: $(($S )) . [OK]
650 IN: smudge 2pkt_1+1.file $M2 $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK]
651 IN: smudge 2pkt_2-1.file $M3 $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
652 IN: smudge 2pkt_2__.file $M4 $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK]
653 IN: smudge 3pkt_2+1.file $M5 $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
656 test_cmp_exclude_clean expected.log debug.log &&
660 test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE
665 test_expect_success PERL 'required process filter with clean error should fail' '
666 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
667 test_config_global filter.protocol.required true &&
674 echo "*.r filter=protocol" >.gitattributes &&
676 cp "$TEST_ROOT/test.o" test.r &&
677 echo "this is going to fail" >clean-write-fail.r &&
678 echo "content-test3-subdir" >test3.r &&
680 test_must_fail git add .
684 test_expect_success PERL 'process filter should restart after unexpected write failure' '
685 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
692 echo "*.r filter=protocol" >.gitattributes &&
694 cp "$TEST_ROOT/test.o" test.r &&
695 cp "$TEST_ROOT/test2.o" test2.r &&
696 echo "this is going to fail" >smudge-write-fail.o &&
697 cp smudge-write-fail.o smudge-write-fail.r &&
699 S=$(test_file_size test.r) &&
700 S2=$(test_file_size test2.r) &&
701 SF=$(test_file_size smudge-write-fail.r) &&
702 M=$(git hash-object test.r) &&
703 M2=$(git hash-object test2.r) &&
704 MF=$(git hash-object smudge-write-fail.r) &&
711 git checkout --quiet --no-progress . 2>git-stderr.log &&
713 grep "smudge write error at" git-stderr.log &&
714 test_i18ngrep "error: external filter" git-stderr.log &&
716 cat >expected.log <<-EOF &&
718 init handshake complete
719 IN: smudge smudge-write-fail.r blob=$MF $SF [OK] -- [WRITE FAIL]
721 init handshake complete
722 IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
723 IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
726 test_cmp_exclude_clean expected.log debug.log &&
728 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
729 test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
732 ! test_cmp smudge-write-fail.o smudge-write-fail.r &&
733 rot13.sh <smudge-write-fail.o >expected &&
734 git cat-file blob :smudge-write-fail.r >actual &&
735 test_cmp expected actual
739 test_expect_success PERL 'process filter should not be restarted if it signals an error' '
740 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
747 echo "*.r filter=protocol" >.gitattributes &&
749 cp "$TEST_ROOT/test.o" test.r &&
750 cp "$TEST_ROOT/test2.o" test2.r &&
751 echo "this will cause an error" >error.o &&
752 cp error.o error.r &&
754 S=$(test_file_size test.r) &&
755 S2=$(test_file_size test2.r) &&
756 SE=$(test_file_size error.r) &&
757 M=$(git hash-object test.r) &&
758 M2=$(git hash-object test2.r) &&
759 ME=$(git hash-object error.r) &&
765 filter_git checkout --quiet --no-progress . &&
766 cat >expected.log <<-EOF &&
768 init handshake complete
769 IN: smudge error.r blob=$ME $SE [OK] -- [ERROR]
770 IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
771 IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
774 test_cmp_exclude_clean expected.log debug.log &&
776 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
777 test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
778 test_cmp error.o error.r
782 test_expect_success PERL 'process filter abort stops processing of all further files' '
783 test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
790 echo "*.r filter=protocol" >.gitattributes &&
792 cp "$TEST_ROOT/test.o" test.r &&
793 cp "$TEST_ROOT/test2.o" test2.r &&
794 echo "error this blob and all future blobs" >abort.o &&
795 cp abort.o abort.r &&
797 M="blob=$(git hash-object abort.r)" &&
799 SA=$(test_file_size abort.r) &&
805 # Note: This test assumes that Git filters files in alphabetical
806 # order ("abort.r" before "test.r").
807 filter_git checkout --quiet --no-progress . &&
808 cat >expected.log <<-EOF &&
810 init handshake complete
811 IN: smudge abort.r $M $SA [OK] -- [ABORT]
814 test_cmp_exclude_clean expected.log debug.log &&
816 test_cmp "$TEST_ROOT/test.o" test.r &&
817 test_cmp "$TEST_ROOT/test2.o" test2.r &&
818 test_cmp abort.o abort.r
822 test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
823 test_config_global filter.protocol.process cat &&
824 test_config_global filter.protocol.required true &&
831 echo "*.r filter=protocol" >.gitattributes &&
833 cp "$TEST_ROOT/test.o" test.r &&
834 test_must_fail git add . 2>git-stderr.log &&
835 grep "expected git-filter-server" git-stderr.log
839 test_expect_success PERL 'delayed checkout in process filter' '
840 test_config_global filter.a.process "rot13-filter.pl a.log clean smudge delay" &&
841 test_config_global filter.a.required true &&
842 test_config_global filter.b.process "rot13-filter.pl b.log clean smudge delay" &&
843 test_config_global filter.b.required true &&
850 echo "*.a filter=a" >.gitattributes &&
851 echo "*.b filter=b" >>.gitattributes &&
852 cp "$TEST_ROOT/test.o" test.a &&
853 cp "$TEST_ROOT/test.o" test-delay10.a &&
854 cp "$TEST_ROOT/test.o" test-delay11.a &&
855 cp "$TEST_ROOT/test.o" test-delay20.a &&
856 cp "$TEST_ROOT/test.o" test-delay10.b &&
858 git commit -m "test commit"
861 S=$(test_file_size "$TEST_ROOT/test.o") &&
862 PM="ref=refs/heads/main treeish=$(git -C repo rev-parse --verify main) " &&
863 M="${PM}blob=$(git -C repo rev-parse --verify main:test.a)" &&
866 init handshake complete
867 IN: smudge test.a $M $S [OK] -- OUT: $S . [OK]
868 IN: smudge test-delay10.a $M $S [OK] -- [DELAYED]
869 IN: smudge test-delay11.a $M $S [OK] -- [DELAYED]
870 IN: smudge test-delay20.a $M $S [OK] -- [DELAYED]
871 IN: list_available_blobs test-delay10.a test-delay11.a [OK]
872 IN: smudge test-delay10.a $M 0 [OK] -- OUT: $S . [OK]
873 IN: smudge test-delay11.a $M 0 [OK] -- OUT: $S . [OK]
874 IN: list_available_blobs test-delay20.a [OK]
875 IN: smudge test-delay20.a $M 0 [OK] -- OUT: $S . [OK]
876 IN: list_available_blobs [OK]
881 init handshake complete
882 IN: smudge test-delay10.b $M $S [OK] -- [DELAYED]
883 IN: list_available_blobs test-delay10.b [OK]
884 IN: smudge test-delay10.b $M 0 [OK] -- OUT: $S . [OK]
885 IN: list_available_blobs [OK]
889 rm -rf repo-cloned &&
890 filter_git clone repo repo-cloned &&
891 test_cmp_count a.exp repo-cloned/a.log &&
892 test_cmp_count b.exp repo-cloned/b.log &&
896 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
897 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
898 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
899 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
900 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b &&
903 filter_git checkout . &&
904 # We are not checking out a ref here, so filter out ref metadata.
905 sed -e "s!$PM!!" ../a.exp >a.exp.filtered &&
906 sed -e "s!$PM!!" ../b.exp >b.exp.filtered &&
907 test_cmp_count a.exp.filtered a.log &&
908 test_cmp_count b.exp.filtered b.log &&
910 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
911 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
912 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
913 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
914 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b
918 test_expect_success PERL 'missing file in delayed checkout' '
919 test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
920 test_config_global filter.bug.required true &&
927 echo "*.a filter=bug" >.gitattributes &&
928 cp "$TEST_ROOT/test.o" missing-delay.a &&
930 git commit -m "test commit"
933 rm -rf repo-cloned &&
934 test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
935 grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
938 test_expect_success PERL 'invalid file in delayed checkout' '
939 test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
940 test_config_global filter.bug.required true &&
947 echo "*.a filter=bug" >.gitattributes &&
948 cp "$TEST_ROOT/test.o" invalid-delay.a &&
949 cp "$TEST_ROOT/test.o" unfiltered &&
951 git commit -m "test commit"
954 rm -rf repo-cloned &&
955 test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
956 grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log