diff options
Diffstat (limited to 'builtin/rm.c')
| -rw-r--r-- | builtin/rm.c | 158 | 
1 files changed, 116 insertions, 42 deletions
diff --git a/builtin/rm.c b/builtin/rm.c index 7b91d52f39..9b59ab3a64 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -9,7 +9,9 @@  #include "cache-tree.h"  #include "tree-walk.h"  #include "parse-options.h" +#include "string-list.h"  #include "submodule.h" +#include "pathspec.h"  static const char * const builtin_rm_usage[] = {  	N_("git rm [options] [--] <file>..."), @@ -36,15 +38,52 @@ static int get_ours_cache_pos(const char *path, int pos)  	return -1;  } +static void print_error_files(struct string_list *files_list, +			      const char *main_msg, +			      const char *hints_msg, +			      int *errs) +{ +	if (files_list->nr) { +		int i; +		struct strbuf err_msg = STRBUF_INIT; + +		strbuf_addstr(&err_msg, main_msg); +		for (i = 0; i < files_list->nr; i++) +			strbuf_addf(&err_msg, +				    "\n    %s", +				    files_list->items[i].string); +		if (advice_rm_hints) +			strbuf_addstr(&err_msg, hints_msg); +		*errs = error("%s", err_msg.buf); +		strbuf_release(&err_msg); +	} +} + +static void error_removing_concrete_submodules(struct string_list *files, int *errs) +{ +	print_error_files(files, +			  Q_("the following submodule (or one of its nested " +			     "submodules)\n" +			     "uses a .git directory:", +			     "the following submodules (or one of its nested " +			     "submodules)\n" +			     "use a .git directory:", files->nr), +			  _("\n(use 'rm -rf' if you really want to remove " +			    "it including all of its history)"), +			  errs); +	string_list_clear(files, 0); +} +  static int check_submodules_use_gitfiles(void)  {  	int i;  	int errs = 0; +	struct string_list files = STRING_LIST_INIT_NODUP;  	for (i = 0; i < list.nr; i++) {  		const char *name = list.entry[i].name;  		int pos; -		struct cache_entry *ce; +		const struct cache_entry *ce;  		struct stat st;  		pos = cache_name_pos(name, strlen(name)); @@ -61,12 +100,11 @@ static int check_submodules_use_gitfiles(void)  			continue;  		if (!submodule_uses_gitfile(name)) -			errs = error(_("submodule '%s' (or one of its nested " -				     "submodules) uses a .git directory\n" -				     "(use 'rm -rf' if you really want to remove " -				     "it including all of its history)"), name); +			string_list_append(&files, name);  	} +	error_removing_concrete_submodules(&files, &errs); +  	return errs;  } @@ -81,12 +119,16 @@ static int check_local_mod(unsigned char *head, int index_only)  	 */  	int i, no_head;  	int errs = 0; +	struct string_list files_staged = STRING_LIST_INIT_NODUP; +	struct string_list files_cached = STRING_LIST_INIT_NODUP; +	struct string_list files_submodule = STRING_LIST_INIT_NODUP; +	struct string_list files_local = STRING_LIST_INIT_NODUP;  	no_head = is_null_sha1(head);  	for (i = 0; i < list.nr; i++) {  		struct stat st;  		int pos; -		struct cache_entry *ce; +		const struct cache_entry *ce;  		const char *name = list.entry[i].name;  		unsigned char sha1[20];  		unsigned mode; @@ -171,29 +213,50 @@ static int check_local_mod(unsigned char *head, int index_only)  		 */  		if (local_changes && staged_changes) {  			if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD)) -				errs = error(_("'%s' has staged content different " -					     "from both the file and the HEAD\n" -					     "(use -f to force removal)"), name); +				string_list_append(&files_staged, name);  		}  		else if (!index_only) {  			if (staged_changes) -				errs = error(_("'%s' has changes staged in the index\n" -					     "(use --cached to keep the file, " -					     "or -f to force removal)"), name); +				string_list_append(&files_cached, name);  			if (local_changes) {  				if (S_ISGITLINK(ce->ce_mode) && -				    !submodule_uses_gitfile(name)) { -					errs = error(_("submodule '%s' (or one of its nested " -						     "submodules) uses a .git directory\n" -						     "(use 'rm -rf' if you really want to remove " -						     "it including all of its history)"), name); -				} else -					errs = error(_("'%s' has local modifications\n" -						     "(use --cached to keep the file, " -						     "or -f to force removal)"), name); +				    !submodule_uses_gitfile(name)) +					string_list_append(&files_submodule, name); +				else +					string_list_append(&files_local, name);  			}  		}  	} +	print_error_files(&files_staged, +			  Q_("the following file has staged content different " +			     "from both the\nfile and the HEAD:", +			     "the following files have staged content different" +			     " from both the\nfile and the HEAD:", +			     files_staged.nr), +			  _("\n(use -f to force removal)"), +			  &errs); +	string_list_clear(&files_staged, 0); +	print_error_files(&files_cached, +			  Q_("the following file has changes " +			     "staged in the index:", +			     "the following files have changes " +			     "staged in the index:", files_cached.nr), +			  _("\n(use --cached to keep the file," +			    " or -f to force removal)"), +			  &errs); +	string_list_clear(&files_cached, 0); + +	error_removing_concrete_submodules(&files_submodule, &errs); + +	print_error_files(&files_local, +			  Q_("the following file has local modifications:", +			     "the following files have local modifications:", +			     files_local.nr), +			  _("\n(use --cached to keep the file," +			    " or -f to force removal)"), +			  &errs); +	string_list_clear(&files_local, 0); +  	return errs;  } @@ -205,10 +268,10 @@ static int ignore_unmatch = 0;  static struct option builtin_rm_options[] = {  	OPT__DRY_RUN(&show_only, N_("dry run")),  	OPT__QUIET(&quiet, N_("do not list removed files")), -	OPT_BOOLEAN( 0 , "cached",         &index_only, N_("only remove from the index")), +	OPT_BOOL( 0 , "cached",         &index_only, N_("only remove from the index")),  	OPT__FORCE(&force, N_("override the up-to-date check")), -	OPT_BOOLEAN('r', NULL,             &recursive,  N_("allow recursive removal")), -	OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch, +	OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")), +	OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,  				N_("exit with a zero status even if nothing matched")),  	OPT_END(),  }; @@ -216,9 +279,10 @@ static struct option builtin_rm_options[] = {  int cmd_rm(int argc, const char **argv, const char *prefix)  {  	int i, newfd; -	const char **pathspec; +	struct pathspec pathspec;  	char *seen; +	gitmodules_config();  	git_config(git_default_config, NULL);  	argc = parse_options(argc, argv, prefix, builtin_rm_options, @@ -249,31 +313,32 @@ int cmd_rm(int argc, const char **argv, const char *prefix)  		}  	} -	pathspec = get_pathspec(prefix, argv); -	refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); +	parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv); +	refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL); -	seen = NULL; -	for (i = 0; pathspec[i] ; i++) -		/* nothing */; -	seen = xcalloc(i, 1); +	seen = xcalloc(pathspec.nr, 1);  	for (i = 0; i < active_nr; i++) { -		struct cache_entry *ce = active_cache[i]; -		if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) +		const struct cache_entry *ce = active_cache[i]; +		if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))  			continue;  		ALLOC_GROW(list.entry, list.nr + 1, list.alloc);  		list.entry[list.nr].name = ce->name; -		list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); +		list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); +		if (list.entry[list.nr++].is_submodule && +		    !is_staging_gitmodules_ok()) +			die (_("Please, stage your changes to .gitmodules or stash them to proceed"));  	} -	if (pathspec) { -		const char *match; +	if (pathspec.nr) { +		const char *original;  		int seen_any = 0; -		for (i = 0; (match = pathspec[i]) != NULL ; i++) { +		for (i = 0; i < pathspec.nr; i++) { +			original = pathspec.items[i].original;  			if (!seen[i]) {  				if (!ignore_unmatch) {  					die(_("pathspec '%s' did not match any files"), -					    match); +					    original);  				}  			}  			else { @@ -281,10 +346,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)  			}  			if (!recursive && seen[i] == MATCHED_RECURSIVELY)  				die(_("not removing '%s' recursively without -r"), -				    *match ? match : "."); +				    *original ? original : ".");  		} -		if (! seen_any) +		if (!seen_any)  			exit(0);  	} @@ -334,13 +399,15 @@ int cmd_rm(int argc, const char **argv, const char *prefix)  	 * in the middle)  	 */  	if (!index_only) { -		int removed = 0; +		int removed = 0, gitmodules_modified = 0;  		for (i = 0; i < list.nr; i++) {  			const char *path = list.entry[i].name;  			if (list.entry[i].is_submodule) {  				if (is_empty_dir(path)) {  					if (!rmdir(path)) {  						removed = 1; +						if (!remove_path_from_gitmodules(path)) +							gitmodules_modified = 1;  						continue;  					}  				} else { @@ -348,9 +415,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix)  					strbuf_addstr(&buf, path);  					if (!remove_dir_recursively(&buf, 0)) {  						removed = 1; +						if (!remove_path_from_gitmodules(path)) +							gitmodules_modified = 1;  						strbuf_release(&buf);  						continue; -					} +					} else if (!file_exists(path)) +						/* Submodule was removed by user */ +						if (!remove_path_from_gitmodules(path)) +							gitmodules_modified = 1;  					strbuf_release(&buf);  					/* Fallthrough and let remove_path() fail. */  				} @@ -362,6 +434,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)  			if (!removed)  				die_errno("git rm: '%s'", path);  		} +		if (gitmodules_modified) +			stage_updated_gitmodules();  	}  	if (active_cache_changed) {  | 
