Code

Merge branch 'jc/maint-clone-alternates' into maint
[git.git] / dir.c
diff --git a/dir.c b/dir.c
index 109e05b01346ac13296dfbcfa2355a43d97731cd..08281d2ef74ea7790913e71f08d00299a1825765 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -14,12 +14,27 @@ struct path_simplify {
        const char *path;
 };
 
-static int read_directory_recursive(struct dir_struct *dir,
-       const char *path, const char *base, int baselen,
+static int read_directory_recursive(struct dir_struct *dir, const char *path, int len,
        int check_only, const struct path_simplify *simplify);
-static int get_dtype(struct dirent *de, const char *path);
+static int get_dtype(struct dirent *de, const char *path, int len);
 
-int common_prefix(const char **pathspec)
+/* helper string functions with support for the ignore_case flag */
+int strcmp_icase(const char *a, const char *b)
+{
+       return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
+}
+
+int strncmp_icase(const char *a, const char *b, size_t count)
+{
+       return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
+}
+
+int fnmatch_icase(const char *pattern, const char *string, int flags)
+{
+       return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
+}
+
+static int common_prefix(const char **pathspec)
 {
        const char *path, *slash, *next;
        int prefix;
@@ -32,33 +47,63 @@ int common_prefix(const char **pathspec)
        if (!slash)
                return 0;
 
+       /*
+        * The first 'prefix' characters of 'path' are common leading
+        * path components among the pathspecs we have seen so far,
+        * including the trailing slash.
+        */
        prefix = slash - path + 1;
        while ((next = *++pathspec) != NULL) {
-               int len = strlen(next);
-               if (len >= prefix && !memcmp(path, next, prefix))
+               int len, last_matching_slash = -1;
+               for (len = 0; len < prefix && next[len] == path[len]; len++)
+                       if (next[len] == '/')
+                               last_matching_slash = len;
+               if (len == prefix)
                        continue;
-               len = prefix - 1;
-               for (;;) {
-                       if (!len)
-                               return 0;
-                       if (next[--len] != '/')
-                               continue;
-                       if (memcmp(path, next, len+1))
-                               continue;
-                       prefix = len + 1;
-                       break;
-               }
+               if (last_matching_slash < 0)
+                       return 0;
+               prefix = last_matching_slash + 1;
        }
        return prefix;
 }
 
-static inline int special_char(unsigned char c1)
+int fill_directory(struct dir_struct *dir, const char **pathspec)
 {
-       return !c1 || c1 == '*' || c1 == '[' || c1 == '?' || c1 == '\\';
+       const char *path;
+       int len;
+
+       /*
+        * Calculate common prefix for the pathspec, and
+        * use that to optimize the directory walk
+        */
+       len = common_prefix(pathspec);
+       path = "";
+
+       if (len)
+               path = xmemdupz(*pathspec, len);
+
+       /* Read the directory and prune it */
+       read_directory(dir, path, len, pathspec);
+       return len;
+}
+
+int within_depth(const char *name, int namelen,
+                       int depth, int max_depth)
+{
+       const char *cp = name, *cpe = name + namelen;
+
+       while (cp < cpe) {
+               if (*cp++ != '/')
+                       continue;
+               depth++;
+               if (depth > max_depth)
+                       return 0;
+       }
+       return 1;
 }
 
 /*
- * Does 'match' matches the given name?
+ * Does 'match' match the given name?
  * A match is found if
  *
  * (1) the 'match' string is leading directory of 'name', or
@@ -77,16 +122,30 @@ static int match_one(const char *match, const char *name, int namelen)
        if (!*match)
                return MATCHED_RECURSIVELY;
 
-       for (;;) {
-               unsigned char c1 = *match;
-               unsigned char c2 = *name;
-               if (special_char(c1))
-                       break;
-               if (c1 != c2)
-                       return 0;
-               match++;
-               name++;
-               namelen--;
+       if (ignore_case) {
+               for (;;) {
+                       unsigned char c1 = tolower(*match);
+                       unsigned char c2 = tolower(*name);
+                       if (c1 == '\0' || is_glob_special(c1))
+                               break;
+                       if (c1 != c2)
+                               return 0;
+                       match++;
+                       name++;
+                       namelen--;
+               }
+       } else {
+               for (;;) {
+                       unsigned char c1 = *match;
+                       unsigned char c2 = *name;
+                       if (c1 == '\0' || is_glob_special(c1))
+                               break;
+                       if (c1 != c2)
+                               return 0;
+                       match++;
+                       name++;
+                       namelen--;
+               }
        }
 
 
@@ -95,8 +154,8 @@ static int match_one(const char *match, const char *name, int namelen)
         * we need to match by fnmatch
         */
        matchlen = strlen(match);
-       if (strncmp(match, name, matchlen))
-               return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
+       if (strncmp_icase(match, name, matchlen))
+               return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
 
        if (namelen == matchlen)
                return MATCHED_EXACTLY;
@@ -113,25 +172,117 @@ static int match_one(const char *match, const char *name, int namelen)
  * and a mark is left in seen[] array for pathspec element that
  * actually matched anything.
  */
-int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
+int match_pathspec(const char **pathspec, const char *name, int namelen,
+               int prefix, char *seen)
 {
-       int retval;
-       const char *match;
+       int i, retval = 0;
+
+       if (!pathspec)
+               return 1;
 
        name += prefix;
        namelen -= prefix;
 
-       for (retval = 0; (match = *pathspec++) != NULL; seen++) {
+       for (i = 0; pathspec[i] != NULL; i++) {
                int how;
-               if (retval && *seen == MATCHED_EXACTLY)
+               const char *match = pathspec[i] + prefix;
+               if (seen && seen[i] == MATCHED_EXACTLY)
                        continue;
-               match += prefix;
                how = match_one(match, name, namelen);
                if (how) {
                        if (retval < how)
                                retval = how;
-                       if (*seen < how)
-                               *seen = how;
+                       if (seen && seen[i] < how)
+                               seen[i] = how;
+               }
+       }
+       return retval;
+}
+
+/*
+ * Does 'match' match the given name?
+ * A match is found if
+ *
+ * (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'.
+ *
+ * and the return value tells which case it was.
+ *
+ * It returns 0 when there is no match.
+ */
+static int match_pathspec_item(const struct pathspec_item *item, int prefix,
+                              const char *name, int namelen)
+{
+       /* name/namelen has prefix cut off by caller */
+       const char *match = item->match + prefix;
+       int matchlen = item->len - prefix;
+
+       /* If the match was just the prefix, we matched */
+       if (!*match)
+               return MATCHED_RECURSIVELY;
+
+       if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
+               if (matchlen == namelen)
+                       return MATCHED_EXACTLY;
+
+               if (match[matchlen-1] == '/' || name[matchlen] == '/')
+                       return MATCHED_RECURSIVELY;
+       }
+
+       if (item->use_wildcard && !fnmatch(match, name, 0))
+               return MATCHED_FNMATCH;
+
+       return 0;
+}
+
+/*
+ * Given a name and a list of pathspecs, see if the name matches
+ * any of the pathspecs.  The caller is also interested in seeing
+ * all pathspec matches some names it calls this function with
+ * (otherwise the user could have mistyped the unmatched pathspec),
+ * and a mark is left in seen[] array for pathspec element that
+ * actually matched anything.
+ */
+int match_pathspec_depth(const struct pathspec *ps,
+                        const char *name, int namelen,
+                        int prefix, char *seen)
+{
+       int i, retval = 0;
+
+       if (!ps->nr) {
+               if (!ps->recursive || ps->max_depth == -1)
+                       return MATCHED_RECURSIVELY;
+
+               if (within_depth(name, namelen, 0, ps->max_depth))
+                       return MATCHED_EXACTLY;
+               else
+                       return 0;
+       }
+
+       name += prefix;
+       namelen -= prefix;
+
+       for (i = ps->nr - 1; i >= 0; i--) {
+               int how;
+               if (seen && seen[i] == MATCHED_EXACTLY)
+                       continue;
+               how = match_pathspec_item(ps->items+i, prefix, name, namelen);
+               if (ps->recursive && ps->max_depth != -1 &&
+                   how && how != MATCHED_FNMATCH) {
+                       int len = ps->items[i].len;
+                       if (name[len] == '/')
+                               len++;
+                       if (within_depth(name+len, namelen-len, 0, ps->max_depth))
+                               how = MATCHED_EXACTLY;
+                       else
+                               how = 0;
+               }
+               if (how) {
+                       if (retval < how)
+                               retval = how;
+                       if (seen && seen[i] < how)
+                               seen[i] = how;
                }
        }
        return retval;
@@ -139,7 +290,7 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
 
 static int no_wildcard(const char *string)
 {
-       return string[strcspn(string, "*?[{")] == '\0';
+       return string[strcspn(string, "*?[{\\")] == '\0';
 }
 
 void add_exclude(const char *string, const char *base,
@@ -158,7 +309,7 @@ void add_exclude(const char *string, const char *base,
        if (len && string[len - 1] == '/') {
                char *s;
                x = xmalloc(sizeof(*x) + len);
-               s = (char*)(x+1);
+               s = (char *)(x+1);
                memcpy(s, string, len - 1);
                s[len - 1] = '\0';
                string = s;
@@ -183,36 +334,87 @@ void add_exclude(const char *string, const char *base,
        which->excludes[which->nr++] = x;
 }
 
-static int add_excludes_from_file_1(const char *fname,
-                                   const char *base,
-                                   int baselen,
-                                   char **buf_p,
-                                   struct exclude_list *which)
+static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
+{
+       int pos, len;
+       unsigned long sz;
+       enum object_type type;
+       void *data;
+       struct index_state *istate = &the_index;
+
+       len = strlen(path);
+       pos = index_name_pos(istate, path, len);
+       if (pos < 0)
+               return NULL;
+       if (!ce_skip_worktree(istate->cache[pos]))
+               return NULL;
+       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+       if (!data || type != OBJ_BLOB) {
+               free(data);
+               return NULL;
+       }
+       *size = xsize_t(sz);
+       return data;
+}
+
+void free_excludes(struct exclude_list *el)
+{
+       int i;
+
+       for (i = 0; i < el->nr; i++)
+               free(el->excludes[i]);
+       free(el->excludes);
+
+       el->nr = 0;
+       el->excludes = NULL;
+}
+
+int add_excludes_from_file_to_list(const char *fname,
+                                  const char *base,
+                                  int baselen,
+                                  char **buf_p,
+                                  struct exclude_list *which,
+                                  int check_index)
 {
        struct stat st;
        int fd, i;
-       size_t size;
+       size_t size = 0;
        char *buf, *entry;
 
        fd = open(fname, O_RDONLY);
-       if (fd < 0 || fstat(fd, &st) < 0)
-               goto err;
-       size = xsize_t(st.st_size);
-       if (size == 0) {
-               close(fd);
-               return 0;
+       if (fd < 0 || fstat(fd, &st) < 0) {
+               if (0 <= fd)
+                       close(fd);
+               if (!check_index ||
+                   (buf = read_skip_worktree_file_from_index(fname, &size)) == NULL)
+                       return -1;
+               if (size == 0) {
+                       free(buf);
+                       return 0;
+               }
+               if (buf[size-1] != '\n') {
+                       buf = xrealloc(buf, size+1);
+                       buf[size++] = '\n';
+               }
        }
-       buf = xmalloc(size+1);
-       if (read_in_full(fd, buf, size) != size)
-       {
-               free(buf);
-               goto err;
+       else {
+               size = xsize_t(st.st_size);
+               if (size == 0) {
+                       close(fd);
+                       return 0;
+               }
+               buf = xmalloc(size+1);
+               if (read_in_full(fd, buf, size) != size) {
+                       free(buf);
+                       close(fd);
+                       return -1;
+               }
+               buf[size++] = '\n';
+               close(fd);
        }
-       close(fd);
 
        if (buf_p)
                *buf_p = buf;
-       buf[size++] = '\n';
        entry = buf;
        for (i = 0; i < size; i++) {
                if (buf[i] == '\n') {
@@ -224,17 +426,12 @@ static int add_excludes_from_file_1(const char *fname,
                }
        }
        return 0;
-
- err:
-       if (0 <= fd)
-               close(fd);
-       return -1;
 }
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
-       if (add_excludes_from_file_1(fname, "", 0, NULL,
-                                    &dir->exclude_list[EXC_FILE]) < 0)
+       if (add_excludes_from_file_to_list(fname, "", 0, NULL,
+                                          &dir->exclude_list[EXC_FILE], 0) < 0)
                die("cannot use %s as an exclude file", fname);
 }
 
@@ -283,21 +480,21 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
                memcpy(dir->basebuf + current, base + current,
                       stk->baselen - current);
                strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
-               add_excludes_from_file_1(dir->basebuf,
-                                        dir->basebuf, stk->baselen,
-                                        &stk->filebuf, el);
+               add_excludes_from_file_to_list(dir->basebuf,
+                                              dir->basebuf, stk->baselen,
+                                              &stk->filebuf, el, 1);
                dir->exclude_stack = stk;
                current = stk->baselen;
        }
        dir->basebuf[baselen] = '\0';
 }
 
-/* Scan the list and let the last match determines the fate.
+/* Scan the list and let the last match determine the fate.
  * Return 1 for exclude, 0 for include and -1 for undecided.
  */
-static int excluded_1(const char *pathname,
-                     int pathlen, const char *basename, int *dtype,
-                     struct exclude_list *el)
+int excluded_from_list(const char *pathname,
+                      int pathlen, const char *basename, int *dtype,
+                      struct exclude_list *el)
 {
        int i;
 
@@ -309,7 +506,7 @@ static int excluded_1(const char *pathname,
 
                        if (x->flags & EXC_FLAG_MUSTBEDIR) {
                                if (*dtype == DT_UNKNOWN)
-                                       *dtype = get_dtype(NULL, pathname);
+                                       *dtype = get_dtype(NULL, pathname, pathlen);
                                if (*dtype != DT_DIR)
                                        continue;
                        }
@@ -317,14 +514,14 @@ static int excluded_1(const char *pathname,
                        if (x->flags & EXC_FLAG_NODIR) {
                                /* match basename */
                                if (x->flags & EXC_FLAG_NOWILDCARD) {
-                                       if (!strcmp(exclude, basename))
+                                       if (!strcmp_icase(exclude, basename))
                                                return to_exclude;
                                } else if (x->flags & EXC_FLAG_ENDSWITH) {
                                        if (x->patternlen - 1 <= pathlen &&
-                                           !strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
+                                           !strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
                                                return to_exclude;
                                } else {
-                                       if (fnmatch(exclude, basename, 0) == 0)
+                                       if (fnmatch_icase(exclude, basename, 0) == 0)
                                                return to_exclude;
                                }
                        }
@@ -339,14 +536,14 @@ static int excluded_1(const char *pathname,
 
                                if (pathlen < baselen ||
                                    (baselen && pathname[baselen-1] != '/') ||
-                                   strncmp(pathname, x->base, baselen))
+                                   strncmp_icase(pathname, x->base, baselen))
                                    continue;
 
                                if (x->flags & EXC_FLAG_NOWILDCARD) {
-                                       if (!strcmp(exclude, pathname + baselen))
+                                       if (!strcmp_icase(exclude, pathname + baselen))
                                                return to_exclude;
                                } else {
-                                       if (fnmatch(exclude, pathname+baselen,
+                                       if (fnmatch_icase(exclude, pathname+baselen,
                                                    FNM_PATHNAME) == 0)
                                            return to_exclude;
                                }
@@ -365,8 +562,8 @@ int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
 
        prep_exclude(dir, pathname, basename-pathname);
        for (st = EXC_CMDL; st <= EXC_FILE; st++) {
-               switch (excluded_1(pathname, pathlen, basename,
-                                  dtype_p, &dir->exclude_list[st])) {
+               switch (excluded_from_list(pathname, pathlen, basename,
+                                          dtype_p, &dir->exclude_list[st])) {
                case 0:
                        return 0;
                case 1:
@@ -387,7 +584,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
        return ent;
 }
 
-struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
+static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
        if (cache_name_exists(pathname, len, ignore_case))
                return NULL;
@@ -398,7 +595,7 @@ struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int
 
 struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
 {
-       if (cache_name_pos(pathname, len) >= 0)
+       if (!cache_name_is_other(pathname, len))
                return NULL;
 
        ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc);
@@ -408,9 +605,42 @@ struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname,
 enum exist_status {
        index_nonexistent = 0,
        index_directory,
-       index_gitdir,
+       index_gitdir
 };
 
+/*
+ * Do not use the alphabetically stored index to look up
+ * the directory name; instead, use the case insensitive
+ * name hash.
+ */
+static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
+{
+       struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+       unsigned char endchar;
+
+       if (!ce)
+               return index_nonexistent;
+       endchar = ce->name[len];
+
+       /*
+        * The cache_entry structure returned will contain this dirname
+        * and possibly additional path components.
+        */
+       if (endchar == '/')
+               return index_directory;
+
+       /*
+        * If there are no additional path components, then this cache_entry
+        * represents a submodule.  Submodules, despite being directories,
+        * are stored in the cache without a closing slash.
+        */
+       if (!endchar && S_ISGITLINK(ce->ce_mode))
+               return index_gitdir;
+
+       /* This should never be hit, but it exists just in case. */
+       return index_nonexistent;
+}
+
 /*
  * The index sorts alphabetically by entry name, which
  * means that a gitlink sorts as '\0' at the end, while
@@ -420,7 +650,12 @@ enum exist_status {
  */
 static enum exist_status directory_exists_in_index(const char *dirname, int len)
 {
-       int pos = cache_name_pos(dirname, len);
+       int pos;
+
+       if (ignore_case)
+               return directory_exists_in_index_icase(dirname, len);
+
+       pos = cache_name_pos(dirname, len);
        if (pos < 0)
                pos = -pos-1;
        while (pos < active_nr) {
@@ -476,7 +711,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
 enum directory_treatment {
        show_directory,
        ignore_directory,
-       recurse_into_directory,
+       recurse_into_directory
 };
 
 static enum directory_treatment treat_directory(struct dir_struct *dir,
@@ -489,14 +724,14 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
                return recurse_into_directory;
 
        case index_gitdir:
-               if (dir->show_other_directories)
+               if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
                        return ignore_directory;
                return show_directory;
 
        case index_nonexistent:
-               if (dir->show_other_directories)
+               if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
                        break;
-               if (!dir->no_gitlinks) {
+               if (!(dir->flags & DIR_NO_GITLINKS)) {
                        unsigned char sha1[20];
                        if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
                                return show_directory;
@@ -505,9 +740,9 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
        }
 
        /* This is the "show_other_directories" case */
-       if (!dir->hide_empty_directories)
+       if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
                return show_directory;
-       if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify))
+       if (!read_directory_recursive(dir, dirname, len, 1, simplify))
                return ignore_directory;
        return show_directory;
 }
@@ -537,23 +772,80 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli
        return 0;
 }
 
-static int in_pathspec(const char *path, int len, const struct path_simplify *simplify)
+/*
+ * This function tells us whether an excluded path matches a
+ * list of "interesting" pathspecs. That is, whether a path matched
+ * by any of the pathspecs could possibly be ignored by excluding
+ * the specified path. This can happen if:
+ *
+ *   1. the path is mentioned explicitly in the pathspec
+ *
+ *   2. the path is a directory prefix of some element in the
+ *      pathspec
+ */
+static int exclude_matches_pathspec(const char *path, int len,
+               const struct path_simplify *simplify)
 {
        if (simplify) {
                for (; simplify->path; simplify++) {
                        if (len == simplify->len
                            && !memcmp(path, simplify->path, len))
                                return 1;
+                       if (len < simplify->len
+                           && simplify->path[len] == '/'
+                           && !memcmp(path, simplify->path, len))
+                               return 1;
                }
        }
        return 0;
 }
 
-static int get_dtype(struct dirent *de, const char *path)
+static int get_index_dtype(const char *path, int len)
+{
+       int pos;
+       struct cache_entry *ce;
+
+       ce = cache_name_exists(path, len, 0);
+       if (ce) {
+               if (!ce_uptodate(ce))
+                       return DT_UNKNOWN;
+               if (S_ISGITLINK(ce->ce_mode))
+                       return DT_DIR;
+               /*
+                * Nobody actually cares about the
+                * difference between DT_LNK and DT_REG
+                */
+               return DT_REG;
+       }
+
+       /* Try to look it up as a directory */
+       pos = cache_name_pos(path, len);
+       if (pos >= 0)
+               return DT_UNKNOWN;
+       pos = -pos-1;
+       while (pos < active_nr) {
+               ce = active_cache[pos++];
+               if (strncmp(ce->name, path, len))
+                       break;
+               if (ce->name[len] > '/')
+                       break;
+               if (ce->name[len] < '/')
+                       continue;
+               if (!ce_uptodate(ce))
+                       break;  /* continue? */
+               return DT_DIR;
+       }
+       return DT_UNKNOWN;
+}
+
+static int get_dtype(struct dirent *de, const char *path, int len)
 {
        int dtype = de ? DTYPE(de) : DT_UNKNOWN;
        struct stat st;
 
+       if (dtype != DT_UNKNOWN)
+               return dtype;
+       dtype = get_index_dtype(path, len);
        if (dtype != DT_UNKNOWN)
                return dtype;
        if (lstat(path, &st))
@@ -567,6 +859,92 @@ static int get_dtype(struct dirent *de, const char *path)
        return dtype;
 }
 
+enum path_treatment {
+       path_ignored,
+       path_handled,
+       path_recurse
+};
+
+static enum path_treatment treat_one_path(struct dir_struct *dir,
+                                         char *path, int *len,
+                                         const struct path_simplify *simplify,
+                                         int dtype, struct dirent *de)
+{
+       int exclude = excluded(dir, path, &dtype);
+       if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
+           && exclude_matches_pathspec(path, *len, simplify))
+               dir_add_ignored(dir, path, *len);
+
+       /*
+        * Excluded? If we don't explicitly want to show
+        * ignored files, ignore it
+        */
+       if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
+               return path_ignored;
+
+       if (dtype == DT_UNKNOWN)
+               dtype = get_dtype(de, path, *len);
+
+       /*
+        * Do we want to see just the ignored files?
+        * We still need to recurse into directories,
+        * even if we don't ignore them, since the
+        * directory may contain files that we do..
+        */
+       if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
+               if (dtype != DT_DIR)
+                       return path_ignored;
+       }
+
+       switch (dtype) {
+       default:
+               return path_ignored;
+       case DT_DIR:
+               memcpy(path + *len, "/", 2);
+               (*len)++;
+               switch (treat_directory(dir, path, *len, simplify)) {
+               case show_directory:
+                       if (exclude != !!(dir->flags
+                                         & DIR_SHOW_IGNORED))
+                               return path_ignored;
+                       break;
+               case recurse_into_directory:
+                       return path_recurse;
+               case ignore_directory:
+                       return path_ignored;
+               }
+               break;
+       case DT_REG:
+       case DT_LNK:
+               break;
+       }
+       return path_handled;
+}
+
+static enum path_treatment treat_path(struct dir_struct *dir,
+                                     struct dirent *de,
+                                     char *path, int path_max,
+                                     int baselen,
+                                     const struct path_simplify *simplify,
+                                     int *len)
+{
+       int dtype;
+
+       if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
+               return path_ignored;
+       *len = strlen(de->d_name);
+       /* Ignore overly long pathnames! */
+       if (*len + baselen + 8 > path_max)
+               return path_ignored;
+       memcpy(path + baselen, de->d_name, *len + 1);
+       *len += baselen;
+       if (simplify_away(path, *len, simplify))
+               return path_ignored;
+
+       dtype = DTYPE(de);
+       return treat_one_path(dir, path, len, simplify, dtype, de);
+}
+
 /*
  * Read a directory tree. We currently ignore anything but
  * directories, regular files and symlinks. That's because git
@@ -576,88 +954,37 @@ static int get_dtype(struct dirent *de, const char *path)
  * Also, we ignore the name ".git" (even if it is not a directory).
  * That likely will not change.
  */
-static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
+static int read_directory_recursive(struct dir_struct *dir,
+                                   const char *base, int baselen,
+                                   int check_only,
+                                   const struct path_simplify *simplify)
 {
-       DIR *fdir = opendir(path);
+       DIR *fdir = opendir(*base ? base : ".");
        int contents = 0;
 
        if (fdir) {
                struct dirent *de;
-               char fullname[PATH_MAX + 1];
-               memcpy(fullname, base, baselen);
+               char path[PATH_MAX + 1];
+               memcpy(path, base, baselen);
 
                while ((de = readdir(fdir)) != NULL) {
-                       int len, dtype;
-                       int exclude;
-
-                       if ((de->d_name[0] == '.') &&
-                           (de->d_name[1] == 0 ||
-                            !strcmp(de->d_name + 1, ".") ||
-                            !strcmp(de->d_name + 1, "git")))
-                               continue;
-                       len = strlen(de->d_name);
-                       /* Ignore overly long pathnames! */
-                       if (len + baselen + 8 > sizeof(fullname))
-                               continue;
-                       memcpy(fullname + baselen, de->d_name, len+1);
-                       if (simplify_away(fullname, baselen + len, simplify))
+                       int len;
+                       switch (treat_path(dir, de, path, sizeof(path),
+                                          baselen, simplify, &len)) {
+                       case path_recurse:
+                               contents += read_directory_recursive
+                                       (dir, path, len, 0, simplify);
                                continue;
-
-                       dtype = DTYPE(de);
-                       exclude = excluded(dir, fullname, &dtype);
-                       if (exclude && dir->collect_ignored
-                           && in_pathspec(fullname, baselen + len, simplify))
-                               dir_add_ignored(dir, fullname, baselen + len);
-
-                       /*
-                        * Excluded? If we don't explicitly want to show
-                        * ignored files, ignore it
-                        */
-                       if (exclude && !dir->show_ignored)
-                               continue;
-
-                       if (dtype == DT_UNKNOWN)
-                               dtype = get_dtype(de, fullname);
-
-                       /*
-                        * Do we want to see just the ignored files?
-                        * We still need to recurse into directories,
-                        * even if we don't ignore them, since the
-                        * directory may contain files that we do..
-                        */
-                       if (!exclude && dir->show_ignored) {
-                               if (dtype != DT_DIR)
-                                       continue;
-                       }
-
-                       switch (dtype) {
-                       default:
+                       case path_ignored:
                                continue;
-                       case DT_DIR:
-                               memcpy(fullname + baselen + len, "/", 2);
-                               len++;
-                               switch (treat_directory(dir, fullname, baselen + len, simplify)) {
-                               case show_directory:
-                                       if (exclude != dir->show_ignored)
-                                               continue;
-                                       break;
-                               case recurse_into_directory:
-                                       contents += read_directory_recursive(dir,
-                                               fullname, fullname, baselen + len, 0, simplify);
-                                       continue;
-                               case ignore_directory:
-                                       continue;
-                               }
-                               break;
-                       case DT_REG:
-                       case DT_LNK:
+                       case path_handled:
                                break;
                        }
                        contents++;
                        if (check_only)
                                goto exit_early;
                        else
-                               dir_add_name(dir, fullname, baselen + len);
+                               dir_add_name(dir, path, len);
                }
 exit_early:
                closedir(fdir);
@@ -680,17 +1007,12 @@ static int cmp_name(const void *p1, const void *p2)
  */
 static int simple_length(const char *match)
 {
-       const char special[256] = {
-               [0] = 1, ['?'] = 1,
-               ['\\'] = 1, ['*'] = 1,
-               ['['] = 1
-       };
        int len = -1;
 
        for (;;) {
                unsigned char c = *match++;
                len++;
-               if (special[c])
+               if (c == '\0' || is_glob_special(c))
                        return len;
        }
 }
@@ -725,11 +1047,51 @@ static void free_simplify(struct path_simplify *simplify)
        free(simplify);
 }
 
-int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
+static int treat_leading_path(struct dir_struct *dir,
+                             const char *path, int len,
+                             const struct path_simplify *simplify)
 {
-       struct path_simplify *simplify = create_simplify(pathspec);
+       char pathbuf[PATH_MAX];
+       int baselen, blen;
+       const char *cp;
 
-       read_directory_recursive(dir, path, base, baselen, 0, simplify);
+       while (len && path[len - 1] == '/')
+               len--;
+       if (!len)
+               return 1;
+       baselen = 0;
+       while (1) {
+               cp = path + baselen + !!baselen;
+               cp = memchr(cp, '/', path + len - cp);
+               if (!cp)
+                       baselen = len;
+               else
+                       baselen = cp - path;
+               memcpy(pathbuf, path, baselen);
+               pathbuf[baselen] = '\0';
+               if (!is_directory(pathbuf))
+                       return 0;
+               if (simplify_away(pathbuf, baselen, simplify))
+                       return 0;
+               blen = baselen;
+               if (treat_one_path(dir, pathbuf, &blen, simplify,
+                                  DT_DIR, NULL) == path_ignored)
+                       return 0; /* do not recurse into it */
+               if (len <= baselen)
+                       return 1; /* finished checking */
+       }
+}
+
+int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
+{
+       struct path_simplify *simplify;
+
+       if (has_symlink_leading_path(path, len))
+               return dir->nr;
+
+       simplify = create_simplify(pathspec);
+       if (!len || treat_leading_path(dir, path, len, simplify))
+               read_directory_recursive(dir, path, len, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
        qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
@@ -743,66 +1105,90 @@ int file_exists(const char *f)
 }
 
 /*
- * get_relative_cwd() gets the prefix of the current working directory
- * relative to 'dir'.  If we are not inside 'dir', it returns NULL.
- *
- * As a convenience, it also returns NULL if 'dir' is already NULL.  The
- * reason for this behaviour is that it is natural for functions returning
- * directory names to return NULL to say "this directory does not exist"
- * or "this directory is invalid".  These cases are usually handled the
- * same as if the cwd is not inside 'dir' at all, so get_relative_cwd()
- * returns NULL for both of them.
- *
- * Most notably, get_relative_cwd(buffer, size, get_git_work_tree())
- * unifies the handling of "outside work tree" with "no work tree at all".
+ * Given two normalized paths (a trailing slash is ok), if subdir is
+ * outside dir, return -1.  Otherwise return the offset in subdir that
+ * can be used as relative path to dir.
  */
-char *get_relative_cwd(char *buffer, int size, const char *dir)
+int dir_inside_of(const char *subdir, const char *dir)
 {
-       char *cwd = buffer;
-
-       if (!dir)
-               return NULL;
-       if (!getcwd(buffer, size))
-               die("can't find the current directory: %s", strerror(errno));
+       int offset = 0;
 
-       if (!is_absolute_path(dir))
-               dir = make_absolute_path(dir);
+       assert(dir && subdir && *dir && *subdir);
 
-       while (*dir && *dir == *cwd) {
+       while (*dir && *subdir && *dir == *subdir) {
                dir++;
-               cwd++;
+               subdir++;
+               offset++;
        }
-       if (*dir)
-               return NULL;
-       if (*cwd == '/')
-               return cwd + 1;
-       return cwd;
+
+       /* hel[p]/me vs hel[l]/yeah */
+       if (*dir && *subdir)
+               return -1;
+
+       if (!*subdir)
+               return !*dir ? offset : -1; /* same dir */
+
+       /* foo/[b]ar vs foo/[] */
+       if (is_dir_sep(dir[-1]))
+               return is_dir_sep(subdir[-1]) ? offset : -1;
+
+       /* foo[/]bar vs foo[] */
+       return is_dir_sep(*subdir) ? offset + 1 : -1;
 }
 
 int is_inside_dir(const char *dir)
 {
-       char buffer[PATH_MAX];
-       return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
+       char cwd[PATH_MAX];
+       if (!dir)
+               return 0;
+       if (!getcwd(cwd, sizeof(cwd)))
+               die_errno("can't find the current directory");
+       return dir_inside_of(cwd, dir) >= 0;
+}
+
+int is_empty_dir(const char *path)
+{
+       DIR *dir = opendir(path);
+       struct dirent *e;
+       int ret = 1;
+
+       if (!dir)
+               return 0;
+
+       while ((e = readdir(dir)) != NULL)
+               if (!is_dot_or_dotdot(e->d_name)) {
+                       ret = 0;
+                       break;
+               }
+
+       closedir(dir);
+       return ret;
 }
 
-int remove_dir_recursively(struct strbuf *path, int only_empty)
+int remove_dir_recursively(struct strbuf *path, int flag)
 {
-       DIR *dir = opendir(path->buf);
+       DIR *dir;
        struct dirent *e;
        int ret = 0, original_len = path->len, len;
+       int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
+       unsigned char submodule_head[20];
+
+       if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+           !resolve_gitlink_ref(path->buf, "HEAD", submodule_head))
+               /* Do not descend and nuke a nested git work tree. */
+               return 0;
 
+       dir = opendir(path->buf);
        if (!dir)
-               return -1;
+               return rmdir(path->buf);
        if (path->buf[original_len - 1] != '/')
                strbuf_addch(path, '/');
 
        len = path->len;
        while ((e = readdir(dir)) != NULL) {
                struct stat st;
-               if ((e->d_name[0] == '.') &&
-                   ((e->d_name[1] == 0) ||
-                    ((e->d_name[1] == '.') && e->d_name[2] == 0)))
-                       continue; /* "." and ".." */
+               if (is_dot_or_dotdot(e->d_name))
+                       continue;
 
                strbuf_setlen(path, len);
                strbuf_addstr(path, e->d_name);
@@ -837,3 +1223,70 @@ void setup_standard_excludes(struct dir_struct *dir)
        if (excludes_file && !access(excludes_file, R_OK))
                add_excludes_from_file(dir, excludes_file);
 }
+
+int remove_path(const char *name)
+{
+       char *slash;
+
+       if (unlink(name) && errno != ENOENT)
+               return -1;
+
+       slash = strrchr(name, '/');
+       if (slash) {
+               char *dirs = xstrdup(name);
+               slash = dirs + (slash - name);
+               do {
+                       *slash = '\0';
+               } while (rmdir(dirs) == 0 && (slash = strrchr(dirs, '/')));
+               free(dirs);
+       }
+       return 0;
+}
+
+static int pathspec_item_cmp(const void *a_, const void *b_)
+{
+       struct pathspec_item *a, *b;
+
+       a = (struct pathspec_item *)a_;
+       b = (struct pathspec_item *)b_;
+       return strcmp(a->match, b->match);
+}
+
+int init_pathspec(struct pathspec *pathspec, const char **paths)
+{
+       const char **p = paths;
+       int i;
+
+       memset(pathspec, 0, sizeof(*pathspec));
+       if (!p)
+               return 0;
+       while (*p)
+               p++;
+       pathspec->raw = paths;
+       pathspec->nr = p - paths;
+       if (!pathspec->nr)
+               return 0;
+
+       pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
+       for (i = 0; i < pathspec->nr; i++) {
+               struct pathspec_item *item = pathspec->items+i;
+               const char *path = paths[i];
+
+               item->match = path;
+               item->len = strlen(path);
+               item->use_wildcard = !no_wildcard(path);
+               if (item->use_wildcard)
+                       pathspec->has_wildcard = 1;
+       }
+
+       qsort(pathspec->items, pathspec->nr,
+             sizeof(struct pathspec_item), pathspec_item_cmp);
+
+       return 0;
+}
+
+void free_pathspec(struct pathspec *pathspec)
+{
+       free(pathspec->items);
+       pathspec->items = NULL;
+}