From: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> Full pattern must be quoted. So 'pat"t"ern attr' will give exactly 'pat"t"ern', not 'pattern'. Also clarify that leading whitespaces are not part of the pattern and document comment syntax. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> Signed-off-by: Brandon Williams <bmwill@xxxxxxxxxx> --- Documentation/gitattributes.txt | 8 +++++--- attr.c | 15 +++++++++++++-- t/t0003-attributes.sh | 26 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index e0b66c122..3173dee7e 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -21,9 +21,11 @@ Each line in `gitattributes` file is of form: pattern attr1 attr2 ... That is, a pattern followed by an attributes list, -separated by whitespaces. When the pattern matches the -path in question, the attributes listed on the line are given to -the path. +separated by whitespaces. Leading and trailing whitespaces are +ignored. Lines that begin with '#' are ignored. Patterns +that begin with a double quote are quoted in C style. +When the pattern matches the path in question, the attributes +listed on the line are given to the path. Each attribute can be in one of these states for a given path: diff --git a/attr.c b/attr.c index d180c7833..e1c630f79 100644 --- a/attr.c +++ b/attr.c @@ -13,6 +13,7 @@ #include "attr.h" #include "dir.h" #include "utf8.h" +#include "quote.h" const char git_attr__true[] = "(builtin)true"; const char git_attr__false[] = "\0(builtin)false"; @@ -212,12 +213,21 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, const char *cp, *name, *states; struct match_attr *res = NULL; int is_macro; + struct strbuf pattern = STRBUF_INIT; cp = line + strspn(line, blank); if (!*cp || *cp == '#') return NULL; name = cp; - namelen = strcspn(name, blank); + + if (*cp == '"' && !unquote_c_style(&pattern, name, &states)) { + name = pattern.buf; + namelen = pattern.len; + } else { + namelen = strcspn(name, blank); + states = name + namelen; + } + if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && starts_with(name, ATTRIBUTE_MACRO_PREFIX)) { if (!macro_ok) { @@ -239,7 +249,6 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, else is_macro = 0; - states = name + namelen; states += strspn(states, blank); /* First pass to count the attr_states */ @@ -282,9 +291,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, cannot_trust_maybe_real = 1; } + strbuf_release(&pattern); return res; fail_return: + strbuf_release(&pattern); free(res); return NULL; } diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index f0fbb4255..f19ae4f8c 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -13,10 +13,31 @@ attr_check () { test_line_count = 0 err } +attr_check_quote () { + + path="$1" + quoted_path="$2" + expect="$3" + + git check-attr test -- "$path" >actual && + echo "\"$quoted_path\": test: $expect" >expect && + test_cmp expect actual + +} + +test_expect_success 'open-quoted pathname' ' + echo "\"a test=a" >.gitattributes && + test_must_fail attr_check a a +' + + test_expect_success 'setup' ' mkdir -p a/b/d a/c b && ( 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" @@ -69,6 +90,11 @@ test_expect_success 'command line checks' ' ' test_expect_success 'attribute test' ' + + attr_check " d " d && + attr_check e e && + attr_check_quote e\" e\\\" e && + attr_check f f && attr_check a/f f && attr_check a/c/f f && -- 2.11.0.483.g087da7b7c-goog