@ -582,6 +582,22 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
@@ -582,6 +582,22 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
return d;
}
static void do_invalidate_gitignore(struct untracked_cache_dir *dir)
{
int i;
dir->valid = 0;
dir->untracked_nr = 0;
for (i = 0; i < dir->dirs_nr; i++)
do_invalidate_gitignore(dir->dirs[i]);
}
static void invalidate_gitignore(struct untracked_cache *uc,
struct untracked_cache_dir *dir)
{
uc->gitignore_invalidated++;
do_invalidate_gitignore(dir);
}
/*
* 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
@ -698,6 +714,13 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
@@ -698,6 +714,13 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
struct sha1_stat *sha1_stat)
{
struct exclude_list *el;
/*
* catch setup_standard_excludes() that's called before
* dir->untracked is assigned. That function behaves
* differently when dir->untracked is non-NULL.
*/
if (!dir->untracked)
dir->unmanaged_exclude_files++;
el = add_exclude_list(dir, EXC_FILE, fname);
if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0)
die("cannot use %s as an exclude file", fname);
@ -705,6 +728,7 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
@@ -705,6 +728,7 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
{
dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
add_excludes_from_file_1(dir, fname, NULL);
}
@ -1573,9 +1597,87 @@ static int treat_leading_path(struct dir_struct *dir,
@@ -1573,9 +1597,87 @@ static int treat_leading_path(struct dir_struct *dir,
return rc;
}
static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
int base_len,
const struct pathspec *pathspec)
{
struct untracked_cache_dir *root;
if (!dir->untracked)
return NULL;
/*
* We only support $GIT_DIR/info/exclude and core.excludesfile
* as the global ignore rule files. Any other additions
* (e.g. from command line) invalidate the cache. This
* condition also catches running setup_standard_excludes()
* before setting dir->untracked!
*/
if (dir->unmanaged_exclude_files)
return NULL;
/*
* Optimize for the main use case only: whole-tree git
* status. More work involved in treat_leading_path() if we
* use cache on just a subset of the worktree. pathspec
* support could make the matter even worse.
*/
if (base_len || (pathspec && pathspec->nr))
return NULL;
/* Different set of flags may produce different results */
if (dir->flags != dir->untracked->dir_flags ||
/*
* See treat_directory(), case index_nonexistent. Without
* this flag, we may need to also cache .git file content
* for the resolve_gitlink_ref() call, which we don't.
*/
!(dir->flags & DIR_SHOW_OTHER_DIRECTORIES) ||
/* We don't support collecting ignore files */
(dir->flags & (DIR_SHOW_IGNORED | DIR_SHOW_IGNORED_TOO |
DIR_COLLECT_IGNORED)))
return NULL;
/*
* If we use .gitignore in the cache and now you change it to
* .gitexclude, everything will go wrong.
*/
if (dir->exclude_per_dir != dir->untracked->exclude_per_dir &&
strcmp(dir->exclude_per_dir, dir->untracked->exclude_per_dir))
return NULL;
/*
* EXC_CMDL is not considered in the cache. If people set it,
* skip the cache.
*/
if (dir->exclude_list_group[EXC_CMDL].nr)
return NULL;
if (!dir->untracked->root) {
const int len = sizeof(*dir->untracked->root);
dir->untracked->root = xmalloc(len);
memset(dir->untracked->root, 0, len);
}
/* Validate $GIT_DIR/info/exclude and core.excludesfile */
root = dir->untracked->root;
if (hashcmp(dir->ss_info_exclude.sha1,
dir->untracked->ss_info_exclude.sha1)) {
invalidate_gitignore(dir->untracked, root);
dir->untracked->ss_info_exclude = dir->ss_info_exclude;
}
if (hashcmp(dir->ss_excludes_file.sha1,
dir->untracked->ss_excludes_file.sha1)) {
invalidate_gitignore(dir->untracked, root);
dir->untracked->ss_excludes_file = dir->ss_excludes_file;
}
return root;
}
int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
{
struct path_simplify *simplify;
struct untracked_cache_dir *untracked;
/*
* Check out create_simplify()
@ -1599,10 +1701,15 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
@@ -1599,10 +1701,15 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
* create_simplify().
*/
simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
untracked = validate_untracked_cache(dir, len, pathspec);
if (!untracked)
/*
* make sure untracked cache code path is disabled,
* e.g. prep_exclude()
*/
dir->untracked = NULL;
if (!len || treat_leading_path(dir, path, len, simplify))
read_directory_recursive(dir, path, len,
dir->untracked ? dir->untracked->root : NULL,
0, simplify);
read_directory_recursive(dir, path, len, untracked, 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);