clean: teach clean -d to preserve ignored paths
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index 6b4eeef584309fb31fdf2fac78e220f990344b9a..31c6e1dac008aa3c47e9e4c688c3043b1f91a9b6 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1805,7 +1805,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        return dir_state;
 }
 
-static int cmp_name(const void *p1, const void *p2)
+int cmp_dir_entry(const void *p1, const void *p2)
 {
        const struct dir_entry *e1 = *(const struct dir_entry **)p1;
        const struct dir_entry *e2 = *(const struct dir_entry **)p2;
@@ -1813,6 +1813,14 @@ static int cmp_name(const void *p1, const void *p2)
        return name_compare(e1->name, e1->len, e2->name, e2->len);
 }
 
+/* check if *out lexically strictly contains *in */
+int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in)
+{
+       return (out->len < in->len) &&
+               (out->name[out->len - 1] == '/') &&
+               !memcmp(out->name, in->name, out->len);
+}
+
 static int treat_leading_path(struct dir_struct *dir,
                              const char *path, int len,
                              const struct pathspec *pathspec)
@@ -2026,8 +2034,32 @@ int read_directory(struct dir_struct *dir, const char *path,
                dir->untracked = NULL;
        if (!len || treat_leading_path(dir, path, len, pathspec))
                read_directory_recursive(dir, path, len, untracked, 0, pathspec);
-       QSORT(dir->entries, dir->nr, cmp_name);
-       QSORT(dir->ignored, dir->ignored_nr, cmp_name);
+       QSORT(dir->entries, dir->nr, cmp_dir_entry);
+       QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
+
+       /*
+        * If DIR_SHOW_IGNORED_TOO is set, read_directory_recursive() will
+        * also pick up untracked contents of untracked dirs; by default
+        * we discard these, but given DIR_KEEP_UNTRACKED_CONTENTS we do not.
+        */
+       if ((dir->flags & DIR_SHOW_IGNORED_TOO) &&
+                    !(dir->flags & DIR_KEEP_UNTRACKED_CONTENTS)) {
+               int i, j;
+
+               /* remove from dir->entries untracked contents of untracked dirs */
+               for (i = j = 0; j < dir->nr; j++) {
+                       if (i &&
+                           check_dir_entry_contains(dir->entries[i - 1], dir->entries[j])) {
+                               free(dir->entries[j]);
+                               dir->entries[j] = NULL;
+                       } else {
+                               dir->entries[i++] = dir->entries[j];
+                       }
+               }
+
+               dir->nr = i;
+       }
+
        if (dir->untracked) {
                static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
                trace_printf_key(&trace_untracked_stats,