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 <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nguyễn Thái Ngọc Duy committed
Mar 8, 2015 at 17:12 UTC
ccad261f07900b55029f3fd42a9ec8f17229808f
2 files changed
+114
-3
dir.c
+110
-3
@@ -582,6 +582,22 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
582
return d;
583
}
584
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
+
601
/*
602
* Given a file with name "fname", read it (either from disk, or from
603
* 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,
714
struct sha1_stat *sha1_stat)
715
{
716
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++;
724
el = add_exclude_list(dir, EXC_FILE, fname);
725
if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0)
726
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,
728
729
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
730
{
731
+ dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
732
add_excludes_from_file_1(dir, fname, NULL);
733
}
734
@@ -1573,9 +1597,87 @@ static int treat_leading_path(struct dir_struct *dir,
1597
return rc;
1598
}
1599
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
+
1677
int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
1678
{
1679
struct path_simplify *simplify;
1680
+ struct untracked_cache_dir *untracked;
1681
1682
/*
1683
* Check out create_simplify()
@@ -1599,10 +1701,15 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
1701
* create_simplify().
1702
*/
1703
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;
1711
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);
1713
free_simplify(simplify);
1714
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
1715
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
dir.h
+4
@@ -115,6 +115,8 @@ struct untracked_cache_dir {
115
unsigned int untracked_alloc, dirs_nr, dirs_alloc;
116
unsigned int untracked_nr;
117
unsigned int check_only : 1;
118
+ /* all data in this struct are good */
119
+ unsigned int valid : 1;
120
/* null SHA-1 means this directory does not have .gitignore */
121
unsigned char exclude_sha1[20];
122
char name[FLEX_ARRAY];
@@ -132,6 +134,7 @@ struct untracked_cache {
134
struct untracked_cache_dir *root;
135
/* Statistics */
136
int dir_created;
137
+ int gitignore_invalidated;
138
};
139
140
struct dir_struct {
@@ -186,6 +189,7 @@ struct dir_struct {
189
struct untracked_cache *untracked;
190
struct sha1_stat ss_info_exclude;
191
struct sha1_stat ss_excludes_file;
192
+ unsigned unmanaged_exclude_files;
193
};
194
195
/*