diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index c80ca5d..f7954dd 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 quotation mark 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 6aff695..fdc4aae 100644
--- a/attr.c
+++ b/attr.c
@@ -2,6 +2,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "attr.h"
+#include "quote.h"
const char git_attr__true[] = "(builtin)true";
const char git_attr__false[] = "\0(builtin)false";
@@ -181,21 +182,40 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
{
int namelen;
int num_attr;
- const char *cp, *name;
+ const char *cp, *name, *ep;
struct match_attr *res = NULL;
int pass;
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 == '"') {
+ if (unquote_c_style(&pattern, name, &ep)) {
+ fprintf(stderr, "Misquoted pattern at %s:%d\n"
+ "Pattern is taken literally.\n",
+ src, lineno);
+ namelen = strcspn(name, blank);
+ ep = name + namelen;
+ }
+ else {
+ namelen = ep - name;
+ name = pattern.buf;
+ }
+ }
+ else {
+ namelen = strcspn(name, blank);
+ ep = name + namelen;
+ }
+
if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
!prefixcmp(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;@@ -206,6 +226,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
fprintf(stderr,
"%.*s is not a valid attribute name: %s:%d\n",
namelen, name, src, lineno);
+ strbuf_release(&pattern);
return NULL;
}
}
@@ -215,12 +236,14 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
for (pass = 0; pass < 2; pass++) {
/* pass 0 counts and allocates, pass 1 fills */
num_attr = 0;
- cp = name + namelen;
+ cp = ep;
cp = cp + strspn(cp, blank);
while (*cp) {
cp = parse_attr(src, lineno, cp, &num_attr, res);
- if (!cp)
+ if (!cp) {
+ strbuf_release(&pattern);
return NULL;
+ }
}
if (pass)
break;@@ -238,6 +261,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
res->is_macro = is_macro;
res->num_attr = num_attr;
}
+ strbuf_release(&pattern);
return res;
}
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 25205ac..7c55482 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -10,17 +10,32 @@ attr_check () {
expect="$2"
git check-attr test -- "$path" >actual &&
- echo "$path: test: $2" >expect &&
+ echo "$path: test: $expect" >expect &&
test_cmp expect actual
}
+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 'setup' '
mkdir -p a/b/d a/c &&
(
echo "[attr]notest !test"
+ echo "\"c test=c"
+ 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"@@ -44,6 +59,10 @@ test_expect_success 'setup' '
test_expect_success 'attribute test' '
+ attr_check_quote \"c \\\"c c &&
+ 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 &&
--
1.7.3.2.210.g045198