git-grep: show pathnames relative to the current directory
By default, the command shows pathnames relative to the current directory. Use --full-name (the same flag to do so in ls-files) if you want to see the full pathname relative to the project root. This makes it very pleasant to run in Emacs compilation (or "grep-find") buffer. Signed-off-by: Junio C Hamano <junkio@cox.net>maint
							parent
							
								
									c8769f76d9
								
							
						
					
					
						commit
						0d042fecf2
					
				|  | @ -11,7 +11,7 @@ SYNOPSIS | ||||||
| [verse] | [verse] | ||||||
| 'git-grep' [--cached] | 'git-grep' [--cached] | ||||||
| 	   [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp] | 	   [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp] | ||||||
| 	   [-v | --invert-match] | 	   [-v | --invert-match] [--full-name] | ||||||
| 	   [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings] | 	   [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings] | ||||||
| 	   [-n] [-l | --files-with-matches] [-L | --files-without-match] | 	   [-n] [-l | --files-with-matches] [-L | --files-without-match] | ||||||
| 	   [-c | --count] | 	   [-c | --count] | ||||||
|  | @ -47,6 +47,12 @@ OPTIONS | ||||||
| -v | --invert-match:: | -v | --invert-match:: | ||||||
| 	Select non-matching lines. | 	Select non-matching lines. | ||||||
|  |  | ||||||
|  | --full-name:: | ||||||
|  | 	When run from a subdirectory, the command usually | ||||||
|  | 	outputs paths relative to the current directory.  This | ||||||
|  | 	option forces paths to be output relative to the project | ||||||
|  | 	top directory. | ||||||
|  |  | ||||||
| -E | --extended-regexp | -G | --basic-regexp:: | -E | --extended-regexp | -G | --basic-regexp:: | ||||||
| 	Use POSIX extended/basic regexp for patterns.  Default | 	Use POSIX extended/basic regexp for patterns.  Default | ||||||
| 	is to use basic regexp. | 	is to use basic regexp. | ||||||
|  |  | ||||||
|  | @ -123,6 +123,7 @@ struct grep_opt { | ||||||
| 	struct grep_pat *pattern_list; | 	struct grep_pat *pattern_list; | ||||||
| 	struct grep_pat **pattern_tail; | 	struct grep_pat **pattern_tail; | ||||||
| 	struct grep_expr *pattern_expression; | 	struct grep_expr *pattern_expression; | ||||||
|  | 	int prefix_length; | ||||||
| 	regex_t regexp; | 	regex_t regexp; | ||||||
| 	unsigned linenum:1; | 	unsigned linenum:1; | ||||||
| 	unsigned invert:1; | 	unsigned invert:1; | ||||||
|  | @ -136,6 +137,7 @@ struct grep_opt { | ||||||
| #define GREP_BINARY_TEXT	2 | #define GREP_BINARY_TEXT	2 | ||||||
| 	unsigned binary:2; | 	unsigned binary:2; | ||||||
| 	unsigned extended:1; | 	unsigned extended:1; | ||||||
|  | 	unsigned relative:1; | ||||||
| 	int regflags; | 	int regflags; | ||||||
| 	unsigned pre_context; | 	unsigned pre_context; | ||||||
| 	unsigned post_context; | 	unsigned post_context; | ||||||
|  | @ -632,19 +634,40 @@ static int grep_buffer(struct grep_opt *opt, const char *name, | ||||||
| 	return !!last_hit; | 	return !!last_hit; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name) | static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len) | ||||||
| { | { | ||||||
| 	unsigned long size; | 	unsigned long size; | ||||||
| 	char *data; | 	char *data; | ||||||
| 	char type[20]; | 	char type[20]; | ||||||
|  | 	char *to_free = NULL; | ||||||
| 	int hit; | 	int hit; | ||||||
|  |  | ||||||
| 	data = read_sha1_file(sha1, type, &size); | 	data = read_sha1_file(sha1, type, &size); | ||||||
| 	if (!data) { | 	if (!data) { | ||||||
| 		error("'%s': unable to read %s", name, sha1_to_hex(sha1)); | 		error("'%s': unable to read %s", name, sha1_to_hex(sha1)); | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
|  | 	if (opt->relative && opt->prefix_length) { | ||||||
|  | 		static char name_buf[PATH_MAX]; | ||||||
|  | 		char *cp; | ||||||
|  | 		int name_len = strlen(name) - opt->prefix_length + 1; | ||||||
|  |  | ||||||
|  | 		if (!tree_name_len) | ||||||
|  | 			name += opt->prefix_length; | ||||||
|  | 		else { | ||||||
|  | 			if (ARRAY_SIZE(name_buf) <= name_len) | ||||||
|  | 				cp = to_free = xmalloc(name_len); | ||||||
|  | 			else | ||||||
|  | 				cp = name_buf; | ||||||
|  | 			memcpy(cp, name, tree_name_len); | ||||||
|  | 			strcpy(cp + tree_name_len, | ||||||
|  | 			       name + tree_name_len + opt->prefix_length); | ||||||
|  | 			name = cp; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	hit = grep_buffer(opt, name, data, size); | 	hit = grep_buffer(opt, name, data, size); | ||||||
| 	free(data); | 	free(data); | ||||||
|  | 	free(to_free); | ||||||
| 	return hit; | 	return hit; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -674,6 +697,8 @@ static int grep_file(struct grep_opt *opt, const char *filename) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 	close(i); | 	close(i); | ||||||
|  | 	if (opt->relative && opt->prefix_length) | ||||||
|  | 		filename += opt->prefix_length; | ||||||
| 	i = grep_buffer(opt, filename, data, st.st_size); | 	i = grep_buffer(opt, filename, data, st.st_size); | ||||||
| 	free(data); | 	free(data); | ||||||
| 	return i; | 	return i; | ||||||
|  | @ -720,7 +745,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) | ||||||
| 	char *argptr = randarg; | 	char *argptr = randarg; | ||||||
| 	struct grep_pat *p; | 	struct grep_pat *p; | ||||||
|  |  | ||||||
| 	if (opt->extended) | 	if (opt->extended || (opt->relative && opt->prefix_length)) | ||||||
| 		return -1; | 		return -1; | ||||||
| 	len = nr = 0; | 	len = nr = 0; | ||||||
| 	push_arg("grep"); | 	push_arg("grep"); | ||||||
|  | @ -845,7 +870,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) | ||||||
| 		if (!pathspec_matches(paths, ce->name)) | 		if (!pathspec_matches(paths, ce->name)) | ||||||
| 			continue; | 			continue; | ||||||
| 		if (cached) | 		if (cached) | ||||||
| 			hit |= grep_sha1(opt, ce->sha1, ce->name); | 			hit |= grep_sha1(opt, ce->sha1, ce->name, 0); | ||||||
| 		else | 		else | ||||||
| 			hit |= grep_file(opt, ce->name); | 			hit |= grep_file(opt, ce->name); | ||||||
| 	} | 	} | ||||||
|  | @ -860,11 +885,12 @@ static int grep_tree(struct grep_opt *opt, const char **paths, | ||||||
| 	int hit = 0; | 	int hit = 0; | ||||||
| 	struct name_entry entry; | 	struct name_entry entry; | ||||||
| 	char *down; | 	char *down; | ||||||
| 	char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100); | 	int tn_len = strlen(tree_name); | ||||||
|  | 	char *path_buf = xmalloc(PATH_MAX + tn_len + 100); | ||||||
|  |  | ||||||
| 	if (tree_name[0]) { | 	if (tn_len) { | ||||||
| 		int offset = sprintf(path_buf, "%s:", tree_name); | 		tn_len = sprintf(path_buf, "%s:", tree_name); | ||||||
| 		down = path_buf + offset; | 		down = path_buf + tn_len; | ||||||
| 		strcat(down, base); | 		strcat(down, base); | ||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
|  | @ -886,7 +912,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, | ||||||
| 		if (!pathspec_matches(paths, down)) | 		if (!pathspec_matches(paths, down)) | ||||||
| 			; | 			; | ||||||
| 		else if (S_ISREG(entry.mode)) | 		else if (S_ISREG(entry.mode)) | ||||||
| 			hit |= grep_sha1(opt, entry.sha1, path_buf); | 			hit |= grep_sha1(opt, entry.sha1, path_buf, tn_len); | ||||||
| 		else if (S_ISDIR(entry.mode)) { | 		else if (S_ISDIR(entry.mode)) { | ||||||
| 			char type[20]; | 			char type[20]; | ||||||
| 			struct tree_desc sub; | 			struct tree_desc sub; | ||||||
|  | @ -907,7 +933,7 @@ static int grep_object(struct grep_opt *opt, const char **paths, | ||||||
| 		       struct object *obj, const char *name) | 		       struct object *obj, const char *name) | ||||||
| { | { | ||||||
| 	if (obj->type == OBJ_BLOB) | 	if (obj->type == OBJ_BLOB) | ||||||
| 		return grep_sha1(opt, obj->sha1, name); | 		return grep_sha1(opt, obj->sha1, name, 0); | ||||||
| 	if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { | 	if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { | ||||||
| 		struct tree_desc tree; | 		struct tree_desc tree; | ||||||
| 		void *data; | 		void *data; | ||||||
|  | @ -945,6 +971,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) | ||||||
| 	int i; | 	int i; | ||||||
|  |  | ||||||
| 	memset(&opt, 0, sizeof(opt)); | 	memset(&opt, 0, sizeof(opt)); | ||||||
|  | 	opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; | ||||||
|  | 	opt.relative = 1; | ||||||
| 	opt.pattern_tail = &opt.pattern_list; | 	opt.pattern_tail = &opt.pattern_list; | ||||||
| 	opt.regflags = REG_NEWLINE; | 	opt.regflags = REG_NEWLINE; | ||||||
|  |  | ||||||
|  | @ -1118,6 +1146,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix) | ||||||
| 			} | 			} | ||||||
| 			die(emsg_missing_argument, arg); | 			die(emsg_missing_argument, arg); | ||||||
| 		} | 		} | ||||||
|  | 		if (!strcmp("--full-name", arg)) { | ||||||
|  | 			opt.relative = 0; | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
| 		if (!strcmp("--", arg)) { | 		if (!strcmp("--", arg)) { | ||||||
| 			/* later processing wants to have this at argv[1] */ | 			/* later processing wants to have this at argv[1] */ | ||||||
| 			argv--; | 			argv--; | ||||||
|  | @ -1176,8 +1208,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix) | ||||||
| 			verify_filename(prefix, argv[j]); | 			verify_filename(prefix, argv[j]); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (i < argc) | 	if (i < argc) { | ||||||
| 		paths = get_pathspec(prefix, argv + i); | 		paths = get_pathspec(prefix, argv + i); | ||||||
|  | 		if (opt.prefix_length && opt.relative) { | ||||||
|  | 			/* Make sure we do not get outside of paths */ | ||||||
|  | 			for (i = 0; paths[i]; i++) | ||||||
|  | 				if (strncmp(prefix, paths[i], opt.prefix_length)) | ||||||
|  | 					die("git-grep: cannot generate relative filenames containing '..'"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	else if (prefix) { | 	else if (prefix) { | ||||||
| 		paths = xcalloc(2, sizeof(const char *)); | 		paths = xcalloc(2, sizeof(const char *)); | ||||||
| 		paths[0] = prefix; | 		paths[0] = prefix; | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| # Copyright (c) 2006 Junio C Hamano | # Copyright (c) 2006 Junio C Hamano | ||||||
| # | # | ||||||
|  |  | ||||||
| test_description='git grep -w | test_description='git grep various. | ||||||
| ' | ' | ||||||
|  |  | ||||||
| . ./test-lib.sh | . ./test-lib.sh | ||||||
|  | @ -19,7 +19,9 @@ test_expect_success setup ' | ||||||
| 	echo x x xx x >x && | 	echo x x xx x >x && | ||||||
| 	echo y yy >y && | 	echo y yy >y && | ||||||
| 	echo zzz > z && | 	echo zzz > z && | ||||||
| 	git add file x y z && | 	mkdir t && | ||||||
|  | 	echo test >t/t && | ||||||
|  | 	git add file x y z t/t && | ||||||
| 	git commit -m initial | 	git commit -m initial | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -80,6 +82,31 @@ do | ||||||
| 			diff expected actual | 			diff expected actual | ||||||
| 		fi | 		fi | ||||||
| 	' | 	' | ||||||
|  |  | ||||||
|  | 	test_expect_success "grep $L (t-1)" ' | ||||||
|  | 		echo "${HC}t/t:1:test" >expected && | ||||||
|  | 		git grep -n -e test $H >actual && | ||||||
|  | 		diff expected actual | ||||||
|  | 	' | ||||||
|  |  | ||||||
|  | 	test_expect_success "grep $L (t-2)" ' | ||||||
|  | 		echo "${HC}t:1:test" >expected && | ||||||
|  | 		( | ||||||
|  | 			cd t && | ||||||
|  | 			git grep -n -e test $H | ||||||
|  | 		) >actual && | ||||||
|  | 		diff expected actual | ||||||
|  | 	' | ||||||
|  |  | ||||||
|  | 	test_expect_success "grep $L (t-3)" ' | ||||||
|  | 		echo "${HC}t/t:1:test" >expected && | ||||||
|  | 		( | ||||||
|  | 			cd t && | ||||||
|  | 			git grep --full-name -n -e test $H | ||||||
|  | 		) >actual && | ||||||
|  | 		diff expected actual | ||||||
|  | 	' | ||||||
|  |  | ||||||
| done | done | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano