|
11 | 11 | #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
|
12 | 12 |
|
13 | 13 | /**
|
14 |
| - * A negative ignore pattern can match a positive one without |
15 |
| - * wildcards if its pattern equals the tail of the positive |
16 |
| - * pattern. Thus |
| 14 | + * A negative ignore pattern can negate a positive one without |
| 15 | + * wildcards if it is a basename only and equals the basename of |
| 16 | + * the positive pattern. Thus |
17 | 17 | *
|
18 | 18 | * foo/bar
|
19 | 19 | * !bar
|
20 | 20 | *
|
21 |
| - * would result in foo/bar being unignored again. |
| 21 | + * would result in foo/bar being unignored again while |
| 22 | + * |
| 23 | + * moo/foo/bar |
| 24 | + * !foo/bar |
| 25 | + * |
| 26 | + * would do nothing. The reverse also holds true: a positive |
| 27 | + * basename pattern can be negated by unignoring the basename in |
| 28 | + * subdirectories. Thus |
| 29 | + * |
| 30 | + * bar |
| 31 | + * !foo/bar |
| 32 | + * |
| 33 | + * would result in foo/bar being unignored again. As with the |
| 34 | + * first case, |
| 35 | + * |
| 36 | + * foo/bar |
| 37 | + * !moo/foo/bar |
| 38 | + * |
| 39 | + * would do nothing, again. |
22 | 40 | */
|
23 | 41 | static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
|
24 | 42 | {
|
| 43 | + git_attr_fnmatch *longer, *shorter; |
25 | 44 | char *p;
|
26 | 45 |
|
27 | 46 | if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
|
28 | 47 | && (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
|
29 |
| - /* |
30 |
| - * no chance of matching if rule is shorter than |
31 |
| - * the negated one |
32 |
| - */ |
33 |
| - if (rule->length < neg->length) |
| 48 | + |
| 49 | + /* If lengths match we need to have an exact match */ |
| 50 | + if (rule->length == neg->length) { |
| 51 | + return strcmp(rule->pattern, neg->pattern) == 0; |
| 52 | + } else if (rule->length < neg->length) { |
| 53 | + shorter = rule; |
| 54 | + longer = neg; |
| 55 | + } else { |
| 56 | + shorter = neg; |
| 57 | + longer = rule; |
| 58 | + } |
| 59 | + |
| 60 | + /* Otherwise, we need to check if the shorter |
| 61 | + * rule is a basename only (that is, it contains |
| 62 | + * no path separator) and, if so, if it |
| 63 | + * matches the tail of the longer rule */ |
| 64 | + p = longer->pattern + longer->length - shorter->length; |
| 65 | + |
| 66 | + if (p[-1] != '/') |
| 67 | + return false; |
| 68 | + if (memchr(shorter->pattern, '/', shorter->length) != NULL) |
34 | 69 | return false;
|
35 | 70 |
|
36 |
| - /* |
37 |
| - * shift pattern so its tail aligns with the |
38 |
| - * negated pattern |
39 |
| - */ |
40 |
| - p = rule->pattern + rule->length - neg->length; |
41 |
| - if (strcmp(p, neg->pattern) == 0) |
42 |
| - return true; |
| 71 | + return memcmp(p, shorter->pattern, shorter->length) == 0; |
43 | 72 | }
|
44 | 73 |
|
45 | 74 | return false;
|
|
0 commit comments