@@ -118,14 +118,32 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
118118 return retval ;
119119}
120120
121+ static int no_wildcard (const char * string )
122+ {
123+ return string [strcspn (string , "*?[{" )] == '\0' ;
124+ }
125+
121126void add_exclude (const char * string , const char * base ,
122127 int baselen , struct exclude_list * which )
123128{
124129 struct exclude * x = xmalloc (sizeof (* x ));
125130
131+ x -> to_exclude = 1 ;
132+ if (* string == '!' ) {
133+ x -> to_exclude = 0 ;
134+ string ++ ;
135+ }
126136 x -> pattern = string ;
137+ x -> patternlen = strlen (string );
127138 x -> base = base ;
128139 x -> baselen = baselen ;
140+ x -> flags = 0 ;
141+ if (!strchr (string , '/' ))
142+ x -> flags |= EXC_FLAG_NODIR ;
143+ if (no_wildcard (string ))
144+ x -> flags |= EXC_FLAG_NOWILDCARD ;
145+ if (* string == '*' && no_wildcard (string + 1 ))
146+ x -> flags |= EXC_FLAG_ENDSWITH ;
129147 if (which -> nr == which -> alloc ) {
130148 which -> alloc = alloc_nr (which -> alloc );
131149 which -> excludes = xrealloc (which -> excludes ,
@@ -209,7 +227,7 @@ void pop_exclude_per_directory(struct dir_struct *dir, int stk)
209227 * Return 1 for exclude, 0 for include and -1 for undecided.
210228 */
211229static int excluded_1 (const char * pathname ,
212- int pathlen ,
230+ int pathlen , const char * basename ,
213231 struct exclude_list * el )
214232{
215233 int i ;
@@ -218,19 +236,21 @@ static int excluded_1(const char *pathname,
218236 for (i = el -> nr - 1 ; 0 <= i ; i -- ) {
219237 struct exclude * x = el -> excludes [i ];
220238 const char * exclude = x -> pattern ;
221- int to_exclude = 1 ;
239+ int to_exclude = x -> to_exclude ;
222240
223- if (* exclude == '!' ) {
224- to_exclude = 0 ;
225- exclude ++ ;
226- }
227-
228- if (!strchr (exclude , '/' )) {
241+ if (x -> flags & EXC_FLAG_NODIR ) {
229242 /* match basename */
230- const char * basename = strrchr (pathname , '/' );
231- basename = (basename ) ? basename + 1 : pathname ;
232- if (fnmatch (exclude , basename , 0 ) == 0 )
233- return to_exclude ;
243+ if (x -> flags & EXC_FLAG_NOWILDCARD ) {
244+ if (!strcmp (exclude , basename ))
245+ return to_exclude ;
246+ } else if (x -> flags & EXC_FLAG_ENDSWITH ) {
247+ if (x -> patternlen - 1 <= pathlen &&
248+ !strcmp (exclude + 1 , pathname + pathlen - x -> patternlen + 1 ))
249+ return to_exclude ;
250+ } else {
251+ if (fnmatch (exclude , basename , 0 ) == 0 )
252+ return to_exclude ;
253+ }
234254 }
235255 else {
236256 /* match with FNM_PATHNAME:
@@ -246,9 +266,14 @@ static int excluded_1(const char *pathname,
246266 strncmp (pathname , x -> base , baselen ))
247267 continue ;
248268
249- if (fnmatch (exclude , pathname + baselen ,
250- FNM_PATHNAME ) == 0 )
251- return to_exclude ;
269+ if (x -> flags & EXC_FLAG_NOWILDCARD ) {
270+ if (!strcmp (exclude , pathname + baselen ))
271+ return to_exclude ;
272+ } else {
273+ if (fnmatch (exclude , pathname + baselen ,
274+ FNM_PATHNAME ) == 0 )
275+ return to_exclude ;
276+ }
252277 }
253278 }
254279 }
@@ -259,9 +284,11 @@ int excluded(struct dir_struct *dir, const char *pathname)
259284{
260285 int pathlen = strlen (pathname );
261286 int st ;
287+ const char * basename = strrchr (pathname , '/' );
288+ basename = (basename ) ? basename + 1 : pathname ;
262289
263290 for (st = EXC_CMDL ; st <= EXC_FILE ; st ++ ) {
264- switch (excluded_1 (pathname , pathlen , & dir -> exclude_list [st ])) {
291+ switch (excluded_1 (pathname , pathlen , basename , & dir -> exclude_list [st ])) {
265292 case 0 :
266293 return 0 ;
267294 case 1 :
0 commit comments