@matheusbsbs / matheus-bagliano / commits / a4d7d2c6db
Fork 0

log --author/--committer: really match only with name part

When we tried to find commits done by AUTHOR, the first implementation tried to pattern match a line with "^author .*AUTHOR", which later was enhanced to strip leading caret and look for "^author AUTHOR" when the search pattern was anchored at the left end (i.e. --author="^AUTHOR"). This had a few problems: * When looking for fixed strings (e.g. "git log -F --author=x --grep=y"), the regexp internally used "^author .*x" would never match anything; * To match at the end (e.g. "git log --author='google.com>$'"), the generated regexp has to also match the trailing timestamp part the commit header lines have. Also, in order to determine if the '$' at the end means "match at the end of the line" or just a literal dollar sign (probably backslash-quoted), we would need to parse the regexp ourselves. An earlier alternative tried to make sure that a line matches "^author " (to limit by field name) and the user supplied pattern at the same time. While it solved the -F problem by introducing a special override for matching the "^author ", it did not solve the trailing timestamp nor tail match problem. It also would have matched every commit if --author=author was asked for, not because the author's email part had this string, but because every commit header line that talks about the author begins with that field name, regardleses of who wrote it. Instead of piling more hacks on top of hacks, this rethinks the grep machinery that is used to look for strings in the commit header, and makes sure that (1) field name matches literally at the beginning of the line, followed by a SP, and (2) the user supplied pattern is matched against the remainder of the line, excluding the trailing timestamp data. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Sep 4, 2008 at 22:15 UTC a4d7d2c6dbc9e1294034171c31d87b24cf3b492e
4 files changed +114 -17
grep.c
+52
@@ -2,6 +2,19 @@
2 #include "grep.h"
3 #include "xdiff-interface.h"
4
5 +void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
6 +{
7 + struct grep_pat *p = xcalloc(1, sizeof(*p));
8 + p->pattern = pat;
9 + p->origin = "header";
10 + p->no = 0;
11 + p->token = GREP_PATTERN_HEAD;
12 + p->field = field;
13 + *opt->pattern_tail = p;
14 + opt->pattern_tail = &p->next;
15 + p->next = NULL;
16 +}
17 +
18 void append_grep_pattern(struct grep_opt *opt, const char *pat,
19 const char *origin, int no, enum grep_pat_token t)
20 {
@@ -247,16 +260,53 @@ static int fixmatch(const char *pattern, char *line, regmatch_t *match)
260 }
261 }
262
263 +static int strip_timestamp(char *bol, char **eol_p)
264 +{
265 + char *eol = *eol_p;
266 + int ch;
267 +
268 + while (bol < --eol) {
269 + if (*eol != '>')
270 + continue;
271 + *eol_p = ++eol;
272 + ch = *eol;
273 + *eol = '\0';
274 + return ch;
275 + }
276 + return 0;
277 +}
278 +
279 +static struct {
280 + const char *field;
281 + size_t len;
282 +} header_field[] = {
283 + { "author ", 7 },
284 + { "committer ", 10 },
285 +};
286 +
287 static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
288 {
289 int hit = 0;
290 int at_true_bol = 1;
291 + int saved_ch = 0;
292 regmatch_t pmatch[10];
293
294 if ((p->token != GREP_PATTERN) &&
295 ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
296 return 0;
297
298 + if (p->token == GREP_PATTERN_HEAD) {
299 + const char *field;
300 + size_t len;
301 + assert(p->field < ARRAY_SIZE(header_field));
302 + field = header_field[p->field].field;
303 + len = header_field[p->field].len;
304 + if (strncmp(bol, field, len))
305 + return 0;
306 + bol += len;
307 + saved_ch = strip_timestamp(bol, &eol);
308 + }
309 +
310 again:
311 if (!opt->fixed) {
312 regex_t *exp = &p->regexp;
@@ -298,6 +348,8 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
348 goto again;
349 }
350 }
351 + if (p->token == GREP_PATTERN_HEAD && saved_ch)
352 + *eol = saved_ch;
353 return hit;
354 }
355
grep.h
+7
@@ -17,12 +17,18 @@ enum grep_context {
17 GREP_CONTEXT_BODY,
18 };
19
20 +enum grep_header_field {
21 + GREP_HEADER_AUTHOR = 0,
22 + GREP_HEADER_COMMITTER,
23 +};
24 +
25 struct grep_pat {
26 struct grep_pat *next;
27 const char *origin;
28 int no;
29 enum grep_pat_token token;
30 const char *pattern;
31 + enum grep_header_field field;
32 regex_t regexp;
33 };
34
@@ -74,6 +80,7 @@ struct grep_opt {
80 };
81
82 extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
83 +extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
84 extern void compile_grep_patterns(struct grep_opt *opt);
85 extern void free_grep_patterns(struct grep_opt *opt);
86 extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
revision.c
+4 -17
@@ -953,22 +953,9 @@ static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token
953 append_grep_pattern(&revs->grep_filter, ptn, "command line", 0, what);
954 }
955
956 -static void add_header_grep(struct rev_info *revs, const char *field, const char *pattern)
956 +static void add_header_grep(struct rev_info *revs, enum grep_header_field field, const char *pattern)
957 {
958 - char *pat;
959 - const char *prefix;
960 - int patlen, fldlen;
961 -
962 - fldlen = strlen(field);
963 - patlen = strlen(pattern);
964 - pat = xmalloc(patlen + fldlen + 10);
965 - prefix = ".*";
966 - if (*pattern == '^') {
967 - prefix = "";
968 - pattern++;
969 - }
970 - sprintf(pat, "^%s %s%s", field, prefix, pattern);
971 - add_grep(revs, pat, GREP_PATTERN_HEAD);
958 + append_header_grep_pattern(&revs->grep_filter, field, pattern);
959 }
960
961 static void add_message_grep(struct rev_info *revs, const char *pattern)
@@ -1154,9 +1141,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
1141 * Grepping the commit log
1142 */
1143 else if (!prefixcmp(arg, "--author=")) {
1157 - add_header_grep(revs, "author", arg+9);
1144 + add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
1145 } else if (!prefixcmp(arg, "--committer=")) {
1159 - add_header_grep(revs, "committer", arg+12);
1146 + add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
1147 } else if (!prefixcmp(arg, "--grep=")) {
1148 add_message_grep(revs, arg+7);
1149 } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
t/t7002-grep.sh
+51
@@ -22,6 +22,7 @@ test_expect_success setup '
22 mkdir t &&
23 echo test >t/t &&
24 git add file x y z t/t &&
25 + test_tick &&
26 git commit -m initial
27 '
28
@@ -113,4 +114,54 @@ do
114
115 done
116
117 +test_expect_success 'log grep setup' '
118 + echo a >>file &&
119 + test_tick &&
120 + GIT_AUTHOR_NAME="With * Asterisk" \
121 + GIT_AUTHOR_EMAIL="xyzzy@frotz.com" \
122 + git commit -a -m "second" &&
123 +
124 + echo a >>file &&
125 + test_tick &&
126 + git commit -a -m "third"
127 +
128 +'
129 +
130 +test_expect_success 'log grep (1)' '
131 + git log --author=author --pretty=tformat:%s >actual &&
132 + ( echo third ; echo initial ) >expect &&
133 + test_cmp expect actual
134 +'
135 +
136 +test_expect_success 'log grep (2)' '
137 + git log --author=" * " -F --pretty=tformat:%s >actual &&
138 + ( echo second ) >expect &&
139 + test_cmp expect actual
140 +'
141 +
142 +test_expect_success 'log grep (3)' '
143 + git log --author="^A U" --pretty=tformat:%s >actual &&
144 + ( echo third ; echo initial ) >expect &&
145 + test_cmp expect actual
146 +'
147 +
148 +test_expect_success 'log grep (4)' '
149 + git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
150 + ( echo second ) >expect &&
151 + test_cmp expect actual
152 +'
153 +
154 +test_expect_success 'log grep (5)' '
155 + git log --author=Thor -F --grep=Thu --pretty=tformat:%s >actual &&
156 + ( echo third ; echo initial ) >expect &&
157 + test_cmp expect actual
158 +'
159 +
160 +test_expect_success 'log grep (6)' '
161 + git log --author=-0700 --pretty=tformat:%s >actual &&
162 + >expect &&
163 + test_cmp expect actual
164 +
165 +'
166 +
167 test_done