return compile_pattern_or(list);
}
+static struct grep_expr *grep_not_expr(struct grep_expr *expr)
+{
+ struct grep_expr *z = xcalloc(1, sizeof(*z));
+ z->node = GREP_NODE_NOT;
+ z->u.unary = expr;
+ return z;
+}
+
static struct grep_expr *grep_true_expr(void)
{
struct grep_expr *z = xcalloc(1, sizeof(*z));
}
}
- if (opt->all_match || header_expr)
+ if (opt->all_match || opt->no_body_match || header_expr)
opt->extended = 1;
else if (!opt->extended)
return;
if (p)
die("incomplete pattern expression: %s", p->pattern);
+ if (opt->no_body_match && opt->pattern_expression)
+ opt->pattern_expression = grep_not_expr(opt->pattern_expression);
+
if (!header_expr)
return;
if (h && (*col < 0 || tmp.rm_so < *col))
*col = tmp.rm_so;
}
+ if (x->u.atom->token == GREP_PATTERN_BODY)
+ opt->body_hit |= h;
break;
case GREP_NODE_NOT:
/*
* we do not have to do the two-pass grep when we do not check
* buffer-wide "all-match".
*/
- if (!opt->all_match)
+ if (!opt->all_match && !opt->no_body_match)
return grep_source_1(opt, gs, 0);
/* Otherwise the toplevel "or" terms hit a bit differently.
* We first clear hit markers from them.
*/
clr_hit_marker(opt->pattern_expression);
+ opt->body_hit = 0;
grep_source_1(opt, gs, 1);
- if (!chk_hit_marker(opt->pattern_expression))
+ if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
+ return 0;
+ if (opt->no_body_match && opt->body_hit)
return 0;
return grep_source_1(opt, gs, 0);
int word_regexp;
int fixed;
int all_match;
+ int no_body_match;
+ int body_hit;
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
} else if (!strcmp(arg, "--all-match")) {
revs->grep_filter.all_match = 1;
} else if (!strcmp(arg, "--invert-grep")) {
- revs->invert_grep = 1;
+ revs->grep_filter.no_body_match = 1;
} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
if (strcmp(optarg, "none"))
git_log_output_encoding = xstrdup(optarg);
(char *)message, strlen(message));
strbuf_release(&buf);
unuse_commit_buffer(commit, message);
- return opt->invert_grep ? !retval : retval;
+ return retval;
}
static inline int want_ancestry(const struct rev_info *revs)
/* Filter by commit log message */
struct grep_opt grep_filter;
- /* Negate the match of grep_filter */
- int invert_grep;
/* Display history graph */
struct git_graph *graph;
test_cmp expect actual
'
+test_expect_success 'set up commits with different authors' '
+ git checkout --orphan authors &&
+ test_commit --author "Jim <jim@example.com>" jim_1 &&
+ test_commit --author "Val <val@example.com>" val_1 &&
+ test_commit --author "Val <val@example.com>" val_2 &&
+ test_commit --author "Jim <jim@example.com>" jim_2 &&
+ test_commit --author "Val <val@example.com>" val_3 &&
+ test_commit --author "Jim <jim@example.com>" jim_3
+'
+
+test_expect_success 'log --invert-grep --grep --author' '
+ cat >expect <<-\EOF &&
+ val_3
+ val_1
+ EOF
+ git log --format=%s --author=Val --grep 2 --invert-grep >actual &&
+ test_cmp expect actual
+'
+
test_done