]> git.ipfire.org Git - thirdparty/git.git/commitdiff
dir: add generic "walk all files" helper
authorDerrick Stolee <stolee@gmail.com>
Fri, 12 Sep 2025 10:30:08 +0000 (10:30 +0000)
committerJunio C Hamano <gitster@pobox.com>
Fri, 12 Sep 2025 15:59:52 +0000 (08:59 -0700)
There is sometimes a need to visit every file within a directory,
recursively. The main example is remove_dir_recursively(), though it has
some extra flags that make it want to iterate over paths in a custom
way. There is also the fill_directory() approach but that involves an
index and a pathspec.

This change adds a new for_each_file_in_dir() method that will be
helpful in the next change.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dir.c
dir.h

diff --git a/dir.c b/dir.c
index dfb4d40103fb4a7d38c3748c2ab4acd8e1be1930..7731f7b9d305b7ca25e7a154cd93cdc790ede9e2 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -30,6 +30,7 @@
 #include "read-cache-ll.h"
 #include "setup.h"
 #include "sparse-index.h"
+#include "strbuf.h"
 #include "submodule-config.h"
 #include "symlinks.h"
 #include "trace2.h"
@@ -87,6 +88,33 @@ struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp)
        return e;
 }
 
+int for_each_file_in_dir(struct strbuf *path, file_iterator fn, const void *data)
+{
+       struct dirent *e;
+       int res = 0;
+       size_t baselen = path->len;
+       DIR *dir = opendir(path->buf);
+
+       if (!dir)
+               return 0;
+
+       while (!res && (e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
+               unsigned char dtype = get_dtype(e, path, 0);
+               strbuf_setlen(path, baselen);
+               strbuf_addstr(path, e->d_name);
+
+               if (dtype == DT_REG) {
+                       res = fn(path->buf, data);
+               } else if (dtype == DT_DIR) {
+                       strbuf_addch(path, '/');
+                       res = for_each_file_in_dir(path, fn, data);
+               }
+       }
+
+       closedir(dir);
+       return res;
+}
+
 int count_slashes(const char *s)
 {
        int cnt = 0;
diff --git a/dir.h b/dir.h
index fc9be7b427a134e46bcd66c8df42375db47727fc..20d4a078d61ef8851c668b37d250860fc5d6f677 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -536,6 +536,20 @@ int get_sparse_checkout_patterns(struct pattern_list *pl);
  */
 int remove_dir_recursively(struct strbuf *path, int flag);
 
+/*
+ * This function pointer type is called on each file discovered in
+ * for_each_file_in_dir. The iteration stops if this method returns
+ * non-zero.
+ */
+typedef int (*file_iterator)(const char *path, const void *data);
+
+struct strbuf;
+/*
+ * Given a directory path, recursively visit each file within, including
+ * within subdirectories.
+ */
+int for_each_file_in_dir(struct strbuf *path, file_iterator fn, const void *data);
+
 /*
  * Tries to remove the path, along with leading empty directories so long as
  * those empty directories are not startup_info->original_cwd.  Ignores