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