@matheusbsbs / matheus-bagliano / commits / 5aaeb733f5
Fork 0

log --author: take union of multiple "author" requests

In the olden days, log --author=me --committer=him --grep=this --grep=that used to be turned into: (OR (HEADER-AUTHOR me) (HEADER-COMMITTER him) (PATTERN this) (PATTERN that)) showing my patches that do not have any "this" nor "that", which was totally useless. 80235ba ("log --author=me --grep=it" should find intersection, not union, 2010-01-17) improved it greatly to turn the same into: (ALL-MATCH (HEADER-AUTHOR me) (HEADER-COMMITTER him) (OR (PATTERN this) (PATTERN that))) That is, "show only patches by me and committed by him, that have either this or that", which is a lot more natural thing to ask. We however need to be a bit more clever when the user asks more than one "author" (or "committer"); because a commit has only one author (and one committer), they ought to be interpreted as asking for union to be useful. The current implementation simply added another author/committer pattern at the same top-level for ALL-MATCH to insist on matching all, finding nothing. Turn log --author=me --author=her \ --committer=him --committer=you \ --grep=this --grep=that into (ALL-MATCH (OR (HEADER-AUTHOR me) (HEADER-AUTHOR her)) (OR (HEADER-COMMITTER him) (HEADER-COMMITTER you)) (OR (PATTERN this) (PATTERN that))) instead. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Sep 12, 2010 at 22:15 UTC 5aaeb733f5745b37878901c4687ba67c6a70e581
3 files changed +83 -13
grep.c
+53 -12
@@ -189,17 +189,32 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
189 return compile_pattern_or(list);
190 }
191
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)
209 {
210 struct grep_pat *p;
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;
200 - header_expr = compile_pattern_expr(&p);
201 - if (p)
202 - die("incomplete pattern expression: %s", p->pattern);
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.");
@@ -207,6 +222,33 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
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;
239 + }
240 + header_group[p->field] = grep_or_expr(h, header_group[p->field]);
241 + }
242 +
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
@@ -242,22 +284,18 @@ void compile_grep_patterns(struct grep_opt *opt)
284 if (!header_expr)
285 return;
286
245 - if (opt->pattern_expression) {
246 - struct grep_expr *z;
247 - z = xcalloc(1, sizeof(*z));
248 - z->node = GREP_NODE_OR;
249 - z->u.binary.left = opt->pattern_expression;
250 - z->u.binary.right = header_expr;
251 - opt->pattern_expression = z;
252 - } else {
287 + if (!opt->pattern_expression)
288 opt->pattern_expression = header_expr;
254 - }
289 + else
290 + opt->pattern_expression = grep_or_expr(opt->pattern_expression,
291 + header_expr);
292 opt->all_match = 1;
293 }
294
295 static void free_pattern_expr(struct grep_expr *x)
296 {
297 switch (x->node) {
298 + case GREP_NODE_TRUE:
299 case GREP_NODE_ATOM:
300 break;
301 case GREP_NODE_NOT:
@@ -486,6 +524,9 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
524 if (!x)
525 die("Not a valid grep expression");
526 switch (x->node) {
527 + case GREP_NODE_TRUE:
528 + h = 1;
529 + break;
530 case GREP_NODE_ATOM:
531 h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
532 break;
grep.h
+2
@@ -22,6 +22,7 @@ enum grep_header_field {
22 GREP_HEADER_AUTHOR = 0,
23 GREP_HEADER_COMMITTER
24 };
25 +#define GREP_HEADER_FIELD_MAX (GREP_HEADER_COMMITTER + 1)
26
27 struct grep_pat {
28 struct grep_pat *next;
@@ -41,6 +42,7 @@ enum grep_expr_node {
42 GREP_NODE_ATOM,
43 GREP_NODE_NOT,
44 GREP_NODE_AND,
45 + GREP_NODE_TRUE,
46 GREP_NODE_OR
47 };
48
t/t7810-grep.sh
+28 -1
@@ -324,8 +324,13 @@ test_expect_success 'log grep setup' '
324
325 echo a >>file &&
326 test_tick &&
327 - git commit -a -m "third"
327 + git commit -a -m "third" &&
328
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"
334 '
335
336 test_expect_success 'log grep (1)' '
@@ -372,6 +377,28 @@ test_expect_success 'log --grep --author implicitly uses all-match' '
377 test_cmp expect actual
378 '
379
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 +
402 test_expect_success 'grep with CE_VALID file' '
403 git update-index --assume-unchanged t/t &&
404 rm t/t &&