diff options
-rw-r--r-- | Documentation/gitattributes.txt | 8 | ||||
-rw-r--r-- | attr.c | 20 | ||||
-rwxr-xr-x | t/t0003-attributes.sh | 26 |
3 files changed, 49 insertions, 5 deletions
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index e3b1de8033..2990c1638d 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: @@ -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"; @@ -225,17 +226,30 @@ 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 != '"') { + namelen = strcspn(name, blank); + states = name + namelen; + } else { + const char *ep; + if (unquote_c_style(&pattern, name, &ep)) + die("Broken attribute line: %s", line); + name = pattern.buf; + namelen = pattern.len; + states = ep; + } + if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && starts_with(name, ATTRIBUTE_MACRO_PREFIX)) { if (!macro_ok) { fprintf(stderr, "%s not allowed: %s:%d\n", name, src, lineno); + strbuf_release(&pattern); return NULL; } is_macro = 1; @@ -244,13 +258,13 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, namelen = strcspn(name, blank); if (!attr_name_valid(name, namelen)) { report_invalid_attr(name, namelen, src, lineno); + strbuf_release(&pattern); return NULL; } } else is_macro = 0; - states = name + namelen; states += strspn(states, blank); /* First pass to count the attr_states */ @@ -278,6 +292,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, if (res->u.pat.flags & EXC_FLAG_NEGATIVE) { warning(_("Negative patterns are ignored in git attributes\n" "Use '\\!' for literal leading exclamation.")); + strbuf_release(&pattern); return NULL; } } @@ -293,6 +308,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, cannot_trust_maybe_real = 1; } + strbuf_release(&pattern); return res; } diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index f0fbb42554..f19ae4f8cc 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 && |