Skip to content

Commit

Permalink
"log --author=me --grep=it" should find intersection, not union
Browse files Browse the repository at this point in the history
Historically, any grep filter in "git log" family of commands were taken
as restricting to commits with any of the words in the commit log message.
However, the user almost always want to find commits "done by this person
on that topic".  With "--all-match" option, a series of grep patterns can
be turned into a requirement that all of them must produce a match, but
that makes it impossible to ask for "done by me, on either this or that"
with:

	log --author=me --committer=him --grep=this --grep=that

because it will require both "this" and "that" to appear.

Change the "header" parser of grep library to treat the headers specially,
and parse it as:

	(all-match-OR (HEADER-AUTHOR me)
		      (HEADER-COMMITTER him)
		      (OR
		      	(PATTERN this)
			(PATTERN that) ) )

Even though the "log" command line parser doesn't give direct access to
the extended grep syntax to group terms with parentheses, this change will
cover the majority of the case the users would want.

This incidentally revealed that one test in t7002 was bogus.  It ran:

	log --author=Thor --grep=Thu --format='%s'

and expected (wrongly) "Thu" to match "Thursday" in the author/committer
date, but that would never match, as the timestamp in raw commit buffer
does not have the name of the day-of-the-week.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Junio C Hamano committed Jan 26, 2010
1 parent ff6d26a commit 80235ba
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 10 deletions.
1 change: 1 addition & 0 deletions builtin-grep.c
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
opt.relative = 1;
opt.pathname = 1;
opt.pattern_tail = &opt.pattern_list;
opt.header_tail = &opt.header_list;
opt.regflags = REG_NEWLINE;
opt.max_depth = -1;

Expand Down
5 changes: 3 additions & 2 deletions builtin-rev-list.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
revs.diff)
usage(rev_list_usage);

save_commit_buffer = revs.verbose_header ||
revs.grep_filter.pattern_list;
save_commit_buffer = (revs.verbose_header ||
revs.grep_filter.pattern_list ||
revs.grep_filter.header_list);
if (bisect_list)
revs.limited = 1;

Expand Down
46 changes: 40 additions & 6 deletions grep.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field fie
p->no = 0;
p->token = GREP_PATTERN_HEAD;
p->field = field;
*opt->pattern_tail = p;
opt->pattern_tail = &p->next;
*opt->header_tail = p;
opt->header_tail = &p->next;
p->next = NULL;
}

Expand Down Expand Up @@ -172,9 +172,26 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
void compile_grep_patterns(struct grep_opt *opt)
{
struct grep_pat *p;

if (opt->all_match)
opt->extended = 1;
struct grep_expr *header_expr = NULL;

if (opt->header_list) {
p = opt->header_list;
header_expr = compile_pattern_expr(&p);
if (p)
die("incomplete pattern expression: %s", p->pattern);
for (p = opt->header_list; p; p = p->next) {
switch (p->token) {
case GREP_PATTERN: /* atom */
case GREP_PATTERN_HEAD:
case GREP_PATTERN_BODY:
compile_regexp(p, opt);
break;
default:
opt->extended = 1;
break;
}
}
}

for (p = opt->pattern_list; p; p = p->next) {
switch (p->token) {
Expand All @@ -189,7 +206,9 @@ void compile_grep_patterns(struct grep_opt *opt)
}
}

if (!opt->extended)
if (opt->all_match || header_expr)
opt->extended = 1;
else if (!opt->extended)
return;

/* Then bundle them up in an expression.
Expand All @@ -200,6 +219,21 @@ void compile_grep_patterns(struct grep_opt *opt)
opt->pattern_expression = compile_pattern_expr(&p);
if (p)
die("incomplete pattern expression: %s", p->pattern);

if (!header_expr)
return;

if (opt->pattern_expression) {
struct grep_expr *z;
z = xcalloc(1, sizeof(*z));
z->node = GREP_NODE_OR;
z->u.binary.left = opt->pattern_expression;
z->u.binary.right = header_expr;
opt->pattern_expression = z;
} else {
opt->pattern_expression = header_expr;
}
opt->all_match = 1;
}

static void free_pattern_expr(struct grep_expr *x)
Expand Down
2 changes: 2 additions & 0 deletions grep.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ struct grep_expr {
struct grep_opt {
struct grep_pat *pattern_list;
struct grep_pat **pattern_tail;
struct grep_pat *header_list;
struct grep_pat **header_tail;
struct grep_expr *pattern_expression;
const char *prefix;
int prefix_length;
Expand Down
3 changes: 2 additions & 1 deletion revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)

revs->grep_filter.status_only = 1;
revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
revs->grep_filter.regflags = REG_NEWLINE;

diff_setup(&revs->diffopt);
Expand Down Expand Up @@ -1751,7 +1752,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)

static int commit_match(struct commit *commit, struct rev_info *opt)
{
if (!opt->grep_filter.pattern_list)
if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
return 1;
return grep_buffer(&opt->grep_filter,
NULL, /* we say nothing, not even filename */
Expand Down
10 changes: 9 additions & 1 deletion t/t7002-grep.sh
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ test_expect_success 'log grep (4)' '
'

test_expect_success 'log grep (5)' '
git log --author=Thor -F --grep=Thu --pretty=tformat:%s >actual &&
git log --author=Thor -F --pretty=tformat:%s >actual &&
( echo third ; echo initial ) >expect &&
test_cmp expect actual
'
Expand All @@ -368,6 +368,14 @@ test_expect_success 'log grep (6)' '
test_cmp expect actual
'

test_expect_success 'log --grep --author implicitly uses all-match' '
# grep matches initial and second but not third
# author matches only initial and third
git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
echo initial >expect &&
test_cmp expect actual
'

test_expect_success 'grep with CE_VALID file' '
git update-index --assume-unchanged t/t &&
rm t/t &&
Expand Down

0 comments on commit 80235ba

Please sign in to comment.