struct cached_dir {
DIR *fdir;
struct untracked_cache_dir *untracked;
+ int nr_files;
+ int nr_dirs;
+
struct dirent *de;
+ const char *file;
+ struct untracked_cache_dir *ucd;
};
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
do_invalidate_gitignore(dir);
}
+static void invalidate_directory(struct untracked_cache *uc,
+ struct untracked_cache_dir *dir)
+{
+ uc->dir_invalidated++;
+ dir->valid = 0;
+ dir->untracked_nr = 0;
+}
+
/*
* Given a file with name "fname", read it (either from disk, or from
* the index if "check_index" is non-zero), parse it and store the
}
}
+static enum path_treatment treat_path_fast(struct dir_struct *dir,
+ struct untracked_cache_dir *untracked,
+ struct cached_dir *cdir,
+ struct strbuf *path,
+ int baselen,
+ const struct path_simplify *simplify)
+{
+ strbuf_setlen(path, baselen);
+ if (!cdir->ucd) {
+ strbuf_addstr(path, cdir->file);
+ return path_untracked;
+ }
+ strbuf_addstr(path, cdir->ucd->name);
+ /* treat_one_path() does this before it calls treat_directory() */
+ if (path->buf[path->len - 1] != '/')
+ strbuf_addch(path, '/');
+ if (cdir->ucd->check_only)
+ /*
+ * check_only is set as a result of treat_directory() getting
+ * to its bottom. Verify again the same set of directories
+ * with check_only set.
+ */
+ return read_directory_recursive(dir, path->buf, path->len,
+ cdir->ucd, 1, simplify);
+ /*
+ * We get path_recurse in the first run when
+ * directory_exists_in_index() returns index_nonexistent. We
+ * are sure that new changes in the index does not impact the
+ * outcome. Return now.
+ */
+ return path_recurse;
+}
+
static enum path_treatment treat_path(struct dir_struct *dir,
struct untracked_cache_dir *untracked,
struct cached_dir *cdir,
int dtype;
struct dirent *de = cdir->de;
+ if (!de)
+ return treat_path_fast(dir, untracked, cdir, path,
+ baselen, simplify);
if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
return path_none;
strbuf_setlen(path, baselen);
dir->untracked[dir->untracked_nr++] = xstrdup(name);
}
+static int valid_cached_dir(struct dir_struct *dir,
+ struct untracked_cache_dir *untracked,
+ struct strbuf *path,
+ int check_only)
+{
+ struct stat st;
+
+ if (!untracked)
+ return 0;
+
+ if (stat(path->len ? path->buf : ".", &st)) {
+ invalidate_directory(dir->untracked, untracked);
+ memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
+ return 0;
+ }
+ if (!untracked->valid ||
+ match_stat_data(&untracked->stat_data, &st)) {
+ if (untracked->valid)
+ invalidate_directory(dir->untracked, untracked);
+ fill_stat_data(&untracked->stat_data, &st);
+ return 0;
+ }
+
+ if (untracked->check_only != !!check_only) {
+ invalidate_directory(dir->untracked, untracked);
+ return 0;
+ }
+
+ /*
+ * prep_exclude will be called eventually on this directory,
+ * but it's called much later in last_exclude_matching(). We
+ * need it now to determine the validity of the cache for this
+ * path. The next calls will be nearly no-op, the way
+ * prep_exclude() is designed.
+ */
+ if (path->len && path->buf[path->len - 1] != '/') {
+ strbuf_addch(path, '/');
+ prep_exclude(dir, path->buf, path->len);
+ strbuf_setlen(path, path->len - 1);
+ } else
+ prep_exclude(dir, path->buf, path->len);
+
+ /* hopefully prep_exclude() haven't invalidated this entry... */
+ return untracked->valid;
+}
+
static int open_cached_dir(struct cached_dir *cdir,
struct dir_struct *dir,
struct untracked_cache_dir *untracked,
{
memset(cdir, 0, sizeof(*cdir));
cdir->untracked = untracked;
+ if (valid_cached_dir(dir, untracked, path, check_only))
+ return 0;
cdir->fdir = opendir(path->len ? path->buf : ".");
+ if (dir->untracked)
+ dir->untracked->dir_opened++;
if (!cdir->fdir)
return -1;
return 0;
return -1;
return 0;
}
+ while (cdir->nr_dirs < cdir->untracked->dirs_nr) {
+ struct untracked_cache_dir *d = cdir->untracked->dirs[cdir->nr_dirs];
+ cdir->ucd = d;
+ cdir->nr_dirs++;
+ return 0;
+ }
+ cdir->ucd = NULL;
+ if (cdir->nr_files < cdir->untracked->untracked_nr) {
+ struct untracked_cache_dir *d = cdir->untracked;
+ cdir->file = d->untracked[cdir->nr_files++];
+ return 0;
+ }
return -1;
}
{
if (cdir->fdir)
closedir(cdir->fdir);
+ /*
+ * We have gone through this directory and found no untracked
+ * entries. Mark it valid.
+ */
+ if (cdir->untracked)
+ cdir->untracked->valid = 1;
}
/*
if (check_only) {
/* abort early if maximum state has been reached */
if (dir_state == path_untracked) {
- if (untracked)
+ if (cdir.fdir)
add_untracked(untracked, path.buf + baselen);
break;
}
if (dir->flags & DIR_SHOW_IGNORED)
break;
dir_add_name(dir, path.buf, path.len);
- if (untracked)
+ if (cdir.fdir)
add_untracked(untracked, path.buf + baselen);
break;