git-clean: refactor git-clean into two phases
[gitweb.git] / builtin / clean.c
index 04e396b17acc2443a663dd085ef812c3c2746d27..77ec1f350feb96f25b79da7f1ef0e51ed015d28b 100644 (file)
@@ -15,6 +15,7 @@
 #include "quote.h"
 
 static int force = -1; /* unset */
+static struct string_list del_list = STRING_LIST_INIT_DUP;
 
 static const char *const builtin_clean_usage[] = {
        N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
@@ -56,7 +57,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
        if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
                        !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
                if (!quiet) {
-                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       quote_path_relative(path->buf, prefix, &quoted);
                        printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
                                        quoted.buf);
                }
@@ -70,7 +71,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                /* an empty dir could be removed even if it is unreadble */
                res = dry_run ? 0 : rmdir(path->buf);
                if (res) {
-                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       quote_path_relative(path->buf, prefix, &quoted);
                        warning(_(msg_warn_remove_failed), quoted.buf);
                        *dir_gone = 0;
                }
@@ -94,7 +95,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                        if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
                                ret = 1;
                        if (gone) {
-                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               quote_path_relative(path->buf, prefix, &quoted);
                                string_list_append(&dels, quoted.buf);
                        } else
                                *dir_gone = 0;
@@ -102,10 +103,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                } else {
                        res = dry_run ? 0 : unlink(path->buf);
                        if (!res) {
-                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               quote_path_relative(path->buf, prefix, &quoted);
                                string_list_append(&dels, quoted.buf);
                        } else {
-                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               quote_path_relative(path->buf, prefix, &quoted);
                                warning(_(msg_warn_remove_failed), quoted.buf);
                                *dir_gone = 0;
                                ret = 1;
@@ -127,7 +128,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                if (!res)
                        *dir_gone = 1;
                else {
-                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       quote_path_relative(path->buf, prefix, &quoted);
                        warning(_(msg_warn_remove_failed), quoted.buf);
                        *dir_gone = 0;
                        ret = 1;
@@ -148,12 +149,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
        int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
        int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
-       struct strbuf directory = STRBUF_INIT;
+       struct strbuf abs_path = STRBUF_INIT;
        struct dir_struct dir;
        static const char **pathspec;
        struct strbuf buf = STRBUF_INIT;
        struct string_list exclude_list = STRING_LIST_INIT_NODUP;
        struct exclude_list *el;
+       struct string_list_item *item;
        const char *qname;
        char *seen = NULL;
        struct option options[] = {
@@ -223,6 +225,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                int matches = 0;
                struct cache_entry *ce;
                struct stat st;
+               const char *rel;
 
                /*
                 * Remove the '/' at the end that directory
@@ -242,13 +245,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                continue; /* Yup, this one exists unmerged */
                }
 
-               /*
-                * we might have removed this as part of earlier
-                * recursive directory removal, so lstat() here could
-                * fail with ENOENT.
-                */
                if (lstat(ent->name, &st))
-                       continue;
+                       die_errno("Cannot lstat '%s'", ent->name);
 
                if (pathspec) {
                        memset(seen, 0, argc > 0 ? argc : 1);
@@ -257,33 +255,61 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                }
 
                if (S_ISDIR(st.st_mode)) {
-                       strbuf_addstr(&directory, ent->name);
                        if (remove_directories || (matches == MATCHED_EXACTLY)) {
-                               if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
-                                       errors++;
-                               if (gone && !quiet) {
-                                       qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
-                                       printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
-                               }
+                               rel = relative_path(ent->name, prefix, &buf);
+                               string_list_append(&del_list, rel);
                        }
-                       strbuf_reset(&directory);
                } else {
                        if (pathspec && !matches)
                                continue;
-                       res = dry_run ? 0 : unlink(ent->name);
+                       rel = relative_path(ent->name, prefix, &buf);
+                       string_list_append(&del_list, rel);
+               }
+       }
+
+       /* TODO: do interactive git-clean here, which will modify del_list */
+
+       for_each_string_list_item(item, &del_list) {
+               struct stat st;
+
+               if (prefix)
+                       strbuf_addstr(&abs_path, prefix);
+
+               strbuf_addstr(&abs_path, item->string);
+
+               /*
+                * we might have removed this as part of earlier
+                * recursive directory removal, so lstat() here could
+                * fail with ENOENT.
+                */
+               if (lstat(abs_path.buf, &st))
+                       continue;
+
+               if (S_ISDIR(st.st_mode)) {
+                       if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
+                               errors++;
+                       if (gone && !quiet) {
+                               qname = quote_path_relative(item->string, NULL, &buf);
+                               printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
+                       }
+               } else {
+                       res = dry_run ? 0 : unlink(abs_path.buf);
                        if (res) {
-                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               qname = quote_path_relative(item->string, NULL, &buf);
                                warning(_(msg_warn_remove_failed), qname);
                                errors++;
                        } else if (!quiet) {
-                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               qname = quote_path_relative(item->string, NULL, &buf);
                                printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
                        }
                }
+               strbuf_reset(&abs_path);
        }
        free(seen);
 
-       strbuf_release(&directory);
+       strbuf_release(&abs_path);
+       strbuf_release(&buf);
+       string_list_clear(&del_list, 0);
        string_list_clear(&exclude_list, 0);
        return (errors != 0);
 }