Merge branch 'ds/include-exclude'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 Sep 2019 04:19:31 +0000 (13:19 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 Sep 2019 04:19:32 +0000 (13:19 +0900)
The internal code originally invented for ".gitignore" processing
got reshuffled and renamed to make it less tied to "excluding" and
stress more that it is about "matching", as it has been reused for
things like sparse checkout specification that want to check if a
path is "included".

* ds/include-exclude:
unpack-trees: rename 'is_excluded_from_list()'
treewide: rename 'exclude' methods to 'pattern'
treewide: rename 'EXCL_FLAG_' to 'PATTERN_FLAG_'
treewide: rename 'struct exclude_list' to 'struct pattern_list'
treewide: rename 'struct exclude' to 'struct path_pattern'

1  2 
builtin/clean.c
builtin/ls-files.c
dir.c
unpack-trees.c
diff --combined builtin/clean.c
index d5579da716e06f372132f827852b0f49f1e4af1d,4920c925938c7e38b65db840c91044fe3cbb30b7..851beb7f0debb4322122e9aa40aa642fe4ff9be2
@@@ -34,7 -34,6 +34,7 @@@ static const char *msg_would_remove = N
  static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
  static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
  static const char *msg_warn_remove_failed = N_("failed to remove %s");
 +static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
  
  enum color_clean {
        CLEAN_COLOR_RESET = 0,
@@@ -195,7 -194,7 +195,7 @@@ static int remove_dirs(struct strbuf *p
                strbuf_setlen(path, len);
                strbuf_addstr(path, e->d_name);
                if (lstat(path->buf, &st))
 -                      ; /* fall thru */
 +                      warning_errno(_(msg_warn_lstat_failed), path->buf);
                else if (S_ISDIR(st.st_mode)) {
                        if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
                                ret = 1;
@@@ -648,7 -647,7 +648,7 @@@ static int filter_by_patterns_cmd(void
        struct strbuf confirm = STRBUF_INIT;
        struct strbuf **ignore_list;
        struct string_list_item *item;
-       struct exclude_list *el;
+       struct pattern_list *pl;
        int changed = -1, i;
  
        for (;;) {
                        break;
  
                memset(&dir, 0, sizeof(dir));
-               el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
+               pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude");
                ignore_list = strbuf_split_max(&confirm, ' ', 0);
  
                for (i = 0; ignore_list[i]; i++) {
                        if (!ignore_list[i]->len)
                                continue;
  
-                       add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
+                       add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1));
                }
  
                changed = 0;
@@@ -901,7 -900,7 +901,7 @@@ int cmd_clean(int argc, const char **ar
        struct pathspec pathspec;
        struct strbuf buf = STRBUF_INIT;
        struct string_list exclude_list = STRING_LIST_INIT_NODUP;
-       struct exclude_list *el;
+       struct pattern_list *pl;
        struct string_list_item *item;
        const char *qname;
        struct option options[] = {
        if (!ignored)
                setup_standard_excludes(&dir);
  
-       el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
+       pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
        for (i = 0; i < exclude_list.nr; i++)
-               add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
+               add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1));
  
        parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_CWD,
diff --combined builtin/ls-files.c
index 670e8fb93c9320686410cabb8938a106be8e8158,5acc39694984ab638396e778ac06cbe61a9c704f..f069a028cea1afa09edda8d039437fe39d9585ac
@@@ -373,7 -373,7 +373,7 @@@ static void prune_index(struct index_st
        first = pos;
        last = istate->cache_nr;
        while (last > first) {
 -              int next = (last + first) >> 1;
 +              int next = first + ((last - first) >> 1);
                const struct cache_entry *ce = istate->cache[next];
                if (!strncmp(ce->name, prefix, prefixlen)) {
                        first = next+1;
@@@ -492,7 -492,7 +492,7 @@@ static int option_parse_exclude_from(co
        BUG_ON_OPT_NEG(unset);
  
        exc_given = 1;
-       add_excludes_from_file(dir, arg);
+       add_patterns_from_file(dir, arg);
  
        return 0;
  }
@@@ -516,7 -516,7 +516,7 @@@ int cmd_ls_files(int argc, const char *
        int require_work_tree = 0, show_tag = 0, i;
        const char *max_prefix;
        struct dir_struct dir;
-       struct exclude_list *el;
+       struct pattern_list *pl;
        struct string_list exclude_list = STRING_LIST_INIT_NODUP;
        struct option builtin_ls_files_options[] = {
                /* Think twice before adding "--nul" synonym to this */
  
        argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
                        ls_files_usage, 0);
-       el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
+       pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
        for (i = 0; i < exclude_list.nr; i++) {
-               add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
+               add_pattern(exclude_list.items[i].string, "", 0, pl, --exclude_args);
        }
        if (show_tag || show_valid_bit || show_fsmonitor_bit) {
                tag_cached = "H ";
diff --combined dir.c
index d021c908e5d162cfd4d961ab11d16b1b9eb7124a,34972abdaf1d5b24713bfa8e97274adf2d99f135..cab9c2a4588669f3aef3637ada377712998fa01a
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -561,7 -561,7 +561,7 @@@ int no_wildcard(const char *string
        return string[simple_length(string)] == '\0';
  }
  
- void parse_exclude_pattern(const char **pattern,
+ void parse_path_pattern(const char **pattern,
                           int *patternlen,
                           unsigned *flags,
                           int *nowildcardlen)
  
        *flags = 0;
        if (*p == '!') {
-               *flags |= EXC_FLAG_NEGATIVE;
+               *flags |= PATTERN_FLAG_NEGATIVE;
                p++;
        }
        len = strlen(p);
        if (len && p[len - 1] == '/') {
                len--;
-               *flags |= EXC_FLAG_MUSTBEDIR;
+               *flags |= PATTERN_FLAG_MUSTBEDIR;
        }
        for (i = 0; i < len; i++) {
                if (p[i] == '/')
                        break;
        }
        if (i == len)
-               *flags |= EXC_FLAG_NODIR;
+               *flags |= PATTERN_FLAG_NODIR;
        *nowildcardlen = simple_length(p);
        /*
         * we should have excluded the trailing slash from 'p' too,
        if (*nowildcardlen > len)
                *nowildcardlen = len;
        if (*p == '*' && no_wildcard(p + 1))
-               *flags |= EXC_FLAG_ENDSWITH;
+               *flags |= PATTERN_FLAG_ENDSWITH;
        *pattern = p;
        *patternlen = len;
  }
  
- void add_exclude(const char *string, const char *base,
-                int baselen, struct exclude_list *el, int srcpos)
+ void add_pattern(const char *string, const char *base,
+                int baselen, struct pattern_list *pl, int srcpos)
  {
-       struct exclude *x;
+       struct path_pattern *pattern;
        int patternlen;
        unsigned flags;
        int nowildcardlen;
  
-       parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);
-       if (flags & EXC_FLAG_MUSTBEDIR) {
-               FLEXPTR_ALLOC_MEM(x, pattern, string, patternlen);
+       parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
+       if (flags & PATTERN_FLAG_MUSTBEDIR) {
+               FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen);
        } else {
-               x = xmalloc(sizeof(*x));
-               x->pattern = string;
+               pattern = xmalloc(sizeof(*pattern));
+               pattern->pattern = string;
        }
-       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;
+       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;
  }
  
  static int read_skip_worktree_file_from_index(const struct index_state *istate,
  }
  
  /*
-  * Frees memory within el which was allocated for exclude patterns and
-  * the file buffer.  Does not free el itself.
+  * Frees memory within pl which was allocated for exclude patterns and
+  * the file buffer.  Does not free pl itself.
   */
- void clear_exclude_list(struct exclude_list *el)
+ void clear_pattern_list(struct pattern_list *pl)
  {
        int i;
  
-       for (i = 0; i < el->nr; i++)
-               free(el->excludes[i]);
-       free(el->excludes);
-       free(el->filebuf);
+       for (i = 0; i < pl->nr; i++)
+               free(pl->patterns[i]);
+       free(pl->patterns);
+       free(pl->filebuf);
  
-       memset(el, 0, sizeof(*el));
+       memset(pl, 0, sizeof(*pl));
  }
  
  static void trim_trailing_spaces(char *buf)
@@@ -701,7 -701,7 +701,7 @@@ static struct untracked_cache_dir *look
        first = 0;
        last = dir->dirs_nr;
        while (last > first) {
 -              int cmp, next = (last + first) >> 1;
 +              int cmp, next = first + ((last - first) >> 1);
                d = dir->dirs[next];
                cmp = strncmp(name, d->name, len);
                if (!cmp && strlen(d->name) > len)
@@@ -762,21 -762,21 +762,21 @@@ static void invalidate_directory(struc
                dir->dirs[i]->recurse = 0;
  }
  
- static int add_excludes_from_buffer(char *buf, size_t size,
+ static int add_patterns_from_buffer(char *buf, size_t size,
                                    const char *base, int baselen,
-                                   struct exclude_list *el);
+                                   struct pattern_list *pl);
  
  /*
   * 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 "el".
+  * exclude rules in "pl".
   *
   * If "ss" is not NULL, compute SHA-1 of the exclude file and fill
-  * stat data from disk (only valid if add_excludes returns zero). If
+  * stat data from disk (only valid if add_patterns returns zero). If
   * ss_valid is non-zero, "ss" must contain good value as input.
   */
- static int add_excludes(const char *fname, const char *base, int baselen,
-                       struct exclude_list *el, struct index_state *istate,
+ static int add_patterns(const char *fname, const char *base, int baselen,
+                       struct pattern_list *pl, struct index_state *istate,
                        struct oid_stat *oid_stat)
  {
        struct stat st;
                }
        }
  
-       add_excludes_from_buffer(buf, size, base, baselen, el);
+       add_patterns_from_buffer(buf, size, base, baselen, pl);
        return 0;
  }
  
- static int add_excludes_from_buffer(char *buf, size_t size,
+ static int add_patterns_from_buffer(char *buf, size_t size,
                                    const char *base, int baselen,
-                                   struct exclude_list *el)
+                                   struct pattern_list *pl)
  {
        int i, lineno = 1;
        char *entry;
  
-       el->filebuf = buf;
+       pl->filebuf = buf;
  
        if (skip_utf8_bom(&buf, size))
-               size -= buf - el->filebuf;
+               size -= buf - pl->filebuf;
  
        entry = buf;
  
                        if (entry != buf + i && entry[0] != '#') {
                                buf[i - (i && buf[i-1] == '\r')] = 0;
                                trim_trailing_spaces(entry);
-                               add_exclude(entry, base, baselen, el, lineno);
+                               add_pattern(entry, base, baselen, pl, lineno);
                        }
                        lineno++;
                        entry = buf + i + 1;
        return 0;
  }
  
- int add_excludes_from_file_to_list(const char *fname, const char *base,
-                                  int baselen, struct exclude_list *el,
+ int add_patterns_from_file_to_list(const char *fname, const char *base,
+                                  int baselen, struct pattern_list *pl,
                                   struct index_state *istate)
  {
-       return add_excludes(fname, base, baselen, el, istate, NULL);
+       return add_patterns(fname, base, baselen, pl, istate, NULL);
  }
  
- int add_excludes_from_blob_to_list(
+ int add_patterns_from_blob_to_list(
        struct object_id *oid,
        const char *base, int baselen,
-       struct exclude_list *el)
+       struct pattern_list *pl)
  {
        char *buf;
        size_t size;
        if (r != 1)
                return r;
  
-       add_excludes_from_buffer(buf, size, base, baselen, el);
+       add_patterns_from_buffer(buf, size, base, baselen, pl);
        return 0;
  }
  
- struct exclude_list *add_exclude_list(struct dir_struct *dir,
+ struct pattern_list *add_pattern_list(struct dir_struct *dir,
                                      int group_type, const char *src)
  {
-       struct exclude_list *el;
+       struct pattern_list *pl;
        struct exclude_list_group *group;
  
        group = &dir->exclude_list_group[group_type];
-       ALLOC_GROW(group->el, group->nr + 1, group->alloc);
-       el = &group->el[group->nr++];
-       memset(el, 0, sizeof(*el));
-       el->src = src;
-       return el;
+       ALLOC_GROW(group->pl, group->nr + 1, group->alloc);
+       pl = &group->pl[group->nr++];
+       memset(pl, 0, sizeof(*pl));
+       pl->src = src;
+       return pl;
  }
  
  /*
   * Used to set up core.excludesfile and .git/info/exclude lists.
   */
- static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
+ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname,
                                     struct oid_stat *oid_stat)
  {
-       struct exclude_list *el;
+       struct pattern_list *pl;
        /*
         * catch setup_standard_excludes() that's called before
         * dir->untracked is assigned. That function behaves
         */
        if (!dir->untracked)
                dir->unmanaged_exclude_files++;
-       el = add_exclude_list(dir, EXC_FILE, fname);
-       if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
+       pl = add_pattern_list(dir, EXC_FILE, fname);
+       if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0)
                die(_("cannot use %s as an exclude file"), fname);
  }
  
- void add_excludes_from_file(struct dir_struct *dir, const char *fname)
+ void add_patterns_from_file(struct dir_struct *dir, const char *fname)
  {
        dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
-       add_excludes_from_file_1(dir, fname, NULL);
+       add_patterns_from_file_1(dir, fname, NULL);
  }
  
  int match_basename(const char *basename, int basenamelen,
                if (patternlen == basenamelen &&
                    !fspathncmp(pattern, basename, basenamelen))
                        return 1;
-       } else if (flags & EXC_FLAG_ENDSWITH) {
+       } else if (flags & PATTERN_FLAG_ENDSWITH) {
                /* "*literal" matching against "fooliteral" */
                if (patternlen - 1 <= basenamelen &&
                    !fspathncmp(pattern + 1,
@@@ -1021,85 -1021,97 +1021,97 @@@ int match_pathname(const char *pathname
   * any, determines the fate.  Returns the exclude_list element which
   * matched, or NULL for undecided.
   */
- static struct exclude *last_exclude_matching_from_list(const char *pathname,
+ static struct path_pattern *last_matching_pattern_from_list(const char *pathname,
                                                       int pathlen,
                                                       const char *basename,
                                                       int *dtype,
-                                                      struct exclude_list *el,
+                                                      struct pattern_list *pl,
                                                       struct index_state *istate)
  {
-       struct exclude *exc = NULL; /* undecided */
+       struct path_pattern *res = NULL; /* undecided */
        int i;
  
-       if (!el->nr)
+       if (!pl->nr)
                return NULL;    /* undefined */
  
-       for (i = el->nr - 1; 0 <= i; i--) {
-               struct exclude *x = el->excludes[i];
-               const char *exclude = x->pattern;
-               int prefix = x->nowildcardlen;
+       for (i = pl->nr - 1; 0 <= i; i--) {
+               struct path_pattern *pattern = pl->patterns[i];
+               const char *exclude = pattern->pattern;
+               int prefix = pattern->nowildcardlen;
  
-               if (x->flags & EXC_FLAG_MUSTBEDIR) {
+               if (pattern->flags & PATTERN_FLAG_MUSTBEDIR) {
                        if (*dtype == DT_UNKNOWN)
                                *dtype = get_dtype(NULL, istate, pathname, pathlen);
                        if (*dtype != DT_DIR)
                                continue;
                }
  
-               if (x->flags & EXC_FLAG_NODIR) {
+               if (pattern->flags & PATTERN_FLAG_NODIR) {
                        if (match_basename(basename,
                                           pathlen - (basename - pathname),
-                                          exclude, prefix, x->patternlen,
-                                          x->flags)) {
-                               exc = x;
+                                          exclude, prefix, pattern->patternlen,
+                                          pattern->flags)) {
+                               res = pattern;
                                break;
                        }
                        continue;
                }
  
-               assert(x->baselen == 0 || x->base[x->baselen - 1] == '/');
+               assert(pattern->baselen == 0 ||
+                      pattern->base[pattern->baselen - 1] == '/');
                if (match_pathname(pathname, pathlen,
-                                  x->base, x->baselen ? x->baselen - 1 : 0,
-                                  exclude, prefix, x->patternlen, x->flags)) {
-                       exc = x;
+                                  pattern->base,
+                                  pattern->baselen ? pattern->baselen - 1 : 0,
+                                  exclude, prefix, pattern->patternlen,
+                                  pattern->flags)) {
+                       res = pattern;
                        break;
                }
        }
-       return exc;
+       return res;
  }
  
  /*
-  * Scan the list and let the last match determine the fate.
-  * Return 1 for exclude, 0 for include and -1 for undecided.
+  * 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.
   */
- 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 */
+ 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;
+       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;
  }
  
- 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)
+ 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)
  {
        int i, j;
        struct exclude_list_group *group;
-       struct exclude *exclude;
+       struct path_pattern *pattern;
        for (i = EXC_CMDL; i <= EXC_FILE; i++) {
                group = &dir->exclude_list_group[i];
                for (j = group->nr - 1; j >= 0; j--) {
-                       exclude = last_exclude_matching_from_list(
+                       pattern = last_matching_pattern_from_list(
                                pathname, pathlen, basename, dtype_p,
-                               &group->el[j], istate);
-                       if (exclude)
-                               return exclude;
+                               &group->pl[j], istate);
+                       if (pattern)
+                               return pattern;
                }
        }
        return NULL;
@@@ -1114,7 -1126,7 +1126,7 @@@ static void prep_exclude(struct dir_str
                         const char *base, int baselen)
  {
        struct exclude_list_group *group;
-       struct exclude_list *el;
+       struct pattern_list *pl;
        struct exclude_stack *stk = NULL;
        struct untracked_cache_dir *untracked;
        int current;
                if (stk->baselen <= baselen &&
                    !strncmp(dir->basebuf.buf, base, stk->baselen))
                        break;
-               el = &group->el[dir->exclude_stack->exclude_ix];
+               pl = &group->pl[dir->exclude_stack->exclude_ix];
                dir->exclude_stack = stk->prev;
-               dir->exclude = NULL;
-               free((char *)el->src); /* see strbuf_detach() below */
-               clear_exclude_list(el);
+               dir->pattern = NULL;
+               free((char *)pl->src); /* see strbuf_detach() below */
+               clear_pattern_list(pl);
                free(stk);
                group->nr--;
        }
  
        /* Skip traversing into sub directories if the parent is excluded */
-       if (dir->exclude)
+       if (dir->pattern)
                return;
  
        /*
                stk->baselen = cp - base;
                stk->exclude_ix = group->nr;
                stk->ucd = untracked;
-               el = add_exclude_list(dir, EXC_DIRS, NULL);
+               pl = add_pattern_list(dir, EXC_DIRS, NULL);
                strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
                assert(stk->baselen == dir->basebuf.len);
  
                if (stk->baselen) {
                        int dt = DT_DIR;
                        dir->basebuf.buf[stk->baselen - 1] = 0;
-                       dir->exclude = last_exclude_matching_from_lists(dir,
+                       dir->pattern = last_matching_pattern_from_lists(dir,
                                                                        istate,
                                dir->basebuf.buf, stk->baselen - 1,
                                dir->basebuf.buf + current, &dt);
                        dir->basebuf.buf[stk->baselen - 1] = '/';
-                       if (dir->exclude &&
-                           dir->exclude->flags & EXC_FLAG_NEGATIVE)
-                               dir->exclude = NULL;
-                       if (dir->exclude) {
+                       if (dir->pattern &&
+                           dir->pattern->flags & PATTERN_FLAG_NEGATIVE)
+                               dir->pattern = NULL;
+                       if (dir->pattern) {
                                dir->exclude_stack = stk;
                                return;
                        }
                        /*
                         * dir->basebuf gets reused by the traversal, but we
                         * need fname to remain unchanged to ensure the src
-                        * member of each struct exclude correctly
+                        * member of each struct path_pattern correctly
                         * back-references its source file.  Other invocations
-                        * of add_exclude_list provide stable strings, so we
+                        * of add_pattern_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);
-                       el->src = strbuf_detach(&sb, NULL);
-                       add_excludes(el->src, el->src, stk->baselen, el, istate,
+                       pl->src = strbuf_detach(&sb, NULL);
+                       add_patterns(pl->src, pl->src, stk->baselen, pl, 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_exclude_matching(). When the cache is
-                * used, last_exclude_matching() will not be called and
+                * times more in last_matching_pattern(). When the cache is
+                * used, last_matching_pattern() 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_exclude_matching(). Be careful about ignore rule
+                * last_matching_pattern(). Be careful about ignore rule
                 * order, though, if you do that.
                 */
                if (untracked &&
   * Returns the exclude_list element which matched, or NULL for
   * undecided.
   */
- struct exclude *last_exclude_matching(struct dir_struct *dir,
+ struct path_pattern *last_matching_pattern(struct dir_struct *dir,
                                      struct index_state *istate,
                                      const char *pathname,
                                      int *dtype_p)
  
        prep_exclude(dir, istate, pathname, basename-pathname);
  
-       if (dir->exclude)
-               return dir->exclude;
+       if (dir->pattern)
+               return dir->pattern;
  
-       return last_exclude_matching_from_lists(dir, istate, pathname, pathlen,
+       return last_matching_pattern_from_lists(dir, istate, pathname, pathlen,
                        basename, dtype_p);
  }
  
  int is_excluded(struct dir_struct *dir, struct index_state *istate,
                const char *pathname, int *dtype_p)
  {
-       struct exclude *exclude =
-               last_exclude_matching(dir, istate, pathname, dtype_p);
-       if (exclude)
-               return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
+       struct path_pattern *pattern =
+               last_matching_pattern(dir, istate, pathname, dtype_p);
+       if (pattern)
+               return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
        return 0;
  }
  
@@@ -1808,7 -1820,7 +1820,7 @@@ static int valid_cached_dir(struct dir_
  
        /*
         * prep_exclude will be called eventually on this directory,
-        * but it's called much later in last_exclude_matching(). We
+        * but it's called much later in last_matching_pattern(). 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.
@@@ -2488,14 -2500,14 +2500,14 @@@ void setup_standard_excludes(struct dir
        if (!excludes_file)
                excludes_file = xdg_config_home("ignore");
        if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
-               add_excludes_from_file_1(dir, excludes_file,
+               add_patterns_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_excludes_from_file_1(dir, path,
+                       add_patterns_from_file_1(dir, path,
                                                 dir->untracked ? &dir->ss_info_exclude : NULL);
        }
  }
@@@ -2527,18 -2539,18 +2539,18 @@@ void clear_directory(struct dir_struct 
  {
        int i, j;
        struct exclude_list_group *group;
-       struct exclude_list *el;
+       struct pattern_list *pl;
        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++) {
-                       el = &group->el[j];
+                       pl = &group->pl[j];
                        if (i == EXC_DIRS)
-                               free((char *)el->src);
-                       clear_exclude_list(el);
+                               free((char *)pl->src);
+                       clear_pattern_list(pl);
                }
-               free(group->el);
+               free(group->pl);
        }
  
        stk = dir->exclude_stack;
diff --combined unpack-trees.c
index 9c25126aecaabc8f0e3d0841ee1aa959acdf8dbd,cd548f4fa2d94447ae6ceefd0c260484c2075420..f0f56d40ac9944e5801955cb56e78d6024b78e52
@@@ -16,7 -16,7 +16,7 @@@
  #include "submodule-config.h"
  #include "fsmonitor.h"
  #include "object-store.h"
 -#include "fetch-object.h"
 +#include "promisor-remote.h"
  
  /*
   * Error messages expected by scripts out of plumbing commands such as
@@@ -315,7 -315,7 +315,7 @@@ static struct progress *get_progress(st
                        total++;
        }
  
 -      return start_delayed_progress(_("Checking out files"), total);
 +      return start_delayed_progress(_("Updating files"), total);
  }
  
  static void setup_collided_checkout_detection(struct checkout *state,
@@@ -400,7 -400,7 +400,7 @@@ static int check_updates(struct unpack_
                load_gitmodules_file(index, &state);
  
        enable_delayed_checkout(&state);
 -      if (repository_format_partial_clone && o->update && !o->dry_run) {
 +      if (has_promisor_remote() && o->update && !o->dry_run) {
                /*
                 * Prefetch the objects that are to be checked out in the loop
                 * below.
                        oid_array_append(&to_fetch, &ce->oid);
                }
                if (to_fetch.nr)
 -                      fetch_objects(repository_format_partial_clone,
 -                                    to_fetch.oid, to_fetch.nr);
 +                      promisor_remote_get_direct(the_repository,
 +                                                 to_fetch.oid, to_fetch.nr);
                oid_array_clear(&to_fetch);
        }
        for (i = 0; i < index->cache_nr; i++) {
@@@ -632,7 -632,7 +632,7 @@@ static int unpack_index_entry(struct ca
        return ret;
  }
  
 -static int find_cache_pos(struct traverse_info *, const struct name_entry *);
 +static int find_cache_pos(struct traverse_info *, const char *p, size_t len);
  
  static void restore_cache_bottom(struct traverse_info *info, int bottom)
  {
@@@ -651,7 -651,7 +651,7 @@@ static int switch_cache_bottom(struct t
        if (o->diff_index_cached)
                return 0;
        ret = o->cache_bottom;
 -      pos = find_cache_pos(info->prev, &info->name);
 +      pos = find_cache_pos(info->prev, info->name, info->namelen);
  
        if (pos < -1)
                o->cache_bottom = -2 - pos;
@@@ -686,19 -686,21 +686,19 @@@ static int index_pos_by_traverse_info(s
                                      struct traverse_info *info)
  {
        struct unpack_trees_options *o = info->data;
 -      int len = traverse_path_len(info, names);
 -      char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */);
 +      struct strbuf name = STRBUF_INIT;
        int pos;
  
 -      make_traverse_path(name, info, names);
 -      name[len++] = '/';
 -      name[len] = '\0';
 -      pos = index_name_pos(o->src_index, name, len);
 +      strbuf_make_traverse_path(&name, info, names->path, names->pathlen);
 +      strbuf_addch(&name, '/');
 +      pos = index_name_pos(o->src_index, name.buf, name.len);
        if (pos >= 0)
                BUG("This is a directory and should not exist in index");
        pos = -pos - 1;
 -      if (!starts_with(o->src_index->cache[pos]->name, name) ||
 -          (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name)))
 +      if (!starts_with(o->src_index->cache[pos]->name, name.buf) ||
 +          (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf)))
                BUG("pos must point at the first entry in this directory");
 -      free(name);
 +      strbuf_release(&name);
        return pos;
  }
  
@@@ -809,10 -811,8 +809,10 @@@ static int traverse_trees_recursive(in
        newinfo = *info;
        newinfo.prev = info;
        newinfo.pathspec = info->pathspec;
 -      newinfo.name = *p;
 -      newinfo.pathlen += tree_entry_len(p) + 1;
 +      newinfo.name = p->path;
 +      newinfo.namelen = p->pathlen;
 +      newinfo.mode = p->mode;
 +      newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);
        newinfo.df_conflicts |= df_conflicts;
  
        /*
                        const struct object_id *oid = NULL;
                        if (dirmask & 1)
                                oid = &names[i].oid;
 -                      buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
 +                      buf[nr_buf++] = fill_tree_descriptor(the_repository, t + i, oid);
                }
        }
  
   * itself - the caller needs to do the final check for the cache
   * entry having more data at the end!
   */
 -static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
 +static int do_compare_entry_piecewise(const struct cache_entry *ce,
 +                                    const struct traverse_info *info,
 +                                    const char *name, size_t namelen,
 +                                    unsigned mode)
  {
 -      int len, pathlen, ce_len;
 +      int pathlen, ce_len;
        const char *ce_name;
  
        if (info->prev) {
                int cmp = do_compare_entry_piecewise(ce, info->prev,
 -                                                   &info->name);
 +                                                   info->name, info->namelen,
 +                                                   info->mode);
                if (cmp)
                        return cmp;
        }
        ce_len -= pathlen;
        ce_name = ce->name + pathlen;
  
 -      len = tree_entry_len(n);
 -      return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
 +      return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
  }
  
  static int do_compare_entry(const struct cache_entry *ce,
                            const struct traverse_info *info,
 -                          const struct name_entry *n)
 +                          const char *name, size_t namelen,
 +                          unsigned mode)
  {
 -      int len, pathlen, ce_len;
 +      int pathlen, ce_len;
        const char *ce_name;
        int cmp;
  
         * it is quicker to use the precomputed version.
         */
        if (!info->traverse_path)
 -              return do_compare_entry_piecewise(ce, info, n);
 +              return do_compare_entry_piecewise(ce, info, name, namelen, mode);
  
        cmp = strncmp(ce->name, info->traverse_path, info->pathlen);
        if (cmp)
        ce_len -= pathlen;
        ce_name = ce->name + pathlen;
  
 -      len = tree_entry_len(n);
 -      return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
 +      return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
  }
  
  static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
  {
 -      int cmp = do_compare_entry(ce, info, n);
 +      int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);
        if (cmp)
                return cmp;
  
         * Even if the beginning compared identically, the ce should
         * compare as bigger than a directory leading up to it!
         */
 -      return ce_namelen(ce) > traverse_path_len(info, n);
 +      return ce_namelen(ce) > traverse_path_len(info, tree_entry_len(n));
  }
  
  static int ce_in_traverse_path(const struct cache_entry *ce,
  {
        if (!info->prev)
                return 1;
 -      if (do_compare_entry(ce, info->prev, &info->name))
 +      if (do_compare_entry(ce, info->prev,
 +                           info->name, info->namelen, info->mode))
                return 0;
        /*
         * If ce (blob) is the same name as the path (which is a tree
@@@ -958,7 -954,7 +958,7 @@@ static struct cache_entry *create_ce_en
        struct index_state *istate,
        int is_transient)
  {
 -      int len = traverse_path_len(info, n);
 +      size_t len = traverse_path_len(info, tree_entry_len(n));
        struct cache_entry *ce =
                is_transient ?
                make_empty_transient_cache_entry(len) :
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
        oidcpy(&ce->oid, &n->oid);
 -      make_traverse_path(ce->name, info, n);
 +      /* len+1 because the cache_entry allocates space for NUL */
 +      make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen);
  
        return ce;
  }
@@@ -1062,12 -1057,13 +1062,12 @@@ static int unpack_failed(struct unpack_
   * the directory.
   */
  static int find_cache_pos(struct traverse_info *info,
 -                        const struct name_entry *p)
 +                        const char *p, size_t p_len)
  {
        int pos;
        struct unpack_trees_options *o = info->data;
        struct index_state *index = o->src_index;
        int pfxlen = info->pathlen;
 -      int p_len = tree_entry_len(p);
  
        for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
                const struct cache_entry *ce = index->cache[pos];
                        ce_len = ce_slash - ce_name;
                else
                        ce_len = ce_namelen(ce) - pfxlen;
 -              cmp = name_compare(p->path, p_len, ce_name, ce_len);
 +              cmp = name_compare(p, p_len, ce_name, ce_len);
                /*
                 * Exact match; if we have a directory we need to
                 * delay returning it.
                 * E.g.  ce_name == "t-i", and p->path == "t"; we may
                 * have "t/a" in the index.
                 */
 -              if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) &&
 +              if (p_len < ce_len && !memcmp(ce_name, p, p_len) &&
                    ce_name[p_len] < '/')
                        continue; /* keep looking */
                break;
  static struct cache_entry *find_cache_entry(struct traverse_info *info,
                                            const struct name_entry *p)
  {
 -      int pos = find_cache_pos(info, p);
 +      int pos = find_cache_pos(info, p->path, p->pathlen);
        struct unpack_trees_options *o = info->data;
  
        if (0 <= pos)
@@@ -1142,10 -1138,10 +1142,10 @@@ static void debug_path(struct traverse_
  {
        if (info->prev) {
                debug_path(info->prev);
 -              if (*info->prev->name.path)
 +              if (*info->prev->name)
                        putchar('/');
        }
 -      printf("%s", info->name.path);
 +      printf("%s", info->name);
  }
  
  static void debug_name_entry(int i, struct name_entry *n)
@@@ -1269,7 -1265,8 +1269,8 @@@ static int clear_ce_flags_1(struct inde
                            struct cache_entry **cache, int nr,
                            struct strbuf *prefix,
                            int select_mask, int clear_mask,
-                           struct exclude_list *el, int defval);
+                           struct pattern_list *pl,
+                           enum pattern_match_result default_match);
  
  /* Whole directory matching */
  static int clear_ce_flags_dir(struct index_state *istate,
                              struct strbuf *prefix,
                              char *basename,
                              int select_mask, int clear_mask,
-                             struct exclude_list *el, int defval)
+                             struct pattern_list *pl,
+                             enum pattern_match_result default_match)
  {
        struct cache_entry **cache_end;
        int dtype = DT_DIR;
-       int ret = is_excluded_from_list(prefix->buf, prefix->len,
-                                       basename, &dtype, el, istate);
        int rc;
+       enum pattern_match_result ret;
+       ret = path_matches_pattern_list(prefix->buf, prefix->len,
+                                       basename, &dtype, pl, istate);
  
        strbuf_addch(prefix, '/');
  
        /* If undecided, use matching result of parent dir in defval */
-       if (ret < 0)
-               ret = defval;
+       if (ret == UNDECIDED)
+               ret = default_match;
  
        for (cache_end = cache; cache_end != cache + nr; cache_end++) {
                struct cache_entry *ce = *cache_end;
        }
  
        /*
-        * TODO: check el, if there are no patterns that may conflict
+        * TODO: check pl, if there are no patterns that may conflict
         * with ret (iow, we know in advance the incl/excl
         * decision for the entire directory), clear flag here without
         * calling clear_ce_flags_1(). That function will call
-        * the expensive is_excluded_from_list() on every entry.
+        * the expensive path_matches_pattern_list() on every entry.
         */
        rc = clear_ce_flags_1(istate, cache, cache_end - cache,
                              prefix,
                              select_mask, clear_mask,
-                             el, ret);
+                             pl, ret);
        strbuf_setlen(prefix, prefix->len - 1);
        return rc;
  }
  
  /*
   * Traverse the index, find every entry that matches according to
-  * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the
+  * o->pl. Do "ce_flags &= ~clear_mask" on those entries. Return the
   * number of traversed entries.
   *
   * If select_mask is non-zero, only entries whose ce_flags has on of
@@@ -1331,7 -1330,8 +1334,8 @@@ static int clear_ce_flags_1(struct inde
                            struct cache_entry **cache, int nr,
                            struct strbuf *prefix,
                            int select_mask, int clear_mask,
-                           struct exclude_list *el, int defval)
+                           struct pattern_list *pl,
+                           enum pattern_match_result default_match)
  {
        struct cache_entry **cache_end = cache + nr;
  
        while(cache != cache_end) {
                struct cache_entry *ce = *cache;
                const char *name, *slash;
-               int len, dtype, ret;
+               int len, dtype;
+               enum pattern_match_result ret;
  
                if (select_mask && !(ce->ce_flags & select_mask)) {
                        cache++;
                                                       prefix,
                                                       prefix->buf + prefix->len - len,
                                                       select_mask, clear_mask,
-                                                      el, defval);
+                                                      pl, default_match);
  
                        /* clear_c_f_dir eats a whole dir already? */
                        if (processed) {
                        strbuf_addch(prefix, '/');
                        cache += clear_ce_flags_1(istate, cache, cache_end - cache,
                                                  prefix,
-                                                 select_mask, clear_mask, el, defval);
+                                                 select_mask, clear_mask, pl,
+                                                 default_match);
                        strbuf_setlen(prefix, prefix->len - len - 1);
                        continue;
                }
  
                /* Non-directory */
                dtype = ce_to_dtype(ce);
-               ret = is_excluded_from_list(ce->name, ce_namelen(ce),
-                                           name, &dtype, el, istate);
-               if (ret < 0)
-                       ret = defval;
-               if (ret > 0)
+               ret = path_matches_pattern_list(ce->name,
+                                               ce_namelen(ce),
+                                               name, &dtype, pl, istate);
+               if (ret == UNDECIDED)
+                       ret = default_match;
+               if (ret == MATCHED)
                        ce->ce_flags &= ~clear_mask;
                cache++;
        }
  
  static int clear_ce_flags(struct index_state *istate,
                          int select_mask, int clear_mask,
-                         struct exclude_list *el)
+                         struct pattern_list *pl)
  {
        static struct strbuf prefix = STRBUF_INIT;
  
                                istate->cache_nr,
                                &prefix,
                                select_mask, clear_mask,
-                               el, 0);
+                               pl, 0);
  }
  
  /*
   * Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout
   */
- static void mark_new_skip_worktree(struct exclude_list *el,
+ static void mark_new_skip_worktree(struct pattern_list *pl,
                                   struct index_state *istate,
                                   int select_flag, int skip_wt_flag)
  {
         * 2. Widen worktree according to sparse-checkout file.
         * Matched entries will have skip_wt_flag cleared (i.e. "in")
         */
-       clear_ce_flags(istate, select_flag, skip_wt_flag, el);
+       clear_ce_flags(istate, select_flag, skip_wt_flag, pl);
  }
  
  static int verify_absent(const struct cache_entry *,
@@@ -1457,21 -1460,21 +1464,21 @@@ int unpack_trees(unsigned len, struct t
  {
        int i, ret;
        static struct cache_entry *dfc;
-       struct exclude_list el;
+       struct pattern_list pl;
  
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
  
        trace_performance_enter();
-       memset(&el, 0, sizeof(el));
+       memset(&pl, 0, sizeof(pl));
        if (!core_apply_sparse_checkout || !o->update)
                o->skip_sparse_checkout = 1;
        if (!o->skip_sparse_checkout) {
                char *sparse = git_pathdup("info/sparse-checkout");
-               if (add_excludes_from_file_to_list(sparse, "", 0, &el, NULL) < 0)
+               if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0)
                        o->skip_sparse_checkout = 1;
                else
-                       o->el = &el;
+                       o->pl = &pl;
                free(sparse);
        }
  
         * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
         */
        if (!o->skip_sparse_checkout)
-               mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
+               mark_new_skip_worktree(o->pl, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
  
        if (!dfc)
                dfc = xcalloc(1, cache_entry_size(0));
                 * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
                 * so apply_sparse_checkout() won't attempt to remove it from worktree
                 */
-               mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
+               mark_new_skip_worktree(o->pl, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
  
                ret = 0;
                for (i = 0; i < o->result.cache_nr; i++) {
  
  done:
        trace_performance_leave("unpack_trees");
-       clear_exclude_list(&el);
+       clear_pattern_list(&pl);
        return ret;
  
  return_failed: