Skip to content

Commit ccad261

Browse files
pcloudsgitster
authored andcommitted
untracked cache: initial untracked cache validation
Make sure the starting conditions and all global exclude files are good to go. If not, either disable untracked cache completely, or wipe out the cache and start fresh. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0dcb8d7 commit ccad261

File tree

2 files changed

+114
-3
lines changed

2 files changed

+114
-3
lines changed

dir.c

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,22 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
582582
return d;
583583
}
584584

585+
static void do_invalidate_gitignore(struct untracked_cache_dir *dir)
586+
{
587+
int i;
588+
dir->valid = 0;
589+
dir->untracked_nr = 0;
590+
for (i = 0; i < dir->dirs_nr; i++)
591+
do_invalidate_gitignore(dir->dirs[i]);
592+
}
593+
594+
static void invalidate_gitignore(struct untracked_cache *uc,
595+
struct untracked_cache_dir *dir)
596+
{
597+
uc->gitignore_invalidated++;
598+
do_invalidate_gitignore(dir);
599+
}
600+
585601
/*
586602
* Given a file with name "fname", read it (either from disk, or from
587603
* the index if "check_index" is non-zero), parse it and store the
@@ -698,13 +714,21 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
698714
struct sha1_stat *sha1_stat)
699715
{
700716
struct exclude_list *el;
717+
/*
718+
* catch setup_standard_excludes() that's called before
719+
* dir->untracked is assigned. That function behaves
720+
* differently when dir->untracked is non-NULL.
721+
*/
722+
if (!dir->untracked)
723+
dir->unmanaged_exclude_files++;
701724
el = add_exclude_list(dir, EXC_FILE, fname);
702725
if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0)
703726
die("cannot use %s as an exclude file", fname);
704727
}
705728

706729
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
707730
{
731+
dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
708732
add_excludes_from_file_1(dir, fname, NULL);
709733
}
710734

@@ -1573,9 +1597,87 @@ static int treat_leading_path(struct dir_struct *dir,
15731597
return rc;
15741598
}
15751599

1600+
static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
1601+
int base_len,
1602+
const struct pathspec *pathspec)
1603+
{
1604+
struct untracked_cache_dir *root;
1605+
1606+
if (!dir->untracked)
1607+
return NULL;
1608+
1609+
/*
1610+
* We only support $GIT_DIR/info/exclude and core.excludesfile
1611+
* as the global ignore rule files. Any other additions
1612+
* (e.g. from command line) invalidate the cache. This
1613+
* condition also catches running setup_standard_excludes()
1614+
* before setting dir->untracked!
1615+
*/
1616+
if (dir->unmanaged_exclude_files)
1617+
return NULL;
1618+
1619+
/*
1620+
* Optimize for the main use case only: whole-tree git
1621+
* status. More work involved in treat_leading_path() if we
1622+
* use cache on just a subset of the worktree. pathspec
1623+
* support could make the matter even worse.
1624+
*/
1625+
if (base_len || (pathspec && pathspec->nr))
1626+
return NULL;
1627+
1628+
/* Different set of flags may produce different results */
1629+
if (dir->flags != dir->untracked->dir_flags ||
1630+
/*
1631+
* See treat_directory(), case index_nonexistent. Without
1632+
* this flag, we may need to also cache .git file content
1633+
* for the resolve_gitlink_ref() call, which we don't.
1634+
*/
1635+
!(dir->flags & DIR_SHOW_OTHER_DIRECTORIES) ||
1636+
/* We don't support collecting ignore files */
1637+
(dir->flags & (DIR_SHOW_IGNORED | DIR_SHOW_IGNORED_TOO |
1638+
DIR_COLLECT_IGNORED)))
1639+
return NULL;
1640+
1641+
/*
1642+
* If we use .gitignore in the cache and now you change it to
1643+
* .gitexclude, everything will go wrong.
1644+
*/
1645+
if (dir->exclude_per_dir != dir->untracked->exclude_per_dir &&
1646+
strcmp(dir->exclude_per_dir, dir->untracked->exclude_per_dir))
1647+
return NULL;
1648+
1649+
/*
1650+
* EXC_CMDL is not considered in the cache. If people set it,
1651+
* skip the cache.
1652+
*/
1653+
if (dir->exclude_list_group[EXC_CMDL].nr)
1654+
return NULL;
1655+
1656+
if (!dir->untracked->root) {
1657+
const int len = sizeof(*dir->untracked->root);
1658+
dir->untracked->root = xmalloc(len);
1659+
memset(dir->untracked->root, 0, len);
1660+
}
1661+
1662+
/* Validate $GIT_DIR/info/exclude and core.excludesfile */
1663+
root = dir->untracked->root;
1664+
if (hashcmp(dir->ss_info_exclude.sha1,
1665+
dir->untracked->ss_info_exclude.sha1)) {
1666+
invalidate_gitignore(dir->untracked, root);
1667+
dir->untracked->ss_info_exclude = dir->ss_info_exclude;
1668+
}
1669+
if (hashcmp(dir->ss_excludes_file.sha1,
1670+
dir->untracked->ss_excludes_file.sha1)) {
1671+
invalidate_gitignore(dir->untracked, root);
1672+
dir->untracked->ss_excludes_file = dir->ss_excludes_file;
1673+
}
1674+
return root;
1675+
}
1676+
15761677
int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
15771678
{
15781679
struct path_simplify *simplify;
1680+
struct untracked_cache_dir *untracked;
15791681

15801682
/*
15811683
* Check out create_simplify()
@@ -1599,10 +1701,15 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
15991701
* create_simplify().
16001702
*/
16011703
simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
1704+
untracked = validate_untracked_cache(dir, len, pathspec);
1705+
if (!untracked)
1706+
/*
1707+
* make sure untracked cache code path is disabled,
1708+
* e.g. prep_exclude()
1709+
*/
1710+
dir->untracked = NULL;
16021711
if (!len || treat_leading_path(dir, path, len, simplify))
1603-
read_directory_recursive(dir, path, len,
1604-
dir->untracked ? dir->untracked->root : NULL,
1605-
0, simplify);
1712+
read_directory_recursive(dir, path, len, untracked, 0, simplify);
16061713
free_simplify(simplify);
16071714
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
16081715
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);

dir.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ struct untracked_cache_dir {
115115
unsigned int untracked_alloc, dirs_nr, dirs_alloc;
116116
unsigned int untracked_nr;
117117
unsigned int check_only : 1;
118+
/* all data in this struct are good */
119+
unsigned int valid : 1;
118120
/* null SHA-1 means this directory does not have .gitignore */
119121
unsigned char exclude_sha1[20];
120122
char name[FLEX_ARRAY];
@@ -132,6 +134,7 @@ struct untracked_cache {
132134
struct untracked_cache_dir *root;
133135
/* Statistics */
134136
int dir_created;
137+
int gitignore_invalidated;
135138
};
136139

137140
struct dir_struct {
@@ -186,6 +189,7 @@ struct dir_struct {
186189
struct untracked_cache *untracked;
187190
struct sha1_stat ss_info_exclude;
188191
struct sha1_stat ss_excludes_file;
192+
unsigned unmanaged_exclude_files;
189193
};
190194

191195
/*

0 commit comments

Comments
 (0)