git-clean: refactor git-clean into two phases

Before introducing interactive git-clean, refactor git-clean operations into two phases: * hold cleaning items in del_list, * and remove them in a separate loop at the end. We will introduce interactive git-clean between the two phases. The interactive git-clean will show what would be done and must confirm before do real cleaning. Signed-off-by: Jiang Xin <worldhello.net@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jiang Xin committed Jun 25, 2013 at 23:53 UTC 396049e5fb62ea921379d02133e1ff00cc47bb3f
1 file changed +45 -19
builtin/clean.c
+45 -19
@@ -15,6 +15,7 @@
15 #include "quote.h"
16
17 static int force = -1; /* unset */
18 +static struct string_list del_list = STRING_LIST_INIT_DUP;
19
20 static const char *const builtin_clean_usage[] = {
21 N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
@@ -148,12 +149,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
149 int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
150 int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
151 int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
151 - struct strbuf directory = STRBUF_INIT;
152 + struct strbuf abs_path = STRBUF_INIT;
153 struct dir_struct dir;
154 static const char **pathspec;
155 struct strbuf buf = STRBUF_INIT;
156 struct string_list exclude_list = STRING_LIST_INIT_NODUP;
157 struct exclude_list *el;
158 + struct string_list_item *item;
159 const char *qname;
160 char *seen = NULL;
161 struct option options[] = {
@@ -223,6 +225,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
225 int matches = 0;
226 struct cache_entry *ce;
227 struct stat st;
228 + const char *rel;
229
230 /*
231 * Remove the '/' at the end that directory
@@ -242,13 +245,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
245 continue; /* Yup, this one exists unmerged */
246 }
247
245 - /*
246 - * we might have removed this as part of earlier
247 - * recursive directory removal, so lstat() here could
248 - * fail with ENOENT.
249 - */
248 if (lstat(ent->name, &st))
251 - continue;
249 + die_errno("Cannot lstat '%s'", ent->name);
250
251 if (pathspec) {
252 memset(seen, 0, argc > 0 ? argc : 1);
@@ -257,33 +255,61 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
255 }
256
257 if (S_ISDIR(st.st_mode)) {
260 - strbuf_addstr(&directory, ent->name);
258 if (remove_directories || (matches == MATCHED_EXACTLY)) {
262 - if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
263 - errors++;
264 - if (gone && !quiet) {
265 - qname = quote_path_relative(directory.buf, prefix, &buf);
266 - printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
267 - }
259 + rel = relative_path(ent->name, prefix, &buf);
260 + string_list_append(&del_list, rel);
261 }
269 - strbuf_reset(&directory);
262 } else {
263 if (pathspec && !matches)
264 continue;
273 - res = dry_run ? 0 : unlink(ent->name);
265 + rel = relative_path(ent->name, prefix, &buf);
266 + string_list_append(&del_list, rel);
267 + }
268 + }
269 +
270 + /* TODO: do interactive git-clean here, which will modify del_list */
271 +
272 + for_each_string_list_item(item, &del_list) {
273 + struct stat st;
274 +
275 + if (prefix)
276 + strbuf_addstr(&abs_path, prefix);
277 +
278 + strbuf_addstr(&abs_path, item->string);
279 +
280 + /*
281 + * we might have removed this as part of earlier
282 + * recursive directory removal, so lstat() here could
283 + * fail with ENOENT.
284 + */
285 + if (lstat(abs_path.buf, &st))
286 + continue;
287 +
288 + if (S_ISDIR(st.st_mode)) {
289 + if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
290 + errors++;
291 + if (gone && !quiet) {
292 + qname = quote_path_relative(item->string, NULL, &buf);
293 + printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
294 + }
295 + } else {
296 + res = dry_run ? 0 : unlink(abs_path.buf);
297 if (res) {
275 - qname = quote_path_relative(ent->name, prefix, &buf);
298 + qname = quote_path_relative(item->string, NULL, &buf);
299 warning(_(msg_warn_remove_failed), qname);
300 errors++;
301 } else if (!quiet) {
279 - qname = quote_path_relative(ent->name, prefix, &buf);
302 + qname = quote_path_relative(item->string, NULL, &buf);
303 printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
304 }
305 }
306 + strbuf_reset(&abs_path);
307 }
308 free(seen);
309
286 - strbuf_release(&directory);
310 + strbuf_release(&abs_path);
311 + strbuf_release(&buf);
312 + string_list_clear(&del_list, 0);
313 string_list_clear(&exclude_list, 0);
314 return (errors != 0);
315 }