Merge branch 'jm/status-ignored-directory-optim'

"git status --ignored", when noticing that a directory without any
tracked path is ignored, still enumerated all the ignored paths in
the directory, which is unnecessary.  The codepath has been
optimized to avoid this overhead.

* jm/status-ignored-directory-optim:
  Improve performance of git status --ignored
maint
Junio C Hamano 2017-09-29 11:23:40 +09:00
commit 075bc9c798
1 changed files with 41 additions and 6 deletions

47
dir.c
View File

@ -49,7 +49,7 @@ struct cached_dir {
static enum path_treatment read_directory_recursive(struct dir_struct *dir, static enum path_treatment read_directory_recursive(struct dir_struct *dir,
struct index_state *istate, const char *path, int len, struct index_state *istate, const char *path, int len,
struct untracked_cache_dir *untracked, struct untracked_cache_dir *untracked,
int check_only, const struct pathspec *pathspec); int check_only, int stop_at_first_file, const struct pathspec *pathspec);
static int get_dtype(struct dirent *de, struct index_state *istate, static int get_dtype(struct dirent *de, struct index_state *istate,
const char *path, int len); const char *path, int len);


@ -1404,8 +1404,13 @@ static enum path_treatment treat_directory(struct dir_struct *dir,


untracked = lookup_untracked(dir->untracked, untracked, untracked = lookup_untracked(dir->untracked, untracked,
dirname + baselen, len - baselen); dirname + baselen, len - baselen);

/*
* If this is an excluded directory, then we only need to check if
* the directory contains any files.
*/
return read_directory_recursive(dir, istate, dirname, len, return read_directory_recursive(dir, istate, dirname, len,
untracked, 1, pathspec); untracked, 1, exclude, pathspec);
} }


/* /*
@ -1633,7 +1638,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
* with check_only set. * with check_only set.
*/ */
return read_directory_recursive(dir, istate, path->buf, path->len, return read_directory_recursive(dir, istate, path->buf, path->len,
cdir->ucd, 1, pathspec); cdir->ucd, 1, 0, pathspec);
/* /*
* We get path_recurse in the first run when * We get path_recurse in the first run when
* directory_exists_in_index() returns index_nonexistent. We * directory_exists_in_index() returns index_nonexistent. We
@ -1793,12 +1798,20 @@ static void close_cached_dir(struct cached_dir *cdir)
* Also, we ignore the name ".git" (even if it is not a directory). * Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change. * That likely will not change.
* *
* If 'stop_at_first_file' is specified, 'path_excluded' is returned
* to signal that a file was found. This is the least significant value that
* indicates that a file was encountered that does not depend on the order of
* whether an untracked or exluded path was encountered first.
*
* Returns the most significant path_treatment value encountered in the scan. * Returns the most significant path_treatment value encountered in the scan.
* If 'stop_at_first_file' is specified, `path_excluded` is the most
* significant path_treatment value that will be returned.
*/ */

static enum path_treatment read_directory_recursive(struct dir_struct *dir, static enum path_treatment read_directory_recursive(struct dir_struct *dir,
struct index_state *istate, const char *base, int baselen, struct index_state *istate, const char *base, int baselen,
struct untracked_cache_dir *untracked, int check_only, struct untracked_cache_dir *untracked, int check_only,
const struct pathspec *pathspec) int stop_at_first_file, const struct pathspec *pathspec)
{ {
struct cached_dir cdir; struct cached_dir cdir;
enum path_treatment state, subdir_state, dir_state = path_none; enum path_treatment state, subdir_state, dir_state = path_none;
@ -1832,12 +1845,34 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
subdir_state = subdir_state =
read_directory_recursive(dir, istate, path.buf, read_directory_recursive(dir, istate, path.buf,
path.len, ud, path.len, ud,
check_only, pathspec); check_only, stop_at_first_file, pathspec);
if (subdir_state > dir_state) if (subdir_state > dir_state)
dir_state = subdir_state; dir_state = subdir_state;
} }


if (check_only) { if (check_only) {
if (stop_at_first_file) {
/*
* If stopping at first file, then
* signal that a file was found by
* returning `path_excluded`. This is
* to return a consistent value
* regardless of whether an ignored or
* excluded file happened to be
* encountered 1st.
*
* In current usage, the
* `stop_at_first_file` is passed when
* an ancestor directory has matched
* an exclude pattern, so any found
* files will be excluded.
*/
if (dir_state >= path_excluded) {
dir_state = path_excluded;
break;
}
}

/* abort early if maximum state has been reached */ /* abort early if maximum state has been reached */
if (dir_state == path_untracked) { if (dir_state == path_untracked) {
if (cdir.fdir) if (cdir.fdir)
@ -2108,7 +2143,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
*/ */
dir->untracked = NULL; dir->untracked = NULL;
if (!len || treat_leading_path(dir, istate, path, len, pathspec)) if (!len || treat_leading_path(dir, istate, path, len, pathspec))
read_directory_recursive(dir, istate, path, len, untracked, 0, pathspec); read_directory_recursive(dir, istate, path, len, untracked, 0, 0, pathspec);
QSORT(dir->entries, dir->nr, cmp_dir_entry); QSORT(dir->entries, dir->nr, cmp_dir_entry);
QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry); QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);