mirror of
https://github.com/git/git
synced 2024-08-25 10:45:59 +00:00
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>
This commit is contained in:
parent
c8769f76d9
commit
0d042fecf2
|
@ -11,7 +11,7 @@ SYNOPSIS
|
|||
[verse]
|
||||
'git-grep' [--cached]
|
||||
[-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]
|
||||
[-n] [-l | --files-with-matches] [-L | --files-without-match]
|
||||
[-c | --count]
|
||||
|
@ -47,6 +47,12 @@ OPTIONS
|
|||
-v | --invert-match::
|
||||
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::
|
||||
Use POSIX extended/basic regexp for patterns. Default
|
||||
is to use basic regexp.
|
||||
|
|
|
@ -123,6 +123,7 @@ struct grep_opt {
|
|||
struct grep_pat *pattern_list;
|
||||
struct grep_pat **pattern_tail;
|
||||
struct grep_expr *pattern_expression;
|
||||
int prefix_length;
|
||||
regex_t regexp;
|
||||
unsigned linenum:1;
|
||||
unsigned invert:1;
|
||||
|
@ -136,6 +137,7 @@ struct grep_opt {
|
|||
#define GREP_BINARY_TEXT 2
|
||||
unsigned binary:2;
|
||||
unsigned extended:1;
|
||||
unsigned relative:1;
|
||||
int regflags;
|
||||
unsigned pre_context;
|
||||
unsigned post_context;
|
||||
|
@ -632,19 +634,40 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
|
|||
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;
|
||||
char *data;
|
||||
char type[20];
|
||||
char *to_free = NULL;
|
||||
int hit;
|
||||
|
||||
data = read_sha1_file(sha1, type, &size);
|
||||
if (!data) {
|
||||
error("'%s': unable to read %s", name, sha1_to_hex(sha1));
|
||||
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);
|
||||
free(data);
|
||||
free(to_free);
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
@ -674,6 +697,8 @@ static int grep_file(struct grep_opt *opt, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
close(i);
|
||||
if (opt->relative && opt->prefix_length)
|
||||
filename += opt->prefix_length;
|
||||
i = grep_buffer(opt, filename, data, st.st_size);
|
||||
free(data);
|
||||
return i;
|
||||
|
@ -720,7 +745,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
|||
char *argptr = randarg;
|
||||
struct grep_pat *p;
|
||||
|
||||
if (opt->extended)
|
||||
if (opt->extended || (opt->relative && opt->prefix_length))
|
||||
return -1;
|
||||
len = nr = 0;
|
||||
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))
|
||||
continue;
|
||||
if (cached)
|
||||
hit |= grep_sha1(opt, ce->sha1, ce->name);
|
||||
hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
|
||||
else
|
||||
hit |= grep_file(opt, ce->name);
|
||||
}
|
||||
|
@ -860,11 +885,12 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
|
|||
int hit = 0;
|
||||
struct name_entry entry;
|
||||
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]) {
|
||||
int offset = sprintf(path_buf, "%s:", tree_name);
|
||||
down = path_buf + offset;
|
||||
if (tn_len) {
|
||||
tn_len = sprintf(path_buf, "%s:", tree_name);
|
||||
down = path_buf + tn_len;
|
||||
strcat(down, base);
|
||||
}
|
||||
else {
|
||||
|
@ -886,7 +912,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
|
|||
if (!pathspec_matches(paths, down))
|
||||
;
|
||||
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)) {
|
||||
char type[20];
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
struct tree_desc tree;
|
||||
void *data;
|
||||
|
@ -945,6 +971,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
int i;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
|
||||
opt.relative = 1;
|
||||
opt.pattern_tail = &opt.pattern_list;
|
||||
opt.regflags = REG_NEWLINE;
|
||||
|
||||
|
@ -1118,6 +1146,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
die(emsg_missing_argument, arg);
|
||||
}
|
||||
if (!strcmp("--full-name", arg)) {
|
||||
opt.relative = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--", arg)) {
|
||||
/* later processing wants to have this at argv[1] */
|
||||
argv--;
|
||||
|
@ -1176,8 +1208,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
verify_filename(prefix, argv[j]);
|
||||
}
|
||||
|
||||
if (i < argc)
|
||||
if (i < argc) {
|
||||
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) {
|
||||
paths = xcalloc(2, sizeof(const char *));
|
||||
paths[0] = prefix;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Copyright (c) 2006 Junio C Hamano
|
||||
#
|
||||
|
||||
test_description='git grep -w
|
||||
test_description='git grep various.
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
@ -19,7 +19,9 @@ test_expect_success setup '
|
|||
echo x x xx x >x &&
|
||||
echo y yy >y &&
|
||||
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
|
||||
'
|
||||
|
||||
|
@ -80,6 +82,31 @@ do
|
|||
diff expected actual
|
||||
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
|
||||
|
||||
test_done
|
||||
|
|
Loading…
Reference in a new issue