diff options
Diffstat (limited to 'third_party/git/dir.c')
-rw-r--r-- | third_party/git/dir.c | 842 |
1 files changed, 224 insertions, 618 deletions
diff --git a/third_party/git/dir.c b/third_party/git/dir.c index 0ffb1b330245..d021c908e5d1 100644 --- a/third_party/git/dir.c +++ b/third_party/git/dir.c @@ -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 */ @@ -41,8 +43,7 @@ struct cached_dir { int nr_files; int nr_dirs; - const char *d_name; - int d_type; + struct dirent *de; const char *file; struct untracked_cache_dir *ucd; }; @@ -51,8 +52,8 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, struct index_state *istate, const char *path, int len, struct untracked_cache_dir *untracked, int check_only, int stop_at_first_file, const struct pathspec *pathspec); -static int resolve_dtype(int dtype, struct index_state *istate, - const char *path, int len); +static int get_dtype(struct dirent *de, struct index_state *istate, + const char *path, int len); int count_slashes(const char *s) { @@ -138,7 +139,7 @@ static size_t common_prefix_len(const struct pathspec *pathspec) * ":(icase)path" is treated as a pathspec full of * wildcard. In other words, only prefix is considered common * prefix. If the pathspec is abc/foo abc/bar, running in - * subdir xyz, the common prefix is still xyz, not xyz/abc as + * subdir xyz, the common prefix is still xyz, not xuz/abc as * in non-:(icase). */ GUARD_PATHSPEC(pathspec, @@ -272,30 +273,19 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat, #define DO_MATCH_EXCLUDE (1<<0) #define DO_MATCH_DIRECTORY (1<<1) -#define DO_MATCH_LEADING_PATHSPEC (1<<2) +#define DO_MATCH_SUBMODULE (1<<2) /* - * Does the given pathspec match the given name? A match is found if - * - * (1) the pathspec string is leading directory of 'name' ("RECURSIVELY"), or - * (2) the pathspec string has a leading part matching 'name' ("LEADING"), or - * (3) the pathspec string is a wildcard and matches 'name' ("WILDCARD"), or - * (4) the pathspec string is exactly the same as 'name' ("EXACT"). + * Does 'match' match the given name? + * A match is found if * - * Return value tells which case it was (1-4), or 0 when there is no match. + * (1) the 'match' string is leading directory of 'name', or + * (2) the 'match' string is a wildcard and matches 'name', or + * (3) the 'match' string is exactly the same as 'name'. * - * It may be instructive to look at a small table of concrete examples - * to understand the differences between 1, 2, and 4: + * and the return value tells which case it was. * - * Pathspecs - * | a/b | a/b/ | a/b/c - * ------+-----------+-----------+------------ - * a/b | EXACT | EXACT[1] | LEADING[2] - * Names a/b/ | RECURSIVE | EXACT | LEADING[2] - * a/b/c | RECURSIVE | RECURSIVE | EXACT - * - * [1] Only if DO_MATCH_DIRECTORY is passed; otherwise, this is NOT a match. - * [2] Only if DO_MATCH_LEADING_PATHSPEC is passed; otherwise, not a match. + * It returns 0 when there is no match. */ static int match_pathspec_item(const struct index_state *istate, const struct pathspec_item *item, int prefix, @@ -363,29 +353,21 @@ static int match_pathspec_item(const struct index_state *istate, item->nowildcard_len - prefix)) return MATCHED_FNMATCH; - /* Perform checks to see if "name" is a leading string of the pathspec */ - if (flags & DO_MATCH_LEADING_PATHSPEC) { + /* Perform checks to see if "name" is a super set of the pathspec */ + if (flags & DO_MATCH_SUBMODULE) { /* name is a literal prefix of the pathspec */ - int offset = name[namelen-1] == '/' ? 1 : 0; if ((namelen < matchlen) && - (match[namelen-offset] == '/') && + (match[namelen] == '/') && !ps_strncmp(item, match, name, namelen)) - return MATCHED_RECURSIVELY_LEADING_PATHSPEC; + return MATCHED_RECURSIVELY; - /* name doesn't match up to the first wild character */ + /* name" doesn't match up to the first wild character */ if (item->nowildcard_len < item->len && ps_strncmp(item, match, name, item->nowildcard_len - prefix)) return 0; /* - * name has no wildcard, and it didn't match as a leading - * pathspec so return. - */ - if (item->nowildcard_len == item->len) - return 0; - - /* * Here is where we would perform a wildmatch to check if * "name" can be matched as a directory (or a prefix) against * the pathspec. Since wildmatch doesn't have this capability @@ -394,7 +376,7 @@ static int match_pathspec_item(const struct index_state *istate, * The submodules themselves will be able to perform more * accurate matching to determine if the pathspec matches. */ - return MATCHED_RECURSIVELY_LEADING_PATHSPEC; + return MATCHED_RECURSIVELY; } return 0; @@ -515,7 +497,7 @@ int submodule_path_match(const struct index_state *istate, strlen(submodule_name), 0, seen, DO_MATCH_DIRECTORY | - DO_MATCH_LEADING_PATHSPEC); + DO_MATCH_SUBMODULE); return matched; } @@ -579,7 +561,7 @@ int no_wildcard(const char *string) return string[simple_length(string)] == '\0'; } -void parse_path_pattern(const char **pattern, +void parse_exclude_pattern(const char **pattern, int *patternlen, unsigned *flags, int *nowildcardlen) @@ -589,20 +571,20 @@ void parse_path_pattern(const char **pattern, *flags = 0; if (*p == '!') { - *flags |= PATTERN_FLAG_NEGATIVE; + *flags |= EXC_FLAG_NEGATIVE; p++; } len = strlen(p); if (len && p[len - 1] == '/') { len--; - *flags |= PATTERN_FLAG_MUSTBEDIR; + *flags |= EXC_FLAG_MUSTBEDIR; } for (i = 0; i < len; i++) { if (p[i] == '/') break; } if (i == len) - *flags |= PATTERN_FLAG_NODIR; + *flags |= EXC_FLAG_NODIR; *nowildcardlen = simple_length(p); /* * we should have excluded the trailing slash from 'p' too, @@ -612,261 +594,35 @@ void parse_path_pattern(const char **pattern, if (*nowildcardlen > len) *nowildcardlen = len; if (*p == '*' && no_wildcard(p + 1)) - *flags |= PATTERN_FLAG_ENDSWITH; + *flags |= EXC_FLAG_ENDSWITH; *pattern = p; *patternlen = len; } -int pl_hashmap_cmp(const void *unused_cmp_data, - const struct hashmap_entry *a, - const struct hashmap_entry *b, - const void *key) +void add_exclude(const char *string, const char *base, + int baselen, struct exclude_list *el, int srcpos) { - const struct pattern_entry *ee1 = - container_of(a, struct pattern_entry, ent); - const struct pattern_entry *ee2 = - container_of(b, struct pattern_entry, ent); - - size_t min_len = ee1->patternlen <= ee2->patternlen - ? ee1->patternlen - : ee2->patternlen; - - if (ignore_case) - return strncasecmp(ee1->pattern, ee2->pattern, min_len); - return strncmp(ee1->pattern, ee2->pattern, min_len); -} - -static char *dup_and_filter_pattern(const char *pattern) -{ - char *set, *read; - size_t count = 0; - char *result = xstrdup(pattern); - - set = result; - read = result; - - while (*read) { - /* skip escape characters (once) */ - if (*read == '\\') - read++; - - *set = *read; - - set++; - read++; - count++; - } - *set = 0; - - if (count > 2 && - *(set - 1) == '*' && - *(set - 2) == '/') - *(set - 2) = 0; - - return result; -} - -static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given) -{ - struct pattern_entry *translated; - char *truncated; - char *data = NULL; - const char *prev, *cur, *next; - - if (!pl->use_cone_patterns) - return; - - if (given->flags & PATTERN_FLAG_NEGATIVE && - given->flags & PATTERN_FLAG_MUSTBEDIR && - !strcmp(given->pattern, "/*")) { - pl->full_cone = 0; - return; - } - - if (!given->flags && !strcmp(given->pattern, "/*")) { - pl->full_cone = 1; - return; - } - - if (given->patternlen < 2 || - *given->pattern == '*' || - strstr(given->pattern, "**")) { - /* Not a cone pattern. */ - warning(_("unrecognized pattern: '%s'"), given->pattern); - goto clear_hashmaps; - } - - prev = given->pattern; - cur = given->pattern + 1; - next = given->pattern + 2; - - while (*cur) { - /* Watch for glob characters '*', '\', '[', '?' */ - if (!is_glob_special(*cur)) - goto increment; - - /* But only if *prev != '\\' */ - if (*prev == '\\') - goto increment; - - /* But allow the initial '\' */ - if (*cur == '\\' && - is_glob_special(*next)) - goto increment; - - /* But a trailing '/' then '*' is fine */ - if (*prev == '/' && - *cur == '*' && - *next == 0) - goto increment; - - /* Not a cone pattern. */ - warning(_("unrecognized pattern: '%s'"), given->pattern); - goto clear_hashmaps; - - increment: - prev++; - cur++; - next++; - } - - if (given->patternlen > 2 && - !strcmp(given->pattern + given->patternlen - 2, "/*")) { - if (!(given->flags & PATTERN_FLAG_NEGATIVE)) { - /* Not a cone pattern. */ - warning(_("unrecognized pattern: '%s'"), given->pattern); - goto clear_hashmaps; - } - - truncated = dup_and_filter_pattern(given->pattern); - - translated = xmalloc(sizeof(struct pattern_entry)); - translated->pattern = truncated; - translated->patternlen = given->patternlen - 2; - hashmap_entry_init(&translated->ent, - ignore_case ? - strihash(translated->pattern) : - strhash(translated->pattern)); - - if (!hashmap_get_entry(&pl->recursive_hashmap, - translated, ent, NULL)) { - /* We did not see the "parent" included */ - warning(_("unrecognized negative pattern: '%s'"), - given->pattern); - free(truncated); - free(translated); - goto clear_hashmaps; - } - - hashmap_add(&pl->parent_hashmap, &translated->ent); - hashmap_remove(&pl->recursive_hashmap, &translated->ent, &data); - free(data); - return; - } - - if (given->flags & PATTERN_FLAG_NEGATIVE) { - warning(_("unrecognized negative pattern: '%s'"), - given->pattern); - goto clear_hashmaps; - } - - translated = xmalloc(sizeof(struct pattern_entry)); - - translated->pattern = dup_and_filter_pattern(given->pattern); - translated->patternlen = given->patternlen; - hashmap_entry_init(&translated->ent, - ignore_case ? - strihash(translated->pattern) : - strhash(translated->pattern)); - - hashmap_add(&pl->recursive_hashmap, &translated->ent); - - if (hashmap_get_entry(&pl->parent_hashmap, translated, ent, NULL)) { - /* we already included this at the parent level */ - warning(_("your sparse-checkout file may have issues: pattern '%s' is repeated"), - given->pattern); - hashmap_remove(&pl->parent_hashmap, &translated->ent, &data); - free(data); - free(translated); - } - - return; - -clear_hashmaps: - warning(_("disabling cone pattern matching")); - hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent); - hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent); - pl->use_cone_patterns = 0; -} - -static int hashmap_contains_path(struct hashmap *map, - struct strbuf *pattern) -{ - struct pattern_entry p; - - /* Check straight mapping */ - p.pattern = pattern->buf; - p.patternlen = pattern->len; - hashmap_entry_init(&p.ent, - ignore_case ? - strihash(p.pattern) : - strhash(p.pattern)); - return !!hashmap_get_entry(map, &p, ent, NULL); -} - -int hashmap_contains_parent(struct hashmap *map, - const char *path, - struct strbuf *buffer) -{ - char *slash_pos; - - strbuf_setlen(buffer, 0); - - if (path[0] != '/') - strbuf_addch(buffer, '/'); - - strbuf_addstr(buffer, path); - - slash_pos = strrchr(buffer->buf, '/'); - - while (slash_pos > buffer->buf) { - strbuf_setlen(buffer, slash_pos - buffer->buf); - - if (hashmap_contains_path(map, buffer)) - return 1; - - slash_pos = strrchr(buffer->buf, '/'); - } - - return 0; -} - -void add_pattern(const char *string, const char *base, - int baselen, struct pattern_list *pl, int srcpos) -{ - struct path_pattern *pattern; + struct exclude *x; int patternlen; unsigned flags; int nowildcardlen; - parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen); - if (flags & PATTERN_FLAG_MUSTBEDIR) { - FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen); + parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen); + if (flags & EXC_FLAG_MUSTBEDIR) { + FLEXPTR_ALLOC_MEM(x, pattern, string, patternlen); } else { - pattern = xmalloc(sizeof(*pattern)); - pattern->pattern = string; + x = xmalloc(sizeof(*x)); + x->pattern = string; } - pattern->patternlen = patternlen; - pattern->nowildcardlen = nowildcardlen; - pattern->base = base; - pattern->baselen = baselen; - pattern->flags = flags; - pattern->srcpos = srcpos; - ALLOC_GROW(pl->patterns, pl->nr + 1, pl->alloc); - pl->patterns[pl->nr++] = pattern; - pattern->pl = pl; - - add_pattern_to_hashsets(pl, pattern); + x->patternlen = patternlen; + x->nowildcardlen = nowildcardlen; + x->base = base; + x->baselen = baselen; + x->flags = flags; + x->srcpos = srcpos; + ALLOC_GROW(el->excludes, el->nr + 1, el->alloc); + el->excludes[el->nr++] = x; + x->el = el; } static int read_skip_worktree_file_from_index(const struct index_state *istate, @@ -887,19 +643,19 @@ static int read_skip_worktree_file_from_index(const struct index_state *istate, } /* - * Frees memory within pl which was allocated for exclude patterns and - * the file buffer. Does not free pl itself. + * Frees memory within el which was allocated for exclude patterns and + * the file buffer. Does not free el itself. */ -void clear_pattern_list(struct pattern_list *pl) +void clear_exclude_list(struct exclude_list *el) { int i; - for (i = 0; i < pl->nr; i++) - free(pl->patterns[i]); - free(pl->patterns); - free(pl->filebuf); + for (i = 0; i < el->nr; i++) + free(el->excludes[i]); + free(el->excludes); + free(el->filebuf); - memset(pl, 0, sizeof(*pl)); + memset(el, 0, sizeof(*el)); } static void trim_trailing_spaces(char *buf) @@ -1006,21 +762,21 @@ static void invalidate_directory(struct untracked_cache *uc, dir->dirs[i]->recurse = 0; } -static int add_patterns_from_buffer(char *buf, size_t size, +static int add_excludes_from_buffer(char *buf, size_t size, const char *base, int baselen, - struct pattern_list *pl); + struct exclude_list *el); /* * Given a file with name "fname", read it (either from disk, or from * an index if 'istate' is non-null), parse it and store the - * exclude rules in "pl". + * exclude rules in "el". * * If "ss" is not NULL, compute SHA-1 of the exclude file and fill - * stat data from disk (only valid if add_patterns returns zero). If + * stat data from disk (only valid if add_excludes returns zero). If * ss_valid is non-zero, "ss" must contain good value as input. */ -static int add_patterns(const char *fname, const char *base, int baselen, - struct pattern_list *pl, struct index_state *istate, +static int add_excludes(const char *fname, const char *base, int baselen, + struct exclude_list *el, struct index_state *istate, struct oid_stat *oid_stat) { struct stat st; @@ -1074,31 +830,28 @@ static int add_patterns(const char *fname, const char *base, int baselen, oidcpy(&oid_stat->oid, &istate->cache[pos]->oid); else - hash_object_file(the_hash_algo, buf, size, - "blob", &oid_stat->oid); + hash_object_file(buf, size, "blob", + &oid_stat->oid); fill_stat_data(&oid_stat->stat, &st); oid_stat->valid = 1; } } - add_patterns_from_buffer(buf, size, base, baselen, pl); + add_excludes_from_buffer(buf, size, base, baselen, el); return 0; } -static int add_patterns_from_buffer(char *buf, size_t size, +static int add_excludes_from_buffer(char *buf, size_t size, const char *base, int baselen, - struct pattern_list *pl) + struct exclude_list *el) { int i, lineno = 1; char *entry; - hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0); - hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0); - - pl->filebuf = buf; + el->filebuf = buf; if (skip_utf8_bom(&buf, size)) - size -= buf - pl->filebuf; + size -= buf - el->filebuf; entry = buf; @@ -1107,7 +860,7 @@ static int add_patterns_from_buffer(char *buf, size_t size, if (entry != buf + i && entry[0] != '#') { buf[i - (i && buf[i-1] == '\r')] = 0; trim_trailing_spaces(entry); - add_pattern(entry, base, baselen, pl, lineno); + add_exclude(entry, base, baselen, el, lineno); } lineno++; entry = buf + i + 1; @@ -1116,17 +869,17 @@ static int add_patterns_from_buffer(char *buf, size_t size, return 0; } -int add_patterns_from_file_to_list(const char *fname, const char *base, - int baselen, struct pattern_list *pl, +int add_excludes_from_file_to_list(const char *fname, const char *base, + int baselen, struct exclude_list *el, struct index_state *istate) { - return add_patterns(fname, base, baselen, pl, istate, NULL); + return add_excludes(fname, base, baselen, el, istate, NULL); } -int add_patterns_from_blob_to_list( +int add_excludes_from_blob_to_list( struct object_id *oid, const char *base, int baselen, - struct pattern_list *pl) + struct exclude_list *el) { char *buf; size_t size; @@ -1136,31 +889,31 @@ int add_patterns_from_blob_to_list( if (r != 1) return r; - add_patterns_from_buffer(buf, size, base, baselen, pl); + add_excludes_from_buffer(buf, size, base, baselen, el); return 0; } -struct pattern_list *add_pattern_list(struct dir_struct *dir, +struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type, const char *src) { - struct pattern_list *pl; + struct exclude_list *el; struct exclude_list_group *group; group = &dir->exclude_list_group[group_type]; - ALLOC_GROW(group->pl, group->nr + 1, group->alloc); - pl = &group->pl[group->nr++]; - memset(pl, 0, sizeof(*pl)); - pl->src = src; - return pl; + ALLOC_GROW(group->el, group->nr + 1, group->alloc); + el = &group->el[group->nr++]; + memset(el, 0, sizeof(*el)); + el->src = src; + return el; } /* * Used to set up core.excludesfile and .git/info/exclude lists. */ -static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname, +static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname, struct oid_stat *oid_stat) { - struct pattern_list *pl; + struct exclude_list *el; /* * catch setup_standard_excludes() that's called before * dir->untracked is assigned. That function behaves @@ -1168,15 +921,15 @@ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname, */ if (!dir->untracked) dir->unmanaged_exclude_files++; - pl = add_pattern_list(dir, EXC_FILE, fname); - if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0) + el = add_exclude_list(dir, EXC_FILE, fname); + if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0) die(_("cannot use %s as an exclude file"), fname); } -void add_patterns_from_file(struct dir_struct *dir, const char *fname) +void add_excludes_from_file(struct dir_struct *dir, const char *fname) { dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */ - add_patterns_from_file_1(dir, fname, NULL); + add_excludes_from_file_1(dir, fname, NULL); } int match_basename(const char *basename, int basenamelen, @@ -1187,7 +940,7 @@ int match_basename(const char *basename, int basenamelen, if (patternlen == basenamelen && !fspathncmp(pattern, basename, basenamelen)) return 1; - } else if (flags & PATTERN_FLAG_ENDSWITH) { + } else if (flags & EXC_FLAG_ENDSWITH) { /* "*literal" matching against "fooliteral" */ if (patternlen - 1 <= basenamelen && !fspathncmp(pattern + 1, @@ -1268,138 +1021,85 @@ int match_pathname(const char *pathname, int pathlen, * any, determines the fate. Returns the exclude_list element which * matched, or NULL for undecided. */ -static struct path_pattern *last_matching_pattern_from_list(const char *pathname, +static struct exclude *last_exclude_matching_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, - struct pattern_list *pl, + struct exclude_list *el, struct index_state *istate) { - struct path_pattern *res = NULL; /* undecided */ + struct exclude *exc = NULL; /* undecided */ int i; - if (!pl->nr) + if (!el->nr) return NULL; /* undefined */ - for (i = pl->nr - 1; 0 <= i; i--) { - struct path_pattern *pattern = pl->patterns[i]; - const char *exclude = pattern->pattern; - int prefix = pattern->nowildcardlen; + for (i = el->nr - 1; 0 <= i; i--) { + struct exclude *x = el->excludes[i]; + const char *exclude = x->pattern; + int prefix = x->nowildcardlen; - if (pattern->flags & PATTERN_FLAG_MUSTBEDIR) { - *dtype = resolve_dtype(*dtype, istate, pathname, pathlen); + if (x->flags & EXC_FLAG_MUSTBEDIR) { + if (*dtype == DT_UNKNOWN) + *dtype = get_dtype(NULL, istate, pathname, pathlen); if (*dtype != DT_DIR) continue; } - if (pattern->flags & PATTERN_FLAG_NODIR) { + if (x->flags & EXC_FLAG_NODIR) { if (match_basename(basename, pathlen - (basename - pathname), - exclude, prefix, pattern->patternlen, - pattern->flags)) { - res = pattern; + exclude, prefix, x->patternlen, + x->flags)) { + exc = x; break; } continue; } - assert(pattern->baselen == 0 || - pattern->base[pattern->baselen - 1] == '/'); + assert(x->baselen == 0 || x->base[x->baselen - 1] == '/'); if (match_pathname(pathname, pathlen, - pattern->base, - pattern->baselen ? pattern->baselen - 1 : 0, - exclude, prefix, pattern->patternlen, - pattern->flags)) { - res = pattern; + x->base, x->baselen ? x->baselen - 1 : 0, + exclude, prefix, x->patternlen, x->flags)) { + exc = x; break; } } - return res; + return exc; } /* - * Scan the list of patterns to determine if the ordered list - * of patterns matches on 'pathname'. - * - * Return 1 for a match, 0 for not matched and -1 for undecided. + * Scan the list and let the last match determine the fate. + * Return 1 for exclude, 0 for include and -1 for undecided. */ -enum pattern_match_result path_matches_pattern_list( - const char *pathname, int pathlen, - const char *basename, int *dtype, - struct pattern_list *pl, - struct index_state *istate) -{ - struct path_pattern *pattern; - struct strbuf parent_pathname = STRBUF_INIT; - int result = NOT_MATCHED; - const char *slash_pos; - - if (!pl->use_cone_patterns) { - pattern = last_matching_pattern_from_list(pathname, pathlen, basename, - dtype, pl, istate); - if (pattern) { - if (pattern->flags & PATTERN_FLAG_NEGATIVE) - return NOT_MATCHED; - else - return MATCHED; - } - - return UNDECIDED; - } - - if (pl->full_cone) - return MATCHED; - - strbuf_addch(&parent_pathname, '/'); - strbuf_add(&parent_pathname, pathname, pathlen); - - if (hashmap_contains_path(&pl->recursive_hashmap, - &parent_pathname)) { - result = MATCHED_RECURSIVE; - goto done; - } - - slash_pos = strrchr(parent_pathname.buf, '/'); - - if (slash_pos == parent_pathname.buf) { - /* include every file in root */ - result = MATCHED; - goto done; - } - - strbuf_setlen(&parent_pathname, slash_pos - parent_pathname.buf); - - if (hashmap_contains_path(&pl->parent_hashmap, &parent_pathname)) { - result = MATCHED; - goto done; - } - - if (hashmap_contains_parent(&pl->recursive_hashmap, - pathname, - &parent_pathname)) - result = MATCHED_RECURSIVE; - -done: - strbuf_release(&parent_pathname); - return result; +int is_excluded_from_list(const char *pathname, + int pathlen, const char *basename, int *dtype, + struct exclude_list *el, struct index_state *istate) +{ + struct exclude *exclude; + exclude = last_exclude_matching_from_list(pathname, pathlen, basename, + dtype, el, istate); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; + return -1; /* undecided */ } -static struct path_pattern *last_matching_pattern_from_lists( - struct dir_struct *dir, struct index_state *istate, - const char *pathname, int pathlen, - const char *basename, int *dtype_p) +static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir, + struct index_state *istate, + const char *pathname, int pathlen, const char *basename, + int *dtype_p) { int i, j; struct exclude_list_group *group; - struct path_pattern *pattern; + struct exclude *exclude; for (i = EXC_CMDL; i <= EXC_FILE; i++) { group = &dir->exclude_list_group[i]; for (j = group->nr - 1; j >= 0; j--) { - pattern = last_matching_pattern_from_list( + exclude = last_exclude_matching_from_list( pathname, pathlen, basename, dtype_p, - &group->pl[j], istate); - if (pattern) - return pattern; + &group->el[j], istate); + if (exclude) + return exclude; } } return NULL; @@ -1414,7 +1114,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) { struct exclude_list_group *group; - struct pattern_list *pl; + struct exclude_list *el; struct exclude_stack *stk = NULL; struct untracked_cache_dir *untracked; int current; @@ -1430,17 +1130,17 @@ static void prep_exclude(struct dir_struct *dir, if (stk->baselen <= baselen && !strncmp(dir->basebuf.buf, base, stk->baselen)) break; - pl = &group->pl[dir->exclude_stack->exclude_ix]; + el = &group->el[dir->exclude_stack->exclude_ix]; dir->exclude_stack = stk->prev; - dir->pattern = NULL; - free((char *)pl->src); /* see strbuf_detach() below */ - clear_pattern_list(pl); + dir->exclude = NULL; + free((char *)el->src); /* see strbuf_detach() below */ + clear_exclude_list(el); free(stk); group->nr--; } /* Skip traversing into sub directories if the parent is excluded */ - if (dir->pattern) + if (dir->exclude) return; /* @@ -1481,7 +1181,7 @@ static void prep_exclude(struct dir_struct *dir, stk->baselen = cp - base; stk->exclude_ix = group->nr; stk->ucd = untracked; - pl = add_pattern_list(dir, EXC_DIRS, NULL); + el = add_exclude_list(dir, EXC_DIRS, NULL); strbuf_add(&dir->basebuf, base + current, stk->baselen - current); assert(stk->baselen == dir->basebuf.len); @@ -1489,15 +1189,15 @@ static void prep_exclude(struct dir_struct *dir, if (stk->baselen) { int dt = DT_DIR; dir->basebuf.buf[stk->baselen - 1] = 0; - dir->pattern = last_matching_pattern_from_lists(dir, + dir->exclude = last_exclude_matching_from_lists(dir, istate, dir->basebuf.buf, stk->baselen - 1, dir->basebuf.buf + current, &dt); dir->basebuf.buf[stk->baselen - 1] = '/'; - if (dir->pattern && - dir->pattern->flags & PATTERN_FLAG_NEGATIVE) - dir->pattern = NULL; - if (dir->pattern) { + if (dir->exclude && + dir->exclude->flags & EXC_FLAG_NEGATIVE) + dir->exclude = NULL; + if (dir->exclude) { dir->exclude_stack = stk; return; } @@ -1523,30 +1223,30 @@ static void prep_exclude(struct dir_struct *dir, /* * dir->basebuf gets reused by the traversal, but we * need fname to remain unchanged to ensure the src - * member of each struct path_pattern correctly + * member of each struct exclude correctly * back-references its source file. Other invocations - * of add_pattern_list provide stable strings, so we + * of add_exclude_list provide stable strings, so we * strbuf_detach() and free() here in the caller. */ struct strbuf sb = STRBUF_INIT; strbuf_addbuf(&sb, &dir->basebuf); strbuf_addstr(&sb, dir->exclude_per_dir); - pl->src = strbuf_detach(&sb, NULL); - add_patterns(pl->src, pl->src, stk->baselen, pl, istate, + el->src = strbuf_detach(&sb, NULL); + add_excludes(el->src, el->src, stk->baselen, el, istate, untracked ? &oid_stat : NULL); } /* * NEEDSWORK: when untracked cache is enabled, prep_exclude() * will first be called in valid_cached_dir() then maybe many - * times more in last_matching_pattern(). When the cache is - * used, last_matching_pattern() will not be called and + * times more in last_exclude_matching(). When the cache is + * used, last_exclude_matching() will not be called and * reading .gitignore content will be a waste. * * So when it's called by valid_cached_dir() and we can get * .gitignore SHA-1 from the index (i.e. .gitignore is not * modified on work tree), we could delay reading the * .gitignore content until we absolutely need it in - * last_matching_pattern(). Be careful about ignore rule + * last_exclude_matching(). Be careful about ignore rule * order, though, if you do that. */ if (untracked && @@ -1566,7 +1266,7 @@ static void prep_exclude(struct dir_struct *dir, * Returns the exclude_list element which matched, or NULL for * undecided. */ -struct path_pattern *last_matching_pattern(struct dir_struct *dir, +struct exclude *last_exclude_matching(struct dir_struct *dir, struct index_state *istate, const char *pathname, int *dtype_p) @@ -1577,10 +1277,10 @@ struct path_pattern *last_matching_pattern(struct dir_struct *dir, prep_exclude(dir, istate, pathname, basename-pathname); - if (dir->pattern) - return dir->pattern; + if (dir->exclude) + return dir->exclude; - return last_matching_pattern_from_lists(dir, istate, pathname, pathlen, + return last_exclude_matching_from_lists(dir, istate, pathname, pathlen, basename, dtype_p); } @@ -1592,10 +1292,10 @@ struct path_pattern *last_matching_pattern(struct dir_struct *dir, int is_excluded(struct dir_struct *dir, struct index_state *istate, const char *pathname, int *dtype_p) { - struct path_pattern *pattern = - last_matching_pattern(dir, istate, pathname, dtype_p); - if (pattern) - return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1; + struct exclude *exclude = + last_exclude_matching(dir, istate, pathname, dtype_p); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return 0; } @@ -1730,8 +1430,6 @@ static enum path_treatment treat_directory(struct dir_struct *dir, const char *dirname, int len, int baselen, int exclude, const struct pathspec *pathspec) { - int nested_repo = 0; - /* The "len-1" is to strip the final '/' */ switch (directory_exists_in_index(istate, dirname, len-1)) { case index_directory: @@ -1741,17 +1439,6 @@ static enum path_treatment treat_directory(struct dir_struct *dir, return path_none; case index_nonexistent: - if ((dir->flags & DIR_SKIP_NESTED_GIT) || - !(dir->flags & DIR_NO_GITLINKS)) { - struct strbuf sb = STRBUF_INIT; - strbuf_addstr(&sb, dirname); - nested_repo = is_nonbare_repository_dir(&sb); - strbuf_release(&sb); - } - if (nested_repo) - return ((dir->flags & DIR_SKIP_NESTED_GIT) ? path_none : - (exclude ? path_excluded : path_untracked)); - if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) break; if (exclude && @@ -1778,6 +1465,13 @@ static enum path_treatment treat_directory(struct dir_struct *dir, return path_none; } + if (!(dir->flags & DIR_NO_GITLINKS)) { + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, dirname); + if (is_nonbare_repository_dir(&sb)) + return exclude ? path_excluded : path_untracked; + strbuf_release(&sb); + } return path_recurse; } @@ -1913,9 +1607,10 @@ static int get_index_dtype(struct index_state *istate, return DT_UNKNOWN; } -static int resolve_dtype(int dtype, struct index_state *istate, - const char *path, int len) +static int get_dtype(struct dirent *de, struct index_state *istate, + const char *path, int len) { + int dtype = de ? DTYPE(de) : DT_UNKNOWN; struct stat st; if (dtype != DT_UNKNOWN) @@ -1940,13 +1635,14 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, struct strbuf *path, int baselen, const struct pathspec *pathspec, - int dtype) + int dtype, struct dirent *de) { int exclude; int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case); enum path_treatment path_treatment; - dtype = resolve_dtype(dtype, istate, path->buf, path->len); + if (dtype == DT_UNKNOWN) + dtype = get_dtype(de, istate, path->buf, path->len); /* Always exclude indexed files */ if (dtype != DT_DIR && has_path_in_index) @@ -2054,18 +1750,21 @@ static enum path_treatment treat_path(struct dir_struct *dir, int baselen, const struct pathspec *pathspec) { - if (!cdir->d_name) + int dtype; + struct dirent *de = cdir->de; + + if (!de) return treat_path_fast(dir, untracked, cdir, istate, path, baselen, pathspec); - if (is_dot_or_dotdot(cdir->d_name) || !fspathcmp(cdir->d_name, ".git")) + if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git")) return path_none; strbuf_setlen(path, baselen); - strbuf_addstr(path, cdir->d_name); + strbuf_addstr(path, de->d_name); if (simplify_away(path->buf, path->len, pathspec)) return path_none; - return treat_one_path(dir, untracked, istate, path, baselen, pathspec, - cdir->d_type); + dtype = DTYPE(de); + return treat_one_path(dir, untracked, istate, path, baselen, pathspec, dtype, de); } static void add_untracked(struct untracked_cache_dir *dir, const char *name) @@ -2109,7 +1808,7 @@ static int valid_cached_dir(struct dir_struct *dir, /* * prep_exclude will be called eventually on this directory, - * but it's called much later in last_matching_pattern(). We + * but it's called much later in last_exclude_matching(). We * need it now to determine the validity of the cache for this * path. The next calls will be nearly no-op, the way * prep_exclude() is designed. @@ -2153,17 +1852,10 @@ static int open_cached_dir(struct cached_dir *cdir, static int read_cached_dir(struct cached_dir *cdir) { - struct dirent *de; - if (cdir->fdir) { - de = readdir(cdir->fdir); - if (!de) { - cdir->d_name = NULL; - cdir->d_type = DT_UNKNOWN; + cdir->de = readdir(cdir->fdir); + if (!cdir->de) return -1; - } - cdir->d_name = de->d_name; - cdir->d_type = DTYPE(de); return 0; } while (cdir->nr_dirs < cdir->untracked->dirs_nr) { @@ -2199,40 +1891,6 @@ static void close_cached_dir(struct cached_dir *cdir) } } -static void add_path_to_appropriate_result_list(struct dir_struct *dir, - struct untracked_cache_dir *untracked, - struct cached_dir *cdir, - struct index_state *istate, - struct strbuf *path, - int baselen, - const struct pathspec *pathspec, - enum path_treatment state) -{ - /* add the path to the appropriate result list */ - switch (state) { - case path_excluded: - if (dir->flags & DIR_SHOW_IGNORED) - dir_add_name(dir, istate, path->buf, path->len); - else if ((dir->flags & DIR_SHOW_IGNORED_TOO) || - ((dir->flags & DIR_COLLECT_IGNORED) && - exclude_matches_pathspec(path->buf, path->len, - pathspec))) - dir_add_ignored(dir, istate, path->buf, path->len); - break; - - case path_untracked: - if (dir->flags & DIR_SHOW_IGNORED) - break; - dir_add_name(dir, istate, path->buf, path->len); - if (cdir->fdir) - add_untracked(untracked, path->buf + baselen); - break; - - default: - break; - } -} - /* * Read a directory tree. We currently ignore anything but * directories, regular files and symlinks. That's because git @@ -2257,15 +1915,6 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, struct untracked_cache_dir *untracked, int check_only, int stop_at_first_file, const struct pathspec *pathspec) { - /* - * WARNING WARNING WARNING: - * - * Any updates to the traversal logic here may need corresponding - * updates in treat_leading_path(). See the commit message for the - * commit adding this warning as well as the commit preceding it - * for details. - */ - struct cached_dir cdir; enum path_treatment state, subdir_state, dir_state = path_none; struct strbuf path = STRBUF_INIT; @@ -2289,11 +1938,8 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, /* recurse into subdir if instructed by treat_path */ if ((state == path_recurse) || ((state == path_untracked) && - (resolve_dtype(cdir.d_type, istate, path.buf, path.len) == DT_DIR) && - ((dir->flags & DIR_SHOW_IGNORED_TOO) || - (pathspec && - do_match_pathspec(istate, pathspec, path.buf, path.len, - baselen, NULL, DO_MATCH_LEADING_PATHSPEC) == MATCHED_RECURSIVELY_LEADING_PATHSPEC)))) { + (dir->flags & DIR_SHOW_IGNORED_TOO) && + (get_dtype(cdir.de, istate, path.buf, path.len) == DT_DIR))) { struct untracked_cache_dir *ud; ud = lookup_untracked(dir->untracked, untracked, path.buf + baselen, @@ -2304,12 +1950,6 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, check_only, stop_at_first_file, pathspec); if (subdir_state > dir_state) dir_state = subdir_state; - - if (pathspec && - !match_pathspec(istate, pathspec, path.buf, path.len, - 0 /* prefix */, NULL, - 0 /* do NOT special case dirs */)) - state = path_none; } if (check_only) { @@ -2345,9 +1985,29 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, continue; } - add_path_to_appropriate_result_list(dir, untracked, &cdir, - istate, &path, baselen, - pathspec, state); + /* add the path to the appropriate result list */ + switch (state) { + case path_excluded: + if (dir->flags & DIR_SHOW_IGNORED) + dir_add_name(dir, istate, path.buf, path.len); + else if ((dir->flags & DIR_SHOW_IGNORED_TOO) || + ((dir->flags & DIR_COLLECT_IGNORED) && + exclude_matches_pathspec(path.buf, path.len, + pathspec))) + dir_add_ignored(dir, istate, path.buf, path.len); + break; + + case path_untracked: + if (dir->flags & DIR_SHOW_IGNORED) + break; + dir_add_name(dir, istate, path.buf, path.len); + if (cdir.fdir) + add_untracked(untracked, path.buf + baselen); + break; + + default: + break; + } } close_cached_dir(&cdir); out: @@ -2377,95 +2037,41 @@ static int treat_leading_path(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec) { - /* - * WARNING WARNING WARNING: - * - * Any updates to the traversal logic here may need corresponding - * updates in read_directory_recursive(). See 777b420347 (dir: - * synchronize treat_leading_path() and read_directory_recursive(), - * 2019-12-19) and its parent commit for details. - */ - struct strbuf sb = STRBUF_INIT; - struct strbuf subdir = STRBUF_INIT; - int prevlen, baselen; + int baselen, rc = 0; const char *cp; - struct cached_dir cdir; - enum path_treatment state = path_none; - - /* - * For each directory component of path, we are going to check whether - * that path is relevant given the pathspec. For example, if path is - * foo/bar/baz/ - * then we will ask treat_path() whether we should go into foo, then - * whether we should go into bar, then whether baz is relevant. - * Checking each is important because e.g. if path is - * .git/info/ - * then we need to check .git to know we shouldn't traverse it. - * If the return from treat_path() is: - * * path_none, for any path, we return false. - * * path_recurse, for all path components, we return true - * * <anything else> for some intermediate component, we make sure - * to add that path to the relevant list but return false - * signifying that we shouldn't recurse into it. - */ + int old_flags = dir->flags; while (len && path[len - 1] == '/') len--; if (!len) return 1; - - memset(&cdir, 0, sizeof(cdir)); - cdir.d_type = DT_DIR; baselen = 0; - prevlen = 0; + dir->flags &= ~DIR_SHOW_OTHER_DIRECTORIES; while (1) { - prevlen = baselen + !!baselen; - cp = path + prevlen; + cp = path + baselen + !!baselen; cp = memchr(cp, '/', path + len - cp); if (!cp) baselen = len; else baselen = cp - path; - strbuf_reset(&sb); + strbuf_setlen(&sb, 0); strbuf_add(&sb, path, baselen); if (!is_directory(sb.buf)) break; - strbuf_reset(&sb); - strbuf_add(&sb, path, prevlen); - strbuf_reset(&subdir); - strbuf_add(&subdir, path+prevlen, baselen-prevlen); - cdir.d_name = subdir.buf; - state = treat_path(dir, NULL, &cdir, istate, &sb, prevlen, - pathspec); - if (state == path_untracked && - resolve_dtype(cdir.d_type, istate, sb.buf, sb.len) == DT_DIR && - (dir->flags & DIR_SHOW_IGNORED_TOO || - do_match_pathspec(istate, pathspec, sb.buf, sb.len, - baselen, NULL, DO_MATCH_LEADING_PATHSPEC) == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) { - if (!match_pathspec(istate, pathspec, sb.buf, sb.len, - 0 /* prefix */, NULL, - 0 /* do NOT special case dirs */)) - state = path_none; - add_path_to_appropriate_result_list(dir, NULL, &cdir, - istate, - &sb, baselen, - pathspec, state); - state = path_recurse; - } - - if (state != path_recurse) + if (simplify_away(sb.buf, sb.len, pathspec)) + break; + if (treat_one_path(dir, NULL, istate, &sb, baselen, pathspec, + DT_DIR, NULL) == path_none) break; /* do not recurse into it */ - if (len <= baselen) + if (len <= baselen) { + rc = 1; break; /* finished checking */ + } } - add_path_to_appropriate_result_list(dir, NULL, &cdir, istate, - &sb, baselen, pathspec, - state); - - strbuf_release(&subdir); strbuf_release(&sb); - return state == path_recurse; + dir->flags = old_flags; + return rc; } static const char *get_ident_string(void) @@ -2840,7 +2446,7 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up) * wanted anyway */ continue; - /* fall through */ + /* fall thru */ } else if (S_ISDIR(st.st_mode)) { if (!remove_dir_recurse(path, flag, &kept_down)) continue; /* happy */ @@ -2882,14 +2488,14 @@ void setup_standard_excludes(struct dir_struct *dir) if (!excludes_file) excludes_file = xdg_config_home("ignore"); if (excludes_file && !access_or_warn(excludes_file, R_OK, 0)) - add_patterns_from_file_1(dir, excludes_file, + add_excludes_from_file_1(dir, excludes_file, dir->untracked ? &dir->ss_excludes_file : NULL); /* per repository user preference */ if (startup_info->have_repository) { const char *path = git_path_info_exclude(); if (!access_or_warn(path, R_OK, 0)) - add_patterns_from_file_1(dir, path, + add_excludes_from_file_1(dir, path, dir->untracked ? &dir->ss_info_exclude : NULL); } } @@ -2921,18 +2527,18 @@ void clear_directory(struct dir_struct *dir) { int i, j; struct exclude_list_group *group; - struct pattern_list *pl; + struct exclude_list *el; struct exclude_stack *stk; for (i = EXC_CMDL; i <= EXC_FILE; i++) { group = &dir->exclude_list_group[i]; for (j = 0; j < group->nr; j++) { - pl = &group->pl[j]; + el = &group->el[j]; if (i == EXC_DIRS) - free((char *)pl->src); - clear_pattern_list(pl); + free((char *)el->src); + clear_exclude_list(el); } - free(group->pl); + free(group->el); } stk = dir->exclude_stack; |