summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2006-08-11 00:44:42 -0700
committerJunio C Hamano <junkio@cox.net>2006-08-11 19:08:10 -0700
commit0d042fecf2f4130717d198f9cc2d792e72370bda (patch)
tree45f19a371877c20fcae9166795e17143863a0982
parentc8769f76d992ed391d169cd71be009ca17727271 (diff)
downloadgit-0d042fecf2f4130717d198f9cc2d792e72370bda.tar.gz
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>
-rw-r--r--Documentation/git-grep.txt8
-rw-r--r--builtin-grep.c59
-rwxr-xr-xt/t7002-grep.sh31
3 files changed, 85 insertions, 13 deletions
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index dc7683383c..7545dd9a3e 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -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.
diff --git a/builtin-grep.c b/builtin-grep.c
index 93b7e07b30..a561612e7e 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -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;
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 00a7d762ce..6bfb899ed1 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -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