4848 */
4949static int does_negate_pattern (git_attr_fnmatch * rule , git_attr_fnmatch * neg )
5050{
51+ int (* cmp )(const char * , const char * , size_t );
5152 git_attr_fnmatch * longer , * shorter ;
5253 char * p ;
5354
54- if ((rule -> flags & GIT_ATTR_FNMATCH_NEGATIVE ) == 0
55- && (neg -> flags & GIT_ATTR_FNMATCH_NEGATIVE ) != 0 ) {
56-
57- /* If lengths match we need to have an exact match */
58- if (rule -> length == neg -> length ) {
59- return strcmp (rule -> pattern , neg -> pattern ) == 0 ;
60- } else if (rule -> length < neg -> length ) {
61- shorter = rule ;
62- longer = neg ;
63- } else {
64- shorter = neg ;
65- longer = rule ;
66- }
55+ if ((rule -> flags & GIT_ATTR_FNMATCH_NEGATIVE ) != 0
56+ || (neg -> flags & GIT_ATTR_FNMATCH_NEGATIVE ) == 0 )
57+ return false;
58+
59+ if (neg -> flags & GIT_ATTR_FNMATCH_ICASE )
60+ cmp = git__strncasecmp ;
61+ else
62+ cmp = strncmp ;
63+
64+ /* If lengths match we need to have an exact match */
65+ if (rule -> length == neg -> length ) {
66+ return cmp (rule -> pattern , neg -> pattern , rule -> length ) == 0 ;
67+ } else if (rule -> length < neg -> length ) {
68+ shorter = rule ;
69+ longer = neg ;
70+ } else {
71+ shorter = neg ;
72+ longer = rule ;
73+ }
6774
68- /* Otherwise, we need to check if the shorter
69- * rule is a basename only (that is, it contains
70- * no path separator) and, if so, if it
71- * matches the tail of the longer rule */
72- p = longer -> pattern + longer -> length - shorter -> length ;
75+ /* Otherwise, we need to check if the shorter
76+ * rule is a basename only (that is, it contains
77+ * no path separator) and, if so, if it
78+ * matches the tail of the longer rule */
79+ p = longer -> pattern + longer -> length - shorter -> length ;
7380
74- if (p [-1 ] != '/' )
75- return false;
76- if (memchr (shorter -> pattern , '/' , shorter -> length ) != NULL )
77- return false;
81+ if (p [-1 ] != '/' )
82+ return false;
83+ if (memchr (shorter -> pattern , '/' , shorter -> length ) != NULL )
84+ return false;
7885
79- return memcmp (p , shorter -> pattern , shorter -> length ) == 0 ;
80- }
81-
82- return false;
86+ return cmp (p , shorter -> pattern , shorter -> length ) == 0 ;
8387}
8488
8589/**
@@ -97,14 +101,18 @@ static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
97101 */
98102static int does_negate_rule (int * out , git_vector * rules , git_attr_fnmatch * match )
99103{
100- int error = 0 ;
104+ int error = 0 , fnflags ;
101105 size_t i ;
102106 git_attr_fnmatch * rule ;
103107 char * path ;
104108 git_buf buf = GIT_BUF_INIT ;
105109
106110 * out = 0 ;
107111
112+ fnflags = FNM_PATHNAME ;
113+ if (match -> flags & GIT_ATTR_FNMATCH_ICASE )
114+ fnflags |= FNM_IGNORECASE ;
115+
108116 /* path of the file relative to the workdir, so we match the rules in subdirs */
109117 if (match -> containing_dir ) {
110118 git_buf_puts (& buf , match -> containing_dir );
@@ -125,12 +133,12 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
125133 continue ;
126134 }
127135
128- /*
129- * When dealing with a directory, we add '/<star>' so
130- * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR
131- * alone isn't enough as that's also set for nagations, so we
132- * need to check that NEGATIVE is off.
133- */
136+ /*
137+ * When dealing with a directory, we add '/<star>' so
138+ * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR
139+ * alone isn't enough as that's also set for nagations, so we
140+ * need to check that NEGATIVE is off.
141+ */
134142 git_buf_clear (& buf );
135143 if (rule -> containing_dir ) {
136144 git_buf_puts (& buf , rule -> containing_dir );
@@ -144,7 +152,7 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
144152 if (error < 0 )
145153 goto out ;
146154
147- if ((error = p_fnmatch (git_buf_cstr (& buf ), path , FNM_PATHNAME )) < 0 ) {
155+ if ((error = p_fnmatch (git_buf_cstr (& buf ), path , fnflags )) < 0 ) {
148156 giterr_set (GITERR_INVALID , "error matching pattern" );
149157 goto out ;
150158 }
@@ -207,8 +215,14 @@ static int parse_ignore_file(
207215
208216 scan = git__next_line (scan );
209217
210- /* if a negative match doesn't actually do anything, throw it away */
211- if (match -> flags & GIT_ATTR_FNMATCH_NEGATIVE )
218+ /*
219+ * If a negative match doesn't actually do anything,
220+ * throw it away. As we cannot always verify whether a
221+ * rule containing wildcards negates another rule, we
222+ * do not optimize away these rules, though.
223+ * */
224+ if (match -> flags & GIT_ATTR_FNMATCH_NEGATIVE
225+ && !(match -> flags & GIT_ATTR_FNMATCH_HASWILD ))
212226 error = does_negate_rule (& valid_rule , & attrs -> rules , match );
213227
214228 if (!error && valid_rule )
0 commit comments