mirror of
https://github.com/git/git
synced 2024-08-27 19:49:21 +00:00
![Jeff King](/assets/img/avatar_default.png)
When accessing a blob for a diff, we may try to reuse file contents in the working tree, under the theory that it is faster to mmap those file contents than it would be to extract the content from the object database. When we have to filter those contents, though, that assumption does not hold. Even for our internal conversions like CRLF, we have to allocate and fill a new buffer anyway. But much worse, for external clean filters we have to exec an arbitrary script, and we have no idea how expensive it may be to run. So let's skip this optimization when conversion into git's "clean" form is required. This applies whenever the "want_file" flag is false. When it's true, the caller actually wants the smudged worktree contents, which the reused file by definition already has (in fact, this is a key optimization going the other direction, since reusing the worktree file there lets us skip smudge filters). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
283 lines
7.8 KiB
Bash
Executable file
283 lines
7.8 KiB
Bash
Executable file
#!/bin/sh
|
|
|
|
test_description='blob conversion via gitattributes'
|
|
|
|
. ./test-lib.sh
|
|
|
|
cat <<EOF >rot13.sh
|
|
#!$SHELL_PATH
|
|
tr \
|
|
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
|
|
'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
|
|
EOF
|
|
chmod +x rot13.sh
|
|
|
|
test_expect_success setup '
|
|
git config filter.rot13.smudge ./rot13.sh &&
|
|
git config filter.rot13.clean ./rot13.sh &&
|
|
|
|
{
|
|
echo "*.t filter=rot13"
|
|
echo "*.i ident"
|
|
} >.gitattributes &&
|
|
|
|
{
|
|
echo a b c d e f g h i j k l m
|
|
echo n o p q r s t u v w x y z
|
|
echo '\''$Id$'\''
|
|
} >test &&
|
|
cat test >test.t &&
|
|
cat test >test.o &&
|
|
cat test >test.i &&
|
|
git add test test.t test.i &&
|
|
rm -f test test.t test.i &&
|
|
git checkout -- test test.t test.i
|
|
'
|
|
|
|
script='s/^\$Id: \([0-9a-f]*\) \$/\1/p'
|
|
|
|
test_expect_success check '
|
|
|
|
cmp test.o test &&
|
|
cmp test.o test.t &&
|
|
|
|
# ident should be stripped in the repository
|
|
git diff --raw --exit-code :test :test.i &&
|
|
id=$(git rev-parse --verify :test) &&
|
|
embedded=$(sed -ne "$script" test.i) &&
|
|
test "z$id" = "z$embedded" &&
|
|
|
|
git cat-file blob :test.t > test.r &&
|
|
|
|
./rot13.sh < test.o > test.t &&
|
|
cmp test.r test.t
|
|
'
|
|
|
|
# If an expanded ident ever gets into the repository, we want to make sure that
|
|
# it is collapsed before being expanded again on checkout
|
|
test_expect_success expanded_in_repo '
|
|
{
|
|
echo "File with expanded keywords"
|
|
echo "\$Id\$"
|
|
echo "\$Id:\$"
|
|
echo "\$Id: 0000000000000000000000000000000000000000 \$"
|
|
echo "\$Id: NoSpaceAtEnd\$"
|
|
echo "\$Id:NoSpaceAtFront \$"
|
|
echo "\$Id:NoSpaceAtEitherEnd\$"
|
|
echo "\$Id: NoTerminatingSymbol"
|
|
echo "\$Id: Foreign Commit With Spaces \$"
|
|
} >expanded-keywords.0 &&
|
|
|
|
{
|
|
cat expanded-keywords.0 &&
|
|
printf "\$Id: NoTerminatingSymbolAtEOF"
|
|
} >expanded-keywords &&
|
|
cat expanded-keywords >expanded-keywords-crlf &&
|
|
git add expanded-keywords expanded-keywords-crlf &&
|
|
git commit -m "File with keywords expanded" &&
|
|
id=$(git rev-parse --verify :expanded-keywords) &&
|
|
|
|
{
|
|
echo "File with expanded keywords"
|
|
echo "\$Id: $id \$"
|
|
echo "\$Id: $id \$"
|
|
echo "\$Id: $id \$"
|
|
echo "\$Id: $id \$"
|
|
echo "\$Id: $id \$"
|
|
echo "\$Id: $id \$"
|
|
echo "\$Id: NoTerminatingSymbol"
|
|
echo "\$Id: Foreign Commit With Spaces \$"
|
|
} >expected-output.0 &&
|
|
{
|
|
cat expected-output.0 &&
|
|
printf "\$Id: NoTerminatingSymbolAtEOF"
|
|
} >expected-output &&
|
|
{
|
|
append_cr <expected-output.0 &&
|
|
printf "\$Id: NoTerminatingSymbolAtEOF"
|
|
} >expected-output-crlf &&
|
|
{
|
|
echo "expanded-keywords ident"
|
|
echo "expanded-keywords-crlf ident text eol=crlf"
|
|
} >>.gitattributes &&
|
|
|
|
rm -f expanded-keywords expanded-keywords-crlf &&
|
|
|
|
git checkout -- expanded-keywords &&
|
|
test_cmp expanded-keywords expected-output &&
|
|
|
|
git checkout -- expanded-keywords-crlf &&
|
|
test_cmp expanded-keywords-crlf expected-output-crlf
|
|
'
|
|
|
|
# The use of %f in a filter definition is expanded to the path to
|
|
# the filename being smudged or cleaned. It must be shell escaped.
|
|
# First, set up some interesting file names and pet them in
|
|
# .gitattributes.
|
|
test_expect_success 'filter shell-escaped filenames' '
|
|
cat >argc.sh <<-EOF &&
|
|
#!$SHELL_PATH
|
|
cat >/dev/null
|
|
echo argc: \$# "\$@"
|
|
EOF
|
|
normal=name-no-magic &&
|
|
special="name with '\''sq'\'' and \$x" &&
|
|
echo some test text >"$normal" &&
|
|
echo some test text >"$special" &&
|
|
git add "$normal" "$special" &&
|
|
git commit -q -m "add files" &&
|
|
echo "name* filter=argc" >.gitattributes &&
|
|
|
|
# delete the files and check them out again, using a smudge filter
|
|
# that will count the args and echo the command-line back to us
|
|
git config filter.argc.smudge "sh ./argc.sh %f" &&
|
|
rm "$normal" "$special" &&
|
|
git checkout -- "$normal" "$special" &&
|
|
|
|
# make sure argc.sh counted the right number of args
|
|
echo "argc: 1 $normal" >expect &&
|
|
test_cmp expect "$normal" &&
|
|
echo "argc: 1 $special" >expect &&
|
|
test_cmp expect "$special" &&
|
|
|
|
# do the same thing, but with more args in the filter expression
|
|
git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
|
|
rm "$normal" "$special" &&
|
|
git checkout -- "$normal" "$special" &&
|
|
|
|
# make sure argc.sh counted the right number of args
|
|
echo "argc: 2 $normal --my-extra-arg" >expect &&
|
|
test_cmp expect "$normal" &&
|
|
echo "argc: 2 $special --my-extra-arg" >expect &&
|
|
test_cmp expect "$special" &&
|
|
:
|
|
'
|
|
|
|
test_expect_success 'required filter should filter data' '
|
|
git config filter.required.smudge ./rot13.sh &&
|
|
git config filter.required.clean ./rot13.sh &&
|
|
git config filter.required.required true &&
|
|
|
|
echo "*.r filter=required" >.gitattributes &&
|
|
|
|
cat test.o >test.r &&
|
|
git add test.r &&
|
|
|
|
rm -f test.r &&
|
|
git checkout -- test.r &&
|
|
cmp test.o test.r &&
|
|
|
|
./rot13.sh <test.o >expected &&
|
|
git cat-file blob :test.r >actual &&
|
|
cmp expected actual
|
|
'
|
|
|
|
test_expect_success 'required filter smudge failure' '
|
|
git config filter.failsmudge.smudge false &&
|
|
git config filter.failsmudge.clean cat &&
|
|
git config filter.failsmudge.required true &&
|
|
|
|
echo "*.fs filter=failsmudge" >.gitattributes &&
|
|
|
|
echo test >test.fs &&
|
|
git add test.fs &&
|
|
rm -f test.fs &&
|
|
test_must_fail git checkout -- test.fs
|
|
'
|
|
|
|
test_expect_success 'required filter clean failure' '
|
|
git config filter.failclean.smudge cat &&
|
|
git config filter.failclean.clean false &&
|
|
git config filter.failclean.required true &&
|
|
|
|
echo "*.fc filter=failclean" >.gitattributes &&
|
|
|
|
echo test >test.fc &&
|
|
test_must_fail git add test.fc
|
|
'
|
|
|
|
test_expect_success 'filtering large input to small output should use little memory' '
|
|
git config filter.devnull.clean "cat >/dev/null" &&
|
|
git config filter.devnull.required true &&
|
|
for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
|
|
echo "30MB filter=devnull" >.gitattributes &&
|
|
GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
|
|
'
|
|
|
|
test_expect_success 'filter that does not read is fine' '
|
|
test-genrandom foo $((128 * 1024 + 1)) >big &&
|
|
echo "big filter=epipe" >.gitattributes &&
|
|
git config filter.epipe.clean "echo xyzzy" &&
|
|
git add big &&
|
|
git cat-file blob :big >actual &&
|
|
echo xyzzy >expect &&
|
|
test_cmp expect actual
|
|
'
|
|
|
|
test_expect_success EXPENSIVE 'filter large file' '
|
|
git config filter.largefile.smudge cat &&
|
|
git config filter.largefile.clean cat &&
|
|
for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
|
|
echo "2GB filter=largefile" >.gitattributes &&
|
|
git add 2GB 2>err &&
|
|
! test -s err &&
|
|
rm -f 2GB &&
|
|
git checkout -- 2GB 2>err &&
|
|
! test -s err
|
|
'
|
|
|
|
test_expect_success "filter: clean empty file" '
|
|
git config filter.in-repo-header.clean "echo cleaned && cat" &&
|
|
git config filter.in-repo-header.smudge "sed 1d" &&
|
|
|
|
echo "empty-in-worktree filter=in-repo-header" >>.gitattributes &&
|
|
>empty-in-worktree &&
|
|
|
|
echo cleaned >expected &&
|
|
git add empty-in-worktree &&
|
|
git show :empty-in-worktree >actual &&
|
|
test_cmp expected actual
|
|
'
|
|
|
|
test_expect_success "filter: smudge empty file" '
|
|
git config filter.empty-in-repo.clean "cat >/dev/null" &&
|
|
git config filter.empty-in-repo.smudge "echo smudged && cat" &&
|
|
|
|
echo "empty-in-repo filter=empty-in-repo" >>.gitattributes &&
|
|
echo dead data walking >empty-in-repo &&
|
|
git add empty-in-repo &&
|
|
|
|
echo smudged >expected &&
|
|
git checkout-index --prefix=filtered- empty-in-repo &&
|
|
test_cmp expected filtered-empty-in-repo
|
|
'
|
|
|
|
test_expect_success 'disable filter with empty override' '
|
|
test_config_global filter.disable.smudge false &&
|
|
test_config_global filter.disable.clean false &&
|
|
test_config filter.disable.smudge false &&
|
|
test_config filter.disable.clean false &&
|
|
|
|
echo "*.disable filter=disable" >.gitattributes &&
|
|
|
|
echo test >test.disable &&
|
|
git -c filter.disable.clean= add test.disable 2>err &&
|
|
test_must_be_empty err &&
|
|
rm -f test.disable &&
|
|
git -c filter.disable.smudge= checkout -- test.disable 2>err &&
|
|
test_must_be_empty err
|
|
'
|
|
|
|
test_expect_success 'diff does not reuse worktree files that need cleaning' '
|
|
test_config filter.counter.clean "echo . >>count; sed s/^/clean:/" &&
|
|
echo "file filter=counter" >.gitattributes &&
|
|
test_commit one file &&
|
|
test_commit two file &&
|
|
|
|
>count &&
|
|
git diff-tree -p HEAD &&
|
|
test_line_count = 0 count
|
|
'
|
|
|
|
test_done
|