When running `git clean`, it will be convenient and safe to show a
confirm dialog and only delete files and directories when confirmed.
The confirm dialog will popup when:

 * run `git clean` in interactive sessions,
 * not a dry run,
 * and not quiet.

There may be existing scripts that call `git clean -f` while leave the
standard input and the standard output of the `git clean` connected to
whatever environment the scripts were started, and such invocation might
trigger the confirm dialog. In this case, add a `-q` option, such as
`git clean -q -f`, then the confirm dialog won't popup.

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

diff --git a/builtin/clean.c b/builtin/clean.c
index 04e39..e31a1 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -169,6 +169,10 @@ int cmd_clean(int argc, const char **argv, const char 
*prefix)
                                N_("remove only ignored files")),
                OPT_END()
        };
+       struct string_list dels = STRING_LIST_INIT_DUP;
+       struct string_list_item *item;
+       struct strbuf confirm = STRBUF_INIT;
+       int confirmed = 0;
 
        git_config(git_clean_config, NULL);
        if (force < 0)
@@ -257,33 +261,67 @@ 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)) 
{
+                               string_list_append(&dels, ent->name);
+                       }
+               } else {
+                       if (pathspec && !matches)
+                               continue;
+                       string_list_append(&dels, ent->name);
+               }
+       }
+
+       if (!isatty(0) || !isatty(1) || quiet || dry_run) {
+               confirmed = 1;
+       }
+
+       if (!confirmed && dels.nr > 0) {
+               for_each_string_list_item(item, &dels) {
+                       qname = quote_path_relative(item->string, -1, &buf, 
prefix);
+                       printf(_(msg_would_remove), qname);
+               }
+               printf(_("Are you sure [y/N]? "));
+               strbuf_getline(&confirm, stdin, '\n');
+               strbuf_trim(&confirm);
+               if (!strcasecmp(confirm.buf, "y")) {
+                       confirmed = 1;
+               }
+       }
+
+       if (confirmed) {
+               for_each_string_list_item(item, &dels) {
+                       struct stat st;
+
+                       if (lstat(item->string, &st))
+                               continue;
+
+                       if (S_ISDIR(st.st_mode)) {
+                               strbuf_addstr(&directory, item->string);
                                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);
                                }
-                       }
-                       strbuf_reset(&directory);
-               } else {
-                       if (pathspec && !matches)
-                               continue;
-                       res = dry_run ? 0 : unlink(ent->name);
-                       if (res) {
-                               qname = quote_path_relative(ent->name, -1, 
&buf, prefix);
-                               warning(_(msg_warn_remove_failed), qname);
-                               errors++;
-                       } else if (!quiet) {
-                               qname = quote_path_relative(ent->name, -1, 
&buf, prefix);
-                               printf(dry_run ? _(msg_would_remove) : 
_(msg_remove), qname);
+                               strbuf_reset(&directory);
+                       } else {
+                               res = dry_run ? 0 : unlink(item->string);
+                               if (res) {
+                                       qname = 
quote_path_relative(item->string, -1, &buf, prefix);
+                                       warning(_(msg_warn_remove_failed), 
qname);
+                                       errors++;
+                               } else if (!quiet) {
+                                       qname = 
quote_path_relative(item->string, -1, &buf, prefix);
+                                       printf(dry_run ? _(msg_would_remove) : 
_(msg_remove), qname);
+                               }
                        }
                }
        }
        free(seen);
 
        strbuf_release(&directory);
+       strbuf_release(&confirm);
        string_list_clear(&exclude_list, 0);
+       string_list_clear(&dels, 0);
        return (errors != 0);
 }
-- 
1.8.2.1.628.gcd33b41

--
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