diff options
Diffstat (limited to 'dir.c')
-rw-r--r-- | dir.c | 149 |
1 files changed, 114 insertions, 35 deletions
@@ -2,6 +2,8 @@ * This handles recursive filename detection with exclude * files, index knowledge etc.. * + * See Documentation/technical/api-directory-listing.txt + * * Copyright (C) Linus Torvalds, 2005-2006 * Junio Hamano, 2005-2006 */ @@ -377,7 +379,7 @@ void parse_exclude_pattern(const char **pattern, } void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *which) + int baselen, struct exclude_list *el) { struct exclude *x; int patternlen; @@ -401,8 +403,8 @@ void add_exclude(const char *string, const char *base, x->base = base; x->baselen = baselen; x->flags = flags; - ALLOC_GROW(which->excludes, which->nr + 1, which->alloc); - which->excludes[which->nr++] = x; + ALLOC_GROW(el->excludes, el->nr + 1, el->alloc); + el->excludes[el->nr++] = x; } static void *read_skip_worktree_file_from_index(const char *path, size_t *size) @@ -428,7 +430,11 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size) return data; } -void free_excludes(struct exclude_list *el) +/* + * Frees memory within el which was allocated for exclude patterns and + * the file buffer. Does not free el itself. + */ +void clear_exclude_list(struct exclude_list *el) { int i; @@ -444,7 +450,7 @@ int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, char **buf_p, - struct exclude_list *which, + struct exclude_list *el, int check_index) { struct stat st; @@ -493,7 +499,7 @@ int add_excludes_from_file_to_list(const char *fname, if (buf[i] == '\n') { if (entry != buf + i && entry[0] != '#') { buf[i - (i && buf[i-1] == '\r')] = 0; - add_exclude(entry, base, baselen, which); + add_exclude(entry, base, baselen, el); } entry = buf + i + 1; } @@ -508,6 +514,10 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname) die("cannot use %s as an exclude file", fname); } +/* + * Loads the per-directory exclude list for the substring of base + * which has a char length of baselen. + */ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) { struct exclude_list *el; @@ -518,7 +528,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX)) return; /* too long a path -- ignore */ - /* Pop the ones that are not the prefix of the path being checked. */ + /* Pop the directories that are not the prefix of the path being checked. */ el = &dir->exclude_list[EXC_DIRS]; while ((stk = dir->exclude_stack) != NULL) { if (stk->baselen <= baselen && @@ -629,22 +639,26 @@ int match_pathname(const char *pathname, int pathlen, ignore_case ? FNM_CASEFOLD : 0) == 0; } -/* Scan the list and let the last match determine the fate. - * Return 1 for exclude, 0 for include and -1 for undecided. +/* + * Scan the given exclude list in reverse to see whether pathname + * should be ignored. The first match (i.e. the last on the list), if + * any, determines the fate. Returns the exclude_list element which + * matched, or NULL for undecided. */ -int excluded_from_list(const char *pathname, - int pathlen, const char *basename, int *dtype, - struct exclude_list *el) +static struct exclude *last_exclude_matching_from_list(const char *pathname, + int pathlen, + const char *basename, + int *dtype, + struct exclude_list *el) { int i; if (!el->nr) - return -1; /* undefined */ + return NULL; /* undefined */ for (i = el->nr - 1; 0 <= i; i--) { struct exclude *x = el->excludes[i]; const char *exclude = x->pattern; - int to_exclude = x->flags & EXC_FLAG_NEGATIVE ? 0 : 1; int prefix = x->nowildcardlen; if (x->flags & EXC_FLAG_MUSTBEDIR) { @@ -659,7 +673,7 @@ int excluded_from_list(const char *pathname, pathlen - (basename - pathname), exclude, prefix, x->patternlen, x->flags)) - return to_exclude; + return x; continue; } @@ -667,28 +681,64 @@ int excluded_from_list(const char *pathname, if (match_pathname(pathname, pathlen, x->base, x->baselen ? x->baselen - 1 : 0, exclude, prefix, x->patternlen, x->flags)) - return to_exclude; + return x; } + return NULL; /* undecided */ +} + +/* + * Scan the list and let the last match determine the fate. + * Return 1 for exclude, 0 for include and -1 for undecided. + */ +int is_excluded_from_list(const char *pathname, + int pathlen, const char *basename, int *dtype, + struct exclude_list *el) +{ + struct exclude *exclude; + exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return -1; /* undecided */ } -static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) +/* + * Loads the exclude lists for the directory containing pathname, then + * scans all exclude lists to determine whether pathname is excluded. + * Returns the exclude_list element which matched, or NULL for + * undecided. + */ +static struct exclude *last_exclude_matching(struct dir_struct *dir, + const char *pathname, + int *dtype_p) { int pathlen = strlen(pathname); int st; + struct exclude *exclude; const char *basename = strrchr(pathname, '/'); basename = (basename) ? basename+1 : pathname; prep_exclude(dir, pathname, basename-pathname); for (st = EXC_CMDL; st <= EXC_FILE; st++) { - switch (excluded_from_list(pathname, pathlen, basename, - dtype_p, &dir->exclude_list[st])) { - case 0: - return 0; - case 1: - return 1; - } + exclude = last_exclude_matching_from_list( + pathname, pathlen, basename, dtype_p, + &dir->exclude_list[st]); + if (exclude) + return exclude; } + return NULL; +} + +/* + * Loads the exclude lists for the directory containing pathname, then + * scans all exclude lists to determine whether pathname is excluded. + * Returns 1 if true, otherwise 0. + */ +static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) +{ + struct exclude *exclude = + last_exclude_matching(dir, pathname, dtype_p); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return 0; } @@ -696,6 +746,7 @@ void path_exclude_check_init(struct path_exclude_check *check, struct dir_struct *dir) { check->dir = dir; + check->exclude = NULL; strbuf_init(&check->path, 256); } @@ -705,32 +756,41 @@ void path_exclude_check_clear(struct path_exclude_check *check) } /* - * Is this name excluded? This is for a caller like show_files() that - * do not honor directory hierarchy and iterate through paths that are - * possibly in an ignored directory. + * For each subdirectory in name, starting with the top-most, checks + * to see if that subdirectory is excluded, and if so, returns the + * corresponding exclude structure. Otherwise, checks whether name + * itself (which is presumably a file) is excluded. * * A path to a directory known to be excluded is left in check->path to * optimize for repeated checks for files in the same excluded directory. */ -int path_excluded(struct path_exclude_check *check, - const char *name, int namelen, int *dtype) +struct exclude *last_exclude_matching_path(struct path_exclude_check *check, + const char *name, int namelen, + int *dtype) { int i; struct strbuf *path = &check->path; + struct exclude *exclude; /* * we allow the caller to pass namelen as an optimization; it * must match the length of the name, as we eventually call - * excluded() on the whole name string. + * is_excluded() on the whole name string. */ if (namelen < 0) namelen = strlen(name); + /* + * If path is non-empty, and name is equal to path or a + * subdirectory of path, name should be excluded, because + * it's inside a directory which is already known to be + * excluded and was previously left in check->path. + */ if (path->len && path->len <= namelen && !memcmp(name, path->buf, path->len) && (!name[path->len] || name[path->len] == '/')) - return 1; + return check->exclude; strbuf_setlen(path, 0); for (i = 0; name[i]; i++) { @@ -738,8 +798,12 @@ int path_excluded(struct path_exclude_check *check, if (ch == '/') { int dt = DT_DIR; - if (excluded(check->dir, path->buf, &dt)) - return 1; + exclude = last_exclude_matching(check->dir, + path->buf, &dt); + if (exclude) { + check->exclude = exclude; + return exclude; + } } strbuf_addch(path, ch); } @@ -747,7 +811,22 @@ int path_excluded(struct path_exclude_check *check, /* An entry in the index; cannot be a directory with subentries */ strbuf_setlen(path, 0); - return excluded(check->dir, name, dtype); + return last_exclude_matching(check->dir, name, dtype); +} + +/* + * Is this name excluded? This is for a caller like show_files() that + * do not honor directory hierarchy and iterate through paths that are + * possibly in an ignored directory. + */ +int is_path_excluded(struct path_exclude_check *check, + const char *name, int namelen, int *dtype) +{ + struct exclude *exclude = + last_exclude_matching_path(check, name, namelen, dtype); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; + return 0; } static struct dir_entry *dir_entry_new(const char *pathname, int len) @@ -1047,7 +1126,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, const struct path_simplify *simplify, int dtype, struct dirent *de) { - int exclude = excluded(dir, path->buf, &dtype); + int exclude = is_excluded(dir, path->buf, &dtype); if (exclude && (dir->flags & DIR_COLLECT_IGNORED) && exclude_matches_pathspec(path->buf, path->len, simplify)) dir_add_ignored(dir, path->buf, path->len); |