Merge branch 'jc/maint-log-grep-all-match-1' into maint
* jc/maint-log-grep-all-match-1: grep.c: make two symbols really file-scope static this time t7810-grep: test --all-match with multiple --grep and --author options t7810-grep: test interaction of multiple --grep and --author options t7810-grep: test multiple --author with --all-match t7810-grep: test multiple --grep with and without --all-match t7810-grep: bring log --grep tests in common form grep.c: mark private file-scope symbols as static log: document use of multiple commit limiting options log --grep/--author: honor --all-match honored for multiple --grep patterns grep: show --debug output only once grep: teach --debug option to dump the parse tree
This commit is contained in:
commit
31d69db340
@ -3,8 +3,15 @@ Commit Limiting
|
||||
|
||||
Besides specifying a range of commits that should be listed using the
|
||||
special notations explained in the description, additional commit
|
||||
limiting may be applied. Note that they are applied before commit
|
||||
ordering and formatting options, such as '--reverse'.
|
||||
limiting may be applied.
|
||||
|
||||
Using more options generally further limits the output (e.g.
|
||||
`--since=<date1>` limits to commits newer than `<date1>`, and using it
|
||||
with `--grep=<pattern>` further limits to commits whose log message
|
||||
has a line that matches `<pattern>`), unless otherwise noted.
|
||||
|
||||
Note that these are applied before commit
|
||||
ordering and formatting options, such as `--reverse`.
|
||||
|
||||
--
|
||||
|
||||
@ -39,16 +46,22 @@ endif::git-rev-list[]
|
||||
--committer=<pattern>::
|
||||
|
||||
Limit the commits output to ones with author/committer
|
||||
header lines that match the specified pattern (regular expression).
|
||||
header lines that match the specified pattern (regular
|
||||
expression). With more than one `--author=<pattern>`,
|
||||
commits whose author matches any of the given patterns are
|
||||
chosen (similarly for multiple `--committer=<pattern>`).
|
||||
|
||||
--grep=<pattern>::
|
||||
|
||||
Limit the commits output to ones with log message that
|
||||
matches the specified pattern (regular expression).
|
||||
matches the specified pattern (regular expression). With
|
||||
more than one `--grep=<pattern>`, commits whose message
|
||||
matches any of the given patterns are chosen (but see
|
||||
`--all-match`).
|
||||
|
||||
--all-match::
|
||||
Limit the commits output to ones that match all given --grep,
|
||||
--author and --committer instead of ones that match at least one.
|
||||
instead of ones that match at least one.
|
||||
|
||||
-i::
|
||||
--regexp-ignore-case::
|
||||
|
@ -209,6 +209,7 @@ static void start_threads(struct grep_opt *opt)
|
||||
int err;
|
||||
struct grep_opt *o = grep_opt_dup(opt);
|
||||
o->output = strbuf_out;
|
||||
o->debug = 0;
|
||||
compile_grep_patterns(o);
|
||||
err = pthread_create(&threads[i], NULL, run, o);
|
||||
|
||||
@ -772,6 +773,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
"indicate hit with exit status without output"),
|
||||
OPT_BOOLEAN(0, "all-match", &opt.all_match,
|
||||
"show only matches from files that match all patterns"),
|
||||
{ OPTION_SET_INT, 0, "debug", &opt.debug, NULL,
|
||||
"show parse tree for grep expression",
|
||||
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },
|
||||
OPT_GROUP(""),
|
||||
{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
|
||||
"pager", "show matching files in the pager",
|
||||
|
119
grep.c
119
grep.c
@ -3,6 +3,10 @@
|
||||
#include "userdiff.h"
|
||||
#include "xdiff-interface.h"
|
||||
|
||||
static int grep_source_load(struct grep_source *gs);
|
||||
static int grep_source_is_binary(struct grep_source *gs);
|
||||
|
||||
|
||||
static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
|
||||
const char *origin, int no,
|
||||
enum grep_pat_token t,
|
||||
@ -332,6 +336,87 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
|
||||
return compile_pattern_or(list);
|
||||
}
|
||||
|
||||
static void indent(int in)
|
||||
{
|
||||
while (in-- > 0)
|
||||
fputc(' ', stderr);
|
||||
}
|
||||
|
||||
static void dump_grep_pat(struct grep_pat *p)
|
||||
{
|
||||
switch (p->token) {
|
||||
case GREP_AND: fprintf(stderr, "*and*"); break;
|
||||
case GREP_OPEN_PAREN: fprintf(stderr, "*(*"); break;
|
||||
case GREP_CLOSE_PAREN: fprintf(stderr, "*)*"); break;
|
||||
case GREP_NOT: fprintf(stderr, "*not*"); break;
|
||||
case GREP_OR: fprintf(stderr, "*or*"); break;
|
||||
|
||||
case GREP_PATTERN: fprintf(stderr, "pattern"); break;
|
||||
case GREP_PATTERN_HEAD: fprintf(stderr, "pattern_head"); break;
|
||||
case GREP_PATTERN_BODY: fprintf(stderr, "pattern_body"); break;
|
||||
}
|
||||
|
||||
switch (p->token) {
|
||||
default: break;
|
||||
case GREP_PATTERN_HEAD:
|
||||
fprintf(stderr, "<head %d>", p->field); break;
|
||||
case GREP_PATTERN_BODY:
|
||||
fprintf(stderr, "<body>"); break;
|
||||
}
|
||||
switch (p->token) {
|
||||
default: break;
|
||||
case GREP_PATTERN_HEAD:
|
||||
case GREP_PATTERN_BODY:
|
||||
case GREP_PATTERN:
|
||||
fprintf(stderr, "%.*s", (int)p->patternlen, p->pattern);
|
||||
break;
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
static void dump_grep_expression_1(struct grep_expr *x, int in)
|
||||
{
|
||||
indent(in);
|
||||
switch (x->node) {
|
||||
case GREP_NODE_TRUE:
|
||||
fprintf(stderr, "true\n");
|
||||
break;
|
||||
case GREP_NODE_ATOM:
|
||||
dump_grep_pat(x->u.atom);
|
||||
break;
|
||||
case GREP_NODE_NOT:
|
||||
fprintf(stderr, "(not\n");
|
||||
dump_grep_expression_1(x->u.unary, in+1);
|
||||
indent(in);
|
||||
fprintf(stderr, ")\n");
|
||||
break;
|
||||
case GREP_NODE_AND:
|
||||
fprintf(stderr, "(and\n");
|
||||
dump_grep_expression_1(x->u.binary.left, in+1);
|
||||
dump_grep_expression_1(x->u.binary.right, in+1);
|
||||
indent(in);
|
||||
fprintf(stderr, ")\n");
|
||||
break;
|
||||
case GREP_NODE_OR:
|
||||
fprintf(stderr, "(or\n");
|
||||
dump_grep_expression_1(x->u.binary.left, in+1);
|
||||
dump_grep_expression_1(x->u.binary.right, in+1);
|
||||
indent(in);
|
||||
fprintf(stderr, ")\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_grep_expression(struct grep_opt *opt)
|
||||
{
|
||||
struct grep_expr *x = opt->pattern_expression;
|
||||
|
||||
if (opt->all_match)
|
||||
fprintf(stderr, "[all-match]\n");
|
||||
dump_grep_expression_1(x, 0);
|
||||
fflush(NULL);
|
||||
}
|
||||
|
||||
static struct grep_expr *grep_true_expr(void)
|
||||
{
|
||||
struct grep_expr *z = xcalloc(1, sizeof(*z));
|
||||
@ -395,7 +480,23 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
|
||||
return header_expr;
|
||||
}
|
||||
|
||||
void compile_grep_patterns(struct grep_opt *opt)
|
||||
static struct grep_expr *grep_splice_or(struct grep_expr *x, struct grep_expr *y)
|
||||
{
|
||||
struct grep_expr *z = x;
|
||||
|
||||
while (x) {
|
||||
assert(x->node == GREP_NODE_OR);
|
||||
if (x->u.binary.right &&
|
||||
x->u.binary.right->node == GREP_NODE_TRUE) {
|
||||
x->u.binary.right = y;
|
||||
break;
|
||||
}
|
||||
x = x->u.binary.right;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
static void compile_grep_patterns_real(struct grep_opt *opt)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
struct grep_expr *header_expr = prep_header_patterns(opt);
|
||||
@ -415,7 +516,7 @@ void compile_grep_patterns(struct grep_opt *opt)
|
||||
|
||||
if (opt->all_match || header_expr)
|
||||
opt->extended = 1;
|
||||
else if (!opt->extended)
|
||||
else if (!opt->extended && !opt->debug)
|
||||
return;
|
||||
|
||||
p = opt->pattern_list;
|
||||
@ -429,12 +530,22 @@ void compile_grep_patterns(struct grep_opt *opt)
|
||||
|
||||
if (!opt->pattern_expression)
|
||||
opt->pattern_expression = header_expr;
|
||||
else if (opt->all_match)
|
||||
opt->pattern_expression = grep_splice_or(header_expr,
|
||||
opt->pattern_expression);
|
||||
else
|
||||
opt->pattern_expression = grep_or_expr(opt->pattern_expression,
|
||||
header_expr);
|
||||
opt->all_match = 1;
|
||||
}
|
||||
|
||||
void compile_grep_patterns(struct grep_opt *opt)
|
||||
{
|
||||
compile_grep_patterns_real(opt);
|
||||
if (opt->debug)
|
||||
dump_grep_expression(opt);
|
||||
}
|
||||
|
||||
static void free_pattern_expr(struct grep_expr *x)
|
||||
{
|
||||
switch (x->node) {
|
||||
@ -1358,7 +1469,7 @@ static int grep_source_load_file(struct grep_source *gs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int grep_source_load(struct grep_source *gs)
|
||||
static int grep_source_load(struct grep_source *gs)
|
||||
{
|
||||
if (gs->buf)
|
||||
return 0;
|
||||
@ -1386,7 +1497,7 @@ void grep_source_load_driver(struct grep_source *gs)
|
||||
grep_attr_unlock();
|
||||
}
|
||||
|
||||
int grep_source_is_binary(struct grep_source *gs)
|
||||
static int grep_source_is_binary(struct grep_source *gs)
|
||||
{
|
||||
grep_source_load_driver(gs);
|
||||
if (gs->driver->binary != -1)
|
||||
|
4
grep.h
4
grep.h
@ -90,6 +90,7 @@ struct grep_opt {
|
||||
int word_regexp;
|
||||
int fixed;
|
||||
int all_match;
|
||||
int debug;
|
||||
#define GREP_BINARY_DEFAULT 0
|
||||
#define GREP_BINARY_NOMATCH 1
|
||||
#define GREP_BINARY_TEXT 2
|
||||
@ -148,11 +149,10 @@ struct grep_source {
|
||||
|
||||
void grep_source_init(struct grep_source *gs, enum grep_source_type type,
|
||||
const char *name, const void *identifier);
|
||||
int grep_source_load(struct grep_source *gs);
|
||||
void grep_source_clear_data(struct grep_source *gs);
|
||||
void grep_source_clear(struct grep_source *gs);
|
||||
void grep_source_load_driver(struct grep_source *gs);
|
||||
int grep_source_is_binary(struct grep_source *gs);
|
||||
|
||||
|
||||
int grep_source(struct grep_opt *opt, struct grep_source *gs);
|
||||
|
||||
|
@ -1598,6 +1598,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
|
||||
} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
|
||||
add_message_grep(revs, optarg);
|
||||
return argcount;
|
||||
} else if (!strcmp(arg, "--grep-debug")) {
|
||||
revs->grep_filter.debug = 1;
|
||||
} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
|
||||
revs->grep_filter.regflags |= REG_EXTENDED;
|
||||
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
|
||||
|
@ -424,31 +424,41 @@ test_expect_success 'log grep setup' '
|
||||
|
||||
test_expect_success 'log grep (1)' '
|
||||
git log --author=author --pretty=tformat:%s >actual &&
|
||||
( echo third ; echo initial ) >expect &&
|
||||
{
|
||||
echo third && echo initial
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log grep (2)' '
|
||||
git log --author=" * " -F --pretty=tformat:%s >actual &&
|
||||
( echo second ) >expect &&
|
||||
{
|
||||
echo second
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log grep (3)' '
|
||||
git log --author="^A U" --pretty=tformat:%s >actual &&
|
||||
( echo third ; echo initial ) >expect &&
|
||||
{
|
||||
echo third && echo initial
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log grep (4)' '
|
||||
git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
|
||||
( echo second ) >expect &&
|
||||
{
|
||||
echo second
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log grep (5)' '
|
||||
git log --author=Thor -F --pretty=tformat:%s >actual &&
|
||||
( echo third ; echo initial ) >expect &&
|
||||
{
|
||||
echo third && echo initial
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
@ -458,11 +468,19 @@ 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_expect_success 'log with multiple --grep uses union' '
|
||||
git log --grep=i --grep=r --format=%s >actual &&
|
||||
{
|
||||
echo fourth && echo third && echo initial
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log --all-match with multiple --grep uses intersection' '
|
||||
git log --all-match --grep=i --grep=r --format=%s >actual &&
|
||||
{
|
||||
echo third
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
@ -474,7 +492,47 @@ test_expect_success 'log with multiple --author uses union' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log with --grep and multiple --author uses all-match' '
|
||||
test_expect_success 'log --all-match with multiple --author still uses union' '
|
||||
git log --all-match --author="Thor" --author="Aster" --format=%s >actual &&
|
||||
{
|
||||
echo third && echo second && echo initial
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log --grep --author uses intersection' '
|
||||
# grep matches only third and fourth
|
||||
# author matches only initial and third
|
||||
git log --author="A U Thor" --grep=r --format=%s >actual &&
|
||||
{
|
||||
echo third
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
|
||||
# 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 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' '
|
||||
# grep matches only initial and third
|
||||
# author matches all but second
|
||||
git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual &&
|
||||
{
|
||||
echo third && echo initial
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log --grep --author --author takes union of authors and intersects with grep' '
|
||||
# grep matches only initial and third
|
||||
# author matches all but second
|
||||
git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
|
||||
{
|
||||
echo third && echo initial
|
||||
@ -482,9 +540,13 @@ test_expect_success 'log with --grep and multiple --author uses all-match' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'log with --grep and multiple --author uses all-match' '
|
||||
git log --author="Thor" --author="Night" --grep=q --format=%s >actual &&
|
||||
>expect &&
|
||||
test_expect_success 'log --all-match --grep --grep --author takes intersection' '
|
||||
# grep matches only third
|
||||
# author matches only initial and third
|
||||
git log --all-match --author="A U Thor" --grep=i --grep=r --format=%s >actual &&
|
||||
{
|
||||
echo third
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user