@@ -1078,10 +1078,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
10781078 (!untracked || !untracked -> valid ||
10791079 /*
10801080 * .. and .gitignore does not exist before
1081- * (i.e. null exclude_sha1 and skip_worktree is
1082- * not set). Then we can skip loading .gitignore,
1083- * which would result in ENOENT anyway.
1084- * skip_worktree is taken care in read_directory()
1081+ * (i.e. null exclude_sha1). Then we can skip
1082+ * loading .gitignore, which would result in
1083+ * ENOENT anyway.
10851084 */
10861085 !is_null_sha1 (untracked -> exclude_sha1 ))) {
10871086 /*
@@ -1298,7 +1297,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
12981297 */
12991298static enum path_treatment treat_directory (struct dir_struct * dir ,
13001299 struct untracked_cache_dir * untracked ,
1301- const char * dirname , int len , int exclude ,
1300+ const char * dirname , int len , int baselen , int exclude ,
13021301 const struct path_simplify * simplify )
13031302{
13041303 /* The "len-1" is to strip the final '/' */
@@ -1325,7 +1324,8 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
13251324 if (!(dir -> flags & DIR_HIDE_EMPTY_DIRECTORIES ))
13261325 return exclude ? path_excluded : path_untracked ;
13271326
1328- untracked = lookup_untracked (dir -> untracked , untracked , dirname , len );
1327+ untracked = lookup_untracked (dir -> untracked , untracked ,
1328+ dirname + baselen , len - baselen );
13291329 return read_directory_recursive (dir , dirname , len ,
13301330 untracked , 1 , simplify );
13311331}
@@ -1445,6 +1445,7 @@ static int get_dtype(struct dirent *de, const char *path, int len)
14451445static enum path_treatment treat_one_path (struct dir_struct * dir ,
14461446 struct untracked_cache_dir * untracked ,
14471447 struct strbuf * path ,
1448+ int baselen ,
14481449 const struct path_simplify * simplify ,
14491450 int dtype , struct dirent * de )
14501451{
@@ -1496,8 +1497,8 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
14961497 return path_none ;
14971498 case DT_DIR :
14981499 strbuf_addch (path , '/' );
1499- return treat_directory (dir , untracked , path -> buf , path -> len , exclude ,
1500- simplify );
1500+ return treat_directory (dir , untracked , path -> buf , path -> len ,
1501+ baselen , exclude , simplify );
15011502 case DT_REG :
15021503 case DT_LNK :
15031504 return exclude ? path_excluded : path_untracked ;
@@ -1558,7 +1559,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
15581559 return path_none ;
15591560
15601561 dtype = DTYPE (de );
1561- return treat_one_path (dir , untracked , path , simplify , dtype , de );
1562+ return treat_one_path (dir , untracked , path , baselen , simplify , dtype , de );
15621563}
15631564
15641565static void add_untracked (struct untracked_cache_dir * dir , const char * name )
@@ -1828,7 +1829,7 @@ static int treat_leading_path(struct dir_struct *dir,
18281829 break ;
18291830 if (simplify_away (sb .buf , sb .len , simplify ))
18301831 break ;
1831- if (treat_one_path (dir , NULL , & sb , simplify ,
1832+ if (treat_one_path (dir , NULL , & sb , baselen , simplify ,
18321833 DT_DIR , NULL ) == path_none )
18331834 break ; /* do not recurse into it */
18341835 if (len <= baselen ) {
@@ -1880,7 +1881,6 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
18801881 const struct pathspec * pathspec )
18811882{
18821883 struct untracked_cache_dir * root ;
1883- int i ;
18841884
18851885 if (!dir -> untracked || getenv ("GIT_DISABLE_UNTRACKED_CACHE" ))
18861886 return NULL ;
@@ -1932,15 +1932,6 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
19321932 if (dir -> exclude_list_group [EXC_CMDL ].nr )
19331933 return NULL ;
19341934
1935- /*
1936- * An optimization in prep_exclude() does not play well with
1937- * CE_SKIP_WORKTREE. It's a rare case anyway, if a single
1938- * entry has that bit set, disable the whole untracked cache.
1939- */
1940- for (i = 0 ; i < active_nr ; i ++ )
1941- if (ce_skip_worktree (active_cache [i ]))
1942- return NULL ;
1943-
19441935 if (!ident_in_untracked (dir -> untracked )) {
19451936 warning (_ ("Untracked cache is disabled on this system." ));
19461937 return NULL ;
@@ -2625,23 +2616,67 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
26252616 return uc ;
26262617}
26272618
2619+ static void invalidate_one_directory (struct untracked_cache * uc ,
2620+ struct untracked_cache_dir * ucd )
2621+ {
2622+ uc -> dir_invalidated ++ ;
2623+ ucd -> valid = 0 ;
2624+ ucd -> untracked_nr = 0 ;
2625+ }
2626+
2627+ /*
2628+ * Normally when an entry is added or removed from a directory,
2629+ * invalidating that directory is enough. No need to touch its
2630+ * ancestors. When a directory is shown as "foo/bar/" in git-status
2631+ * however, deleting or adding an entry may have cascading effect.
2632+ *
2633+ * Say the "foo/bar/file" has become untracked, we need to tell the
2634+ * untracked_cache_dir of "foo" that "bar/" is not an untracked
2635+ * directory any more (because "bar" is managed by foo as an untracked
2636+ * "file").
2637+ *
2638+ * Similarly, if "foo/bar/file" moves from untracked to tracked and it
2639+ * was the last untracked entry in the entire "foo", we should show
2640+ * "foo/" instead. Which means we have to invalidate past "bar" up to
2641+ * "foo".
2642+ *
2643+ * This function traverses all directories from root to leaf. If there
2644+ * is a chance of one of the above cases happening, we invalidate back
2645+ * to root. Otherwise we just invalidate the leaf. There may be a more
2646+ * sophisticated way than checking for SHOW_OTHER_DIRECTORIES to
2647+ * detect these cases and avoid unnecessary invalidation, for example,
2648+ * checking for the untracked entry named "bar/" in "foo", but for now
2649+ * stick to something safe and simple.
2650+ */
2651+ static int invalidate_one_component (struct untracked_cache * uc ,
2652+ struct untracked_cache_dir * dir ,
2653+ const char * path , int len )
2654+ {
2655+ const char * rest = strchr (path , '/' );
2656+
2657+ if (rest ) {
2658+ int component_len = rest - path ;
2659+ struct untracked_cache_dir * d =
2660+ lookup_untracked (uc , dir , path , component_len );
2661+ int ret =
2662+ invalidate_one_component (uc , d , rest + 1 ,
2663+ len - (component_len + 1 ));
2664+ if (ret )
2665+ invalidate_one_directory (uc , dir );
2666+ return ret ;
2667+ }
2668+
2669+ invalidate_one_directory (uc , dir );
2670+ return uc -> dir_flags & DIR_SHOW_OTHER_DIRECTORIES ;
2671+ }
2672+
26282673void untracked_cache_invalidate_path (struct index_state * istate ,
26292674 const char * path )
26302675{
2631- const char * sep ;
2632- struct untracked_cache_dir * d ;
26332676 if (!istate -> untracked || !istate -> untracked -> root )
26342677 return ;
2635- sep = strrchr (path , '/' );
2636- if (sep )
2637- d = lookup_untracked (istate -> untracked ,
2638- istate -> untracked -> root ,
2639- path , sep - path );
2640- else
2641- d = istate -> untracked -> root ;
2642- istate -> untracked -> dir_invalidated ++ ;
2643- d -> valid = 0 ;
2644- d -> untracked_nr = 0 ;
2678+ invalidate_one_component (istate -> untracked , istate -> untracked -> root ,
2679+ path , strlen (path ));
26452680}
26462681
26472682void untracked_cache_remove_from_index (struct index_state * istate ,
0 commit comments