Merge branch 'nd/exclude-workaround-top-heavy'

Attempt to optimize matching with an exclude pattern with a deep
directory hierarchy by taking the part that specifies leading path
without wildcard literally.
This commit is contained in:
Junio C Hamano 2012-06-28 15:19:57 -07:00
commit 653111f99c
2 changed files with 78 additions and 67 deletions

143
dir.c
View File

@ -288,9 +288,24 @@ int match_pathspec_depth(const struct pathspec *ps,
return retval; return retval;
} }
/*
* Return the length of the "simple" part of a path match limiter.
*/
static int simple_length(const char *match)
{
int len = -1;
for (;;) {
unsigned char c = *match++;
len++;
if (c == '\0' || is_glob_special(c))
return len;
}
}
static int no_wildcard(const char *string) static int no_wildcard(const char *string)
{ {
return string[strcspn(string, "*?[{\\")] == '\0'; return string[simple_length(string)] == '\0';
} }
void add_exclude(const char *string, const char *base, void add_exclude(const char *string, const char *base,
@ -326,8 +341,7 @@ void add_exclude(const char *string, const char *base,
x->flags = flags; x->flags = flags;
if (!strchr(string, '/')) if (!strchr(string, '/'))
x->flags |= EXC_FLAG_NODIR; x->flags |= EXC_FLAG_NODIR;
if (no_wildcard(string)) x->nowildcardlen = simple_length(string);
x->flags |= EXC_FLAG_NOWILDCARD;
if (*string == '*' && no_wildcard(string+1)) if (*string == '*' && no_wildcard(string+1))
x->flags |= EXC_FLAG_ENDSWITH; x->flags |= EXC_FLAG_ENDSWITH;
ALLOC_GROW(which->excludes, which->nr + 1, which->alloc); ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
@ -498,57 +512,69 @@ int excluded_from_list(const char *pathname,
{ {
int i; int i;
if (el->nr) { if (!el->nr)
for (i = el->nr - 1; 0 <= i; i--) { return -1; /* undefined */
struct exclude *x = el->excludes[i];
const char *exclude = x->pattern;
int to_exclude = x->to_exclude;
if (x->flags & EXC_FLAG_MUSTBEDIR) { for (i = el->nr - 1; 0 <= i; i--) {
if (*dtype == DT_UNKNOWN) struct exclude *x = el->excludes[i];
*dtype = get_dtype(NULL, pathname, pathlen); const char *name, *exclude = x->pattern;
if (*dtype != DT_DIR) int to_exclude = x->to_exclude;
continue; int namelen, prefix = x->nowildcardlen;
}
if (x->flags & EXC_FLAG_NODIR) { if (x->flags & EXC_FLAG_MUSTBEDIR) {
/* match basename */ if (*dtype == DT_UNKNOWN)
if (x->flags & EXC_FLAG_NOWILDCARD) { *dtype = get_dtype(NULL, pathname, pathlen);
if (!strcmp_icase(exclude, basename)) if (*dtype != DT_DIR)
return to_exclude; continue;
} else if (x->flags & EXC_FLAG_ENDSWITH) {
if (x->patternlen - 1 <= pathlen &&
!strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
return to_exclude;
} else {
if (fnmatch_icase(exclude, basename, 0) == 0)
return to_exclude;
}
}
else {
/* match with FNM_PATHNAME:
* exclude has base (baselen long) implicitly
* in front of it.
*/
int baselen = x->baselen;
if (*exclude == '/')
exclude++;
if (pathlen < baselen ||
(baselen && pathname[baselen-1] != '/') ||
strncmp_icase(pathname, x->base, baselen))
continue;
if (x->flags & EXC_FLAG_NOWILDCARD) {
if (!strcmp_icase(exclude, pathname + baselen))
return to_exclude;
} else {
if (fnmatch_icase(exclude, pathname+baselen,
FNM_PATHNAME) == 0)
return to_exclude;
}
}
} }
if (x->flags & EXC_FLAG_NODIR) {
/* match basename */
if (prefix == x->patternlen) {
if (!strcmp_icase(exclude, basename))
return to_exclude;
} else if (x->flags & EXC_FLAG_ENDSWITH) {
if (x->patternlen - 1 <= pathlen &&
!strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
return to_exclude;
} else {
if (fnmatch_icase(exclude, basename, 0) == 0)
return to_exclude;
}
continue;
}
/* match with FNM_PATHNAME:
* exclude has base (baselen long) implicitly in front of it.
*/
if (*exclude == '/') {
exclude++;
prefix--;
}
if (pathlen < x->baselen ||
(x->baselen && pathname[x->baselen-1] != '/') ||
strncmp_icase(pathname, x->base, x->baselen))
continue;
namelen = x->baselen ? pathlen - x->baselen : pathlen;
name = pathname + pathlen - namelen;
/* if the non-wildcard part is longer than the
remaining pathname, surely it cannot match */
if (prefix > namelen)
continue;
if (prefix) {
if (strncmp_icase(exclude, name, prefix))
continue;
exclude += prefix;
name += prefix;
namelen -= prefix;
}
if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
return to_exclude;
} }
return -1; /* undecided */ return -1; /* undecided */
} }
@ -1055,21 +1081,6 @@ static int cmp_name(const void *p1, const void *p2)
e2->name, e2->len); e2->name, e2->len);
} }
/*
* Return the length of the "simple" part of a path match limiter.
*/
static int simple_length(const char *match)
{
int len = -1;
for (;;) {
unsigned char c = *match++;
len++;
if (c == '\0' || is_glob_special(c))
return len;
}
}
static struct path_simplify *create_simplify(const char **pathspec) static struct path_simplify *create_simplify(const char **pathspec)
{ {
int nr, alloc = 0; int nr, alloc = 0;

2
dir.h
View File

@ -9,7 +9,6 @@ struct dir_entry {
}; };
#define EXC_FLAG_NODIR 1 #define EXC_FLAG_NODIR 1
#define EXC_FLAG_NOWILDCARD 2
#define EXC_FLAG_ENDSWITH 4 #define EXC_FLAG_ENDSWITH 4
#define EXC_FLAG_MUSTBEDIR 8 #define EXC_FLAG_MUSTBEDIR 8
@ -19,6 +18,7 @@ struct exclude_list {
struct exclude { struct exclude {
const char *pattern; const char *pattern;
int patternlen; int patternlen;
int nowildcardlen;
const char *base; const char *base;
int baselen; int baselen;
int to_exclude; int to_exclude;