10
10
#include "cache.h"
11
11
#include "dir.h"
12
12
#include "parse-options.h"
13
+ #include "refs.h"
13
14
#include "string-list.h"
14
15
#include "quote.h"
15
16
@@ -20,6 +21,12 @@ static const char *const builtin_clean_usage[] = {
20
21
NULL
21
22
};
22
23
24
+ static const char * msg_remove = N_ ("Removing %s\n" );
25
+ static const char * msg_would_remove = N_ ("Would remove %s\n" );
26
+ static const char * msg_skip_git_dir = N_ ("Skipping repository %s\n" );
27
+ static const char * msg_would_skip_git_dir = N_ ("Would skip repository %s\n" );
28
+ static const char * msg_warn_remove_failed = N_ ("failed to remove %s" );
29
+
23
30
static int git_clean_config (const char * var , const char * value , void * cb )
24
31
{
25
32
if (!strcmp (var , "clean.requireforce" ))
@@ -34,11 +41,112 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset)
34
41
return 0 ;
35
42
}
36
43
44
+ static int remove_dirs (struct strbuf * path , const char * prefix , int force_flag ,
45
+ int dry_run , int quiet , int * dir_gone )
46
+ {
47
+ DIR * dir ;
48
+ struct strbuf quoted = STRBUF_INIT ;
49
+ struct dirent * e ;
50
+ int res = 0 , ret = 0 , gone = 1 , original_len = path -> len , len , i ;
51
+ unsigned char submodule_head [20 ];
52
+ struct string_list dels = STRING_LIST_INIT_DUP ;
53
+
54
+ * dir_gone = 1 ;
55
+
56
+ if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT ) &&
57
+ !resolve_gitlink_ref (path -> buf , "HEAD" , submodule_head )) {
58
+ if (!quiet ) {
59
+ quote_path_relative (path -> buf , strlen (path -> buf ), & quoted , prefix );
60
+ printf (dry_run ? _ (msg_would_skip_git_dir ) : _ (msg_skip_git_dir ),
61
+ quoted .buf );
62
+ }
63
+
64
+ * dir_gone = 0 ;
65
+ return 0 ;
66
+ }
67
+
68
+ dir = opendir (path -> buf );
69
+ if (!dir ) {
70
+ /* an empty dir could be removed even if it is unreadble */
71
+ res = dry_run ? 0 : rmdir (path -> buf );
72
+ if (res ) {
73
+ quote_path_relative (path -> buf , strlen (path -> buf ), & quoted , prefix );
74
+ warning (_ (msg_warn_remove_failed ), quoted .buf );
75
+ * dir_gone = 0 ;
76
+ }
77
+ return res ;
78
+ }
79
+
80
+ if (path -> buf [original_len - 1 ] != '/' )
81
+ strbuf_addch (path , '/' );
82
+
83
+ len = path -> len ;
84
+ while ((e = readdir (dir )) != NULL ) {
85
+ struct stat st ;
86
+ if (is_dot_or_dotdot (e -> d_name ))
87
+ continue ;
88
+
89
+ strbuf_setlen (path , len );
90
+ strbuf_addstr (path , e -> d_name );
91
+ if (lstat (path -> buf , & st ))
92
+ ; /* fall thru */
93
+ else if (S_ISDIR (st .st_mode )) {
94
+ if (remove_dirs (path , prefix , force_flag , dry_run , quiet , & gone ))
95
+ ret = 1 ;
96
+ if (gone ) {
97
+ quote_path_relative (path -> buf , strlen (path -> buf ), & quoted , prefix );
98
+ string_list_append (& dels , quoted .buf );
99
+ } else
100
+ * dir_gone = 0 ;
101
+ continue ;
102
+ } else {
103
+ res = dry_run ? 0 : unlink (path -> buf );
104
+ if (!res ) {
105
+ quote_path_relative (path -> buf , strlen (path -> buf ), & quoted , prefix );
106
+ string_list_append (& dels , quoted .buf );
107
+ } else {
108
+ quote_path_relative (path -> buf , strlen (path -> buf ), & quoted , prefix );
109
+ warning (_ (msg_warn_remove_failed ), quoted .buf );
110
+ * dir_gone = 0 ;
111
+ ret = 1 ;
112
+ }
113
+ continue ;
114
+ }
115
+
116
+ /* path too long, stat fails, or non-directory still exists */
117
+ * dir_gone = 0 ;
118
+ ret = 1 ;
119
+ break ;
120
+ }
121
+ closedir (dir );
122
+
123
+ strbuf_setlen (path , original_len );
124
+
125
+ if (* dir_gone ) {
126
+ res = dry_run ? 0 : rmdir (path -> buf );
127
+ if (!res )
128
+ * dir_gone = 1 ;
129
+ else {
130
+ quote_path_relative (path -> buf , strlen (path -> buf ), & quoted , prefix );
131
+ warning (_ (msg_warn_remove_failed ), quoted .buf );
132
+ * dir_gone = 0 ;
133
+ ret = 1 ;
134
+ }
135
+ }
136
+
137
+ if (!* dir_gone && !quiet ) {
138
+ for (i = 0 ; i < dels .nr ; i ++ )
139
+ printf (dry_run ? _ (msg_would_remove ) : _ (msg_remove ), dels .items [i ].string );
140
+ }
141
+ string_list_clear (& dels , 0 );
142
+ return ret ;
143
+ }
144
+
37
145
int cmd_clean (int argc , const char * * argv , const char * prefix )
38
146
{
39
- int i ;
40
- int show_only = 0 , remove_directories = 0 , quiet = 0 , ignored = 0 ;
41
- int ignored_only = 0 , config_set = 0 , errors = 0 ;
147
+ int i , res ;
148
+ int dry_run = 0 , remove_directories = 0 , quiet = 0 , ignored = 0 ;
149
+ int ignored_only = 0 , config_set = 0 , errors = 0 , gone = 1 ;
42
150
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT ;
43
151
struct strbuf directory = STRBUF_INIT ;
44
152
struct dir_struct dir ;
@@ -49,7 +157,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
49
157
char * seen = NULL ;
50
158
struct option options [] = {
51
159
OPT__QUIET (& quiet , N_ ("do not print names of files removed" )),
52
- OPT__DRY_RUN (& show_only , N_ ("dry run" )),
160
+ OPT__DRY_RUN (& dry_run , N_ ("dry run" )),
53
161
OPT__FORCE (& force , N_ ("force" )),
54
162
OPT_BOOLEAN ('d' , NULL , & remove_directories ,
55
163
N_ ("remove whole directories" )),
@@ -77,7 +185,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
77
185
if (ignored && ignored_only )
78
186
die (_ ("-x and -X cannot be used together" ));
79
187
80
- if (!show_only && !force ) {
188
+ if (!dry_run && !force ) {
81
189
if (config_set )
82
190
die (_ ("clean.requireForce set to true and neither -n nor -f given; "
83
191
"refusing to clean" ));
@@ -149,38 +257,26 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
149
257
150
258
if (S_ISDIR (st .st_mode )) {
151
259
strbuf_addstr (& directory , ent -> name );
152
- qname = quote_path_relative (directory .buf , directory .len , & buf , prefix );
153
- if (show_only && (remove_directories ||
154
- (matches == MATCHED_EXACTLY ))) {
155
- printf (_ ("Would remove %s\n" ), qname );
156
- } else if (remove_directories ||
157
- (matches == MATCHED_EXACTLY )) {
158
- if (!quiet )
159
- printf (_ ("Removing %s\n" ), qname );
160
- if (remove_dir_recursively (& directory ,
161
- rm_flags ) != 0 ) {
162
- warning (_ ("failed to remove %s" ), qname );
260
+ if (remove_directories || (matches == MATCHED_EXACTLY )) {
261
+ if (remove_dirs (& directory , prefix , rm_flags , dry_run , quiet , & gone ))
163
262
errors ++ ;
263
+ if (gone && !quiet ) {
264
+ qname = quote_path_relative (directory .buf , directory .len , & buf , prefix );
265
+ printf (dry_run ? _ (msg_would_remove ) : _ (msg_remove ), qname );
164
266
}
165
- } else if (show_only ) {
166
- printf (_ ("Would not remove %s\n" ), qname );
167
- } else {
168
- printf (_ ("Not removing %s\n" ), qname );
169
267
}
170
268
strbuf_reset (& directory );
171
269
} else {
172
270
if (pathspec && !matches )
173
271
continue ;
174
- qname = quote_path_relative (ent -> name , -1 , & buf , prefix );
175
- if (show_only ) {
176
- printf (_ ("Would remove %s\n" ), qname );
177
- continue ;
178
- } else if (!quiet ) {
179
- printf (_ ("Removing %s\n" ), qname );
180
- }
181
- if (unlink (ent -> name ) != 0 ) {
182
- warning (_ ("failed to remove %s" ), qname );
272
+ res = dry_run ? 0 : unlink (ent -> name );
273
+ if (res ) {
274
+ qname = quote_path_relative (ent -> name , -1 , & buf , prefix );
275
+ warning (_ (msg_warn_remove_failed ), qname );
183
276
errors ++ ;
277
+ } else if (!quiet ) {
278
+ qname = quote_path_relative (ent -> name , -1 , & buf , prefix );
279
+ printf (dry_run ? _ (msg_would_remove ) : _ (msg_remove ), qname );
184
280
}
185
281
}
186
282
}
0 commit comments