Skip to content

Commit 0d042fe

Browse files
author
Junio C Hamano
committed
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>
1 parent c8769f7 commit 0d042fe

File tree

3 files changed

+85
-13
lines changed

3 files changed

+85
-13
lines changed

Documentation/git-grep.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ SYNOPSIS
1111
[verse]
1212
'git-grep' [--cached]
1313
[-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
14-
[-v | --invert-match]
14+
[-v | --invert-match] [--full-name]
1515
[-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
1616
[-n] [-l | --files-with-matches] [-L | --files-without-match]
1717
[-c | --count]
@@ -47,6 +47,12 @@ OPTIONS
4747
-v | --invert-match::
4848
Select non-matching lines.
4949

50+
--full-name::
51+
When run from a subdirectory, the command usually
52+
outputs paths relative to the current directory. This
53+
option forces paths to be output relative to the project
54+
top directory.
55+
5056
-E | --extended-regexp | -G | --basic-regexp::
5157
Use POSIX extended/basic regexp for patterns. Default
5258
is to use basic regexp.

builtin-grep.c

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ struct grep_opt {
123123
struct grep_pat *pattern_list;
124124
struct grep_pat **pattern_tail;
125125
struct grep_expr *pattern_expression;
126+
int prefix_length;
126127
regex_t regexp;
127128
unsigned linenum:1;
128129
unsigned invert:1;
@@ -136,6 +137,7 @@ struct grep_opt {
136137
#define GREP_BINARY_TEXT 2
137138
unsigned binary:2;
138139
unsigned extended:1;
140+
unsigned relative:1;
139141
int regflags;
140142
unsigned pre_context;
141143
unsigned post_context;
@@ -632,19 +634,40 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
632634
return !!last_hit;
633635
}
634636

635-
static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name)
637+
static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
636638
{
637639
unsigned long size;
638640
char *data;
639641
char type[20];
642+
char *to_free = NULL;
640643
int hit;
644+
641645
data = read_sha1_file(sha1, type, &size);
642646
if (!data) {
643647
error("'%s': unable to read %s", name, sha1_to_hex(sha1));
644648
return 0;
645649
}
650+
if (opt->relative && opt->prefix_length) {
651+
static char name_buf[PATH_MAX];
652+
char *cp;
653+
int name_len = strlen(name) - opt->prefix_length + 1;
654+
655+
if (!tree_name_len)
656+
name += opt->prefix_length;
657+
else {
658+
if (ARRAY_SIZE(name_buf) <= name_len)
659+
cp = to_free = xmalloc(name_len);
660+
else
661+
cp = name_buf;
662+
memcpy(cp, name, tree_name_len);
663+
strcpy(cp + tree_name_len,
664+
name + tree_name_len + opt->prefix_length);
665+
name = cp;
666+
}
667+
}
646668
hit = grep_buffer(opt, name, data, size);
647669
free(data);
670+
free(to_free);
648671
return hit;
649672
}
650673

@@ -674,6 +697,8 @@ static int grep_file(struct grep_opt *opt, const char *filename)
674697
return 0;
675698
}
676699
close(i);
700+
if (opt->relative && opt->prefix_length)
701+
filename += opt->prefix_length;
677702
i = grep_buffer(opt, filename, data, st.st_size);
678703
free(data);
679704
return i;
@@ -720,7 +745,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
720745
char *argptr = randarg;
721746
struct grep_pat *p;
722747

723-
if (opt->extended)
748+
if (opt->extended || (opt->relative && opt->prefix_length))
724749
return -1;
725750
len = nr = 0;
726751
push_arg("grep");
@@ -845,7 +870,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
845870
if (!pathspec_matches(paths, ce->name))
846871
continue;
847872
if (cached)
848-
hit |= grep_sha1(opt, ce->sha1, ce->name);
873+
hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
849874
else
850875
hit |= grep_file(opt, ce->name);
851876
}
@@ -860,11 +885,12 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
860885
int hit = 0;
861886
struct name_entry entry;
862887
char *down;
863-
char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
888+
int tn_len = strlen(tree_name);
889+
char *path_buf = xmalloc(PATH_MAX + tn_len + 100);
864890

865-
if (tree_name[0]) {
866-
int offset = sprintf(path_buf, "%s:", tree_name);
867-
down = path_buf + offset;
891+
if (tn_len) {
892+
tn_len = sprintf(path_buf, "%s:", tree_name);
893+
down = path_buf + tn_len;
868894
strcat(down, base);
869895
}
870896
else {
@@ -886,7 +912,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
886912
if (!pathspec_matches(paths, down))
887913
;
888914
else if (S_ISREG(entry.mode))
889-
hit |= grep_sha1(opt, entry.sha1, path_buf);
915+
hit |= grep_sha1(opt, entry.sha1, path_buf, tn_len);
890916
else if (S_ISDIR(entry.mode)) {
891917
char type[20];
892918
struct tree_desc sub;
@@ -907,7 +933,7 @@ static int grep_object(struct grep_opt *opt, const char **paths,
907933
struct object *obj, const char *name)
908934
{
909935
if (obj->type == OBJ_BLOB)
910-
return grep_sha1(opt, obj->sha1, name);
936+
return grep_sha1(opt, obj->sha1, name, 0);
911937
if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
912938
struct tree_desc tree;
913939
void *data;
@@ -945,6 +971,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
945971
int i;
946972

947973
memset(&opt, 0, sizeof(opt));
974+
opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
975+
opt.relative = 1;
948976
opt.pattern_tail = &opt.pattern_list;
949977
opt.regflags = REG_NEWLINE;
950978

@@ -1118,6 +1146,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
11181146
}
11191147
die(emsg_missing_argument, arg);
11201148
}
1149+
if (!strcmp("--full-name", arg)) {
1150+
opt.relative = 0;
1151+
continue;
1152+
}
11211153
if (!strcmp("--", arg)) {
11221154
/* later processing wants to have this at argv[1] */
11231155
argv--;
@@ -1176,8 +1208,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
11761208
verify_filename(prefix, argv[j]);
11771209
}
11781210

1179-
if (i < argc)
1211+
if (i < argc) {
11801212
paths = get_pathspec(prefix, argv + i);
1213+
if (opt.prefix_length && opt.relative) {
1214+
/* Make sure we do not get outside of paths */
1215+
for (i = 0; paths[i]; i++)
1216+
if (strncmp(prefix, paths[i], opt.prefix_length))
1217+
die("git-grep: cannot generate relative filenames containing '..'");
1218+
}
1219+
}
11811220
else if (prefix) {
11821221
paths = xcalloc(2, sizeof(const char *));
11831222
paths[0] = prefix;

t/t7002-grep.sh

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Copyright (c) 2006 Junio C Hamano
44
#
55

6-
test_description='git grep -w
6+
test_description='git grep various.
77
'
88

99
. ./test-lib.sh
@@ -19,7 +19,9 @@ test_expect_success setup '
1919
echo x x xx x >x &&
2020
echo y yy >y &&
2121
echo zzz > z &&
22-
git add file x y z &&
22+
mkdir t &&
23+
echo test >t/t &&
24+
git add file x y z t/t &&
2325
git commit -m initial
2426
'
2527

@@ -80,6 +82,31 @@ do
8082
diff expected actual
8183
fi
8284
'
85+
86+
test_expect_success "grep $L (t-1)" '
87+
echo "${HC}t/t:1:test" >expected &&
88+
git grep -n -e test $H >actual &&
89+
diff expected actual
90+
'
91+
92+
test_expect_success "grep $L (t-2)" '
93+
echo "${HC}t:1:test" >expected &&
94+
(
95+
cd t &&
96+
git grep -n -e test $H
97+
) >actual &&
98+
diff expected actual
99+
'
100+
101+
test_expect_success "grep $L (t-3)" '
102+
echo "${HC}t/t:1:test" >expected &&
103+
(
104+
cd t &&
105+
git grep --full-name -n -e test $H
106+
) >actual &&
107+
diff expected actual
108+
'
109+
83110
done
84111

85112
test_done

0 commit comments

Comments
 (0)