@@ -59,6 +59,18 @@ void dir_init(struct dir_struct *dir)
59
59
memset (dir , 0 , sizeof (* dir ));
60
60
}
61
61
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
+
62
74
int count_slashes (const char * s )
63
75
{
64
76
int cnt = 0 ;
@@ -1749,13 +1761,13 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,
1749
1761
* Case 3: if we didn't have it in the index previously, we
1750
1762
* have a few sub-cases:
1751
1763
*
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
1754
1766
* also true, in which case we need to check if it contains any
1755
1767
* 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.
1759
1771
* (c) otherwise, we recurse into it.
1760
1772
*/
1761
1773
static enum path_treatment treat_directory (struct dir_struct * dir ,
@@ -1843,7 +1855,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
1843
1855
return path_recurse ;
1844
1856
}
1845
1857
1846
- /* This is the "show_other_directories" case */
1858
+ assert ( dir -> flags & DIR_SHOW_OTHER_DIRECTORIES );
1847
1859
1848
1860
/*
1849
1861
* 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,
1854
1866
if (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC )
1855
1867
return path_recurse ;
1856
1868
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
+
1857
1889
/*
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)
1863
1896
* - DIR_HIDE_EMPTY_DIRECTORIES (because we have to determine if
1864
1897
* the directory is empty)
1865
1898
*/
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
+ }
1878
1905
1879
1906
/*
1880
1907
* 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)
2326
2353
struct dirent * de ;
2327
2354
2328
2355
if (cdir -> fdir ) {
2329
- de = readdir (cdir -> fdir );
2356
+ de = readdir_skip_dot_and_dotdot (cdir -> fdir );
2330
2357
if (!de ) {
2331
2358
cdir -> d_name = NULL ;
2332
2359
cdir -> d_type = DT_UNKNOWN ;
@@ -2440,6 +2467,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
2440
2467
2441
2468
if (open_cached_dir (& cdir , dir , untracked , istate , & path , check_only ))
2442
2469
goto out ;
2470
+ dir -> visited_directories ++ ;
2443
2471
2444
2472
if (untracked )
2445
2473
untracked -> check_only = !!check_only ;
@@ -2448,6 +2476,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
2448
2476
/* check how the file or directory should be treated */
2449
2477
state = treat_path (dir , untracked , & cdir , istate , & path ,
2450
2478
baselen , pathspec );
2479
+ dir -> visited_paths ++ ;
2451
2480
2452
2481
if (state > dir_state )
2453
2482
dir_state = state ;
@@ -2760,15 +2789,53 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
2760
2789
return root ;
2761
2790
}
2762
2791
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
+
2763
2828
int read_directory (struct dir_struct * dir , struct index_state * istate ,
2764
2829
const char * path , int len , const struct pathspec * pathspec )
2765
2830
{
2766
2831
struct untracked_cache_dir * untracked ;
2767
2832
2768
- trace_performance_enter ();
2833
+ trace2_region_enter ("dir" , "read_directory" , istate -> repo );
2834
+ dir -> visited_paths = 0 ;
2835
+ dir -> visited_directories = 0 ;
2769
2836
2770
2837
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 );
2772
2839
return dir -> nr ;
2773
2840
}
2774
2841
@@ -2784,23 +2851,15 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
2784
2851
QSORT (dir -> entries , dir -> nr , cmp_dir_entry );
2785
2852
QSORT (dir -> ignored , dir -> ignored_nr , cmp_dir_entry );
2786
2853
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 );
2788
2857
if (dir -> untracked ) {
2789
2858
static int force_untracked_cache = -1 ;
2790
- static struct trace_key trace_untracked_stats = TRACE_KEY_INIT (UNTRACKED_STATS );
2791
2859
2792
2860
if (force_untracked_cache < 0 )
2793
2861
force_untracked_cache =
2794
2862
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 );
2804
2863
if (force_untracked_cache &&
2805
2864
dir -> untracked == istate -> untracked &&
2806
2865
(dir -> untracked -> dir_opened ||
@@ -2811,6 +2870,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
2811
2870
FREE_AND_NULL (dir -> untracked );
2812
2871
}
2813
2872
}
2873
+
2814
2874
return dir -> nr ;
2815
2875
}
2816
2876
@@ -2892,11 +2952,9 @@ int is_empty_dir(const char *path)
2892
2952
if (!dir )
2893
2953
return 0 ;
2894
2954
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 ;
2900
2958
2901
2959
closedir (dir );
2902
2960
return ret ;
@@ -2936,10 +2994,8 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
2936
2994
strbuf_complete (path , '/' );
2937
2995
2938
2996
len = path -> len ;
2939
- while ((e = readdir (dir )) != NULL ) {
2997
+ while ((e = readdir_skip_dot_and_dotdot (dir )) != NULL ) {
2940
2998
struct stat st ;
2941
- if (is_dot_or_dotdot (e -> d_name ))
2942
- continue ;
2943
2999
2944
3000
strbuf_setlen (path , len );
2945
3001
strbuf_addstr (path , e -> d_name );
0 commit comments