Skip to content

Commit a27d5be

Browse files
committed
Merge branch 'jc/grep-header-all-match-fix'
* jc/grep-header-all-match-fix: log --author: take union of multiple "author" requests grep: move logic to compile header pattern into a separate helper
2 parents 08986de + 5aaeb73 commit a27d5be

File tree

3 files changed

+101
-32
lines changed

3 files changed

+101
-32
lines changed

grep.c

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -189,30 +189,74 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
189189
return compile_pattern_or(list);
190190
}
191191

192-
void compile_grep_patterns(struct grep_opt *opt)
192+
static struct grep_expr *grep_true_expr(void)
193+
{
194+
struct grep_expr *z = xcalloc(1, sizeof(*z));
195+
z->node = GREP_NODE_TRUE;
196+
return z;
197+
}
198+
199+
static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
200+
{
201+
struct grep_expr *z = xcalloc(1, sizeof(*z));
202+
z->node = GREP_NODE_OR;
203+
z->u.binary.left = left;
204+
z->u.binary.right = right;
205+
return z;
206+
}
207+
208+
static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
193209
{
194210
struct grep_pat *p;
195-
struct grep_expr *header_expr = NULL;
196-
197-
if (opt->header_list) {
198-
p = opt->header_list;
199-
header_expr = compile_pattern_expr(&p);
200-
if (p)
201-
die("incomplete pattern expression: %s", p->pattern);
202-
for (p = opt->header_list; p; p = p->next) {
203-
switch (p->token) {
204-
case GREP_PATTERN: /* atom */
205-
case GREP_PATTERN_HEAD:
206-
case GREP_PATTERN_BODY:
207-
compile_regexp(p, opt);
208-
break;
209-
default:
210-
opt->extended = 1;
211-
break;
212-
}
211+
struct grep_expr *header_expr;
212+
struct grep_expr *(header_group[GREP_HEADER_FIELD_MAX]);
213+
enum grep_header_field fld;
214+
215+
if (!opt->header_list)
216+
return NULL;
217+
p = opt->header_list;
218+
for (p = opt->header_list; p; p = p->next) {
219+
if (p->token != GREP_PATTERN_HEAD)
220+
die("bug: a non-header pattern in grep header list.");
221+
if (p->field < 0 || GREP_HEADER_FIELD_MAX <= p->field)
222+
die("bug: unknown header field %d", p->field);
223+
compile_regexp(p, opt);
224+
}
225+
226+
for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++)
227+
header_group[fld] = NULL;
228+
229+
for (p = opt->header_list; p; p = p->next) {
230+
struct grep_expr *h;
231+
struct grep_pat *pp = p;
232+
233+
h = compile_pattern_atom(&pp);
234+
if (!h || pp != p->next)
235+
die("bug: malformed header expr");
236+
if (!header_group[p->field]) {
237+
header_group[p->field] = h;
238+
continue;
213239
}
240+
header_group[p->field] = grep_or_expr(h, header_group[p->field]);
214241
}
215242

243+
header_expr = NULL;
244+
245+
for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++) {
246+
if (!header_group[fld])
247+
continue;
248+
if (!header_expr)
249+
header_expr = grep_true_expr();
250+
header_expr = grep_or_expr(header_group[fld], header_expr);
251+
}
252+
return header_expr;
253+
}
254+
255+
void compile_grep_patterns(struct grep_opt *opt)
256+
{
257+
struct grep_pat *p;
258+
struct grep_expr *header_expr = prep_header_patterns(opt);
259+
216260
for (p = opt->pattern_list; p; p = p->next) {
217261
switch (p->token) {
218262
case GREP_PATTERN: /* atom */
@@ -231,9 +275,6 @@ void compile_grep_patterns(struct grep_opt *opt)
231275
else if (!opt->extended)
232276
return;
233277

234-
/* Then bundle them up in an expression.
235-
* A classic recursive descent parser would do.
236-
*/
237278
p = opt->pattern_list;
238279
if (p)
239280
opt->pattern_expression = compile_pattern_expr(&p);
@@ -243,22 +284,18 @@ void compile_grep_patterns(struct grep_opt *opt)
243284
if (!header_expr)
244285
return;
245286

246-
if (opt->pattern_expression) {
247-
struct grep_expr *z;
248-
z = xcalloc(1, sizeof(*z));
249-
z->node = GREP_NODE_OR;
250-
z->u.binary.left = opt->pattern_expression;
251-
z->u.binary.right = header_expr;
252-
opt->pattern_expression = z;
253-
} else {
287+
if (!opt->pattern_expression)
254288
opt->pattern_expression = header_expr;
255-
}
289+
else
290+
opt->pattern_expression = grep_or_expr(opt->pattern_expression,
291+
header_expr);
256292
opt->all_match = 1;
257293
}
258294

259295
static void free_pattern_expr(struct grep_expr *x)
260296
{
261297
switch (x->node) {
298+
case GREP_NODE_TRUE:
262299
case GREP_NODE_ATOM:
263300
break;
264301
case GREP_NODE_NOT:
@@ -487,6 +524,9 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
487524
if (!x)
488525
die("Not a valid grep expression");
489526
switch (x->node) {
527+
case GREP_NODE_TRUE:
528+
h = 1;
529+
break;
490530
case GREP_NODE_ATOM:
491531
h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
492532
break;

grep.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum grep_header_field {
2222
GREP_HEADER_AUTHOR = 0,
2323
GREP_HEADER_COMMITTER
2424
};
25+
#define GREP_HEADER_FIELD_MAX (GREP_HEADER_COMMITTER + 1)
2526

2627
struct grep_pat {
2728
struct grep_pat *next;
@@ -41,6 +42,7 @@ enum grep_expr_node {
4142
GREP_NODE_ATOM,
4243
GREP_NODE_NOT,
4344
GREP_NODE_AND,
45+
GREP_NODE_TRUE,
4446
GREP_NODE_OR
4547
};
4648

t/t7810-grep.sh

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,13 @@ test_expect_success 'log grep setup' '
324324
325325
echo a >>file &&
326326
test_tick &&
327-
git commit -a -m "third"
327+
git commit -a -m "third" &&
328328
329+
echo a >>file &&
330+
test_tick &&
331+
GIT_AUTHOR_NAME="Night Fall" \
332+
GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
333+
git commit -a -m "fourth"
329334
'
330335

331336
test_expect_success 'log grep (1)' '
@@ -372,6 +377,28 @@ test_expect_success 'log --grep --author implicitly uses all-match' '
372377
test_cmp expect actual
373378
'
374379

380+
test_expect_success 'log with multiple --author uses union' '
381+
git log --author="Thor" --author="Aster" --format=%s >actual &&
382+
{
383+
echo third && echo second && echo initial
384+
} >expect &&
385+
test_cmp expect actual
386+
'
387+
388+
test_expect_success 'log with --grep and multiple --author uses all-match' '
389+
git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
390+
{
391+
echo third && echo initial
392+
} >expect &&
393+
test_cmp expect actual
394+
'
395+
396+
test_expect_success 'log with --grep and multiple --author uses all-match' '
397+
git log --author="Thor" --author="Night" --grep=q --format=%s >actual &&
398+
>expect &&
399+
test_cmp expect actual
400+
'
401+
375402
test_expect_success 'grep with CE_VALID file' '
376403
git update-index --assume-unchanged t/t &&
377404
rm t/t &&

0 commit comments

Comments
 (0)