@@ -1297,7 +1297,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
12971297 */
12981298static enum path_treatment treat_directory (struct dir_struct * dir ,
12991299 struct untracked_cache_dir * untracked ,
1300- const char * dirname , int len , int exclude ,
1300+ const char * dirname , int len , int baselen , int exclude ,
13011301 const struct path_simplify * simplify )
13021302{
13031303 /* The "len-1" is to strip the final '/' */
@@ -1324,7 +1324,8 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
13241324 if (!(dir -> flags & DIR_HIDE_EMPTY_DIRECTORIES ))
13251325 return exclude ? path_excluded : path_untracked ;
13261326
1327- untracked = lookup_untracked (dir -> untracked , untracked , dirname , len );
1327+ untracked = lookup_untracked (dir -> untracked , untracked ,
1328+ dirname + baselen , len - baselen );
13281329 return read_directory_recursive (dir , dirname , len ,
13291330 untracked , 1 , simplify );
13301331}
@@ -1444,6 +1445,7 @@ static int get_dtype(struct dirent *de, const char *path, int len)
14441445static enum path_treatment treat_one_path (struct dir_struct * dir ,
14451446 struct untracked_cache_dir * untracked ,
14461447 struct strbuf * path ,
1448+ int baselen ,
14471449 const struct path_simplify * simplify ,
14481450 int dtype , struct dirent * de )
14491451{
@@ -1495,8 +1497,8 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
14951497 return path_none ;
14961498 case DT_DIR :
14971499 strbuf_addch (path , '/' );
1498- return treat_directory (dir , untracked , path -> buf , path -> len , exclude ,
1499- simplify );
1500+ return treat_directory (dir , untracked , path -> buf , path -> len ,
1501+ baselen , exclude , simplify );
15001502 case DT_REG :
15011503 case DT_LNK :
15021504 return exclude ? path_excluded : path_untracked ;
@@ -1557,7 +1559,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
15571559 return path_none ;
15581560
15591561 dtype = DTYPE (de );
1560- return treat_one_path (dir , untracked , path , simplify , dtype , de );
1562+ return treat_one_path (dir , untracked , path , baselen , simplify , dtype , de );
15611563}
15621564
15631565static void add_untracked (struct untracked_cache_dir * dir , const char * name )
@@ -1827,7 +1829,7 @@ static int treat_leading_path(struct dir_struct *dir,
18271829 break ;
18281830 if (simplify_away (sb .buf , sb .len , simplify ))
18291831 break ;
1830- if (treat_one_path (dir , NULL , & sb , simplify ,
1832+ if (treat_one_path (dir , NULL , & sb , baselen , simplify ,
18311833 DT_DIR , NULL ) == path_none )
18321834 break ; /* do not recurse into it */
18331835 if (len <= baselen ) {
@@ -2616,23 +2618,67 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
26162618 return uc ;
26172619}
26182620
2621+ static void invalidate_one_directory (struct untracked_cache * uc ,
2622+ struct untracked_cache_dir * ucd )
2623+ {
2624+ uc -> dir_invalidated ++ ;
2625+ ucd -> valid = 0 ;
2626+ ucd -> untracked_nr = 0 ;
2627+ }
2628+
2629+ /*
2630+ * Normally when an entry is added or removed from a directory,
2631+ * invalidating that directory is enough. No need to touch its
2632+ * ancestors. When a directory is shown as "foo/bar/" in git-status
2633+ * however, deleting or adding an entry may have cascading effect.
2634+ *
2635+ * Say the "foo/bar/file" has become untracked, we need to tell the
2636+ * untracked_cache_dir of "foo" that "bar/" is not an untracked
2637+ * directory any more (because "bar" is managed by foo as an untracked
2638+ * "file").
2639+ *
2640+ * Similarly, if "foo/bar/file" moves from untracked to tracked and it
2641+ * was the last untracked entry in the entire "foo", we should show
2642+ * "foo/" instead. Which means we have to invalidate past "bar" up to
2643+ * "foo".
2644+ *
2645+ * This function traverses all directories from root to leaf. If there
2646+ * is a chance of one of the above cases happening, we invalidate back
2647+ * to root. Otherwise we just invalidate the leaf. There may be a more
2648+ * sophisticated way than checking for SHOW_OTHER_DIRECTORIES to
2649+ * detect these cases and avoid unnecessary invalidation, for example,
2650+ * checking for the untracked entry named "bar/" in "foo", but for now
2651+ * stick to something safe and simple.
2652+ */
2653+ static int invalidate_one_component (struct untracked_cache * uc ,
2654+ struct untracked_cache_dir * dir ,
2655+ const char * path , int len )
2656+ {
2657+ const char * rest = strchr (path , '/' );
2658+
2659+ if (rest ) {
2660+ int component_len = rest - path ;
2661+ struct untracked_cache_dir * d =
2662+ lookup_untracked (uc , dir , path , component_len );
2663+ int ret =
2664+ invalidate_one_component (uc , d , rest + 1 ,
2665+ len - (component_len + 1 ));
2666+ if (ret )
2667+ invalidate_one_directory (uc , dir );
2668+ return ret ;
2669+ }
2670+
2671+ invalidate_one_directory (uc , dir );
2672+ return uc -> dir_flags & DIR_SHOW_OTHER_DIRECTORIES ;
2673+ }
2674+
26192675void untracked_cache_invalidate_path (struct index_state * istate ,
26202676 const char * path )
26212677{
2622- const char * sep ;
2623- struct untracked_cache_dir * d ;
26242678 if (!istate -> untracked || !istate -> untracked -> root )
26252679 return ;
2626- sep = strrchr (path , '/' );
2627- if (sep )
2628- d = lookup_untracked (istate -> untracked ,
2629- istate -> untracked -> root ,
2630- path , sep - path );
2631- else
2632- d = istate -> untracked -> root ;
2633- istate -> untracked -> dir_invalidated ++ ;
2634- d -> valid = 0 ;
2635- d -> untracked_nr = 0 ;
2680+ invalidate_one_component (istate -> untracked , istate -> untracked -> root ,
2681+ path , strlen (path ));
26362682}
26372683
26382684void untracked_cache_remove_from_index (struct index_state * istate ,
0 commit comments