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