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)
+static int common_prefix(const char **pathspec)
{
const char *path, *slash, *next;
int prefix;
return prefix;
}
+int fill_directory(struct dir_struct *dir, const char **pathspec)
+{
+ 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;
+}
+
/*
* Does 'match' match the given name?
* A match is found if
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;
}
/* This is the "show_other_directories" case */
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;
}
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))
* 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 ? 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;
continue;
len = strlen(de->d_name);
/* Ignore overly long pathnames! */
- if (len + baselen + 8 > sizeof(fullname))
+ if (len + baselen + 8 > sizeof(path))
continue;
- memcpy(fullname + baselen, de->d_name, len+1);
- if (simplify_away(fullname, baselen + len, simplify))
+ memcpy(path + baselen, de->d_name, len+1);
+ len = baselen + len;
+ if (simplify_away(path, len, simplify))
continue;
dtype = DTYPE(de);
- exclude = excluded(dir, fullname, &dtype);
+ exclude = excluded(dir, path, &dtype);
if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
- && in_pathspec(fullname, baselen + len, simplify))
- dir_add_ignored(dir, fullname, baselen + len);
+ && in_pathspec(path, len, simplify))
+ dir_add_ignored(dir, path,len);
/*
* Excluded? If we don't explicitly want to show
continue;
if (dtype == DT_UNKNOWN)
- dtype = get_dtype(de, fullname);
+ dtype = get_dtype(de, path, len);
/*
* Do we want to see just the ignored files?
default:
continue;
case DT_DIR:
- memcpy(fullname + baselen + len, "/", 2);
+ memcpy(path + len, "/", 2);
len++;
- switch (treat_directory(dir, fullname, baselen + len, simplify)) {
+ switch (treat_directory(dir, path, len, simplify)) {
case show_directory:
if (exclude != !!(dir->flags
& DIR_SHOW_IGNORED))
break;
case recurse_into_directory:
contents += read_directory_recursive(dir,
- fullname, fullname, baselen + len, 0, simplify);
+ path, len, 0, simplify);
continue;
case ignore_directory:
continue;
if (check_only)
goto exit_early;
else
- dir_add_name(dir, fullname, baselen + len);
+ dir_add_name(dir, path, len);
}
exit_early:
closedir(fdir);
free(simplify);
}
-int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
+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, strlen(path)))
+ if (has_symlink_leading_path(path, len))
return dir->nr;
simplify = create_simplify(pathspec);
- read_directory_recursive(dir, path, base, baselen, 0, 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);
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] != '/')