Refactor git-clean operations into two phases:

 * collect cleaning candidates in del_list,
 * and remove them in a separate loop at the end.

We will introduce an interactive git-clean between the two phases.
The interactive git-clean will show what would be done and confirm
before do real cleaning.

Signed-off-by: Jiang Xin <worldhello....@gmail.com>
---
 builtin/clean.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 85 insertions(+), 18 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 04e39..ccd4 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -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>..."),
@@ -142,18 +143,61 @@ static int remove_dirs(struct strbuf *path, const char 
*prefix, int force_flag,
        return ret;
 }
 
+/*
+ * Give path as relative to prefix.
+ *
+ * This function is a combination of path_relative (in quote.c) and
+ * relative_path (in path.c)
+ */
+static const char *path_relative(const char *in, const char *prefix)
+{
+       static char buf[PATH_MAX + 1];
+       int off, i;
+       int len, prefix_len;
+
+       len = strlen(in);
+       if (prefix)
+               prefix_len = strlen(prefix);
+       else
+               prefix_len = 0;
+
+       off = 0;
+       i = 0;
+       while (i < prefix_len && i < len && prefix[i] == in[i]) {
+               if (prefix[i] == '/')
+                       off = i + 1;
+               i++;
+       }
+       in += off;
+       len -= off;
+
+       if (i >= prefix_len)
+               return in;
+
+       buf[0] = '\0';
+       while (i < prefix_len) {
+               if (prefix[i] == '/')
+                       strcat(buf, "../");
+               i++;
+       }
+       strcat(buf, in);
+
+       return buf;
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
        int i, res;
        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 +267,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,11 +287,6 @@ 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;
 
@@ -257,33 +297,60 @@ 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 = path_relative(ent->name, prefix);
+                               string_list_append(&del_list, rel);
                        }
-                       strbuf_reset(&directory);
                } else {
                        if (pathspec && !matches)
                                continue;
-                       res = dry_run ? 0 : unlink(ent->name);
+                       rel = path_relative(ent->name, prefix);
+                       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, -1, 
&buf, NULL);
+                               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, -1, 
&buf, NULL);
                                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, -1, 
&buf, NULL);
                                printf(dry_run ? _(msg_would_remove) : 
_(msg_remove), qname);
                        }
                }
+               strbuf_reset(&abs_path);
        }
        free(seen);
 
-       strbuf_release(&directory);
+       strbuf_release(&abs_path);
+       string_list_clear(&del_list, 0);
        string_list_clear(&exclude_list, 0);
        return (errors != 0);
 }
-- 
1.8.3.rc1.341.g24a8a0f

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to