fetch: Speed up fetch of large numbers of refs
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index 8a9e7d8131f995a83324d4be64b554e66e578d72..d0999ba055367c31571b251fb34bb46ed6c7051d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -566,18 +566,55 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si
        return 0;
 }
 
+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 cache_entry *ce;
        struct stat st;
 
        if (dtype != DT_UNKNOWN)
                return dtype;
-       ce = cache_name_exists(path, len, 0);
-       if (ce && ce_uptodate(ce))
-               st.st_mode = ce->ce_mode;
-       else if (lstat(path, &st))
+       dtype = get_index_dtype(path, len);
+       if (dtype != DT_UNKNOWN)
+               return dtype;
+       if (lstat(path, &st))
                return dtype;
        if (S_ISREG(st.st_mode))
                return DT_REG;
@@ -824,12 +861,20 @@ int is_empty_dir(const char *path)
        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;
        if (path->buf[original_len - 1] != '/')