Merge branch 'jc/grep-header-all-match-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 29 Sep 2010 20:49:57 +0000 (13:49 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 Sep 2010 20:49:57 +0000 (13:49 -0700)
* 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

grep.c
grep.h
t/t7810-grep.sh
diff --git a/grep.c b/grep.c
index 82fb349be6f6ef8211d4828101cd8e88bcbb304f..63c4280cac9a87f867451c0d9787d1fe21ea9c2d 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -189,30 +189,74 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
        return compile_pattern_or(list);
 }
 
-void compile_grep_patterns(struct grep_opt *opt)
+static struct grep_expr *grep_true_expr(void)
+{
+       struct grep_expr *z = xcalloc(1, sizeof(*z));
+       z->node = GREP_NODE_TRUE;
+       return z;
+}
+
+static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
+{
+       struct grep_expr *z = xcalloc(1, sizeof(*z));
+       z->node = GREP_NODE_OR;
+       z->u.binary.left = left;
+       z->u.binary.right = right;
+       return z;
+}
+
+static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
 {
        struct grep_pat *p;
-       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;
-                       }
+       struct grep_expr *header_expr;
+       struct grep_expr *(header_group[GREP_HEADER_FIELD_MAX]);
+       enum grep_header_field fld;
+
+       if (!opt->header_list)
+               return NULL;
+       p = opt->header_list;
+       for (p = opt->header_list; p; p = p->next) {
+               if (p->token != GREP_PATTERN_HEAD)
+                       die("bug: a non-header pattern in grep header list.");
+               if (p->field < 0 || GREP_HEADER_FIELD_MAX <= p->field)
+                       die("bug: unknown header field %d", p->field);
+               compile_regexp(p, opt);
+       }
+
+       for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++)
+               header_group[fld] = NULL;
+
+       for (p = opt->header_list; p; p = p->next) {
+               struct grep_expr *h;
+               struct grep_pat *pp = p;
+
+               h = compile_pattern_atom(&pp);
+               if (!h || pp != p->next)
+                       die("bug: malformed header expr");
+               if (!header_group[p->field]) {
+                       header_group[p->field] = h;
+                       continue;
                }
+               header_group[p->field] = grep_or_expr(h, header_group[p->field]);
        }
 
+       header_expr = NULL;
+
+       for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++) {
+               if (!header_group[fld])
+                       continue;
+               if (!header_expr)
+                       header_expr = grep_true_expr();
+               header_expr = grep_or_expr(header_group[fld], header_expr);
+       }
+       return header_expr;
+}
+
+void compile_grep_patterns(struct grep_opt *opt)
+{
+       struct grep_pat *p;
+       struct grep_expr *header_expr = prep_header_patterns(opt);
+
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
                case GREP_PATTERN: /* atom */
@@ -231,9 +275,6 @@ void compile_grep_patterns(struct grep_opt *opt)
        else if (!opt->extended)
                return;
 
-       /* Then bundle them up in an expression.
-        * A classic recursive descent parser would do.
-        */
        p = opt->pattern_list;
        if (p)
                opt->pattern_expression = compile_pattern_expr(&p);
@@ -243,22 +284,18 @@ void compile_grep_patterns(struct grep_opt *opt)
        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 {
+       if (!opt->pattern_expression)
                opt->pattern_expression = header_expr;
-       }
+       else
+               opt->pattern_expression = grep_or_expr(opt->pattern_expression,
+                                                      header_expr);
        opt->all_match = 1;
 }
 
 static void free_pattern_expr(struct grep_expr *x)
 {
        switch (x->node) {
+       case GREP_NODE_TRUE:
        case GREP_NODE_ATOM:
                break;
        case GREP_NODE_NOT:
@@ -487,6 +524,9 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
        if (!x)
                die("Not a valid grep expression");
        switch (x->node) {
+       case GREP_NODE_TRUE:
+               h = 1;
+               break;
        case GREP_NODE_ATOM:
                h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
                break;
diff --git a/grep.h b/grep.h
index efa8cff980af2b2c06dad080876637d16b5c4985..06621fe663545af52fbc42827a8374ab5bd42f38 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -22,6 +22,7 @@ enum grep_header_field {
        GREP_HEADER_AUTHOR = 0,
        GREP_HEADER_COMMITTER
 };
+#define GREP_HEADER_FIELD_MAX (GREP_HEADER_COMMITTER + 1)
 
 struct grep_pat {
        struct grep_pat *next;
@@ -41,6 +42,7 @@ enum grep_expr_node {
        GREP_NODE_ATOM,
        GREP_NODE_NOT,
        GREP_NODE_AND,
+       GREP_NODE_TRUE,
        GREP_NODE_OR
 };
 
index 023f225a4b1f22c66fb291a76690564696d56035..50658845ca8769e963cfc13b20efb892f1f63cc0 100755 (executable)
@@ -324,8 +324,13 @@ test_expect_success 'log grep setup' '
 
        echo a >>file &&
        test_tick &&
-       git commit -a -m "third"
+       git commit -a -m "third" &&
 
+       echo a >>file &&
+       test_tick &&
+       GIT_AUTHOR_NAME="Night Fall" \
+       GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
+       git commit -a -m "fourth"
 '
 
 test_expect_success 'log grep (1)' '
@@ -372,6 +377,28 @@ test_expect_success 'log --grep --author implicitly uses all-match' '
        test_cmp expect actual
 '
 
+test_expect_success 'log with multiple --author uses union' '
+       git log --author="Thor" --author="Aster" --format=%s >actual &&
+       {
+           echo third && echo second && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log with --grep and multiple --author uses all-match' '
+       git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
+       {
+           echo third && echo initial
+       } >expect &&
+       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_cmp expect actual
+'
+
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&