Merge branch 'pw/rebase-i-orig-head'
[git] / t / t0021-conversion.sh
1 #!/bin/sh
2
3 test_description='blob conversion via gitattributes'
4
5 . ./test-lib.sh
6
7 TEST_ROOT="$PWD"
8 PATH=$TEST_ROOT:$PATH
9
10 write_script <<\EOF "$TEST_ROOT/rot13.sh"
11 tr \
12   'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
13   'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
14 EOF
15
16 write_script rot13-filter.pl "$PERL_PATH" \
17         <"$TEST_DIRECTORY"/t0021/rot13-filter.pl
18
19 generate_random_characters () {
20         LEN=$1
21         NAME=$2
22         test-tool genrandom some-seed $LEN |
23                 perl -pe "s/./chr((ord($&) % 26) + ord('a'))/sge" >"$TEST_ROOT/$NAME"
24 }
25
26 filter_git () {
27         rm -f *.log &&
28         git "$@"
29 }
30
31 # Compare two files and ensure that `clean` and `smudge` respectively are
32 # called at least once if specified in the `expect` file. The actual
33 # invocation count is not relevant because their number can vary.
34 # c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
35 test_cmp_count () {
36         expect=$1
37         actual=$2
38         for FILE in "$expect" "$actual"
39         do
40                 sort "$FILE" | uniq -c |
41                 sed -e "s/^ *[0-9][0-9]*[       ]*IN: /x IN: /" >"$FILE.tmp"
42         done &&
43         test_cmp "$expect.tmp" "$actual.tmp" &&
44         rm "$expect.tmp" "$actual.tmp"
45 }
46
47 # Compare two files but exclude all `clean` invocations because Git can
48 # call `clean` zero or more times.
49 # c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
50 test_cmp_exclude_clean () {
51         expect=$1
52         actual=$2
53         for FILE in "$expect" "$actual"
54         do
55                 grep -v "IN: clean" "$FILE" >"$FILE.tmp"
56         done &&
57         test_cmp "$expect.tmp" "$actual.tmp" &&
58         rm "$expect.tmp" "$actual.tmp"
59 }
60
61 # Check that the contents of two files are equal and that their rot13 version
62 # is equal to the committed content.
63 test_cmp_committed_rot13 () {
64         test_cmp "$1" "$2" &&
65         rot13.sh <"$1" >expected &&
66         git cat-file blob :"$2" >actual &&
67         test_cmp expected actual
68 }
69
70 test_expect_success setup '
71         git config filter.rot13.smudge ./rot13.sh &&
72         git config filter.rot13.clean ./rot13.sh &&
73
74         {
75             echo "*.t filter=rot13"
76             echo "*.i ident"
77         } >.gitattributes &&
78
79         {
80             echo a b c d e f g h i j k l m
81             echo n o p q r s t u v w x y z
82             echo '\''$Id$'\''
83         } >test &&
84         cat test >test.t &&
85         cat test >test.o &&
86         cat test >test.i &&
87         git add test test.t test.i &&
88         rm -f test test.t test.i &&
89         git checkout -- test test.t test.i &&
90
91         echo "content-test2" >test2.o &&
92         echo "content-test3 - filename with special characters" >"test3 '\''sq'\'',\$x=.o"
93 '
94
95 script='s/^\$Id: \([0-9a-f]*\) \$/\1/p'
96
97 test_expect_success check '
98
99         test_cmp test.o test &&
100         test_cmp test.o test.t &&
101
102         # ident should be stripped in the repository
103         git diff --raw --exit-code :test :test.i &&
104         id=$(git rev-parse --verify :test) &&
105         embedded=$(sed -ne "$script" test.i) &&
106         test "z$id" = "z$embedded" &&
107
108         git cat-file blob :test.t >test.r &&
109
110         ./rot13.sh <test.o >test.t &&
111         test_cmp test.r test.t
112 '
113
114 # If an expanded ident ever gets into the repository, we want to make sure that
115 # it is collapsed before being expanded again on checkout
116 test_expect_success expanded_in_repo '
117         {
118                 echo "File with expanded keywords"
119                 echo "\$Id\$"
120                 echo "\$Id:\$"
121                 echo "\$Id: 0000000000000000000000000000000000000000 \$"
122                 echo "\$Id: NoSpaceAtEnd\$"
123                 echo "\$Id:NoSpaceAtFront \$"
124                 echo "\$Id:NoSpaceAtEitherEnd\$"
125                 echo "\$Id: NoTerminatingSymbol"
126                 echo "\$Id: Foreign Commit With Spaces \$"
127         } >expanded-keywords.0 &&
128
129         {
130                 cat expanded-keywords.0 &&
131                 printf "\$Id: NoTerminatingSymbolAtEOF"
132         } >expanded-keywords &&
133         cat expanded-keywords >expanded-keywords-crlf &&
134         git add expanded-keywords expanded-keywords-crlf &&
135         git commit -m "File with keywords expanded" &&
136         id=$(git rev-parse --verify :expanded-keywords) &&
137
138         {
139                 echo "File with expanded keywords"
140                 echo "\$Id: $id \$"
141                 echo "\$Id: $id \$"
142                 echo "\$Id: $id \$"
143                 echo "\$Id: $id \$"
144                 echo "\$Id: $id \$"
145                 echo "\$Id: $id \$"
146                 echo "\$Id: NoTerminatingSymbol"
147                 echo "\$Id: Foreign Commit With Spaces \$"
148         } >expected-output.0 &&
149         {
150                 cat expected-output.0 &&
151                 printf "\$Id: NoTerminatingSymbolAtEOF"
152         } >expected-output &&
153         {
154                 append_cr <expected-output.0 &&
155                 printf "\$Id: NoTerminatingSymbolAtEOF"
156         } >expected-output-crlf &&
157         {
158                 echo "expanded-keywords ident"
159                 echo "expanded-keywords-crlf ident text eol=crlf"
160         } >>.gitattributes &&
161
162         rm -f expanded-keywords expanded-keywords-crlf &&
163
164         git checkout -- expanded-keywords &&
165         test_cmp expected-output expanded-keywords &&
166
167         git checkout -- expanded-keywords-crlf &&
168         test_cmp expected-output-crlf expanded-keywords-crlf
169 '
170
171 # The use of %f in a filter definition is expanded to the path to
172 # the filename being smudged or cleaned.  It must be shell escaped.
173 # First, set up some interesting file names and pet them in
174 # .gitattributes.
175 test_expect_success 'filter shell-escaped filenames' '
176         cat >argc.sh <<-EOF &&
177         #!$SHELL_PATH
178         cat >/dev/null
179         echo argc: \$# "\$@"
180         EOF
181         normal=name-no-magic &&
182         special="name  with '\''sq'\'' and \$x" &&
183         echo some test text >"$normal" &&
184         echo some test text >"$special" &&
185         git add "$normal" "$special" &&
186         git commit -q -m "add files" &&
187         echo "name* filter=argc" >.gitattributes &&
188
189         # delete the files and check them out again, using a smudge filter
190         # that will count the args and echo the command-line back to us
191         test_config filter.argc.smudge "sh ./argc.sh %f" &&
192         rm "$normal" "$special" &&
193         git checkout -- "$normal" "$special" &&
194
195         # make sure argc.sh counted the right number of args
196         echo "argc: 1 $normal" >expect &&
197         test_cmp expect "$normal" &&
198         echo "argc: 1 $special" >expect &&
199         test_cmp expect "$special" &&
200
201         # do the same thing, but with more args in the filter expression
202         test_config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
203         rm "$normal" "$special" &&
204         git checkout -- "$normal" "$special" &&
205
206         # make sure argc.sh counted the right number of args
207         echo "argc: 2 $normal --my-extra-arg" >expect &&
208         test_cmp expect "$normal" &&
209         echo "argc: 2 $special --my-extra-arg" >expect &&
210         test_cmp expect "$special" &&
211         :
212 '
213
214 test_expect_success 'required filter should filter data' '
215         test_config filter.required.smudge ./rot13.sh &&
216         test_config filter.required.clean ./rot13.sh &&
217         test_config filter.required.required true &&
218
219         echo "*.r filter=required" >.gitattributes &&
220
221         cat test.o >test.r &&
222         git add test.r &&
223
224         rm -f test.r &&
225         git checkout -- test.r &&
226         test_cmp test.o test.r &&
227
228         ./rot13.sh <test.o >expected &&
229         git cat-file blob :test.r >actual &&
230         test_cmp expected actual
231 '
232
233 test_expect_success 'required filter smudge failure' '
234         test_config filter.failsmudge.smudge false &&
235         test_config filter.failsmudge.clean cat &&
236         test_config filter.failsmudge.required true &&
237
238         echo "*.fs filter=failsmudge" >.gitattributes &&
239
240         echo test >test.fs &&
241         git add test.fs &&
242         rm -f test.fs &&
243         test_must_fail git checkout -- test.fs
244 '
245
246 test_expect_success 'required filter clean failure' '
247         test_config filter.failclean.smudge cat &&
248         test_config filter.failclean.clean false &&
249         test_config filter.failclean.required true &&
250
251         echo "*.fc filter=failclean" >.gitattributes &&
252
253         echo test >test.fc &&
254         test_must_fail git add test.fc
255 '
256
257 test_expect_success 'filtering large input to small output should use little memory' '
258         test_config filter.devnull.clean "cat >/dev/null" &&
259         test_config filter.devnull.required true &&
260         for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
261         echo "30MB filter=devnull" >.gitattributes &&
262         GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
263 '
264
265 test_expect_success 'filter that does not read is fine' '
266         test-tool genrandom foo $((128 * 1024 + 1)) >big &&
267         echo "big filter=epipe" >.gitattributes &&
268         test_config filter.epipe.clean "echo xyzzy" &&
269         git add big &&
270         git cat-file blob :big >actual &&
271         echo xyzzy >expect &&
272         test_cmp expect actual
273 '
274
275 test_expect_success EXPENSIVE 'filter large file' '
276         test_config filter.largefile.smudge cat &&
277         test_config filter.largefile.clean cat &&
278         for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
279         echo "2GB filter=largefile" >.gitattributes &&
280         git add 2GB 2>err &&
281         test_must_be_empty err &&
282         rm -f 2GB &&
283         git checkout -- 2GB 2>err &&
284         test_must_be_empty err
285 '
286
287 test_expect_success "filter: clean empty file" '
288         test_config filter.in-repo-header.clean  "echo cleaned && cat" &&
289         test_config filter.in-repo-header.smudge "sed 1d" &&
290
291         echo "empty-in-worktree    filter=in-repo-header" >>.gitattributes &&
292         >empty-in-worktree &&
293
294         echo cleaned >expected &&
295         git add empty-in-worktree &&
296         git show :empty-in-worktree >actual &&
297         test_cmp expected actual
298 '
299
300 test_expect_success "filter: smudge empty file" '
301         test_config filter.empty-in-repo.clean "cat >/dev/null" &&
302         test_config filter.empty-in-repo.smudge "echo smudged && cat" &&
303
304         echo "empty-in-repo filter=empty-in-repo" >>.gitattributes &&
305         echo dead data walking >empty-in-repo &&
306         git add empty-in-repo &&
307
308         echo smudged >expected &&
309         git checkout-index --prefix=filtered- empty-in-repo &&
310         test_cmp expected filtered-empty-in-repo
311 '
312
313 test_expect_success 'disable filter with empty override' '
314         test_config_global filter.disable.smudge false &&
315         test_config_global filter.disable.clean false &&
316         test_config filter.disable.smudge false &&
317         test_config filter.disable.clean false &&
318
319         echo "*.disable filter=disable" >.gitattributes &&
320
321         echo test >test.disable &&
322         git -c filter.disable.clean= add test.disable 2>err &&
323         test_must_be_empty err &&
324         rm -f test.disable &&
325         git -c filter.disable.smudge= checkout -- test.disable 2>err &&
326         test_must_be_empty err
327 '
328
329 test_expect_success 'diff does not reuse worktree files that need cleaning' '
330         test_config filter.counter.clean "echo . >>count; sed s/^/clean:/" &&
331         echo "file filter=counter" >.gitattributes &&
332         test_commit one file &&
333         test_commit two file &&
334
335         >count &&
336         git diff-tree -p HEAD &&
337         test_line_count = 0 count
338 '
339
340 test_expect_success PERL 'required process filter should filter data' '
341         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
342         test_config_global filter.protocol.required true &&
343         rm -rf repo &&
344         mkdir repo &&
345         (
346                 cd repo &&
347                 git init &&
348
349                 echo "*.r filter=protocol" >.gitattributes &&
350                 git add . &&
351                 git commit -m "test commit 1" &&
352                 git branch empty-branch &&
353
354                 cp "$TEST_ROOT/test.o" test.r &&
355                 cp "$TEST_ROOT/test2.o" test2.r &&
356                 mkdir testsubdir &&
357                 cp "$TEST_ROOT/test3 '\''sq'\'',\$x=.o" "testsubdir/test3 '\''sq'\'',\$x=.r" &&
358                 >test4-empty.r &&
359
360                 S=$(test_file_size test.r) &&
361                 S2=$(test_file_size test2.r) &&
362                 S3=$(test_file_size "testsubdir/test3 '\''sq'\'',\$x=.r") &&
363                 M=$(git hash-object test.r) &&
364                 M2=$(git hash-object test2.r) &&
365                 M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
366                 EMPTY=$(git hash-object /dev/null) &&
367
368                 filter_git add . &&
369                 cat >expected.log <<-EOF &&
370                         START
371                         init handshake complete
372                         IN: clean test.r $S [OK] -- OUT: $S . [OK]
373                         IN: clean test2.r $S2 [OK] -- OUT: $S2 . [OK]
374                         IN: clean test4-empty.r 0 [OK] -- OUT: 0  [OK]
375                         IN: clean testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
376                         STOP
377                 EOF
378                 test_cmp_count expected.log debug.log &&
379
380                 git commit -m "test commit 2" &&
381                 MASTER=$(git rev-parse --verify master) &&
382                 META="ref=refs/heads/master treeish=$MASTER" &&
383                 rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" &&
384
385                 filter_git checkout --quiet --no-progress . &&
386                 cat >expected.log <<-EOF &&
387                         START
388                         init handshake complete
389                         IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
390                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
391                         STOP
392                 EOF
393                 test_cmp_exclude_clean expected.log debug.log &&
394
395                 # Make sure that the file appears dirty, so checkout below has to
396                 # run the configured filter.
397                 touch test.r &&
398                 filter_git checkout --quiet --no-progress empty-branch &&
399                 cat >expected.log <<-EOF &&
400                         START
401                         init handshake complete
402                         IN: clean test.r $S [OK] -- OUT: $S . [OK]
403                         STOP
404                 EOF
405                 test_cmp_exclude_clean expected.log debug.log &&
406
407                 filter_git checkout --quiet --no-progress master &&
408                 cat >expected.log <<-EOF &&
409                         START
410                         init handshake complete
411                         IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
412                         IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
413                         IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0  [OK]
414                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
415                         STOP
416                 EOF
417                 test_cmp_exclude_clean expected.log debug.log &&
418
419                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
420                 test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
421                 test_cmp_committed_rot13 "$TEST_ROOT/test3 '\''sq'\'',\$x=.o" "testsubdir/test3 '\''sq'\'',\$x=.r"
422         )
423 '
424
425 test_expect_success PERL 'required process filter should filter data for various subcommands' '
426         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
427         test_config_global filter.protocol.required true &&
428         (
429                 cd repo &&
430
431                 S=$(test_file_size test.r) &&
432                 S2=$(test_file_size test2.r) &&
433                 S3=$(test_file_size "testsubdir/test3 '\''sq'\'',\$x=.r") &&
434                 M=$(git hash-object test.r) &&
435                 M2=$(git hash-object test2.r) &&
436                 M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
437                 EMPTY=$(git hash-object /dev/null) &&
438
439                 MASTER=$(git rev-parse --verify master) &&
440
441                 cp "$TEST_ROOT/test.o" test5.r &&
442                 git add test5.r &&
443                 git commit -m "test commit 3" &&
444                 git checkout empty-branch &&
445                 filter_git rebase --onto empty-branch master^^ master &&
446                 MASTER2=$(git rev-parse --verify master) &&
447                 META="ref=refs/heads/master treeish=$MASTER2" &&
448                 cat >expected.log <<-EOF &&
449                         START
450                         init handshake complete
451                         IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
452                         IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
453                         IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0  [OK]
454                         IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
455                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
456                         STOP
457                 EOF
458                 test_cmp_exclude_clean expected.log debug.log &&
459
460                 git reset --hard empty-branch &&
461                 filter_git reset --hard $MASTER &&
462                 META="treeish=$MASTER" &&
463                 cat >expected.log <<-EOF &&
464                         START
465                         init handshake complete
466                         IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
467                         IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
468                         IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0  [OK]
469                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
470                         STOP
471                 EOF
472                 test_cmp_exclude_clean expected.log debug.log &&
473
474                 git branch old-master $MASTER &&
475                 git reset --hard empty-branch &&
476                 filter_git reset --hard old-master &&
477                 META="ref=refs/heads/old-master treeish=$MASTER" &&
478                 cat >expected.log <<-EOF &&
479                         START
480                         init handshake complete
481                         IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
482                         IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
483                         IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0  [OK]
484                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
485                         STOP
486                 EOF
487                 test_cmp_exclude_clean expected.log debug.log &&
488
489                 git checkout -b merge empty-branch &&
490                 git branch -f master $MASTER2 &&
491                 filter_git merge master &&
492                 META="treeish=$MASTER2" &&
493                 cat >expected.log <<-EOF &&
494                         START
495                         init handshake complete
496                         IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
497                         IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
498                         IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0  [OK]
499                         IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
500                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
501                         STOP
502                 EOF
503                 test_cmp_exclude_clean expected.log debug.log &&
504
505                 filter_git archive master >/dev/null &&
506                 META="ref=refs/heads/master treeish=$MASTER2" &&
507                 cat >expected.log <<-EOF &&
508                         START
509                         init handshake complete
510                         IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
511                         IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
512                         IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0  [OK]
513                         IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
514                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
515                         STOP
516                 EOF
517                 test_cmp_exclude_clean expected.log debug.log &&
518
519                 TREE="$(git rev-parse $MASTER2^{tree})" &&
520                 filter_git archive $TREE >/dev/null &&
521                 META="treeish=$TREE" &&
522                 cat >expected.log <<-EOF &&
523                         START
524                         init handshake complete
525                         IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
526                         IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
527                         IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0  [OK]
528                         IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
529                         IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
530                         STOP
531                 EOF
532                 test_cmp_exclude_clean expected.log debug.log
533         )
534 '
535
536 test_expect_success PERL 'required process filter takes precedence' '
537         test_config_global filter.protocol.clean false &&
538         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
539         test_config_global filter.protocol.required true &&
540         rm -rf repo &&
541         mkdir repo &&
542         (
543                 cd repo &&
544                 git init &&
545
546                 echo "*.r filter=protocol" >.gitattributes &&
547                 cp "$TEST_ROOT/test.o" test.r &&
548                 S=$(test_file_size test.r) &&
549
550                 # Check that the process filter is invoked here
551                 filter_git add . &&
552                 cat >expected.log <<-EOF &&
553                         START
554                         init handshake complete
555                         IN: clean test.r $S [OK] -- OUT: $S . [OK]
556                         STOP
557                 EOF
558                 test_cmp_count expected.log debug.log
559         )
560 '
561
562 test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
563         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
564         rm -rf repo &&
565         mkdir repo &&
566         (
567                 cd repo &&
568                 git init &&
569
570                 echo "*.r filter=protocol" >.gitattributes &&
571                 cp "$TEST_ROOT/test.o" test.r &&
572                 S=$(test_file_size test.r) &&
573
574                 filter_git add . &&
575                 cat >expected.log <<-EOF &&
576                         START
577                         init handshake complete
578                         IN: clean test.r $S [OK] -- OUT: $S . [OK]
579                         STOP
580                 EOF
581                 test_cmp_count expected.log debug.log &&
582
583                 rm test.r &&
584
585                 filter_git checkout --quiet --no-progress . &&
586                 # If the filter would be used for "smudge", too, we would see
587                 # "IN: smudge test.r 57 [OK] -- OUT: 57 . [OK]" here
588                 cat >expected.log <<-EOF &&
589                         START
590                         init handshake complete
591                         STOP
592                 EOF
593                 test_cmp_exclude_clean expected.log debug.log
594         )
595 '
596
597 test_expect_success PERL 'required process filter should process multiple packets' '
598         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
599         test_config_global filter.protocol.required true &&
600
601         rm -rf repo &&
602         mkdir repo &&
603         (
604                 cd repo &&
605                 git init &&
606
607                 # Generate data requiring 1, 2, 3 packets
608                 S=65516 && # PKTLINE_DATA_MAXLEN -> Maximal size of a packet
609                 generate_random_characters $(($S    )) 1pkt_1__.file &&
610                 generate_random_characters $(($S  +1)) 2pkt_1+1.file &&
611                 generate_random_characters $(($S*2-1)) 2pkt_2-1.file &&
612                 generate_random_characters $(($S*2  )) 2pkt_2__.file &&
613                 generate_random_characters $(($S*2+1)) 3pkt_2+1.file &&
614
615                 for FILE in "$TEST_ROOT"/*.file
616                 do
617                         cp "$FILE" . &&
618                         rot13.sh <"$FILE" >"$FILE.rot13"
619                 done &&
620
621                 echo "*.file filter=protocol" >.gitattributes &&
622                 filter_git add *.file .gitattributes &&
623                 cat >expected.log <<-EOF &&
624                         START
625                         init handshake complete
626                         IN: clean 1pkt_1__.file $(($S    )) [OK] -- OUT: $(($S    )) . [OK]
627                         IN: clean 2pkt_1+1.file $(($S  +1)) [OK] -- OUT: $(($S  +1)) .. [OK]
628                         IN: clean 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
629                         IN: clean 2pkt_2__.file $(($S*2  )) [OK] -- OUT: $(($S*2  )) .. [OK]
630                         IN: clean 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
631                         STOP
632                 EOF
633                 test_cmp_count expected.log debug.log &&
634
635                 M1="blob=$(git hash-object 1pkt_1__.file)" &&
636                 M2="blob=$(git hash-object 2pkt_1+1.file)" &&
637                 M3="blob=$(git hash-object 2pkt_2-1.file)" &&
638                 M4="blob=$(git hash-object 2pkt_2__.file)" &&
639                 M5="blob=$(git hash-object 3pkt_2+1.file)" &&
640                 rm -f *.file debug.log &&
641
642                 filter_git checkout --quiet --no-progress -- *.file &&
643                 cat >expected.log <<-EOF &&
644                         START
645                         init handshake complete
646                         IN: smudge 1pkt_1__.file $M1 $(($S    )) [OK] -- OUT: $(($S    )) . [OK]
647                         IN: smudge 2pkt_1+1.file $M2 $(($S  +1)) [OK] -- OUT: $(($S  +1)) .. [OK]
648                         IN: smudge 2pkt_2-1.file $M3 $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
649                         IN: smudge 2pkt_2__.file $M4 $(($S*2  )) [OK] -- OUT: $(($S*2  )) .. [OK]
650                         IN: smudge 3pkt_2+1.file $M5 $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
651                         STOP
652                 EOF
653                 test_cmp_exclude_clean expected.log debug.log &&
654
655                 for FILE in *.file
656                 do
657                         test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE
658                 done
659         )
660 '
661
662 test_expect_success PERL 'required process filter with clean error should fail' '
663         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
664         test_config_global filter.protocol.required true &&
665         rm -rf repo &&
666         mkdir repo &&
667         (
668                 cd repo &&
669                 git init &&
670
671                 echo "*.r filter=protocol" >.gitattributes &&
672
673                 cp "$TEST_ROOT/test.o" test.r &&
674                 echo "this is going to fail" >clean-write-fail.r &&
675                 echo "content-test3-subdir" >test3.r &&
676
677                 test_must_fail git add .
678         )
679 '
680
681 test_expect_success PERL 'process filter should restart after unexpected write failure' '
682         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
683         rm -rf repo &&
684         mkdir repo &&
685         (
686                 cd repo &&
687                 git init &&
688
689                 echo "*.r filter=protocol" >.gitattributes &&
690
691                 cp "$TEST_ROOT/test.o" test.r &&
692                 cp "$TEST_ROOT/test2.o" test2.r &&
693                 echo "this is going to fail" >smudge-write-fail.o &&
694                 cp smudge-write-fail.o smudge-write-fail.r &&
695
696                 S=$(test_file_size test.r) &&
697                 S2=$(test_file_size test2.r) &&
698                 SF=$(test_file_size smudge-write-fail.r) &&
699                 M=$(git hash-object test.r) &&
700                 M2=$(git hash-object test2.r) &&
701                 MF=$(git hash-object smudge-write-fail.r) &&
702                 rm -f debug.log &&
703
704                 git add . &&
705                 rm -f *.r &&
706
707                 rm -f debug.log &&
708                 git checkout --quiet --no-progress . 2>git-stderr.log &&
709
710                 grep "smudge write error at" git-stderr.log &&
711                 test_i18ngrep "error: external filter" git-stderr.log &&
712
713                 cat >expected.log <<-EOF &&
714                         START
715                         init handshake complete
716                         IN: smudge smudge-write-fail.r blob=$MF $SF [OK] -- [WRITE FAIL]
717                         START
718                         init handshake complete
719                         IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
720                         IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
721                         STOP
722                 EOF
723                 test_cmp_exclude_clean expected.log debug.log &&
724
725                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
726                 test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
727
728                 # Smudge failed
729                 ! test_cmp smudge-write-fail.o smudge-write-fail.r &&
730                 rot13.sh <smudge-write-fail.o >expected &&
731                 git cat-file blob :smudge-write-fail.r >actual &&
732                 test_cmp expected actual
733         )
734 '
735
736 test_expect_success PERL 'process filter should not be restarted if it signals an error' '
737         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
738         rm -rf repo &&
739         mkdir repo &&
740         (
741                 cd repo &&
742                 git init &&
743
744                 echo "*.r filter=protocol" >.gitattributes &&
745
746                 cp "$TEST_ROOT/test.o" test.r &&
747                 cp "$TEST_ROOT/test2.o" test2.r &&
748                 echo "this will cause an error" >error.o &&
749                 cp error.o error.r &&
750
751                 S=$(test_file_size test.r) &&
752                 S2=$(test_file_size test2.r) &&
753                 SE=$(test_file_size error.r) &&
754                 M=$(git hash-object test.r) &&
755                 M2=$(git hash-object test2.r) &&
756                 ME=$(git hash-object error.r) &&
757                 rm -f debug.log &&
758
759                 git add . &&
760                 rm -f *.r &&
761
762                 filter_git checkout --quiet --no-progress . &&
763                 cat >expected.log <<-EOF &&
764                         START
765                         init handshake complete
766                         IN: smudge error.r blob=$ME $SE [OK] -- [ERROR]
767                         IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
768                         IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
769                         STOP
770                 EOF
771                 test_cmp_exclude_clean expected.log debug.log &&
772
773                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
774                 test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
775                 test_cmp error.o error.r
776         )
777 '
778
779 test_expect_success PERL 'process filter abort stops processing of all further files' '
780         test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
781         rm -rf repo &&
782         mkdir repo &&
783         (
784                 cd repo &&
785                 git init &&
786
787                 echo "*.r filter=protocol" >.gitattributes &&
788
789                 cp "$TEST_ROOT/test.o" test.r &&
790                 cp "$TEST_ROOT/test2.o" test2.r &&
791                 echo "error this blob and all future blobs" >abort.o &&
792                 cp abort.o abort.r &&
793
794                 M="blob=$(git hash-object abort.r)" &&
795                 rm -f debug.log &&
796                 SA=$(test_file_size abort.r) &&
797
798                 git add . &&
799                 rm -f *.r &&
800
801
802                 # Note: This test assumes that Git filters files in alphabetical
803                 # order ("abort.r" before "test.r").
804                 filter_git checkout --quiet --no-progress . &&
805                 cat >expected.log <<-EOF &&
806                         START
807                         init handshake complete
808                         IN: smudge abort.r $M $SA [OK] -- [ABORT]
809                         STOP
810                 EOF
811                 test_cmp_exclude_clean expected.log debug.log &&
812
813                 test_cmp "$TEST_ROOT/test.o" test.r &&
814                 test_cmp "$TEST_ROOT/test2.o" test2.r &&
815                 test_cmp abort.o abort.r
816         )
817 '
818
819 test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
820         test_config_global filter.protocol.process cat &&
821         test_config_global filter.protocol.required true &&
822         rm -rf repo &&
823         mkdir repo &&
824         (
825                 cd repo &&
826                 git init &&
827
828                 echo "*.r filter=protocol" >.gitattributes &&
829
830                 cp "$TEST_ROOT/test.o" test.r &&
831                 test_must_fail git add . 2>git-stderr.log &&
832                 grep "expected git-filter-server" git-stderr.log
833         )
834 '
835
836 test_expect_success PERL 'delayed checkout in process filter' '
837         test_config_global filter.a.process "rot13-filter.pl a.log clean smudge delay" &&
838         test_config_global filter.a.required true &&
839         test_config_global filter.b.process "rot13-filter.pl b.log clean smudge delay" &&
840         test_config_global filter.b.required true &&
841
842         rm -rf repo &&
843         mkdir repo &&
844         (
845                 cd repo &&
846                 git init &&
847                 echo "*.a filter=a" >.gitattributes &&
848                 echo "*.b filter=b" >>.gitattributes &&
849                 cp "$TEST_ROOT/test.o" test.a &&
850                 cp "$TEST_ROOT/test.o" test-delay10.a &&
851                 cp "$TEST_ROOT/test.o" test-delay11.a &&
852                 cp "$TEST_ROOT/test.o" test-delay20.a &&
853                 cp "$TEST_ROOT/test.o" test-delay10.b &&
854                 git add . &&
855                 git commit -m "test commit"
856         ) &&
857
858         S=$(test_file_size "$TEST_ROOT/test.o") &&
859         PM="ref=refs/heads/master treeish=$(git -C repo rev-parse --verify master) " &&
860         M="${PM}blob=$(git -C repo rev-parse --verify master:test.a)" &&
861         cat >a.exp <<-EOF &&
862                 START
863                 init handshake complete
864                 IN: smudge test.a $M $S [OK] -- OUT: $S . [OK]
865                 IN: smudge test-delay10.a $M $S [OK] -- [DELAYED]
866                 IN: smudge test-delay11.a $M $S [OK] -- [DELAYED]
867                 IN: smudge test-delay20.a $M $S [OK] -- [DELAYED]
868                 IN: list_available_blobs test-delay10.a test-delay11.a [OK]
869                 IN: smudge test-delay10.a $M 0 [OK] -- OUT: $S . [OK]
870                 IN: smudge test-delay11.a $M 0 [OK] -- OUT: $S . [OK]
871                 IN: list_available_blobs test-delay20.a [OK]
872                 IN: smudge test-delay20.a $M 0 [OK] -- OUT: $S . [OK]
873                 IN: list_available_blobs [OK]
874                 STOP
875         EOF
876         cat >b.exp <<-EOF &&
877                 START
878                 init handshake complete
879                 IN: smudge test-delay10.b $M $S [OK] -- [DELAYED]
880                 IN: list_available_blobs test-delay10.b [OK]
881                 IN: smudge test-delay10.b $M 0 [OK] -- OUT: $S . [OK]
882                 IN: list_available_blobs [OK]
883                 STOP
884         EOF
885
886         rm -rf repo-cloned &&
887         filter_git clone repo repo-cloned &&
888         test_cmp_count a.exp repo-cloned/a.log &&
889         test_cmp_count b.exp repo-cloned/b.log &&
890
891         (
892                 cd repo-cloned &&
893                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
894                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
895                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
896                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
897                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b &&
898
899                 rm *.a *.b &&
900                 filter_git checkout . &&
901                 # We are not checking out a ref here, so filter out ref metadata.
902                 sed -e "s!$PM!!" ../a.exp >a.exp.filtered &&
903                 sed -e "s!$PM!!" ../b.exp >b.exp.filtered &&
904                 test_cmp_count a.exp.filtered a.log &&
905                 test_cmp_count b.exp.filtered b.log &&
906
907                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
908                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
909                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
910                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
911                 test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b
912         )
913 '
914
915 test_expect_success PERL 'missing file in delayed checkout' '
916         test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
917         test_config_global filter.bug.required true &&
918
919         rm -rf repo &&
920         mkdir repo &&
921         (
922                 cd repo &&
923                 git init &&
924                 echo "*.a filter=bug" >.gitattributes &&
925                 cp "$TEST_ROOT/test.o" missing-delay.a &&
926                 git add . &&
927                 git commit -m "test commit"
928         ) &&
929
930         rm -rf repo-cloned &&
931         test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
932         grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
933 '
934
935 test_expect_success PERL 'invalid file in delayed checkout' '
936         test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
937         test_config_global filter.bug.required true &&
938
939         rm -rf repo &&
940         mkdir repo &&
941         (
942                 cd repo &&
943                 git init &&
944                 echo "*.a filter=bug" >.gitattributes &&
945                 cp "$TEST_ROOT/test.o" invalid-delay.a &&
946                 cp "$TEST_ROOT/test.o" unfiltered &&
947                 git add . &&
948                 git commit -m "test commit"
949         ) &&
950
951         rm -rf repo-cloned &&
952         test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
953         grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log
954 '
955
956 test_done