1515#include "quote.h"
1616
1717static int force = -1 ; /* unset */
18+ static int interactive ;
1819static struct string_list del_list = STRING_LIST_INIT_DUP ;
1920
2021static const char * const builtin_clean_usage [] = {
21- N_ ("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..." ),
22+ N_ ("git clean [-d] [-f] [-i] [- n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..." ),
2223 NULL
2324};
2425
@@ -143,6 +144,50 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
143144 return ret ;
144145}
145146
147+ static void interactive_main_loop (void )
148+ {
149+ struct strbuf confirm = STRBUF_INIT ;
150+ struct strbuf buf = STRBUF_INIT ;
151+ struct string_list_item * item ;
152+ const char * qname ;
153+
154+ while (del_list .nr ) {
155+ putchar ('\n' );
156+ for_each_string_list_item (item , & del_list ) {
157+ qname = quote_path_relative (item -> string , NULL , & buf );
158+ printf (_ (msg_would_remove ), qname );
159+ }
160+ putchar ('\n' );
161+
162+ printf (_ ("Remove [y/n]? " ));
163+ if (strbuf_getline (& confirm , stdin , '\n' ) != EOF ) {
164+ strbuf_trim (& confirm );
165+ } else {
166+ /* Ctrl-D is the same as "quit" */
167+ string_list_clear (& del_list , 0 );
168+ putchar ('\n' );
169+ printf_ln ("Bye." );
170+ break ;
171+ }
172+
173+ if (confirm .len ) {
174+ if (!strncasecmp (confirm .buf , "yes" , confirm .len )) {
175+ break ;
176+ } else if (!strncasecmp (confirm .buf , "no" , confirm .len ) ||
177+ !strncasecmp (confirm .buf , "quit" , confirm .len )) {
178+ string_list_clear (& del_list , 0 );
179+ printf_ln ("Bye." );
180+ break ;
181+ } else {
182+ continue ;
183+ }
184+ }
185+ }
186+
187+ strbuf_release (& buf );
188+ strbuf_release (& confirm );
189+ }
190+
146191int cmd_clean (int argc , const char * * argv , const char * prefix )
147192{
148193 int i , res ;
@@ -162,6 +207,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
162207 OPT__QUIET (& quiet , N_ ("do not print names of files removed" )),
163208 OPT__DRY_RUN (& dry_run , N_ ("dry run" )),
164209 OPT__FORCE (& force , N_ ("force" )),
210+ OPT_BOOL ('i' , "interactive" , & interactive , N_ ("interactive cleaning" )),
165211 OPT_BOOLEAN ('d' , NULL , & remove_directories ,
166212 N_ ("remove whole directories" )),
167213 { OPTION_CALLBACK , 'e' , "exclude" , & exclude_list , N_ ("pattern" ),
@@ -188,12 +234,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
188234 if (ignored && ignored_only )
189235 die (_ ("-x and -X cannot be used together" ));
190236
191- if (!dry_run && !force ) {
237+ if (!interactive && ! dry_run && !force ) {
192238 if (config_set )
193- die (_ ("clean.requireForce set to true and neither -n nor -f given; "
239+ die (_ ("clean.requireForce set to true and neither -i, - n nor -f given; "
194240 "refusing to clean" ));
195241 else
196- die (_ ("clean.requireForce defaults to true and neither -n nor -f given; "
242+ die (_ ("clean.requireForce defaults to true and neither -i, - n nor -f given; "
197243 "refusing to clean" ));
198244 }
199245
@@ -267,7 +313,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
267313 }
268314 }
269315
270- /* TODO: do interactive git-clean here, which will modify del_list */
316+ if (interactive && del_list .nr > 0 )
317+ interactive_main_loop ();
271318
272319 for_each_string_list_item (item , & del_list ) {
273320 struct stat st ;
0 commit comments