@@ -59,6 +59,18 @@ void dir_init(struct dir_struct *dir)
5959 memset (dir , 0 , sizeof (* dir ));
6060}
6161
62+ struct dirent *
63+ readdir_skip_dot_and_dotdot (DIR * dirp )
64+ {
65+ struct dirent * e ;
66+
67+ while ((e = readdir (dirp )) != NULL ) {
68+ if (!is_dot_or_dotdot (e -> d_name ))
69+ break ;
70+ }
71+ return e ;
72+ }
73+
6274int count_slashes (const char * s )
6375{
6476 int cnt = 0 ;
@@ -1749,13 +1761,13 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,
17491761 * Case 3: if we didn't have it in the index previously, we
17501762 * have a few sub-cases:
17511763 *
1752- * (a) if "show_other_directories" is true , we show it as
1753- * just a directory, unless "hide_empty_directories" is
1764+ * (a) if DIR_SHOW_OTHER_DIRECTORIES flag is set , we show it as
1765+ * just a directory, unless DIR_HIDE_EMPTY_DIRECTORIES is
17541766 * also true, in which case we need to check if it contains any
17551767 * untracked and / or ignored files.
1756- * (b) if it looks like a git directory, and we don't have
1757- * 'no_gitlinks' set we treat it as a gitlink, and show it
1758- * as a directory.
1768+ * (b) if it looks like a git directory and we don't have the
1769+ * DIR_NO_GITLINKS flag, then we treat it as a gitlink, and
1770+ * show it as a directory.
17591771 * (c) otherwise, we recurse into it.
17601772 */
17611773static enum path_treatment treat_directory (struct dir_struct * dir ,
@@ -1843,7 +1855,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
18431855 return path_recurse ;
18441856 }
18451857
1846- /* This is the "show_other_directories" case */
1858+ assert ( dir -> flags & DIR_SHOW_OTHER_DIRECTORIES );
18471859
18481860 /*
18491861 * If we have a pathspec which could match something _below_ this
@@ -1854,27 +1866,42 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
18541866 if (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC )
18551867 return path_recurse ;
18561868
1869+ /* Special cases for where this directory is excluded/ignored */
1870+ if (excluded ) {
1871+ /*
1872+ * If DIR_SHOW_OTHER_DIRECTORIES is set and we're not
1873+ * hiding empty directories, there is no need to
1874+ * recurse into an ignored directory.
1875+ */
1876+ if (!(dir -> flags & DIR_HIDE_EMPTY_DIRECTORIES ))
1877+ return path_excluded ;
1878+
1879+ /*
1880+ * Even if we are hiding empty directories, we can still avoid
1881+ * recursing into ignored directories for DIR_SHOW_IGNORED_TOO
1882+ * if DIR_SHOW_IGNORED_TOO_MODE_MATCHING is also set.
1883+ */
1884+ if ((dir -> flags & DIR_SHOW_IGNORED_TOO ) &&
1885+ (dir -> flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING ))
1886+ return path_excluded ;
1887+ }
1888+
18571889 /*
1858- * Other than the path_recurse case immediately above, we only need
1859- * to recurse into untracked/ignored directories if either of the
1860- * following bits is set:
1861- * - DIR_SHOW_IGNORED_TOO (because then we need to determine if
1862- * there are ignored entries below)
1890+ * Other than the path_recurse case above, we only need to
1891+ * recurse into untracked directories if any of the following
1892+ * bits is set:
1893+ * - DIR_SHOW_IGNORED (because then we need to determine if
1894+ * there are ignored entries below)
1895+ * - DIR_SHOW_IGNORED_TOO (same as above)
18631896 * - DIR_HIDE_EMPTY_DIRECTORIES (because we have to determine if
18641897 * the directory is empty)
18651898 */
1866- if (!(dir -> flags & (DIR_SHOW_IGNORED_TOO | DIR_HIDE_EMPTY_DIRECTORIES )))
1867- return excluded ? path_excluded : path_untracked ;
1868-
1869- /*
1870- * ...and even if DIR_SHOW_IGNORED_TOO is set, we can still avoid
1871- * recursing into ignored directories if the path is excluded and
1872- * DIR_SHOW_IGNORED_TOO_MODE_MATCHING is also set.
1873- */
1874- if (excluded &&
1875- (dir -> flags & DIR_SHOW_IGNORED_TOO ) &&
1876- (dir -> flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING ))
1877- return path_excluded ;
1899+ if (!excluded &&
1900+ !(dir -> flags & (DIR_SHOW_IGNORED |
1901+ DIR_SHOW_IGNORED_TOO |
1902+ DIR_HIDE_EMPTY_DIRECTORIES ))) {
1903+ return path_untracked ;
1904+ }
18781905
18791906 /*
18801907 * Even if we don't want to know all the paths under an untracked or
@@ -2326,7 +2353,7 @@ static int read_cached_dir(struct cached_dir *cdir)
23262353 struct dirent * de ;
23272354
23282355 if (cdir -> fdir ) {
2329- de = readdir (cdir -> fdir );
2356+ de = readdir_skip_dot_and_dotdot (cdir -> fdir );
23302357 if (!de ) {
23312358 cdir -> d_name = NULL ;
23322359 cdir -> d_type = DT_UNKNOWN ;
@@ -2440,6 +2467,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
24402467
24412468 if (open_cached_dir (& cdir , dir , untracked , istate , & path , check_only ))
24422469 goto out ;
2470+ dir -> visited_directories ++ ;
24432471
24442472 if (untracked )
24452473 untracked -> check_only = !!check_only ;
@@ -2448,6 +2476,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
24482476 /* check how the file or directory should be treated */
24492477 state = treat_path (dir , untracked , & cdir , istate , & path ,
24502478 baselen , pathspec );
2479+ dir -> visited_paths ++ ;
24512480
24522481 if (state > dir_state )
24532482 dir_state = state ;
@@ -2760,15 +2789,53 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
27602789 return root ;
27612790}
27622791
2792+ static void emit_traversal_statistics (struct dir_struct * dir ,
2793+ struct repository * repo ,
2794+ const char * path ,
2795+ int path_len )
2796+ {
2797+ if (!trace2_is_enabled ())
2798+ return ;
2799+
2800+ if (!path_len ) {
2801+ trace2_data_string ("read_directory" , repo , "path" , "" );
2802+ } else {
2803+ struct strbuf tmp = STRBUF_INIT ;
2804+ strbuf_add (& tmp , path , path_len );
2805+ trace2_data_string ("read_directory" , repo , "path" , tmp .buf );
2806+ strbuf_release (& tmp );
2807+ }
2808+
2809+ trace2_data_intmax ("read_directory" , repo ,
2810+ "directories-visited" , dir -> visited_directories );
2811+ trace2_data_intmax ("read_directory" , repo ,
2812+ "paths-visited" , dir -> visited_paths );
2813+
2814+ if (!dir -> untracked )
2815+ return ;
2816+ trace2_data_intmax ("read_directory" , repo ,
2817+ "node-creation" , dir -> untracked -> dir_created );
2818+ trace2_data_intmax ("read_directory" , repo ,
2819+ "gitignore-invalidation" ,
2820+ dir -> untracked -> gitignore_invalidated );
2821+ trace2_data_intmax ("read_directory" , repo ,
2822+ "directory-invalidation" ,
2823+ dir -> untracked -> dir_invalidated );
2824+ trace2_data_intmax ("read_directory" , repo ,
2825+ "opendir" , dir -> untracked -> dir_opened );
2826+ }
2827+
27632828int read_directory (struct dir_struct * dir , struct index_state * istate ,
27642829 const char * path , int len , const struct pathspec * pathspec )
27652830{
27662831 struct untracked_cache_dir * untracked ;
27672832
2768- trace_performance_enter ();
2833+ trace2_region_enter ("dir" , "read_directory" , istate -> repo );
2834+ dir -> visited_paths = 0 ;
2835+ dir -> visited_directories = 0 ;
27692836
27702837 if (has_symlink_leading_path (path , len )) {
2771- trace_performance_leave ( "read directory %.*s " , len , path );
2838+ trace2_region_leave ( "dir " , "read_directory" , istate -> repo );
27722839 return dir -> nr ;
27732840 }
27742841
@@ -2784,23 +2851,15 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
27842851 QSORT (dir -> entries , dir -> nr , cmp_dir_entry );
27852852 QSORT (dir -> ignored , dir -> ignored_nr , cmp_dir_entry );
27862853
2787- trace_performance_leave ("read directory %.*s" , len , path );
2854+ emit_traversal_statistics (dir , istate -> repo , path , len );
2855+
2856+ trace2_region_leave ("dir" , "read_directory" , istate -> repo );
27882857 if (dir -> untracked ) {
27892858 static int force_untracked_cache = -1 ;
2790- static struct trace_key trace_untracked_stats = TRACE_KEY_INIT (UNTRACKED_STATS );
27912859
27922860 if (force_untracked_cache < 0 )
27932861 force_untracked_cache =
27942862 git_env_bool ("GIT_FORCE_UNTRACKED_CACHE" , 0 );
2795- trace_printf_key (& trace_untracked_stats ,
2796- "node creation: %u\n"
2797- "gitignore invalidation: %u\n"
2798- "directory invalidation: %u\n"
2799- "opendir: %u\n" ,
2800- dir -> untracked -> dir_created ,
2801- dir -> untracked -> gitignore_invalidated ,
2802- dir -> untracked -> dir_invalidated ,
2803- dir -> untracked -> dir_opened );
28042863 if (force_untracked_cache &&
28052864 dir -> untracked == istate -> untracked &&
28062865 (dir -> untracked -> dir_opened ||
@@ -2811,6 +2870,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
28112870 FREE_AND_NULL (dir -> untracked );
28122871 }
28132872 }
2873+
28142874 return dir -> nr ;
28152875}
28162876
@@ -2892,11 +2952,9 @@ int is_empty_dir(const char *path)
28922952 if (!dir )
28932953 return 0 ;
28942954
2895- while ((e = readdir (dir )) != NULL )
2896- if (!is_dot_or_dotdot (e -> d_name )) {
2897- ret = 0 ;
2898- break ;
2899- }
2955+ e = readdir_skip_dot_and_dotdot (dir );
2956+ if (e )
2957+ ret = 0 ;
29002958
29012959 closedir (dir );
29022960 return ret ;
@@ -2936,10 +2994,8 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
29362994 strbuf_complete (path , '/' );
29372995
29382996 len = path -> len ;
2939- while ((e = readdir (dir )) != NULL ) {
2997+ while ((e = readdir_skip_dot_and_dotdot (dir )) != NULL ) {
29402998 struct stat st ;
2941- if (is_dot_or_dotdot (e -> d_name ))
2942- continue ;
29432999
29443000 strbuf_setlen (path , len );
29453001 strbuf_addstr (path , e -> d_name );
0 commit comments