On 03/09/2017 01:07 PM, Brandon Williams wrote:
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index fc9320e59..5c32d1905 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -384,6 +384,26 @@ full pathname may have special meaning: + Glob magic is incompatible with literal magic. +attr;; +After `attr:` comes a space separated list of "attribute +requirements", all of which must be met in order for the +path to be considered a match; this is in addition to the +usual non-magic pathspec pattern matching. ++ +Each of the attribute requirements for the path takes one of +these forms: + +- "`ATTR`" requires that the attribute `ATTR` must be set.
As a relative newcomer to attributes, I was confused by the fact that "set" and "set to a value" is different (and likewise "unset" and "unspecified"). Maybe it's worthwhile including a link to "gitattributes" to explain the different (exclusive) states that an attribute can be in.
+ +- "`-ATTR`" requires that the attribute `ATTR` must be unset. + +- "`ATTR=VALUE`" requires that the attribute `ATTR` must be + set to the string `VALUE`. + +- "`!ATTR`" requires that the attribute `ATTR` must be + unspecified.
It would read better to me if you omitted "must" in all 4 bullet points (and it is redundant anyway with "requires"), but I don't feel too strongly about this.
diff --git a/pathspec.c b/pathspec.c index b961f00c8..583ed5208 100644 --- a/pathspec.c +++ b/pathspec.c @@ -87,6 +89,72 @@ static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic) strbuf_addf(sb, ",prefix:%d)", prefixlen); } +static void parse_pathspec_attr_match(struct pathspec_item *item, const char *value) +{ + struct string_list_item *si; + struct string_list list = STRING_LIST_INIT_DUP; + + if (item->attr_check) + die(_("Only one 'attr:' specification is allowed.")); + + if (!value || !strlen(value))
You can write `!*value` instead of `!strlen(value)`.
+ die(_("attr spec must not be empty")); + + string_list_split(&list, value, ' ', -1);
You could avoid some allocations by using the in-place variant (since value is a newly allocated string not used elsewhere) but it is probably not that important in this argument parsing case.
+ string_list_remove_empty_items(&list, 0); + + item->attr_check = attr_check_alloc(); + ALLOC_GROW(item->attr_match, + item->attr_match_nr + list.nr, + item->attr_match_alloc);
Is there a time when this function is called while item->attr_match_nr is not zero?
+ + for_each_string_list_item(si, &list) { + size_t attr_len; + char *attr_name; + const struct git_attr *a; + + int j = item->attr_match_nr++; + const char *attr = si->string; + struct attr_match *am = &item->attr_match[j]; + + switch (*attr) { + case '!': + am->match_mode = MATCH_UNSPECIFIED; + attr++; + attr_len = strlen(attr); + break; + case '-': + am->match_mode = MATCH_UNSET; + attr++; + attr_len = strlen(attr); + break; + default: + attr_len = strcspn(attr, "="); + if (attr[attr_len] != '=') + am->match_mode = MATCH_SET; + else { + am->match_mode = MATCH_VALUE; + am->value = xstrdup(&attr[attr_len + 1]); + if (strchr(am->value, '\\')) + die(_("attr spec values must not contain backslashes")); + } + break; + } + + attr_name = xmemdupz(attr, attr_len); + a = git_attr(attr_name); + if (!a) + die(_("invalid attribute name %s"), attr_name); + + attr_check_append(item->attr_check, a); + + free(attr_name); + } + + string_list_clear(&list, 0); + return;
Redundant return?
@@ -544,6 +628,10 @@ void parse_pathspec(struct pathspec *pathspec, if (item[i].nowildcard_len < item[i].len) pathspec->has_wildcard = 1; pathspec->magic |= item[i].magic; + + if (item[i].attr_check && + item[i].attr_check->nr != item[i].attr_match_nr) + die("BUG: should have same number of entries");
I'm not sure if this check is giving us any benefit - I would expect this type of code before some other code that assumed that the numbers matched, and that will potentially segfault if not.