clean.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. /*
  2. * "git clean" builtin command
  3. *
  4. * Copyright (C) 2007 Shawn Bohrer
  5. *
  6. * Based on git-clean.sh by Pavel Roskin
  7. */
  8. #define USE_THE_INDEX_COMPATIBILITY_MACROS
  9. #include "builtin.h"
  10. #include "cache.h"
  11. #include "config.h"
  12. #include "dir.h"
  13. #include "parse-options.h"
  14. #include "string-list.h"
  15. #include "quote.h"
  16. #include "column.h"
  17. #include "color.h"
  18. #include "pathspec.h"
  19. #include "help.h"
  20. #include "prompt.h"
  21. static int force = -1; /* unset */
  22. static int interactive;
  23. static struct string_list del_list = STRING_LIST_INIT_DUP;
  24. static unsigned int colopts;
  25. static const char *const builtin_clean_usage[] = {
  26. N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
  27. NULL
  28. };
  29. static const char *msg_remove = N_("Removing %s\n");
  30. static const char *msg_would_remove = N_("Would remove %s\n");
  31. static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
  32. static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
  33. static const char *msg_warn_remove_failed = N_("failed to remove %s");
  34. static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
  35. enum color_clean {
  36. CLEAN_COLOR_RESET = 0,
  37. CLEAN_COLOR_PLAIN = 1,
  38. CLEAN_COLOR_PROMPT = 2,
  39. CLEAN_COLOR_HEADER = 3,
  40. CLEAN_COLOR_HELP = 4,
  41. CLEAN_COLOR_ERROR = 5
  42. };
  43. static const char *color_interactive_slots[] = {
  44. [CLEAN_COLOR_ERROR] = "error",
  45. [CLEAN_COLOR_HEADER] = "header",
  46. [CLEAN_COLOR_HELP] = "help",
  47. [CLEAN_COLOR_PLAIN] = "plain",
  48. [CLEAN_COLOR_PROMPT] = "prompt",
  49. [CLEAN_COLOR_RESET] = "reset",
  50. };
  51. static int clean_use_color = -1;
  52. static char clean_colors[][COLOR_MAXLEN] = {
  53. [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
  54. [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
  55. [CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED,
  56. [CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL,
  57. [CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE,
  58. [CLEAN_COLOR_RESET] = GIT_COLOR_RESET,
  59. };
  60. #define MENU_OPTS_SINGLETON 01
  61. #define MENU_OPTS_IMMEDIATE 02
  62. #define MENU_OPTS_LIST_ONLY 04
  63. struct menu_opts {
  64. const char *header;
  65. const char *prompt;
  66. int flags;
  67. };
  68. #define MENU_RETURN_NO_LOOP 10
  69. struct menu_item {
  70. char hotkey;
  71. const char *title;
  72. int selected;
  73. int (*fn)(void);
  74. };
  75. enum menu_stuff_type {
  76. MENU_STUFF_TYPE_STRING_LIST = 1,
  77. MENU_STUFF_TYPE_MENU_ITEM
  78. };
  79. struct menu_stuff {
  80. enum menu_stuff_type type;
  81. int nr;
  82. void *stuff;
  83. };
  84. define_list_config_array(color_interactive_slots);
  85. static int git_clean_config(const char *var, const char *value, void *cb)
  86. {
  87. const char *slot_name;
  88. if (starts_with(var, "column."))
  89. return git_column_config(var, value, "clean", &colopts);
  90. /* honors the color.interactive* config variables which also
  91. applied in git-add--interactive and git-stash */
  92. if (!strcmp(var, "color.interactive")) {
  93. clean_use_color = git_config_colorbool(var, value);
  94. return 0;
  95. }
  96. if (skip_prefix(var, "color.interactive.", &slot_name)) {
  97. int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name);
  98. if (slot < 0)
  99. return 0;
  100. if (!value)
  101. return config_error_nonbool(var);
  102. return color_parse(value, clean_colors[slot]);
  103. }
  104. if (!strcmp(var, "clean.requireforce")) {
  105. force = !git_config_bool(var, value);
  106. return 0;
  107. }
  108. /* inspect the color.ui config variable and others */
  109. return git_color_default_config(var, value, cb);
  110. }
  111. static const char *clean_get_color(enum color_clean ix)
  112. {
  113. if (want_color(clean_use_color))
  114. return clean_colors[ix];
  115. return "";
  116. }
  117. static void clean_print_color(enum color_clean ix)
  118. {
  119. printf("%s", clean_get_color(ix));
  120. }
  121. static int exclude_cb(const struct option *opt, const char *arg, int unset)
  122. {
  123. struct string_list *exclude_list = opt->value;
  124. BUG_ON_OPT_NEG(unset);
  125. string_list_append(exclude_list, arg);
  126. return 0;
  127. }
  128. static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
  129. int dry_run, int quiet, int *dir_gone)
  130. {
  131. DIR *dir;
  132. struct strbuf quoted = STRBUF_INIT;
  133. struct dirent *e;
  134. int res = 0, ret = 0, gone = 1, original_len = path->len, len;
  135. struct string_list dels = STRING_LIST_INIT_DUP;
  136. *dir_gone = 1;
  137. if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
  138. is_nonbare_repository_dir(path)) {
  139. if (!quiet) {
  140. quote_path(path->buf, prefix, &quoted, 0);
  141. printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
  142. quoted.buf);
  143. }
  144. *dir_gone = 0;
  145. goto out;
  146. }
  147. dir = opendir(path->buf);
  148. if (!dir) {
  149. /* an empty dir could be removed even if it is unreadble */
  150. res = dry_run ? 0 : rmdir(path->buf);
  151. if (res) {
  152. int saved_errno = errno;
  153. quote_path(path->buf, prefix, &quoted, 0);
  154. errno = saved_errno;
  155. warning_errno(_(msg_warn_remove_failed), quoted.buf);
  156. *dir_gone = 0;
  157. }
  158. ret = res;
  159. goto out;
  160. }
  161. strbuf_complete(path, '/');
  162. len = path->len;
  163. while ((e = readdir(dir)) != NULL) {
  164. struct stat st;
  165. if (is_dot_or_dotdot(e->d_name))
  166. continue;
  167. strbuf_setlen(path, len);
  168. strbuf_addstr(path, e->d_name);
  169. if (lstat(path->buf, &st))
  170. warning_errno(_(msg_warn_lstat_failed), path->buf);
  171. else if (S_ISDIR(st.st_mode)) {
  172. if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
  173. ret = 1;
  174. if (gone) {
  175. quote_path(path->buf, prefix, &quoted, 0);
  176. string_list_append(&dels, quoted.buf);
  177. } else
  178. *dir_gone = 0;
  179. continue;
  180. } else {
  181. res = dry_run ? 0 : unlink(path->buf);
  182. if (!res) {
  183. quote_path(path->buf, prefix, &quoted, 0);
  184. string_list_append(&dels, quoted.buf);
  185. } else {
  186. int saved_errno = errno;
  187. quote_path(path->buf, prefix, &quoted, 0);
  188. errno = saved_errno;
  189. warning_errno(_(msg_warn_remove_failed), quoted.buf);
  190. *dir_gone = 0;
  191. ret = 1;
  192. }
  193. continue;
  194. }
  195. /* path too long, stat fails, or non-directory still exists */
  196. *dir_gone = 0;
  197. ret = 1;
  198. break;
  199. }
  200. closedir(dir);
  201. strbuf_setlen(path, original_len);
  202. if (*dir_gone) {
  203. res = dry_run ? 0 : rmdir(path->buf);
  204. if (!res)
  205. *dir_gone = 1;
  206. else {
  207. int saved_errno = errno;
  208. quote_path(path->buf, prefix, &quoted, 0);
  209. errno = saved_errno;
  210. warning_errno(_(msg_warn_remove_failed), quoted.buf);
  211. *dir_gone = 0;
  212. ret = 1;
  213. }
  214. }
  215. if (!*dir_gone && !quiet) {
  216. int i;
  217. for (i = 0; i < dels.nr; i++)
  218. printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
  219. }
  220. out:
  221. strbuf_release(&quoted);
  222. string_list_clear(&dels, 0);
  223. return ret;
  224. }
  225. static void pretty_print_dels(void)
  226. {
  227. struct string_list list = STRING_LIST_INIT_DUP;
  228. struct string_list_item *item;
  229. struct strbuf buf = STRBUF_INIT;
  230. const char *qname;
  231. struct column_options copts;
  232. for_each_string_list_item(item, &del_list) {
  233. qname = quote_path(item->string, NULL, &buf, 0);
  234. string_list_append(&list, qname);
  235. }
  236. /*
  237. * always enable column display, we only consult column.*
  238. * about layout strategy and stuff
  239. */
  240. colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
  241. memset(&copts, 0, sizeof(copts));
  242. copts.indent = " ";
  243. copts.padding = 2;
  244. print_columns(&list, colopts, &copts);
  245. strbuf_release(&buf);
  246. string_list_clear(&list, 0);
  247. }
  248. static void pretty_print_menus(struct string_list *menu_list)
  249. {
  250. unsigned int local_colopts = 0;
  251. struct column_options copts;
  252. local_colopts = COL_ENABLED | COL_ROW;
  253. memset(&copts, 0, sizeof(copts));
  254. copts.indent = " ";
  255. copts.padding = 2;
  256. print_columns(menu_list, local_colopts, &copts);
  257. }
  258. static void prompt_help_cmd(int singleton)
  259. {
  260. clean_print_color(CLEAN_COLOR_HELP);
  261. printf(singleton ?
  262. _("Prompt help:\n"
  263. "1 - select a numbered item\n"
  264. "foo - select item based on unique prefix\n"
  265. " - (empty) select nothing\n") :
  266. _("Prompt help:\n"
  267. "1 - select a single item\n"
  268. "3-5 - select a range of items\n"
  269. "2-3,6-9 - select multiple ranges\n"
  270. "foo - select item based on unique prefix\n"
  271. "-... - unselect specified items\n"
  272. "* - choose all items\n"
  273. " - (empty) finish selecting\n"));
  274. clean_print_color(CLEAN_COLOR_RESET);
  275. }
  276. /*
  277. * display menu stuff with number prefix and hotkey highlight
  278. */
  279. static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
  280. {
  281. struct string_list menu_list = STRING_LIST_INIT_DUP;
  282. struct strbuf menu = STRBUF_INIT;
  283. struct menu_item *menu_item;
  284. struct string_list_item *string_list_item;
  285. int i;
  286. switch (stuff->type) {
  287. default:
  288. die("Bad type of menu_stuff when print menu");
  289. case MENU_STUFF_TYPE_MENU_ITEM:
  290. menu_item = (struct menu_item *)stuff->stuff;
  291. for (i = 0; i < stuff->nr; i++, menu_item++) {
  292. const char *p;
  293. int highlighted = 0;
  294. p = menu_item->title;
  295. if ((*chosen)[i] < 0)
  296. (*chosen)[i] = menu_item->selected ? 1 : 0;
  297. strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
  298. for (; *p; p++) {
  299. if (!highlighted && *p == menu_item->hotkey) {
  300. strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
  301. strbuf_addch(&menu, *p);
  302. strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
  303. highlighted = 1;
  304. } else {
  305. strbuf_addch(&menu, *p);
  306. }
  307. }
  308. string_list_append(&menu_list, menu.buf);
  309. strbuf_reset(&menu);
  310. }
  311. break;
  312. case MENU_STUFF_TYPE_STRING_LIST:
  313. i = 0;
  314. for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
  315. if ((*chosen)[i] < 0)
  316. (*chosen)[i] = 0;
  317. strbuf_addf(&menu, "%s%2d: %s",
  318. (*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
  319. string_list_append(&menu_list, menu.buf);
  320. strbuf_reset(&menu);
  321. i++;
  322. }
  323. break;
  324. }
  325. pretty_print_menus(&menu_list);
  326. strbuf_release(&menu);
  327. string_list_clear(&menu_list, 0);
  328. }
  329. static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
  330. {
  331. struct menu_item *menu_item;
  332. struct string_list_item *string_list_item;
  333. int i, len, found = 0;
  334. len = strlen(choice);
  335. switch (menu_stuff->type) {
  336. default:
  337. die("Bad type of menu_stuff when parse choice");
  338. case MENU_STUFF_TYPE_MENU_ITEM:
  339. menu_item = (struct menu_item *)menu_stuff->stuff;
  340. for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
  341. if (len == 1 && *choice == menu_item->hotkey) {
  342. found = i + 1;
  343. break;
  344. }
  345. if (!strncasecmp(choice, menu_item->title, len)) {
  346. if (found) {
  347. if (len == 1) {
  348. /* continue for hotkey matching */
  349. found = -1;
  350. } else {
  351. found = 0;
  352. break;
  353. }
  354. } else {
  355. found = i + 1;
  356. }
  357. }
  358. }
  359. break;
  360. case MENU_STUFF_TYPE_STRING_LIST:
  361. string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
  362. for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
  363. if (!strncasecmp(choice, string_list_item->string, len)) {
  364. if (found) {
  365. found = 0;
  366. break;
  367. }
  368. found = i + 1;
  369. }
  370. }
  371. break;
  372. }
  373. return found;
  374. }
  375. /*
  376. * Parse user input, and return choice(s) for menu (menu_stuff).
  377. *
  378. * Input
  379. * (for single choice)
  380. * 1 - select a numbered item
  381. * foo - select item based on menu title
  382. * - (empty) select nothing
  383. *
  384. * (for multiple choice)
  385. * 1 - select a single item
  386. * 3-5 - select a range of items
  387. * 2-3,6-9 - select multiple ranges
  388. * foo - select item based on menu title
  389. * -... - unselect specified items
  390. * * - choose all items
  391. * - (empty) finish selecting
  392. *
  393. * The parse result will be saved in array **chosen, and
  394. * return number of total selections.
  395. */
  396. static int parse_choice(struct menu_stuff *menu_stuff,
  397. int is_single,
  398. struct strbuf input,
  399. int **chosen)
  400. {
  401. struct strbuf **choice_list, **ptr;
  402. int nr = 0;
  403. int i;
  404. if (is_single) {
  405. choice_list = strbuf_split_max(&input, '\n', 0);
  406. } else {
  407. char *p = input.buf;
  408. do {
  409. if (*p == ',')
  410. *p = ' ';
  411. } while (*p++);
  412. choice_list = strbuf_split_max(&input, ' ', 0);
  413. }
  414. for (ptr = choice_list; *ptr; ptr++) {
  415. char *p;
  416. int choose = 1;
  417. int bottom = 0, top = 0;
  418. int is_range, is_number;
  419. strbuf_trim(*ptr);
  420. if (!(*ptr)->len)
  421. continue;
  422. /* Input that begins with '-'; unchoose */
  423. if (*(*ptr)->buf == '-') {
  424. choose = 0;
  425. strbuf_remove((*ptr), 0, 1);
  426. }
  427. is_range = 0;
  428. is_number = 1;
  429. for (p = (*ptr)->buf; *p; p++) {
  430. if ('-' == *p) {
  431. if (!is_range) {
  432. is_range = 1;
  433. is_number = 0;
  434. } else {
  435. is_number = 0;
  436. is_range = 0;
  437. break;
  438. }
  439. } else if (!isdigit(*p)) {
  440. is_number = 0;
  441. is_range = 0;
  442. break;
  443. }
  444. }
  445. if (is_number) {
  446. bottom = atoi((*ptr)->buf);
  447. top = bottom;
  448. } else if (is_range) {
  449. bottom = atoi((*ptr)->buf);
  450. /* a range can be specified like 5-7 or 5- */
  451. if (!*(strchr((*ptr)->buf, '-') + 1))
  452. top = menu_stuff->nr;
  453. else
  454. top = atoi(strchr((*ptr)->buf, '-') + 1);
  455. } else if (!strcmp((*ptr)->buf, "*")) {
  456. bottom = 1;
  457. top = menu_stuff->nr;
  458. } else {
  459. bottom = find_unique((*ptr)->buf, menu_stuff);
  460. top = bottom;
  461. }
  462. if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
  463. (is_single && bottom != top)) {
  464. clean_print_color(CLEAN_COLOR_ERROR);
  465. printf(_("Huh (%s)?\n"), (*ptr)->buf);
  466. clean_print_color(CLEAN_COLOR_RESET);
  467. continue;
  468. }
  469. for (i = bottom; i <= top; i++)
  470. (*chosen)[i-1] = choose;
  471. }
  472. strbuf_list_free(choice_list);
  473. for (i = 0; i < menu_stuff->nr; i++)
  474. nr += (*chosen)[i];
  475. return nr;
  476. }
  477. /*
  478. * Implement a git-add-interactive compatible UI, which is borrowed
  479. * from git-add--interactive.perl.
  480. *
  481. * Return value:
  482. *
  483. * - Return an array of integers
  484. * - , and it is up to you to free the allocated memory.
  485. * - The array ends with EOF.
  486. * - If user pressed CTRL-D (i.e. EOF), no selection returned.
  487. */
  488. static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
  489. {
  490. struct strbuf choice = STRBUF_INIT;
  491. int *chosen, *result;
  492. int nr = 0;
  493. int eof = 0;
  494. int i;
  495. ALLOC_ARRAY(chosen, stuff->nr);
  496. /* set chosen as uninitialized */
  497. for (i = 0; i < stuff->nr; i++)
  498. chosen[i] = -1;
  499. for (;;) {
  500. if (opts->header) {
  501. printf_ln("%s%s%s",
  502. clean_get_color(CLEAN_COLOR_HEADER),
  503. _(opts->header),
  504. clean_get_color(CLEAN_COLOR_RESET));
  505. }
  506. /* chosen will be initialized by print_highlight_menu_stuff */
  507. print_highlight_menu_stuff(stuff, &chosen);
  508. if (opts->flags & MENU_OPTS_LIST_ONLY)
  509. break;
  510. if (opts->prompt) {
  511. printf("%s%s%s%s",
  512. clean_get_color(CLEAN_COLOR_PROMPT),
  513. _(opts->prompt),
  514. opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
  515. clean_get_color(CLEAN_COLOR_RESET));
  516. }
  517. if (git_read_line_interactively(&choice) == EOF) {
  518. eof = 1;
  519. break;
  520. }
  521. /* help for prompt */
  522. if (!strcmp(choice.buf, "?")) {
  523. prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
  524. continue;
  525. }
  526. /* for a multiple-choice menu, press ENTER (empty) will return back */
  527. if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
  528. break;
  529. nr = parse_choice(stuff,
  530. opts->flags & MENU_OPTS_SINGLETON,
  531. choice,
  532. &chosen);
  533. if (opts->flags & MENU_OPTS_SINGLETON) {
  534. if (nr)
  535. break;
  536. } else if (opts->flags & MENU_OPTS_IMMEDIATE) {
  537. break;
  538. }
  539. }
  540. if (eof) {
  541. result = xmalloc(sizeof(int));
  542. *result = EOF;
  543. } else {
  544. int j = 0;
  545. /*
  546. * recalculate nr, if return back from menu directly with
  547. * default selections.
  548. */
  549. if (!nr) {
  550. for (i = 0; i < stuff->nr; i++)
  551. nr += chosen[i];
  552. }
  553. result = xcalloc(st_add(nr, 1), sizeof(int));
  554. for (i = 0; i < stuff->nr && j < nr; i++) {
  555. if (chosen[i])
  556. result[j++] = i;
  557. }
  558. result[j] = EOF;
  559. }
  560. free(chosen);
  561. strbuf_release(&choice);
  562. return result;
  563. }
  564. static int clean_cmd(void)
  565. {
  566. return MENU_RETURN_NO_LOOP;
  567. }
  568. static int filter_by_patterns_cmd(void)
  569. {
  570. struct dir_struct dir;
  571. struct strbuf confirm = STRBUF_INIT;
  572. struct strbuf **ignore_list;
  573. struct string_list_item *item;
  574. struct pattern_list *pl;
  575. int changed = -1, i;
  576. for (;;) {
  577. if (!del_list.nr)
  578. break;
  579. if (changed)
  580. pretty_print_dels();
  581. clean_print_color(CLEAN_COLOR_PROMPT);
  582. printf(_("Input ignore patterns>> "));
  583. clean_print_color(CLEAN_COLOR_RESET);
  584. if (git_read_line_interactively(&confirm) == EOF)
  585. putchar('\n');
  586. /* quit filter_by_pattern mode if press ENTER or Ctrl-D */
  587. if (!confirm.len)
  588. break;
  589. dir_init(&dir);
  590. pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude");
  591. ignore_list = strbuf_split_max(&confirm, ' ', 0);
  592. for (i = 0; ignore_list[i]; i++) {
  593. strbuf_trim(ignore_list[i]);
  594. if (!ignore_list[i]->len)
  595. continue;
  596. add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1));
  597. }
  598. changed = 0;
  599. for_each_string_list_item(item, &del_list) {
  600. int dtype = DT_UNKNOWN;
  601. if (is_excluded(&dir, &the_index, item->string, &dtype)) {
  602. *item->string = '\0';
  603. changed++;
  604. }
  605. }
  606. if (changed) {
  607. string_list_remove_empty_items(&del_list, 0);
  608. } else {
  609. clean_print_color(CLEAN_COLOR_ERROR);
  610. printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
  611. clean_print_color(CLEAN_COLOR_RESET);
  612. }
  613. strbuf_list_free(ignore_list);
  614. dir_clear(&dir);
  615. }
  616. strbuf_release(&confirm);
  617. return 0;
  618. }
  619. static int select_by_numbers_cmd(void)
  620. {
  621. struct menu_opts menu_opts;
  622. struct menu_stuff menu_stuff;
  623. struct string_list_item *items;
  624. int *chosen;
  625. int i, j;
  626. menu_opts.header = NULL;
  627. menu_opts.prompt = N_("Select items to delete");
  628. menu_opts.flags = 0;
  629. menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
  630. menu_stuff.stuff = &del_list;
  631. menu_stuff.nr = del_list.nr;
  632. chosen = list_and_choose(&menu_opts, &menu_stuff);
  633. items = del_list.items;
  634. for (i = 0, j = 0; i < del_list.nr; i++) {
  635. if (i < chosen[j]) {
  636. *(items[i].string) = '\0';
  637. } else if (i == chosen[j]) {
  638. /* delete selected item */
  639. j++;
  640. continue;
  641. } else {
  642. /* end of chosen (chosen[j] == EOF), won't delete */
  643. *(items[i].string) = '\0';
  644. }
  645. }
  646. string_list_remove_empty_items(&del_list, 0);
  647. free(chosen);
  648. return 0;
  649. }
  650. static int ask_each_cmd(void)
  651. {
  652. struct strbuf confirm = STRBUF_INIT;
  653. struct strbuf buf = STRBUF_INIT;
  654. struct string_list_item *item;
  655. const char *qname;
  656. int changed = 0, eof = 0;
  657. for_each_string_list_item(item, &del_list) {
  658. /* Ctrl-D should stop removing files */
  659. if (!eof) {
  660. qname = quote_path(item->string, NULL, &buf, 0);
  661. /* TRANSLATORS: Make sure to keep [y/N] as is */
  662. printf(_("Remove %s [y/N]? "), qname);
  663. if (git_read_line_interactively(&confirm) == EOF) {
  664. putchar('\n');
  665. eof = 1;
  666. }
  667. }
  668. if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
  669. *item->string = '\0';
  670. changed++;
  671. }
  672. }
  673. if (changed)
  674. string_list_remove_empty_items(&del_list, 0);
  675. strbuf_release(&buf);
  676. strbuf_release(&confirm);
  677. return MENU_RETURN_NO_LOOP;
  678. }
  679. static int quit_cmd(void)
  680. {
  681. string_list_clear(&del_list, 0);
  682. printf(_("Bye.\n"));
  683. return MENU_RETURN_NO_LOOP;
  684. }
  685. static int help_cmd(void)
  686. {
  687. clean_print_color(CLEAN_COLOR_HELP);
  688. printf_ln(_(
  689. "clean - start cleaning\n"
  690. "filter by pattern - exclude items from deletion\n"
  691. "select by numbers - select items to be deleted by numbers\n"
  692. "ask each - confirm each deletion (like \"rm -i\")\n"
  693. "quit - stop cleaning\n"
  694. "help - this screen\n"
  695. "? - help for prompt selection"
  696. ));
  697. clean_print_color(CLEAN_COLOR_RESET);
  698. return 0;
  699. }
  700. static void interactive_main_loop(void)
  701. {
  702. while (del_list.nr) {
  703. struct menu_opts menu_opts;
  704. struct menu_stuff menu_stuff;
  705. struct menu_item menus[] = {
  706. {'c', "clean", 0, clean_cmd},
  707. {'f', "filter by pattern", 0, filter_by_patterns_cmd},
  708. {'s', "select by numbers", 0, select_by_numbers_cmd},
  709. {'a', "ask each", 0, ask_each_cmd},
  710. {'q', "quit", 0, quit_cmd},
  711. {'h', "help", 0, help_cmd},
  712. };
  713. int *chosen;
  714. menu_opts.header = N_("*** Commands ***");
  715. menu_opts.prompt = N_("What now");
  716. menu_opts.flags = MENU_OPTS_SINGLETON;
  717. menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
  718. menu_stuff.stuff = menus;
  719. menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
  720. clean_print_color(CLEAN_COLOR_HEADER);
  721. printf_ln(Q_("Would remove the following item:",
  722. "Would remove the following items:",
  723. del_list.nr));
  724. clean_print_color(CLEAN_COLOR_RESET);
  725. pretty_print_dels();
  726. chosen = list_and_choose(&menu_opts, &menu_stuff);
  727. if (*chosen != EOF) {
  728. int ret;
  729. ret = menus[*chosen].fn();
  730. if (ret != MENU_RETURN_NO_LOOP) {
  731. FREE_AND_NULL(chosen);
  732. if (!del_list.nr) {
  733. clean_print_color(CLEAN_COLOR_ERROR);
  734. printf_ln(_("No more files to clean, exiting."));
  735. clean_print_color(CLEAN_COLOR_RESET);
  736. break;
  737. }
  738. continue;
  739. }
  740. } else {
  741. quit_cmd();
  742. }
  743. FREE_AND_NULL(chosen);
  744. break;
  745. }
  746. }
  747. static void correct_untracked_entries(struct dir_struct *dir)
  748. {
  749. int src, dst, ign;
  750. for (src = dst = ign = 0; src < dir->nr; src++) {
  751. /* skip paths in ignored[] that cannot be inside entries[src] */
  752. while (ign < dir->ignored_nr &&
  753. 0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
  754. ign++;
  755. if (ign < dir->ignored_nr &&
  756. check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
  757. /* entries[src] contains an ignored path, so we drop it */
  758. free(dir->entries[src]);
  759. } else {
  760. struct dir_entry *ent = dir->entries[src++];
  761. /* entries[src] does not contain an ignored path, so we keep it */
  762. dir->entries[dst++] = ent;
  763. /* then discard paths in entries[] contained inside entries[src] */
  764. while (src < dir->nr &&
  765. check_dir_entry_contains(ent, dir->entries[src]))
  766. free(dir->entries[src++]);
  767. /* compensate for the outer loop's loop control */
  768. src--;
  769. }
  770. }
  771. dir->nr = dst;
  772. }
  773. int cmd_clean(int argc, const char **argv, const char *prefix)
  774. {
  775. int i, res;
  776. int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
  777. int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
  778. int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
  779. struct strbuf abs_path = STRBUF_INIT;
  780. struct dir_struct dir;
  781. struct pathspec pathspec;
  782. struct strbuf buf = STRBUF_INIT;
  783. struct string_list exclude_list = STRING_LIST_INIT_NODUP;
  784. struct pattern_list *pl;
  785. struct string_list_item *item;
  786. const char *qname;
  787. struct option options[] = {
  788. OPT__QUIET(&quiet, N_("do not print names of files removed")),
  789. OPT__DRY_RUN(&dry_run, N_("dry run")),
  790. OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
  791. OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
  792. OPT_BOOL('d', NULL, &remove_directories,
  793. N_("remove whole directories")),
  794. OPT_CALLBACK_F('e', "exclude", &exclude_list, N_("pattern"),
  795. N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb),
  796. OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
  797. OPT_BOOL('X', NULL, &ignored_only,
  798. N_("remove only ignored files")),
  799. OPT_END()
  800. };
  801. git_config(git_clean_config, NULL);
  802. if (force < 0)
  803. force = 0;
  804. else
  805. config_set = 1;
  806. argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
  807. 0);
  808. dir_init(&dir);
  809. if (!interactive && !dry_run && !force) {
  810. if (config_set)
  811. die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
  812. "refusing to clean"));
  813. else
  814. die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;"
  815. " refusing to clean"));
  816. }
  817. if (force > 1)
  818. rm_flags = 0;
  819. else
  820. dir.flags |= DIR_SKIP_NESTED_GIT;
  821. dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
  822. if (ignored && ignored_only)
  823. die(_("-x and -X cannot be used together"));
  824. if (!ignored)
  825. setup_standard_excludes(&dir);
  826. if (ignored_only)
  827. dir.flags |= DIR_SHOW_IGNORED;
  828. if (argc) {
  829. /*
  830. * Remaining args implies pathspecs specified, and we should
  831. * recurse within those.
  832. */
  833. remove_directories = 1;
  834. }
  835. if (remove_directories && !ignored_only) {
  836. /*
  837. * We need to know about ignored files too:
  838. *
  839. * If (ignored), then we will delete ignored files as well.
  840. *
  841. * If (!ignored), then even though we not are doing
  842. * anything with ignored files, we need to know about them
  843. * so that we can avoid deleting a directory of untracked
  844. * files that also contains an ignored file within it.
  845. *
  846. * For the (!ignored) case, since we only need to avoid
  847. * deleting ignored files, we can set
  848. * DIR_SHOW_IGNORED_TOO_MODE_MATCHING in order to avoid
  849. * recursing into a directory which is itself ignored.
  850. */
  851. dir.flags |= DIR_SHOW_IGNORED_TOO;
  852. if (!ignored)
  853. dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
  854. /*
  855. * Let the fill_directory() machinery know that we aren't
  856. * just recursing to collect the ignored files; we want all
  857. * the untracked ones so that we can delete them. (Note:
  858. * we could also set DIR_KEEP_UNTRACKED_CONTENTS when
  859. * ignored_only is true, since DIR_KEEP_UNTRACKED_CONTENTS
  860. * only has effect in combination with DIR_SHOW_IGNORED_TOO. It makes
  861. * the code clearer to exclude it, though.
  862. */
  863. dir.flags |= DIR_KEEP_UNTRACKED_CONTENTS;
  864. }
  865. if (read_cache() < 0)
  866. die(_("index file corrupt"));
  867. pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
  868. for (i = 0; i < exclude_list.nr; i++)
  869. add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1));
  870. parse_pathspec(&pathspec, 0,
  871. PATHSPEC_PREFER_CWD,
  872. prefix, argv);
  873. fill_directory(&dir, &the_index, &pathspec);
  874. correct_untracked_entries(&dir);
  875. for (i = 0; i < dir.nr; i++) {
  876. struct dir_entry *ent = dir.entries[i];
  877. int matches = 0;
  878. struct stat st;
  879. const char *rel;
  880. if (!cache_name_is_other(ent->name, ent->len))
  881. continue;
  882. if (lstat(ent->name, &st))
  883. die_errno("Cannot lstat '%s'", ent->name);
  884. if (S_ISDIR(st.st_mode) && !remove_directories &&
  885. matches != MATCHED_EXACTLY)
  886. continue;
  887. rel = relative_path(ent->name, prefix, &buf);
  888. string_list_append(&del_list, rel);
  889. }
  890. dir_clear(&dir);
  891. if (interactive && del_list.nr > 0)
  892. interactive_main_loop();
  893. for_each_string_list_item(item, &del_list) {
  894. struct stat st;
  895. strbuf_reset(&abs_path);
  896. if (prefix)
  897. strbuf_addstr(&abs_path, prefix);
  898. strbuf_addstr(&abs_path, item->string);
  899. /*
  900. * we might have removed this as part of earlier
  901. * recursive directory removal, so lstat() here could
  902. * fail with ENOENT.
  903. */
  904. if (lstat(abs_path.buf, &st))
  905. continue;
  906. if (S_ISDIR(st.st_mode)) {
  907. if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
  908. errors++;
  909. if (gone && !quiet) {
  910. qname = quote_path(item->string, NULL, &buf, 0);
  911. printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
  912. }
  913. } else {
  914. res = dry_run ? 0 : unlink(abs_path.buf);
  915. if (res) {
  916. int saved_errno = errno;
  917. qname = quote_path(item->string, NULL, &buf, 0);
  918. errno = saved_errno;
  919. warning_errno(_(msg_warn_remove_failed), qname);
  920. errors++;
  921. } else if (!quiet) {
  922. qname = quote_path(item->string, NULL, &buf, 0);
  923. printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
  924. }
  925. }
  926. }
  927. strbuf_release(&abs_path);
  928. strbuf_release(&buf);
  929. string_list_clear(&del_list, 0);
  930. string_list_clear(&exclude_list, 0);
  931. return (errors != 0);
  932. }