summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2010-01-17 20:09:06 -0800
committerJunio C Hamano <gitster@pobox.com>2010-01-25 19:28:13 -0800
commit80235ba79ef43349f455cce869397b3e726f4058 (patch)
tree3ec0247fba884fe708ec771833a981cb4ebf61ea
parentff6d26a0e1d8fad775010fa3b689c0c027da8bb0 (diff)
downloadgit-80235ba79ef43349f455cce869397b3e726f4058.tar.gz
"log --author=me --grep=it" should find intersection, not union
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>
-rw-r--r--builtin-grep.c1
-rw-r--r--builtin-rev-list.c5
-rw-r--r--grep.c46
-rw-r--r--grep.h2
-rw-r--r--revision.c3
-rwxr-xr-xt/t7002-grep.sh10
6 files changed, 57 insertions, 10 deletions
diff --git a/builtin-grep.c b/builtin-grep.c
index 529461fb8f..d57c4d9bed 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -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;
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index cd97ded4d2..c53ad9fa08 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -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;
diff --git a/grep.c b/grep.c
index bdadf2c0cc..eee99f31f1 100644
--- a/grep.c
+++ b/grep.c
@@ -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;
}
@@ -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) {
@@ -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.
@@ -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)
diff --git a/grep.h b/grep.h
index 75370f60d7..e39e5146d8 100644
--- a/grep.h
+++ b/grep.h
@@ -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;
diff --git a/revision.c b/revision.c
index 25fa14d93e..c84243d7bb 100644
--- a/revision.c
+++ b/revision.c
@@ -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);
@@ -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 */
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 76c5e091b7..6baa4461f7 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -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
'
@@ -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 &&