grep --all-match
This lets you say: git grep --all-match -e A -e B -e C to find lines that match A or B or C but limit the matches from the files that have all of A, B and C. This is different from git grep -e A --and -e B --and -e C in that the latter looks for a single line that has all of these at the same time. Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
2d5b459107
commit
0ab7befa31
@ -14,7 +14,7 @@ SYNOPSIS
|
|||||||
[-v | --invert-match] [-h|-H] [--full-name]
|
[-v | --invert-match] [-h|-H] [--full-name]
|
||||||
[-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
|
[-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
|
||||||
[-n] [-l | --files-with-matches] [-L | --files-without-match]
|
[-n] [-l | --files-with-matches] [-L | --files-without-match]
|
||||||
[-c | --count]
|
[-c | --count] [--all-match]
|
||||||
[-A <post-context>] [-B <pre-context>] [-C <context>]
|
[-A <post-context>] [-B <pre-context>] [-C <context>]
|
||||||
[-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
|
[-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
|
||||||
[<tree>...]
|
[<tree>...]
|
||||||
@ -96,6 +96,11 @@ OPTIONS
|
|||||||
higher precedence than `--or`. `-e` has to be used for all
|
higher precedence than `--or`. `-e` has to be used for all
|
||||||
patterns.
|
patterns.
|
||||||
|
|
||||||
|
--all-match::
|
||||||
|
When giving multiple pattern expressions combined with `--or`,
|
||||||
|
this flag is specified to limit the match to files that
|
||||||
|
have lines to match all of them.
|
||||||
|
|
||||||
`<tree>...`::
|
`<tree>...`::
|
||||||
Search blobs in the trees for specified patterns.
|
Search blobs in the trees for specified patterns.
|
||||||
|
|
||||||
@ -111,6 +116,10 @@ git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
|
|||||||
Looks for a line that has `#define` and either `MAX_PATH` or
|
Looks for a line that has `#define` and either `MAX_PATH` or
|
||||||
`PATH_MAX`.
|
`PATH_MAX`.
|
||||||
|
|
||||||
|
git grep --all-match -e NODE -e Unexpected::
|
||||||
|
Looks for a line that has `NODE` or `Unexpected` in
|
||||||
|
files that have lines that match both.
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
Originally written by Linus Torvalds <torvalds@osdl.org>, later
|
Originally written by Linus Torvalds <torvalds@osdl.org>, later
|
||||||
|
@ -596,6 +596,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
GREP_CLOSE_PAREN);
|
GREP_CLOSE_PAREN);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp("--all-match", arg)) {
|
||||||
|
opt.all_match = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp("-e", arg)) {
|
if (!strcmp("-e", arg)) {
|
||||||
if (1 < argc) {
|
if (1 < argc) {
|
||||||
append_grep_pattern(&opt, argv[1],
|
append_grep_pattern(&opt, argv[1],
|
||||||
|
114
grep.c
114
grep.c
@ -34,7 +34,7 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct grep_expr *compile_pattern_expr(struct grep_pat **);
|
static struct grep_expr *compile_pattern_or(struct grep_pat **);
|
||||||
static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
|
static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
|
||||||
{
|
{
|
||||||
struct grep_pat *p;
|
struct grep_pat *p;
|
||||||
@ -52,7 +52,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
|
|||||||
return x;
|
return x;
|
||||||
case GREP_OPEN_PAREN:
|
case GREP_OPEN_PAREN:
|
||||||
*list = p->next;
|
*list = p->next;
|
||||||
x = compile_pattern_expr(list);
|
x = compile_pattern_or(list);
|
||||||
if (!x)
|
if (!x)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
|
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
|
||||||
@ -138,6 +138,9 @@ void compile_grep_patterns(struct grep_opt *opt)
|
|||||||
{
|
{
|
||||||
struct grep_pat *p;
|
struct grep_pat *p;
|
||||||
|
|
||||||
|
if (opt->all_match)
|
||||||
|
opt->extended = 1;
|
||||||
|
|
||||||
for (p = opt->pattern_list; p; p = p->next) {
|
for (p = opt->pattern_list; p; p = p->next) {
|
||||||
switch (p->token) {
|
switch (p->token) {
|
||||||
case GREP_PATTERN: /* atom */
|
case GREP_PATTERN: /* atom */
|
||||||
@ -309,40 +312,63 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
|
|||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int match_expr_eval(struct grep_opt *opt,
|
static int match_expr_eval(struct grep_opt *o,
|
||||||
struct grep_expr *x,
|
struct grep_expr *x,
|
||||||
char *bol, char *eol,
|
char *bol, char *eol,
|
||||||
enum grep_context ctx)
|
enum grep_context ctx,
|
||||||
|
int collect_hits)
|
||||||
{
|
{
|
||||||
|
int h = 0;
|
||||||
|
|
||||||
switch (x->node) {
|
switch (x->node) {
|
||||||
case GREP_NODE_ATOM:
|
case GREP_NODE_ATOM:
|
||||||
return match_one_pattern(opt, x->u.atom, bol, eol, ctx);
|
h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
|
||||||
break;
|
break;
|
||||||
case GREP_NODE_NOT:
|
case GREP_NODE_NOT:
|
||||||
return !match_expr_eval(opt, x->u.unary, bol, eol, ctx);
|
h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
|
||||||
|
break;
|
||||||
case GREP_NODE_AND:
|
case GREP_NODE_AND:
|
||||||
return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) &&
|
if (!collect_hits)
|
||||||
match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
|
return (match_expr_eval(o, x->u.binary.left,
|
||||||
|
bol, eol, ctx, 0) &&
|
||||||
|
match_expr_eval(o, x->u.binary.right,
|
||||||
|
bol, eol, ctx, 0));
|
||||||
|
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||||
|
h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
|
||||||
|
break;
|
||||||
case GREP_NODE_OR:
|
case GREP_NODE_OR:
|
||||||
return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) ||
|
if (!collect_hits)
|
||||||
match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
|
return (match_expr_eval(o, x->u.binary.left,
|
||||||
|
bol, eol, ctx, 0) ||
|
||||||
|
match_expr_eval(o, x->u.binary.right,
|
||||||
|
bol, eol, ctx, 0));
|
||||||
|
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||||
|
x->u.binary.left->hit |= h;
|
||||||
|
h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
die("Unexpected node type (internal error) %d\n", x->node);
|
||||||
}
|
}
|
||||||
die("Unexpected node type (internal error) %d\n", x->node);
|
if (collect_hits)
|
||||||
|
x->hit |= h;
|
||||||
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int match_expr(struct grep_opt *opt, char *bol, char *eol,
|
static int match_expr(struct grep_opt *opt, char *bol, char *eol,
|
||||||
enum grep_context ctx)
|
enum grep_context ctx, int collect_hits)
|
||||||
{
|
{
|
||||||
struct grep_expr *x = opt->pattern_expression;
|
struct grep_expr *x = opt->pattern_expression;
|
||||||
return match_expr_eval(opt, x, bol, eol, ctx);
|
return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int match_line(struct grep_opt *opt, char *bol, char *eol,
|
static int match_line(struct grep_opt *opt, char *bol, char *eol,
|
||||||
enum grep_context ctx)
|
enum grep_context ctx, int collect_hits)
|
||||||
{
|
{
|
||||||
struct grep_pat *p;
|
struct grep_pat *p;
|
||||||
if (opt->extended)
|
if (opt->extended)
|
||||||
return match_expr(opt, bol, eol, ctx);
|
return match_expr(opt, bol, eol, ctx, collect_hits);
|
||||||
|
|
||||||
|
/* we do not call with collect_hits without being extended */
|
||||||
for (p = opt->pattern_list; p; p = p->next) {
|
for (p = opt->pattern_list; p; p = p->next) {
|
||||||
if (match_one_pattern(opt, p, bol, eol, ctx))
|
if (match_one_pattern(opt, p, bol, eol, ctx))
|
||||||
return 1;
|
return 1;
|
||||||
@ -350,7 +376,8 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
|
static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
|
char *buf, unsigned long size, int collect_hits)
|
||||||
{
|
{
|
||||||
char *bol = buf;
|
char *bol = buf;
|
||||||
unsigned long left = size;
|
unsigned long left = size;
|
||||||
@ -386,7 +413,7 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||||||
|
|
||||||
while (left) {
|
while (left) {
|
||||||
char *eol, ch;
|
char *eol, ch;
|
||||||
int hit = 0;
|
int hit;
|
||||||
|
|
||||||
eol = end_of_line(bol, &left);
|
eol = end_of_line(bol, &left);
|
||||||
ch = *eol;
|
ch = *eol;
|
||||||
@ -395,9 +422,12 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||||||
if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
|
if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
|
||||||
ctx = GREP_CONTEXT_BODY;
|
ctx = GREP_CONTEXT_BODY;
|
||||||
|
|
||||||
hit = match_line(opt, bol, eol, ctx);
|
hit = match_line(opt, bol, eol, ctx, collect_hits);
|
||||||
*eol = ch;
|
*eol = ch;
|
||||||
|
|
||||||
|
if (collect_hits)
|
||||||
|
goto next_line;
|
||||||
|
|
||||||
/* "grep -v -e foo -e bla" should list lines
|
/* "grep -v -e foo -e bla" should list lines
|
||||||
* that do not have either, so inversion should
|
* that do not have either, so inversion should
|
||||||
* be done outside.
|
* be done outside.
|
||||||
@ -477,6 +507,8 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||||||
}
|
}
|
||||||
|
|
||||||
free(prev);
|
free(prev);
|
||||||
|
if (collect_hits)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (opt->status_only)
|
if (opt->status_only)
|
||||||
return 0;
|
return 0;
|
||||||
@ -496,3 +528,49 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||||||
return !!last_hit;
|
return !!last_hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clr_hit_marker(struct grep_expr *x)
|
||||||
|
{
|
||||||
|
/* All-hit markers are meaningful only at the very top level
|
||||||
|
* OR node.
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
x->hit = 0;
|
||||||
|
if (x->node != GREP_NODE_OR)
|
||||||
|
return;
|
||||||
|
x->u.binary.left->hit = 0;
|
||||||
|
x = x->u.binary.right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chk_hit_marker(struct grep_expr *x)
|
||||||
|
{
|
||||||
|
/* Top level nodes have hit markers. See if they all are hits */
|
||||||
|
while (1) {
|
||||||
|
if (x->node != GREP_NODE_OR)
|
||||||
|
return x->hit;
|
||||||
|
if (!x->u.binary.left->hit)
|
||||||
|
return 0;
|
||||||
|
x = x->u.binary.right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* we do not have to do the two-pass grep when we do not check
|
||||||
|
* buffer-wide "all-match".
|
||||||
|
*/
|
||||||
|
if (!opt->all_match)
|
||||||
|
return grep_buffer_1(opt, name, buf, size, 0);
|
||||||
|
|
||||||
|
/* Otherwise the toplevel "or" terms hit a bit differently.
|
||||||
|
* We first clear hit markers from them.
|
||||||
|
*/
|
||||||
|
clr_hit_marker(opt->pattern_expression);
|
||||||
|
grep_buffer_1(opt, name, buf, size, 1);
|
||||||
|
|
||||||
|
if (!chk_hit_marker(opt->pattern_expression))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return grep_buffer_1(opt, name, buf, size, 0);
|
||||||
|
}
|
||||||
|
2
grep.h
2
grep.h
@ -35,6 +35,7 @@ enum grep_expr_node {
|
|||||||
|
|
||||||
struct grep_expr {
|
struct grep_expr {
|
||||||
enum grep_expr_node node;
|
enum grep_expr_node node;
|
||||||
|
unsigned hit;
|
||||||
union {
|
union {
|
||||||
struct grep_pat *atom;
|
struct grep_pat *atom;
|
||||||
struct grep_expr *unary;
|
struct grep_expr *unary;
|
||||||
@ -59,6 +60,7 @@ struct grep_opt {
|
|||||||
unsigned count:1;
|
unsigned count:1;
|
||||||
unsigned word_regexp:1;
|
unsigned word_regexp:1;
|
||||||
unsigned fixed:1;
|
unsigned fixed:1;
|
||||||
|
unsigned all_match:1;
|
||||||
#define GREP_BINARY_DEFAULT 0
|
#define GREP_BINARY_DEFAULT 0
|
||||||
#define GREP_BINARY_NOMATCH 1
|
#define GREP_BINARY_NOMATCH 1
|
||||||
#define GREP_BINARY_TEXT 2
|
#define GREP_BINARY_TEXT 2
|
||||||
|
Loading…
Reference in New Issue
Block a user