]> git.ipfire.org Git - thirdparty/git.git/blobdiff - dir.c
Merge branch 'ab/trace2-squelch-gcc-warning'
[thirdparty/git.git] / dir.c
diff --git a/dir.c b/dir.c
index c617dc763bd313555d0c358dc4cb49aabd05fa5c..ebe5ec046e050683443507495b3dcfa903c420e4 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -59,6 +59,17 @@ void dir_init(struct dir_struct *dir)
        memset(dir, 0, sizeof(*dir));
 }
 
+struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp)
+{
+       struct dirent *e;
+
+       while ((e = readdir(dirp)) != NULL) {
+               if (!is_dot_or_dotdot(e->d_name))
+                       break;
+       }
+       return e;
+}
+
 int count_slashes(const char *s)
 {
        int cnt = 0;
@@ -1749,13 +1760,13 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,
  * Case 3: if we didn't have it in the index previously, we
  * have a few sub-cases:
  *
- *  (a) if "show_other_directories" is true, we show it as
- *      just a directory, unless "hide_empty_directories" is
+ *  (a) if DIR_SHOW_OTHER_DIRECTORIES flag is set, we show it as
+ *      just a directory, unless DIR_HIDE_EMPTY_DIRECTORIES is
  *      also true, in which case we need to check if it contains any
  *      untracked and / or ignored files.
- *  (b) if it looks like a git directory, and we don't have
- *      'no_gitlinks' set we treat it as a gitlink, and show it
- *      as a directory.
+ *  (b) if it looks like a git directory and we don't have the
+ *      DIR_NO_GITLINKS flag, then we treat it as a gitlink, and
+ *      show it as a directory.
  *  (c) otherwise, we recurse into it.
  */
 static enum path_treatment treat_directory(struct dir_struct *dir,
@@ -1843,7 +1854,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
                return path_recurse;
        }
 
-       /* This is the "show_other_directories" case */
+       assert(dir->flags & DIR_SHOW_OTHER_DIRECTORIES);
 
        /*
         * If we have a pathspec which could match something _below_ this
@@ -1854,27 +1865,42 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
        if (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC)
                return path_recurse;
 
+       /* Special cases for where this directory is excluded/ignored */
+       if (excluded) {
+               /*
+                * If DIR_SHOW_OTHER_DIRECTORIES is set and we're not
+                * hiding empty directories, there is no need to
+                * recurse into an ignored directory.
+                */
+               if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
+                       return path_excluded;
+
+               /*
+                * Even if we are hiding empty directories, we can still avoid
+                * recursing into ignored directories for DIR_SHOW_IGNORED_TOO
+                * if DIR_SHOW_IGNORED_TOO_MODE_MATCHING is also set.
+                */
+               if ((dir->flags & DIR_SHOW_IGNORED_TOO) &&
+                   (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))
+                       return path_excluded;
+       }
+
        /*
-        * Other than the path_recurse case immediately above, we only need
-        * to recurse into untracked/ignored directories if either of the
-        * following bits is set:
-        *   - DIR_SHOW_IGNORED_TOO (because then we need to determine if
-        *                           there are ignored entries below)
+        * Other than the path_recurse case above, we only need to
+        * recurse into untracked directories if any of the following
+        * bits is set:
+        *   - DIR_SHOW_IGNORED (because then we need to determine if
+        *                       there are ignored entries below)
+        *   - DIR_SHOW_IGNORED_TOO (same as above)
         *   - DIR_HIDE_EMPTY_DIRECTORIES (because we have to determine if
         *                                 the directory is empty)
         */
-       if (!(dir->flags & (DIR_SHOW_IGNORED_TOO | DIR_HIDE_EMPTY_DIRECTORIES)))
-               return excluded ? path_excluded : path_untracked;
-
-       /*
-        * ...and even if DIR_SHOW_IGNORED_TOO is set, we can still avoid
-        * recursing into ignored directories if the path is excluded and
-        * DIR_SHOW_IGNORED_TOO_MODE_MATCHING is also set.
-        */
-       if (excluded &&
-           (dir->flags & DIR_SHOW_IGNORED_TOO) &&
-           (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))
-               return path_excluded;
+       if (!excluded &&
+           !(dir->flags & (DIR_SHOW_IGNORED |
+                           DIR_SHOW_IGNORED_TOO |
+                           DIR_HIDE_EMPTY_DIRECTORIES))) {
+               return path_untracked;
+       }
 
        /*
         * Even if we don't want to know all the paths under an untracked or
@@ -2326,7 +2352,7 @@ static int read_cached_dir(struct cached_dir *cdir)
        struct dirent *de;
 
        if (cdir->fdir) {
-               de = readdir(cdir->fdir);
+               de = readdir_skip_dot_and_dotdot(cdir->fdir);
                if (!de) {
                        cdir->d_name = NULL;
                        cdir->d_type = DT_UNKNOWN;
@@ -2440,6 +2466,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 
        if (open_cached_dir(&cdir, dir, untracked, istate, &path, check_only))
                goto out;
+       dir->visited_directories++;
 
        if (untracked)
                untracked->check_only = !!check_only;
@@ -2448,6 +2475,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                /* check how the file or directory should be treated */
                state = treat_path(dir, untracked, &cdir, istate, &path,
                                   baselen, pathspec);
+               dir->visited_paths++;
 
                if (state > dir_state)
                        dir_state = state;
@@ -2760,15 +2788,53 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
        return root;
 }
 
+static void emit_traversal_statistics(struct dir_struct *dir,
+                                     struct repository *repo,
+                                     const char *path,
+                                     int path_len)
+{
+       if (!trace2_is_enabled())
+               return;
+
+       if (!path_len) {
+               trace2_data_string("read_directory", repo, "path", "");
+       } else {
+               struct strbuf tmp = STRBUF_INIT;
+               strbuf_add(&tmp, path, path_len);
+               trace2_data_string("read_directory", repo, "path", tmp.buf);
+               strbuf_release(&tmp);
+       }
+
+       trace2_data_intmax("read_directory", repo,
+                          "directories-visited", dir->visited_directories);
+       trace2_data_intmax("read_directory", repo,
+                          "paths-visited", dir->visited_paths);
+
+       if (!dir->untracked)
+               return;
+       trace2_data_intmax("read_directory", repo,
+                          "node-creation", dir->untracked->dir_created);
+       trace2_data_intmax("read_directory", repo,
+                          "gitignore-invalidation",
+                          dir->untracked->gitignore_invalidated);
+       trace2_data_intmax("read_directory", repo,
+                          "directory-invalidation",
+                          dir->untracked->dir_invalidated);
+       trace2_data_intmax("read_directory", repo,
+                          "opendir", dir->untracked->dir_opened);
+}
+
 int read_directory(struct dir_struct *dir, struct index_state *istate,
                   const char *path, int len, const struct pathspec *pathspec)
 {
        struct untracked_cache_dir *untracked;
 
-       trace_performance_enter();
+       trace2_region_enter("dir", "read_directory", istate->repo);
+       dir->visited_paths = 0;
+       dir->visited_directories = 0;
 
        if (has_symlink_leading_path(path, len)) {
-               trace_performance_leave("read directory %.*s", len, path);
+               trace2_region_leave("dir", "read_directory", istate->repo);
                return dir->nr;
        }
 
@@ -2784,23 +2850,15 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
        QSORT(dir->entries, dir->nr, cmp_dir_entry);
        QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
 
-       trace_performance_leave("read directory %.*s", len, path);
+       emit_traversal_statistics(dir, istate->repo, path, len);
+
+       trace2_region_leave("dir", "read_directory", istate->repo);
        if (dir->untracked) {
                static int force_untracked_cache = -1;
-               static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
 
                if (force_untracked_cache < 0)
                        force_untracked_cache =
                                git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
-               trace_printf_key(&trace_untracked_stats,
-                                "node creation: %u\n"
-                                "gitignore invalidation: %u\n"
-                                "directory invalidation: %u\n"
-                                "opendir: %u\n",
-                                dir->untracked->dir_created,
-                                dir->untracked->gitignore_invalidated,
-                                dir->untracked->dir_invalidated,
-                                dir->untracked->dir_opened);
                if (force_untracked_cache &&
                        dir->untracked == istate->untracked &&
                    (dir->untracked->dir_opened ||
@@ -2811,6 +2869,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
                        FREE_AND_NULL(dir->untracked);
                }
        }
+
        return dir->nr;
 }
 
@@ -2892,11 +2951,9 @@ int is_empty_dir(const char *path)
        if (!dir)
                return 0;
 
-       while ((e = readdir(dir)) != NULL)
-               if (!is_dot_or_dotdot(e->d_name)) {
-                       ret = 0;
-                       break;
-               }
+       e = readdir_skip_dot_and_dotdot(dir);
+       if (e)
+               ret = 0;
 
        closedir(dir);
        return ret;
@@ -2936,10 +2993,8 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
        strbuf_complete(path, '/');
 
        len = path->len;
-       while ((e = readdir(dir)) != NULL) {
+       while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
                struct stat st;
-               if (is_dot_or_dotdot(e->d_name))
-                       continue;
 
                strbuf_setlen(path, len);
                strbuf_addstr(path, e->d_name);