builtin/rm.c: consolidate error reporting for removing submodules
[git] / builtin / rm.c
1 /*
2  * "git rm" builtin command
3  *
4  * Copyright (C) Linus Torvalds 2006
5  */
6 #include "cache.h"
7 #include "builtin.h"
8 #include "dir.h"
9 #include "cache-tree.h"
10 #include "tree-walk.h"
11 #include "parse-options.h"
12 #include "string-list.h"
13 #include "submodule.h"
14
15 static const char * const builtin_rm_usage[] = {
16         N_("git rm [options] [--] <file>..."),
17         NULL
18 };
19
20 static struct {
21         int nr, alloc;
22         struct {
23                 const char *name;
24                 char is_submodule;
25         } *entry;
26 } list;
27
28 static int get_ours_cache_pos(const char *path, int pos)
29 {
30         int i = -pos - 1;
31
32         while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) {
33                 if (ce_stage(active_cache[i]) == 2)
34                         return i;
35                 i++;
36         }
37         return -1;
38 }
39
40 static void print_error_files(struct string_list *files_list,
41                               const char *main_msg,
42                               const char *hints_msg,
43                               int *errs)
44 {
45         if (files_list->nr) {
46                 int i;
47                 struct strbuf err_msg = STRBUF_INIT;
48
49                 strbuf_addstr(&err_msg, main_msg);
50                 for (i = 0; i < files_list->nr; i++)
51                         strbuf_addf(&err_msg,
52                                     "\n    %s",
53                                     files_list->items[i].string);
54                 strbuf_addstr(&err_msg, hints_msg);
55                 *errs = error("%s", err_msg.buf);
56                 strbuf_release(&err_msg);
57         }
58 }
59
60 static void error_removing_concrete_submodules(struct string_list *files, int *errs)
61 {
62         print_error_files(files,
63                           Q_("the following submodule (or one of its nested "
64                              "submodules)\n"
65                              "uses a .git directory:",
66                              "the following submodules (or one of its nested "
67                              "submodules)\n"
68                              "use a .git directory:", files->nr),
69                           _("\n(use 'rm -rf' if you really want to remove "
70                             "it including all of its history)"),
71                           errs);
72         string_list_clear(files, 0);
73 }
74
75 static int check_submodules_use_gitfiles(void)
76 {
77         int i;
78         int errs = 0;
79         struct string_list files = STRING_LIST_INIT_NODUP;
80
81         for (i = 0; i < list.nr; i++) {
82                 const char *name = list.entry[i].name;
83                 int pos;
84                 struct cache_entry *ce;
85                 struct stat st;
86
87                 pos = cache_name_pos(name, strlen(name));
88                 if (pos < 0) {
89                         pos = get_ours_cache_pos(name, pos);
90                         if (pos < 0)
91                                 continue;
92                 }
93                 ce = active_cache[pos];
94
95                 if (!S_ISGITLINK(ce->ce_mode) ||
96                     (lstat(ce->name, &st) < 0) ||
97                     is_empty_dir(name))
98                         continue;
99
100                 if (!submodule_uses_gitfile(name))
101                         string_list_append(&files, name);
102         }
103
104         error_removing_concrete_submodules(&files, &errs);
105
106         return errs;
107 }
108
109 static int check_local_mod(unsigned char *head, int index_only)
110 {
111         /*
112          * Items in list are already sorted in the cache order,
113          * so we could do this a lot more efficiently by using
114          * tree_desc based traversal if we wanted to, but I am
115          * lazy, and who cares if removal of files is a tad
116          * slower than the theoretical maximum speed?
117          */
118         int i, no_head;
119         int errs = 0;
120         struct string_list files_staged = STRING_LIST_INIT_NODUP;
121         struct string_list files_cached = STRING_LIST_INIT_NODUP;
122         struct string_list files_submodule = STRING_LIST_INIT_NODUP;
123         struct string_list files_local = STRING_LIST_INIT_NODUP;
124
125         no_head = is_null_sha1(head);
126         for (i = 0; i < list.nr; i++) {
127                 struct stat st;
128                 int pos;
129                 struct cache_entry *ce;
130                 const char *name = list.entry[i].name;
131                 unsigned char sha1[20];
132                 unsigned mode;
133                 int local_changes = 0;
134                 int staged_changes = 0;
135
136                 pos = cache_name_pos(name, strlen(name));
137                 if (pos < 0) {
138                         /*
139                          * Skip unmerged entries except for populated submodules
140                          * that could lose history when removed.
141                          */
142                         pos = get_ours_cache_pos(name, pos);
143                         if (pos < 0)
144                                 continue;
145
146                         if (!S_ISGITLINK(active_cache[pos]->ce_mode) ||
147                             is_empty_dir(name))
148                                 continue;
149                 }
150                 ce = active_cache[pos];
151
152                 if (lstat(ce->name, &st) < 0) {
153                         if (errno != ENOENT && errno != ENOTDIR)
154                                 warning("'%s': %s", ce->name, strerror(errno));
155                         /* It already vanished from the working tree */
156                         continue;
157                 }
158                 else if (S_ISDIR(st.st_mode)) {
159                         /* if a file was removed and it is now a
160                          * directory, that is the same as ENOENT as
161                          * far as git is concerned; we do not track
162                          * directories unless they are submodules.
163                          */
164                         if (!S_ISGITLINK(ce->ce_mode))
165                                 continue;
166                 }
167
168                 /*
169                  * "rm" of a path that has changes need to be treated
170                  * carefully not to allow losing local changes
171                  * accidentally.  A local change could be (1) file in
172                  * work tree is different since the index; and/or (2)
173                  * the user staged a content that is different from
174                  * the current commit in the index.
175                  *
176                  * In such a case, you would need to --force the
177                  * removal.  However, "rm --cached" (remove only from
178                  * the index) is safe if the index matches the file in
179                  * the work tree or the HEAD commit, as it means that
180                  * the content being removed is available elsewhere.
181                  */
182
183                 /*
184                  * Is the index different from the file in the work tree?
185                  * If it's a submodule, is its work tree modified?
186                  */
187                 if (ce_match_stat(ce, &st, 0) ||
188                     (S_ISGITLINK(ce->ce_mode) &&
189                      !ok_to_remove_submodule(ce->name)))
190                         local_changes = 1;
191
192                 /*
193                  * Is the index different from the HEAD commit?  By
194                  * definition, before the very initial commit,
195                  * anything staged in the index is treated by the same
196                  * way as changed from the HEAD.
197                  */
198                 if (no_head
199                      || get_tree_entry(head, name, sha1, &mode)
200                      || ce->ce_mode != create_ce_mode(mode)
201                      || hashcmp(ce->sha1, sha1))
202                         staged_changes = 1;
203
204                 /*
205                  * If the index does not match the file in the work
206                  * tree and if it does not match the HEAD commit
207                  * either, (1) "git rm" without --cached definitely
208                  * will lose information; (2) "git rm --cached" will
209                  * lose information unless it is about removing an
210                  * "intent to add" entry.
211                  */
212                 if (local_changes && staged_changes) {
213                         if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))
214                                 string_list_append(&files_staged, name);
215                 }
216                 else if (!index_only) {
217                         if (staged_changes)
218                                 string_list_append(&files_cached, name);
219                         if (local_changes) {
220                                 if (S_ISGITLINK(ce->ce_mode) &&
221                                     !submodule_uses_gitfile(name))
222                                         string_list_append(&files_submodule, name);
223                                 else
224                                         string_list_append(&files_local, name);
225                         }
226                 }
227         }
228         print_error_files(&files_staged,
229                           Q_("the following file has staged content different "
230                              "from both the\nfile and the HEAD:",
231                              "the following files have staged content different"
232                              " from both the\nfile and the HEAD:",
233                              files_staged.nr),
234                           _("\n(use -f to force removal)"),
235                           &errs);
236         string_list_clear(&files_staged, 0);
237         print_error_files(&files_cached,
238                           Q_("the following file has changes "
239                              "staged in the index:",
240                              "the following files have changes "
241                              "staged in the index:", files_cached.nr),
242                           _("\n(use --cached to keep the file,"
243                             " or -f to force removal)"),
244                           &errs);
245         string_list_clear(&files_cached, 0);
246
247         error_removing_concrete_submodules(&files_submodule, &errs);
248
249         print_error_files(&files_local,
250                           Q_("the following file has local modifications:",
251                              "the following files have local modifications:",
252                              files_local.nr),
253                           _("\n(use --cached to keep the file,"
254                             " or -f to force removal)"),
255                           &errs);
256         string_list_clear(&files_local, 0);
257
258         return errs;
259 }
260
261 static struct lock_file lock_file;
262
263 static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
264 static int ignore_unmatch = 0;
265
266 static struct option builtin_rm_options[] = {
267         OPT__DRY_RUN(&show_only, N_("dry run")),
268         OPT__QUIET(&quiet, N_("do not list removed files")),
269         OPT_BOOLEAN( 0 , "cached",         &index_only, N_("only remove from the index")),
270         OPT__FORCE(&force, N_("override the up-to-date check")),
271         OPT_BOOLEAN('r', NULL,             &recursive,  N_("allow recursive removal")),
272         OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
273                                 N_("exit with a zero status even if nothing matched")),
274         OPT_END(),
275 };
276
277 int cmd_rm(int argc, const char **argv, const char *prefix)
278 {
279         int i, newfd;
280         const char **pathspec;
281         char *seen;
282
283         git_config(git_default_config, NULL);
284
285         argc = parse_options(argc, argv, prefix, builtin_rm_options,
286                              builtin_rm_usage, 0);
287         if (!argc)
288                 usage_with_options(builtin_rm_usage, builtin_rm_options);
289
290         if (!index_only)
291                 setup_work_tree();
292
293         newfd = hold_locked_index(&lock_file, 1);
294
295         if (read_cache() < 0)
296                 die(_("index file corrupt"));
297
298         /*
299          * Drop trailing directory separators from directories so we'll find
300          * submodules in the index.
301          */
302         for (i = 0; i < argc; i++) {
303                 size_t pathlen = strlen(argv[i]);
304                 if (pathlen && is_dir_sep(argv[i][pathlen - 1]) &&
305                     is_directory(argv[i])) {
306                         do {
307                                 pathlen--;
308                         } while (pathlen && is_dir_sep(argv[i][pathlen - 1]));
309                         argv[i] = xmemdupz(argv[i], pathlen);
310                 }
311         }
312
313         pathspec = get_pathspec(prefix, argv);
314         refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);
315
316         seen = NULL;
317         for (i = 0; pathspec[i] ; i++)
318                 /* nothing */;
319         seen = xcalloc(i, 1);
320
321         for (i = 0; i < active_nr; i++) {
322                 struct cache_entry *ce = active_cache[i];
323                 if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
324                         continue;
325                 ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
326                 list.entry[list.nr].name = ce->name;
327                 list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode);
328         }
329
330         if (pathspec) {
331                 const char *match;
332                 int seen_any = 0;
333                 for (i = 0; (match = pathspec[i]) != NULL ; i++) {
334                         if (!seen[i]) {
335                                 if (!ignore_unmatch) {
336                                         die(_("pathspec '%s' did not match any files"),
337                                             match);
338                                 }
339                         }
340                         else {
341                                 seen_any = 1;
342                         }
343                         if (!recursive && seen[i] == MATCHED_RECURSIVELY)
344                                 die(_("not removing '%s' recursively without -r"),
345                                     *match ? match : ".");
346                 }
347
348                 if (! seen_any)
349                         exit(0);
350         }
351
352         /*
353          * If not forced, the file, the index and the HEAD (if exists)
354          * must match; but the file can already been removed, since
355          * this sequence is a natural "novice" way:
356          *
357          *      rm F; git rm F
358          *
359          * Further, if HEAD commit exists, "diff-index --cached" must
360          * report no changes unless forced.
361          */
362         if (!force) {
363                 unsigned char sha1[20];
364                 if (get_sha1("HEAD", sha1))
365                         hashclr(sha1);
366                 if (check_local_mod(sha1, index_only))
367                         exit(1);
368         } else if (!index_only) {
369                 if (check_submodules_use_gitfiles())
370                         exit(1);
371         }
372
373         /*
374          * First remove the names from the index: we won't commit
375          * the index unless all of them succeed.
376          */
377         for (i = 0; i < list.nr; i++) {
378                 const char *path = list.entry[i].name;
379                 if (!quiet)
380                         printf("rm '%s'\n", path);
381
382                 if (remove_file_from_cache(path))
383                         die(_("git rm: unable to remove %s"), path);
384         }
385
386         if (show_only)
387                 return 0;
388
389         /*
390          * Then, unless we used "--cached", remove the filenames from
391          * the workspace. If we fail to remove the first one, we
392          * abort the "git rm" (but once we've successfully removed
393          * any file at all, we'll go ahead and commit to it all:
394          * by then we've already committed ourselves and can't fail
395          * in the middle)
396          */
397         if (!index_only) {
398                 int removed = 0;
399                 for (i = 0; i < list.nr; i++) {
400                         const char *path = list.entry[i].name;
401                         if (list.entry[i].is_submodule) {
402                                 if (is_empty_dir(path)) {
403                                         if (!rmdir(path)) {
404                                                 removed = 1;
405                                                 continue;
406                                         }
407                                 } else {
408                                         struct strbuf buf = STRBUF_INIT;
409                                         strbuf_addstr(&buf, path);
410                                         if (!remove_dir_recursively(&buf, 0)) {
411                                                 removed = 1;
412                                                 strbuf_release(&buf);
413                                                 continue;
414                                         }
415                                         strbuf_release(&buf);
416                                         /* Fallthrough and let remove_path() fail. */
417                                 }
418                         }
419                         if (!remove_path(path)) {
420                                 removed = 1;
421                                 continue;
422                         }
423                         if (!removed)
424                                 die_errno("git rm: '%s'", path);
425                 }
426         }
427
428         if (active_cache_changed) {
429                 if (write_cache(newfd, active_cache, active_nr) ||
430                     commit_locked_index(&lock_file))
431                         die(_("Unable to write new index file"));
432         }
433
434         return 0;
435 }