Make git-clean a builtin
This replaces git-clean.sh with builtin-clean.c, and moves
git-clean.sh to the examples.
This also introduces a change in behavior when removing directories
explicitly specified as a path.  For example currently:
1. When dir has only untracked files, these two behave differently:
    $ git clean -n dir
    $ git clean -n dir/
the former says "Would not remove dir/", while the latter would say
"Would remove dir/untracked" for all paths under it, but not the
directory itself.
With -d, the former would stop refusing, however since the user
explicitly asked to remove the directory the -d is no longer required.
2. When there are more parameters:
    $ git clean -n dir foo
    $ git clean -n dir/ foo
both cases refuse to remove dir/ unless -d is specified.  Once again
since both cases requested to remove dir the -d is no longer required.
Thanks to Johannes Schindelin for the conversion to using the
parse-options API.
Signed-off-by: Shawn Bohrer <shawn.bohrer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
			
			
				maint
			
			
		
							parent
							
								
									ea55960518
								
							
						
					
					
						commit
						113f10f22f
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							|  | @ -213,7 +213,7 @@ BASIC_LDFLAGS = | |||
|  | ||||
| SCRIPT_SH = \ | ||||
| 	git-bisect.sh git-checkout.sh \ | ||||
| 	git-clean.sh git-clone.sh git-commit.sh \ | ||||
| 	git-clone.sh git-commit.sh \ | ||||
| 	git-ls-remote.sh \ | ||||
| 	git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \ | ||||
| 	git-pull.sh git-rebase.sh git-rebase--interactive.sh \ | ||||
|  | @ -330,6 +330,7 @@ BUILTIN_OBJS = \ | |||
| 	builtin-check-attr.o \ | ||||
| 	builtin-checkout-index.o \ | ||||
| 	builtin-check-ref-format.o \ | ||||
| 	builtin-clean.o \ | ||||
| 	builtin-commit-tree.o \ | ||||
| 	builtin-count-objects.o \ | ||||
| 	builtin-describe.o \ | ||||
|  |  | |||
|  | @ -0,0 +1,154 @@ | |||
| /* | ||||
|  * "git clean" builtin command | ||||
|  * | ||||
|  * Copyright (C) 2007 Shawn Bohrer | ||||
|  * | ||||
|  * Based on git-clean.sh by Pavel Roskin | ||||
|  */ | ||||
|  | ||||
| #include "builtin.h" | ||||
| #include "cache.h" | ||||
| #include "dir.h" | ||||
| #include "parse-options.h" | ||||
|  | ||||
| static int force; | ||||
|  | ||||
| static const char *const builtin_clean_usage[] = { | ||||
| 	"git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...", | ||||
| 	NULL | ||||
| }; | ||||
|  | ||||
| static int git_clean_config(const char *var, const char *value) | ||||
| { | ||||
| 	if (!strcmp(var, "clean.requireforce")) | ||||
| 		force = !git_config_bool(var, value); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int cmd_clean(int argc, const char **argv, const char *prefix) | ||||
| { | ||||
| 	int j; | ||||
| 	int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; | ||||
| 	int ignored_only = 0, baselen = 0; | ||||
| 	struct strbuf directory; | ||||
| 	struct dir_struct dir; | ||||
| 	const char *path, *base; | ||||
| 	static const char **pathspec; | ||||
| 	struct option options[] = { | ||||
| 		OPT__QUIET(&quiet), | ||||
| 		OPT__DRY_RUN(&show_only), | ||||
| 		OPT_BOOLEAN('f', NULL, &force, "force"), | ||||
| 		OPT_BOOLEAN('d', NULL, &remove_directories, | ||||
| 				"remove whole directories"), | ||||
| 		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"), | ||||
| 		OPT_BOOLEAN('X', NULL, &ignored_only, | ||||
| 				"remove only ignored files"), | ||||
| 		OPT_END() | ||||
| 	}; | ||||
|  | ||||
| 	git_config(git_clean_config); | ||||
| 	argc = parse_options(argc, argv, options, builtin_clean_usage, 0); | ||||
|  | ||||
| 	memset(&dir, 0, sizeof(dir)); | ||||
| 	if (ignored_only) { | ||||
| 		dir.show_ignored =1; | ||||
| 		dir.exclude_per_dir = ".gitignore"; | ||||
| 	} | ||||
|  | ||||
| 	if (ignored && ignored_only) | ||||
| 		die("-x and -X cannot be used together"); | ||||
|  | ||||
| 	if (!show_only && !force) | ||||
| 		die("clean.requireForce set and -n or -f not given; refusing to clean"); | ||||
|  | ||||
| 	dir.show_other_directories = 1; | ||||
|  | ||||
| 	if (!ignored) { | ||||
| 		dir.exclude_per_dir = ".gitignore"; | ||||
| 		if (!access(git_path("info/exclude"), F_OK)) { | ||||
| 			char *exclude_path = git_path("info/exclude"); | ||||
| 			add_excludes_from_file(&dir, exclude_path); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pathspec = get_pathspec(prefix, argv); | ||||
| 	read_cache(); | ||||
|  | ||||
| 	/* | ||||
| 	 * Calculate common prefix for the pathspec, and | ||||
| 	 * use that to optimize the directory walk | ||||
| 	 */ | ||||
| 	baselen = common_prefix(pathspec); | ||||
| 	path = "."; | ||||
| 	base = ""; | ||||
| 	if (baselen) | ||||
| 		path = base = xmemdupz(*pathspec, baselen); | ||||
| 	read_directory(&dir, path, base, baselen, pathspec); | ||||
| 	strbuf_init(&directory, 0); | ||||
|  | ||||
| 	for (j = 0; j < dir.nr; ++j) { | ||||
| 		struct dir_entry *ent = dir.entries[j]; | ||||
| 		int len, pos, specs; | ||||
| 		struct cache_entry *ce; | ||||
| 		struct stat st; | ||||
| 		char *seen; | ||||
|  | ||||
| 		/* | ||||
| 		 * Remove the '/' at the end that directory | ||||
| 		 * walking adds for directory entries. | ||||
| 		 */ | ||||
| 		len = ent->len; | ||||
| 		if (len && ent->name[len-1] == '/') | ||||
| 			len--; | ||||
| 		pos = cache_name_pos(ent->name, len); | ||||
| 		if (0 <= pos) | ||||
| 			continue;	/* exact match */ | ||||
| 		pos = -pos - 1; | ||||
| 		if (pos < active_nr) { | ||||
| 			ce = active_cache[pos]; | ||||
| 			if (ce_namelen(ce) == len && | ||||
| 			    !memcmp(ce->name, ent->name, len)) | ||||
| 				continue; /* Yup, this one exists unmerged */ | ||||
| 		} | ||||
|  | ||||
| 		if (!lstat(ent->name, &st) && (S_ISDIR(st.st_mode))) { | ||||
| 			int matched_path = 0; | ||||
| 			strbuf_addstr(&directory, ent->name); | ||||
| 			if (pathspec) { | ||||
| 				for (specs =0; pathspec[specs]; ++specs) | ||||
| 					/* nothing */; | ||||
| 				seen = xcalloc(specs, 1); | ||||
| 				/* Check if directory was explictly passed as | ||||
| 				 * pathspec.  If so we want to remove it */ | ||||
| 				if (match_pathspec(pathspec, ent->name, ent->len, | ||||
| 						   baselen, seen)) | ||||
| 					matched_path = 1; | ||||
| 				free(seen); | ||||
| 			} | ||||
| 			if (show_only && (remove_directories || matched_path)) { | ||||
| 				printf("Would remove %s\n", directory.buf); | ||||
| 			} else if (quiet && (remove_directories || matched_path)) { | ||||
| 				remove_dir_recursively(&directory, 0); | ||||
| 			} else if (remove_directories || matched_path) { | ||||
| 				printf("Removing %s\n", directory.buf); | ||||
| 				remove_dir_recursively(&directory, 0); | ||||
| 			} else if (show_only) { | ||||
| 				printf("Would not remove %s\n", directory.buf); | ||||
| 			} else { | ||||
| 				printf("Not removing %s\n", directory.buf); | ||||
| 			} | ||||
| 			strbuf_reset(&directory); | ||||
| 		} else { | ||||
| 			if (show_only) { | ||||
| 				printf("Would remove %s\n", ent->name); | ||||
| 				continue; | ||||
| 			} else if (!quiet) { | ||||
| 				printf("Removing %s\n", ent->name); | ||||
| 			} | ||||
| 			unlink(ent->name); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	strbuf_release(&directory); | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix); | |||
| extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_cherry(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_clean(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_count_objects(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_describe(int argc, const char **argv, const char *prefix); | ||||
|  |  | |||
							
								
								
									
										1
									
								
								git.c
								
								
								
								
							
							
						
						
									
										1
									
								
								git.c
								
								
								
								
							|  | @ -293,6 +293,7 @@ static void handle_internal_command(int argc, const char **argv) | |||
| 		{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, | ||||
| 		{ "cherry", cmd_cherry, RUN_SETUP }, | ||||
| 		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, | ||||
| 		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, | ||||
| 		{ "commit-tree", cmd_commit_tree, RUN_SETUP }, | ||||
| 		{ "config", cmd_config }, | ||||
| 		{ "count-objects", cmd_count_objects, RUN_SETUP }, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Shawn Bohrer
						Shawn Bohrer