2008-02-07 08:02:08 +00:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description=gitattributes
|
|
|
|
|
2021-10-12 13:56:37 +00:00
|
|
|
TEST_PASSES_SANITIZE_LEAK=true
|
2022-06-03 11:15:06 +00:00
|
|
|
TEST_CREATE_REPO_NO_TEMPLATE=1
|
2008-02-07 08:02:08 +00:00
|
|
|
. ./test-lib.sh
|
|
|
|
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 14:44:32 +00:00
|
|
|
attr_check_basic () {
|
2019-12-20 18:15:50 +00:00
|
|
|
path="$1" expect="$2" git_opts="$3" &&
|
2008-02-07 08:02:08 +00:00
|
|
|
|
2019-12-20 18:15:50 +00:00
|
|
|
git $git_opts check-attr test -- "$path" >actual 2>err &&
|
|
|
|
echo "$path: test: $expect" >expect &&
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 14:44:32 +00:00
|
|
|
test_cmp expect actual
|
|
|
|
}
|
|
|
|
|
|
|
|
attr_check () {
|
|
|
|
attr_check_basic "$@" &&
|
2019-12-20 18:15:51 +00:00
|
|
|
test_must_be_empty err
|
2008-02-07 08:02:08 +00:00
|
|
|
}
|
|
|
|
|
2017-01-28 02:01:50 +00:00
|
|
|
attr_check_quote () {
|
2019-12-20 18:15:50 +00:00
|
|
|
path="$1" quoted_path="$2" expect="$3" &&
|
2017-01-28 02:01:50 +00:00
|
|
|
|
|
|
|
git check-attr test -- "$path" >actual &&
|
|
|
|
echo "\"$quoted_path\": test: $expect" >expect &&
|
|
|
|
test_cmp expect actual
|
2023-01-14 08:30:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
attr_check_source () {
|
|
|
|
path="$1" expect="$2" source="$3" git_opts="$4" &&
|
2017-01-28 02:01:50 +00:00
|
|
|
|
2023-01-14 08:30:38 +00:00
|
|
|
echo "$path: test: $expect" >expect &&
|
2023-05-06 04:15:29 +00:00
|
|
|
|
|
|
|
git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_be_empty err &&
|
|
|
|
|
|
|
|
git $git_opts --attr-source="$source" check-attr test -- "$path" >actual 2>err &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_be_empty err
|
|
|
|
|
2023-10-13 17:39:30 +00:00
|
|
|
git $git_opts -c "attr.tree=$source" check-attr test -- "$path" >actual 2>err &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_be_empty err
|
|
|
|
|
2023-05-06 04:15:29 +00:00
|
|
|
GIT_ATTR_SOURCE="$source" git $git_opts check-attr test -- "$path" >actual 2>err &&
|
2023-01-14 08:30:38 +00:00
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_be_empty err
|
2017-01-28 02:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
test_expect_success 'open-quoted pathname' '
|
|
|
|
echo "\"a test=a" >.gitattributes &&
|
2019-12-20 18:15:52 +00:00
|
|
|
attr_check a unspecified
|
2017-01-28 02:01:50 +00:00
|
|
|
'
|
|
|
|
|
2008-02-07 08:02:08 +00:00
|
|
|
test_expect_success 'setup' '
|
2011-08-04 04:47:45 +00:00
|
|
|
mkdir -p a/b/d a/c b &&
|
2008-02-07 08:02:08 +00:00
|
|
|
(
|
2018-07-02 00:23:55 +00:00
|
|
|
echo "[attr]notest !test" &&
|
|
|
|
echo "\" d \" test=d" &&
|
|
|
|
echo " e test=e" &&
|
|
|
|
echo " e\" test=e" &&
|
|
|
|
echo "f test=f" &&
|
|
|
|
echo "a/i test=a/i" &&
|
|
|
|
echo "onoff test -test" &&
|
|
|
|
echo "offon -test test" &&
|
|
|
|
echo "no notest" &&
|
2011-10-11 15:53:31 +00:00
|
|
|
echo "A/e/F test=A/e/F"
|
2008-02-07 08:02:08 +00:00
|
|
|
) >.gitattributes &&
|
|
|
|
(
|
|
|
|
echo "g test=a/g" &&
|
|
|
|
echo "b/g test=a/b/g"
|
|
|
|
) >a/.gitattributes &&
|
|
|
|
(
|
|
|
|
echo "h test=a/b/h" &&
|
2018-07-02 00:23:55 +00:00
|
|
|
echo "d/* test=a/b/d/*" &&
|
2010-04-06 12:46:44 +00:00
|
|
|
echo "d/yes notest"
|
2011-08-04 04:36:15 +00:00
|
|
|
) >a/b/.gitattributes &&
|
2010-08-31 22:42:43 +00:00
|
|
|
(
|
|
|
|
echo "global test=global"
|
2011-08-04 04:36:31 +00:00
|
|
|
) >"$HOME"/global-gitattributes &&
|
2011-09-22 23:34:05 +00:00
|
|
|
cat <<-EOF >expect-all
|
|
|
|
f: test: f
|
|
|
|
a/f: test: f
|
|
|
|
a/c/f: test: f
|
|
|
|
a/g: test: a/g
|
|
|
|
a/b/g: test: a/b/g
|
|
|
|
b/g: test: unspecified
|
|
|
|
a/b/h: test: a/b/h
|
|
|
|
a/b/d/g: test: a/b/d/*
|
|
|
|
onoff: test: unset
|
|
|
|
offon: test: set
|
|
|
|
no: notest: set
|
|
|
|
no: test: unspecified
|
|
|
|
a/b/d/no: notest: set
|
|
|
|
a/b/d/no: test: a/b/d/*
|
|
|
|
a/b/d/yes: notest: set
|
|
|
|
a/b/d/yes: test: unspecified
|
|
|
|
EOF
|
2008-02-07 08:02:08 +00:00
|
|
|
'
|
|
|
|
|
2023-01-14 08:30:38 +00:00
|
|
|
test_expect_success 'setup branches' '
|
|
|
|
mkdir -p foo/bar &&
|
|
|
|
test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
|
|
|
|
"f test=f\na/i test=n\n" tag-1 &&
|
|
|
|
test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
|
|
|
|
"g test=g\na/i test=m\n" tag-2 &&
|
|
|
|
rm foo/bar/.gitattributes
|
|
|
|
'
|
|
|
|
|
2011-08-04 04:36:14 +00:00
|
|
|
test_expect_success 'command line checks' '
|
2011-08-04 04:36:16 +00:00
|
|
|
test_must_fail git check-attr &&
|
|
|
|
test_must_fail git check-attr -- &&
|
2011-08-04 04:36:29 +00:00
|
|
|
test_must_fail git check-attr test &&
|
|
|
|
test_must_fail git check-attr test -- &&
|
2011-08-04 04:36:16 +00:00
|
|
|
test_must_fail git check-attr -- f &&
|
2023-01-14 08:30:38 +00:00
|
|
|
test_must_fail git check-attr --source &&
|
|
|
|
test_must_fail git check-attr --source not-a-valid-ref &&
|
2011-08-04 04:36:16 +00:00
|
|
|
echo "f" | test_must_fail git check-attr --stdin &&
|
|
|
|
echo "f" | test_must_fail git check-attr --stdin -- f &&
|
|
|
|
echo "f" | test_must_fail git check-attr --stdin test -- f &&
|
2011-08-04 04:36:14 +00:00
|
|
|
test_must_fail git check-attr "" -- f
|
|
|
|
'
|
|
|
|
|
2008-02-07 08:02:08 +00:00
|
|
|
test_expect_success 'attribute test' '
|
2017-01-28 02:01:50 +00:00
|
|
|
|
|
|
|
attr_check " d " d &&
|
|
|
|
attr_check e e &&
|
|
|
|
attr_check_quote e\" e\\\" e &&
|
|
|
|
|
2008-02-07 08:02:08 +00:00
|
|
|
attr_check f f &&
|
|
|
|
attr_check a/f f &&
|
|
|
|
attr_check a/c/f f &&
|
|
|
|
attr_check a/g a/g &&
|
|
|
|
attr_check a/b/g a/b/g &&
|
|
|
|
attr_check b/g unspecified &&
|
|
|
|
attr_check a/b/h a/b/h &&
|
2010-08-28 18:18:36 +00:00
|
|
|
attr_check a/b/d/g "a/b/d/*" &&
|
|
|
|
attr_check onoff unset &&
|
|
|
|
attr_check offon set &&
|
|
|
|
attr_check no unspecified &&
|
|
|
|
attr_check a/b/d/no "a/b/d/*" &&
|
2010-04-06 12:46:44 +00:00
|
|
|
attr_check a/b/d/yes unspecified
|
2008-02-07 08:02:08 +00:00
|
|
|
'
|
|
|
|
|
2011-10-11 15:53:31 +00:00
|
|
|
test_expect_success 'attribute matching is case sensitive when core.ignorecase=0' '
|
|
|
|
|
2019-12-20 18:15:52 +00:00
|
|
|
attr_check F unspecified "-c core.ignorecase=0" &&
|
|
|
|
attr_check a/F unspecified "-c core.ignorecase=0" &&
|
|
|
|
attr_check a/c/F unspecified "-c core.ignorecase=0" &&
|
|
|
|
attr_check a/G unspecified "-c core.ignorecase=0" &&
|
|
|
|
attr_check a/B/g a/g "-c core.ignorecase=0" &&
|
|
|
|
attr_check a/b/G unspecified "-c core.ignorecase=0" &&
|
|
|
|
attr_check a/b/H unspecified "-c core.ignorecase=0" &&
|
|
|
|
attr_check a/b/D/g a/g "-c core.ignorecase=0" &&
|
|
|
|
attr_check oNoFf unspecified "-c core.ignorecase=0" &&
|
|
|
|
attr_check oFfOn unspecified "-c core.ignorecase=0" &&
|
2011-10-11 15:53:31 +00:00
|
|
|
attr_check NO unspecified "-c core.ignorecase=0" &&
|
2019-12-20 18:15:52 +00:00
|
|
|
attr_check a/b/D/NO unspecified "-c core.ignorecase=0" &&
|
2011-10-11 15:53:31 +00:00
|
|
|
attr_check a/b/d/YES a/b/d/* "-c core.ignorecase=0" &&
|
2019-12-20 18:15:52 +00:00
|
|
|
attr_check a/E/f f "-c core.ignorecase=0"
|
2011-10-11 15:53:31 +00:00
|
|
|
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'attribute matching is case insensitive when core.ignorecase=1' '
|
|
|
|
|
|
|
|
attr_check F f "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/F f "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/c/F f "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/G a/g "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/B/g a/b/g "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/b/G a/b/g "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/b/H a/b/h "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=1" &&
|
|
|
|
attr_check oNoFf unset "-c core.ignorecase=1" &&
|
|
|
|
attr_check oFfOn set "-c core.ignorecase=1" &&
|
|
|
|
attr_check NO unspecified "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/b/d/YES unspecified "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/E/f "A/e/F" "-c core.ignorecase=1"
|
|
|
|
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' '
|
2019-12-20 18:15:52 +00:00
|
|
|
attr_check a/B/D/g a/g "-c core.ignorecase=0" &&
|
|
|
|
attr_check A/B/D/NO unspecified "-c core.ignorecase=0" &&
|
2011-10-11 15:53:31 +00:00
|
|
|
attr_check A/b/h a/b/h "-c core.ignorecase=1" &&
|
|
|
|
attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=1" &&
|
|
|
|
attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=1"
|
|
|
|
'
|
|
|
|
|
2011-08-04 04:47:46 +00:00
|
|
|
test_expect_success 'unnormalized paths' '
|
2011-08-04 04:47:44 +00:00
|
|
|
attr_check ./f f &&
|
|
|
|
attr_check ./a/g a/g &&
|
|
|
|
attr_check a/./g a/g &&
|
|
|
|
attr_check a/c/../b/g a/b/g
|
|
|
|
'
|
|
|
|
|
2011-08-04 04:47:46 +00:00
|
|
|
test_expect_success 'relative paths' '
|
2011-08-04 04:47:45 +00:00
|
|
|
(cd a && attr_check ../f f) &&
|
|
|
|
(cd a && attr_check f f) &&
|
|
|
|
(cd a && attr_check i a/i) &&
|
|
|
|
(cd a && attr_check g a/g) &&
|
|
|
|
(cd a && attr_check b/g a/b/g) &&
|
|
|
|
(cd b && attr_check ../a/f f) &&
|
|
|
|
(cd b && attr_check ../a/g a/g) &&
|
|
|
|
(cd b && attr_check ../a/b/g a/b/g)
|
|
|
|
'
|
|
|
|
|
attr: don't confuse prefixes with leading directories
When we prepare the attribute stack for a lookup on a path,
we start with the cached stack from the previous lookup
(because it is common to do several lookups in the same
directory hierarchy). So the first thing we must do in
preparing the stack is to pop any entries that point to
directories we are no longer interested in.
For example, if our stack contains gitattributes for:
foo/bar/baz
foo/bar
foo
but we want to do a lookup in "foo/bar/bleep", then we want
to pop the top element, but retain the others.
To do this we walk down the stack from the top, popping
elements that do not match our lookup directory. However,
the test do this simply checked strncmp, meaning we would
mistake "foo/bar/baz" as a leading directory of
"foo/bar/baz_plus". We must also check that the character
after our match is '/', meaning we matched the whole path
component.
There are two special cases to consider:
1. The top of our attr stack has the empty path. So we
must not check for '/', but rather special-case the
empty path, which always matches.
2. Typically when matching paths in this way, you would
also need to check for a full string match (i.e., the
character after is '\0'). We don't need to do so in
this case, though, because our path string is actually
just the directory component of the path to a file
(i.e., we know that it terminates with "/", because the
filename comes after that).
Helped-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-01-10 18:08:21 +00:00
|
|
|
test_expect_success 'prefixes are not confused with leading directories' '
|
|
|
|
attr_check a_plus/g unspecified &&
|
|
|
|
cat >expect <<-\EOF &&
|
|
|
|
a/g: test: a/g
|
|
|
|
a_plus/g: test: unspecified
|
|
|
|
EOF
|
|
|
|
git check-attr test a/g a_plus/g >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2010-08-31 22:42:43 +00:00
|
|
|
test_expect_success 'core.attributesfile' '
|
|
|
|
attr_check global unspecified &&
|
|
|
|
git config core.attributesfile "$HOME/global-gitattributes" &&
|
|
|
|
attr_check global global &&
|
|
|
|
git config core.attributesfile "~/global-gitattributes" &&
|
|
|
|
attr_check global global &&
|
2011-09-22 23:34:05 +00:00
|
|
|
echo "global test=precedence" >>.gitattributes &&
|
2010-08-31 22:42:43 +00:00
|
|
|
attr_check global precedence
|
|
|
|
'
|
|
|
|
|
2008-10-07 00:16:52 +00:00
|
|
|
test_expect_success 'attribute test: read paths from stdin' '
|
2011-09-22 23:34:05 +00:00
|
|
|
grep -v notest <expect-all >expect &&
|
|
|
|
sed -e "s/:.*//" <expect | git check-attr --stdin test >actual &&
|
2008-10-07 00:16:52 +00:00
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2023-01-14 08:30:37 +00:00
|
|
|
test_expect_success 'setup --all option' '
|
2011-09-22 21:44:20 +00:00
|
|
|
grep -v unspecified <expect-all | sort >specified-all &&
|
2023-01-14 08:30:37 +00:00
|
|
|
sed -e "s/:.*//" <expect-all | uniq >stdin-all
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'attribute test: --all option' '
|
2022-02-23 11:53:47 +00:00
|
|
|
git check-attr --stdin --all <stdin-all >tmp &&
|
|
|
|
sort tmp >actual &&
|
2011-09-22 21:44:20 +00:00
|
|
|
test_cmp specified-all actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'attribute test: --cached option' '
|
2022-02-23 11:53:47 +00:00
|
|
|
git check-attr --cached --stdin --all <stdin-all >tmp &&
|
|
|
|
sort tmp >actual &&
|
tests: use 'test_must_be_empty' instead of 'test_cmp <empty> <out>'
Using 'test_must_be_empty' is shorter and more idiomatic than
>empty &&
test_cmp empty out
as it saves the creation of an empty file. Furthermore, sometimes the
expected empty file doesn't have such a descriptive name like 'empty',
and its creation is far away from the place where it's finally used
for comparison (e.g. in 't7600-merge.sh', where two expected empty
files are created in the 'setup' test, but are used only about 500
lines later).
These cases were found by instrumenting 'test_cmp' to error out the
test script when it's used to compare empty files, and then converted
manually.
Note that even after this patch there still remain a lot of cases
where we use 'test_cmp' to check empty files:
- Sometimes the expected output is not hard-coded in the test, but
'test_cmp' is used to ensure that two similar git commands produce
the same output, and that output happens to be empty, e.g. the
test 'submodule update --merge - ignores --merge for new
submodules' in 't7406-submodule-update.sh'.
- Repetitive common tasks, including preparing the expected results
and running 'test_cmp', are often extracted into a helper
function, and some of this helper's callsites expect no output.
- For the same reason as above, the whole 'test_expect_success'
block is within a helper function, e.g. in 't3070-wildmatch.sh'.
- Or 'test_cmp' is invoked in a loop, e.g. the test 'cvs update
(-p)' in 't9400-git-cvsserver-server.sh'.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-19 21:57:25 +00:00
|
|
|
test_must_be_empty actual &&
|
2011-09-22 21:44:20 +00:00
|
|
|
git add .gitattributes a/.gitattributes a/b/.gitattributes &&
|
2022-02-23 11:53:47 +00:00
|
|
|
git check-attr --cached --stdin --all <stdin-all >tmp &&
|
|
|
|
sort tmp >actual &&
|
2011-09-22 21:44:20 +00:00
|
|
|
test_cmp specified-all actual
|
2011-08-04 04:36:30 +00:00
|
|
|
'
|
|
|
|
|
2008-04-22 18:19:12 +00:00
|
|
|
test_expect_success 'root subdir attribute test' '
|
|
|
|
attr_check a/i a/i &&
|
|
|
|
attr_check subdir/a/i unspecified
|
|
|
|
'
|
|
|
|
|
2012-10-15 06:24:39 +00:00
|
|
|
test_expect_success 'negative patterns' '
|
|
|
|
echo "!f test=bar" >.gitattributes &&
|
2013-03-01 20:06:17 +00:00
|
|
|
git check-attr test -- '"'"'!f'"'"' 2>errors &&
|
2023-10-31 05:23:30 +00:00
|
|
|
test_grep "Negative patterns are ignored" errors
|
2012-10-15 06:24:39 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'patterns starting with exclamation' '
|
|
|
|
echo "\!f test=foo" >.gitattributes &&
|
|
|
|
attr_check "!f" foo
|
|
|
|
'
|
|
|
|
|
2012-10-15 06:26:02 +00:00
|
|
|
test_expect_success '"**" test' '
|
|
|
|
echo "**/f foo=bar" >.gitattributes &&
|
|
|
|
cat <<\EOF >expect &&
|
|
|
|
f: foo: bar
|
|
|
|
a/f: foo: bar
|
|
|
|
a/b/f: foo: bar
|
|
|
|
a/b/c/f: foo: bar
|
|
|
|
EOF
|
|
|
|
git check-attr foo -- "f" >actual 2>err &&
|
|
|
|
git check-attr foo -- "a/f" >>actual 2>>err &&
|
|
|
|
git check-attr foo -- "a/b/f" >>actual 2>>err &&
|
|
|
|
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
|
|
|
|
test_cmp expect actual &&
|
2019-12-20 18:15:51 +00:00
|
|
|
test_must_be_empty err
|
2012-10-15 06:26:02 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success '"**" with no slashes test' '
|
|
|
|
echo "a**f foo=bar" >.gitattributes &&
|
|
|
|
git check-attr foo -- "f" >actual &&
|
|
|
|
cat <<\EOF >expect &&
|
|
|
|
f: foo: unspecified
|
|
|
|
af: foo: bar
|
|
|
|
axf: foo: bar
|
|
|
|
a/f: foo: unspecified
|
|
|
|
a/b/f: foo: unspecified
|
|
|
|
a/b/c/f: foo: unspecified
|
|
|
|
EOF
|
|
|
|
git check-attr foo -- "f" >actual 2>err &&
|
|
|
|
git check-attr foo -- "af" >>actual 2>err &&
|
|
|
|
git check-attr foo -- "axf" >>actual 2>err &&
|
|
|
|
git check-attr foo -- "a/f" >>actual 2>>err &&
|
|
|
|
git check-attr foo -- "a/b/f" >>actual 2>>err &&
|
|
|
|
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
|
|
|
|
test_cmp expect actual &&
|
2019-12-20 18:15:51 +00:00
|
|
|
test_must_be_empty err
|
2012-10-15 06:26:02 +00:00
|
|
|
'
|
|
|
|
|
2014-02-06 18:19:33 +00:00
|
|
|
test_expect_success 'using --git-dir and --work-tree' '
|
|
|
|
mkdir unreal real &&
|
|
|
|
git init real &&
|
|
|
|
echo "file test=in-real" >real/.gitattributes &&
|
|
|
|
(
|
|
|
|
cd unreal &&
|
|
|
|
attr_check file in-real "--git-dir ../real/.git --work-tree ../real"
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2023-01-14 08:30:38 +00:00
|
|
|
test_expect_success 'using --source' '
|
|
|
|
attr_check_source foo/bar/f f tag-1 &&
|
|
|
|
attr_check_source foo/bar/a/i n tag-1 &&
|
|
|
|
attr_check_source foo/bar/f unspecified tag-2 &&
|
|
|
|
attr_check_source foo/bar/a/i m tag-2 &&
|
|
|
|
attr_check_source foo/bar/g g tag-2 &&
|
|
|
|
attr_check_source foo/bar/g unspecified tag-1
|
|
|
|
'
|
|
|
|
|
2008-06-08 15:16:11 +00:00
|
|
|
test_expect_success 'setup bare' '
|
2022-06-03 11:15:06 +00:00
|
|
|
git clone --template= --bare . bare.git
|
2008-06-08 15:16:11 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'bare repository: check that .gitattribute is ignored' '
|
|
|
|
(
|
2014-02-06 18:16:27 +00:00
|
|
|
cd bare.git &&
|
|
|
|
(
|
2018-07-02 00:23:55 +00:00
|
|
|
echo "f test=f" &&
|
2014-02-06 18:16:27 +00:00
|
|
|
echo "a/i test=a/i"
|
|
|
|
) >.gitattributes &&
|
|
|
|
attr_check f unspecified &&
|
|
|
|
attr_check a/f unspecified &&
|
|
|
|
attr_check a/c/f unspecified &&
|
|
|
|
attr_check a/i unspecified &&
|
|
|
|
attr_check subdir/a/i unspecified
|
|
|
|
)
|
2008-06-08 15:16:11 +00:00
|
|
|
'
|
|
|
|
|
2023-10-13 17:39:30 +00:00
|
|
|
bad_attr_source_err="fatal: bad --attr-source or GIT_ATTR_SOURCE"
|
|
|
|
|
|
|
|
test_expect_success '--attr-source is bad' '
|
|
|
|
test_when_finished rm -rf empty &&
|
|
|
|
git init empty &&
|
|
|
|
(
|
|
|
|
cd empty &&
|
|
|
|
echo "$bad_attr_source_err" >expect_err &&
|
|
|
|
test_must_fail git --attr-source=HEAD check-attr test -- f/path 2>err &&
|
|
|
|
test_cmp expect_err err
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'attr.tree when HEAD is unborn' '
|
|
|
|
test_when_finished rm -rf empty &&
|
|
|
|
git init empty &&
|
|
|
|
(
|
|
|
|
cd empty &&
|
|
|
|
echo "f/path: test: unspecified" >expect &&
|
|
|
|
git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
|
|
|
|
test_must_be_empty err &&
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'bad attr source defaults to reading .gitattributes file' '
|
|
|
|
test_when_finished rm -rf empty &&
|
|
|
|
git init empty &&
|
|
|
|
(
|
|
|
|
cd empty &&
|
|
|
|
echo "f/path test=val" >.gitattributes &&
|
|
|
|
echo "f/path: test: val" >expect &&
|
|
|
|
git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
|
|
|
|
test_must_be_empty err &&
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
2023-10-13 17:39:29 +00:00
|
|
|
|
|
|
|
test_expect_success 'bare repo defaults to reading .gitattributes from HEAD' '
|
|
|
|
test_when_finished rm -rf test bare_with_gitattribute &&
|
|
|
|
git init test &&
|
|
|
|
test_commit -C test gitattributes .gitattributes "f/path test=val" &&
|
|
|
|
git clone --bare test bare_with_gitattribute &&
|
|
|
|
echo "f/path: test: val" >expect &&
|
|
|
|
git -C bare_with_gitattribute check-attr test -- f/path >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2023-10-13 17:39:30 +00:00
|
|
|
test_expect_success 'precedence of --attr-source, GIT_ATTR_SOURCE, then attr.tree' '
|
|
|
|
test_when_finished rm -rf empty &&
|
|
|
|
git init empty &&
|
|
|
|
(
|
|
|
|
cd empty &&
|
|
|
|
git checkout -b attr-source &&
|
|
|
|
test_commit "val1" .gitattributes "f/path test=val1" &&
|
|
|
|
git checkout -b attr-tree &&
|
|
|
|
test_commit "val2" .gitattributes "f/path test=val2" &&
|
|
|
|
git checkout attr-source &&
|
|
|
|
echo "f/path: test: val1" >expect &&
|
|
|
|
GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree --attr-source=attr-source \
|
|
|
|
check-attr test -- f/path >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree \
|
|
|
|
check-attr test -- f/path >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2023-01-14 08:30:38 +00:00
|
|
|
test_expect_success 'bare repository: with --source' '
|
|
|
|
(
|
|
|
|
cd bare.git &&
|
|
|
|
attr_check_source foo/bar/f f tag-1 &&
|
|
|
|
attr_check_source foo/bar/a/i n tag-1 &&
|
|
|
|
attr_check_source foo/bar/f unspecified tag-2 &&
|
|
|
|
attr_check_source foo/bar/a/i m tag-2 &&
|
|
|
|
attr_check_source foo/bar/g g tag-2 &&
|
|
|
|
attr_check_source foo/bar/g unspecified tag-1
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2011-09-22 21:44:20 +00:00
|
|
|
test_expect_success 'bare repository: check that --cached honors index' '
|
2014-02-06 18:16:27 +00:00
|
|
|
(
|
|
|
|
cd bare.git &&
|
|
|
|
GIT_INDEX_FILE=../.git/index \
|
|
|
|
git check-attr --cached --stdin --all <../stdin-all |
|
|
|
|
sort >actual &&
|
|
|
|
test_cmp ../specified-all actual
|
|
|
|
)
|
2011-09-22 21:44:20 +00:00
|
|
|
'
|
|
|
|
|
2008-06-08 15:16:11 +00:00
|
|
|
test_expect_success 'bare repository: test info/attributes' '
|
|
|
|
(
|
2014-02-06 18:16:27 +00:00
|
|
|
cd bare.git &&
|
2022-06-03 11:15:06 +00:00
|
|
|
mkdir info &&
|
2014-02-06 18:16:27 +00:00
|
|
|
(
|
2018-07-02 00:23:55 +00:00
|
|
|
echo "f test=f" &&
|
2014-02-06 18:16:27 +00:00
|
|
|
echo "a/i test=a/i"
|
|
|
|
) >info/attributes &&
|
|
|
|
attr_check f f &&
|
|
|
|
attr_check a/f f &&
|
|
|
|
attr_check a/c/f f &&
|
|
|
|
attr_check a/i a/i &&
|
|
|
|
attr_check subdir/a/i unspecified
|
|
|
|
)
|
2008-06-08 15:16:11 +00:00
|
|
|
'
|
|
|
|
|
attr: do not mark queried macros as unset
Since 60a12722ac (attr: remove maybe-real, maybe-macro from git_attr,
2017-01-27), we will always mark an attribute macro (e.g., "binary")
that is specifically queried for as "unspecified", even though listing
_all_ attributes would display it at set. E.g.:
$ echo "* binary" >.gitattributes
$ git check-attr -a file
file: binary: set
file: diff: unset
file: merge: unset
file: text: unset
$ git check-attr binary file
file: binary: unspecified
The problem stems from an incorrect conversion of the optimization from
06a604e670 (attr: avoid heavy work when we know the specified attr is
not defined, 2014-12-28). There we tried in collect_some_attrs() to
avoid even looking at the attr_stack when the user has asked for "foo"
and we know that "foo" did not ever appear in any .gitattributes file.
It used a flag "maybe_real" in each attribute struct, where "real" meant
that the attribute appeared in an actual file (we have to make this
distinction because we also create an attribute struct for any names
that are being queried). But as explained in that commit message, the
meaning of "real" was tangled with some special cases around macros.
When 60a12722ac later refactored the macro code, it dropped maybe_real
entirely. This missed the fact that "maybe_real" could be unset for two
reasons: because of a macro, or because it was never found during
parsing. This had two results:
- the optimization in collect_some_attrs() ceased doing anything
meaningful, since it no longer kept track of "was it found during
parsing"
- worse, it actually kicked in when the caller _did_ ask about a macro
by name, causing us to mark it as unspecified
It should be possible to salvage this optimization, but let's start with
just removing the remnants. It hasn't been doing anything (except
creating bugs) since 60a12722ac, and nobody seems to have noticed the
performance regression. It's more important to fix the correctness
problem clearly first.
I've added two tests here. The second one actually shows off the bug.
The test of "check-attr -a" is not strictly necessary, but we currently
do not test attribute macros much, and the builtin "binary" not at all.
So this increases our general test coverage, as well as making sure we
didn't mess up this related case.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-18 21:34:58 +00:00
|
|
|
test_expect_success 'binary macro expanded by -a' '
|
|
|
|
echo "file binary" >.gitattributes &&
|
|
|
|
cat >expect <<-\EOF &&
|
|
|
|
file: binary: set
|
|
|
|
file: diff: unset
|
|
|
|
file: merge: unset
|
|
|
|
file: text: unset
|
|
|
|
EOF
|
|
|
|
git check-attr -a file >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'query binary macro directly' '
|
|
|
|
echo "file binary" >.gitattributes &&
|
|
|
|
echo file: binary: set >expect &&
|
|
|
|
git check-attr binary file >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 14:44:32 +00:00
|
|
|
test_expect_success SYMLINKS 'set up symlink tests' '
|
|
|
|
echo "* test" >attr &&
|
|
|
|
rm -f .gitattributes
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success SYMLINKS 'symlinks respected in core.attributesFile' '
|
|
|
|
test_when_finished "rm symlink" &&
|
|
|
|
ln -s attr symlink &&
|
|
|
|
test_config core.attributesFile "$(pwd)/symlink" &&
|
|
|
|
attr_check file set
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success SYMLINKS 'symlinks respected in info/attributes' '
|
|
|
|
test_when_finished "rm .git/info/attributes" &&
|
2022-06-03 11:15:06 +00:00
|
|
|
mkdir .git/info &&
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 14:44:32 +00:00
|
|
|
ln -s ../../attr .git/info/attributes &&
|
|
|
|
attr_check file set
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success SYMLINKS 'symlinks not respected in-tree' '
|
|
|
|
test_when_finished "rm -rf .gitattributes subdir" &&
|
|
|
|
ln -s attr .gitattributes &&
|
|
|
|
mkdir subdir &&
|
|
|
|
ln -s ../attr subdir/.gitattributes &&
|
|
|
|
attr_check_basic subdir/file unspecified &&
|
2023-10-31 05:23:30 +00:00
|
|
|
test_grep "unable to access.*gitattributes" err
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 14:44:32 +00:00
|
|
|
'
|
|
|
|
|
2022-12-01 14:45:48 +00:00
|
|
|
test_expect_success 'large attributes line ignored in tree' '
|
|
|
|
test_when_finished "rm .gitattributes" &&
|
|
|
|
printf "path %02043d" 1 >.gitattributes &&
|
|
|
|
git check-attr --all path >actual 2>err &&
|
|
|
|
echo "warning: ignoring overly long attributes line 1" >expect &&
|
|
|
|
test_cmp expect err &&
|
|
|
|
test_must_be_empty actual
|
|
|
|
'
|
|
|
|
|
2022-12-01 14:45:44 +00:00
|
|
|
test_expect_success 'large attributes line ignores trailing content in tree' '
|
|
|
|
test_when_finished "rm .gitattributes" &&
|
|
|
|
# older versions of Git broke lines at 2048 bytes; the 2045 bytes
|
|
|
|
# of 0-padding here is accounting for the three bytes of "a 1", which
|
|
|
|
# would knock "trailing" to the "next" line, where it would be
|
|
|
|
# erroneously parsed.
|
|
|
|
printf "a %02045dtrailing attribute\n" 1 >.gitattributes &&
|
|
|
|
git check-attr --all trailing >actual 2>err &&
|
2022-12-01 14:45:48 +00:00
|
|
|
echo "warning: ignoring overly long attributes line 1" >expect &&
|
|
|
|
test_cmp expect err &&
|
|
|
|
test_must_be_empty actual
|
|
|
|
'
|
|
|
|
|
2022-12-01 14:45:53 +00:00
|
|
|
test_expect_success EXPENSIVE 'large attributes file ignored in tree' '
|
|
|
|
test_when_finished "rm .gitattributes" &&
|
2023-01-22 06:28:39 +00:00
|
|
|
dd if=/dev/zero of=.gitattributes bs=1048576 count=101 2>/dev/null &&
|
2022-12-01 14:45:53 +00:00
|
|
|
git check-attr --all path >/dev/null 2>err &&
|
|
|
|
echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect &&
|
|
|
|
test_cmp expect err
|
|
|
|
'
|
|
|
|
|
2022-12-01 14:45:48 +00:00
|
|
|
test_expect_success 'large attributes line ignored in index' '
|
|
|
|
test_when_finished "git update-index --remove .gitattributes" &&
|
|
|
|
blob=$(printf "path %02043d" 1 | git hash-object -w --stdin) &&
|
|
|
|
git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
|
|
|
|
git check-attr --cached --all path >actual 2>err &&
|
|
|
|
echo "warning: ignoring overly long attributes line 1" >expect &&
|
|
|
|
test_cmp expect err &&
|
2022-12-01 14:45:44 +00:00
|
|
|
test_must_be_empty actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'large attributes line ignores trailing content in index' '
|
|
|
|
test_when_finished "git update-index --remove .gitattributes" &&
|
|
|
|
blob=$(printf "a %02045dtrailing attribute\n" 1 | git hash-object -w --stdin) &&
|
|
|
|
git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
|
|
|
|
git check-attr --cached --all trailing >actual 2>err &&
|
2022-12-01 14:45:48 +00:00
|
|
|
echo "warning: ignoring overly long attributes line 1" >expect &&
|
|
|
|
test_cmp expect err &&
|
2022-12-01 14:45:44 +00:00
|
|
|
test_must_be_empty actual
|
|
|
|
'
|
|
|
|
|
2022-12-01 14:45:53 +00:00
|
|
|
test_expect_success EXPENSIVE 'large attributes file ignored in index' '
|
|
|
|
test_when_finished "git update-index --remove .gitattributes" &&
|
2023-01-22 06:28:39 +00:00
|
|
|
blob=$(dd if=/dev/zero bs=1048576 count=101 2>/dev/null | git hash-object -w --stdin) &&
|
2022-12-01 14:45:53 +00:00
|
|
|
git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
|
|
|
|
git check-attr --cached --all path >/dev/null 2>err &&
|
|
|
|
echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect &&
|
|
|
|
test_cmp expect err
|
|
|
|
'
|
|
|
|
|
2008-02-07 08:02:08 +00:00
|
|
|
test_done
|