{"thread":{"id":"38198","subject":"[PATCH] git-log: added --invert-grep option","startedAt":"2014-12-19T02:14:10Z","lastAt":"2015-03-22T03:39:40Z","messageCount":15,"participants":["Christoph Junghans","Junio C Hamano","Paul Mackerras"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"253824","messageId":"1418955250-22402-1-git-send-email-ottxor@gentoo.org","threadId":"38198","inReplyTo":null,"subject":"[PATCH] git-log: added --invert-grep option","fromName":"Christoph Junghans","fromEmail":"ottxor@gentoo.org","sentAt":"2014-12-19T02:14:10Z","receivedAt":"2014-12-19T02:14:10Z","isPatch":true,"sender":{"key":"ottxor@gentoo.org","avatar":"https://avatars.githubusercontent.com/u/439483?v=4"},"body":"Implements a inverted match for \"git log\", like in the case of\n\"git grep -v\", which is useful from time to time to e.g. filter\nFIXUP message out of \"git log\".\n\nInternally, a new bol 'global_invert' has been introduces as\nrevs->grep_filter.invert inverts the match line-wise, which cannot\nwork as i.e. empty line always not match the pattern given.\n\nSigned-off-by: Christoph Junghans <ottxor@gentoo.org>\n---\n Documentation/rev-list-options.txt     | 4 ++++\n contrib/completion/git-completion.bash | 2 +-\n grep.h                                 | 3 ++-\n revision.c                             | 4 +++-\n 4 files changed, 10 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex afccfdc..6d4671f 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -66,6 +66,10 @@ if it is part of the log message.\n \tLimit the commits output to ones that match all given `--grep`,\n \tinstead of ones that match at least one.\n \n+--invert-grep::\n+\tLimit the commits output to ones with log message that do not\n+\tmatch the pattern specified with `--grep=<pattern>`.\n+\n -i::\n --regexp-ignore-case::\n \tMatch the regular expression limiting patterns without regard to letter\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 2fece98..914c317 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1425,7 +1425,7 @@ __git_log_gitk_options=\"\n # Options that go well for log and shortlog (not gitk)\n __git_log_shortlog_options=\"\n \t--author= --committer= --grep=\n-\t--all-match\n+\t--all-match --invert-grep\n \"\n \n __git_log_pretty_formats=\"oneline short medium full fuller email raw format:\"\ndiff --git a/grep.h b/grep.h\nindex 95f197a..c137103 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -93,7 +93,8 @@ struct grep_opt {\n \tint prefix_length;\n \tregex_t regexp;\n \tint linenum;\n-\tint invert;\n+\tint invert; /** line-wise invert match */\n+\tint global_invert; /** final global invert match */\n \tint ignore_case;\n \tint status_only;\n \tint name_only;\ndiff --git a/revision.c b/revision.c\nindex 75dda92..c8d4c49 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2011,6 +2011,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n \t} else if (!strcmp(arg, \"--all-match\")) {\n \t\trevs->grep_filter.all_match = 1;\n+\t} else if (!strcmp(arg, \"--invert-grep\")) {\n+\t\trevs->grep_filter.global_invert = 1;\n \t} else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n \t\tif (strcmp(optarg, \"none\"))\n \t\t\tgit_log_output_encoding = xstrdup(optarg);\n@@ -2909,7 +2911,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \t\t\t\t     (char *)message, strlen(message));\n \tstrbuf_release(&buf);\n \tunuse_commit_buffer(commit, message);\n-\treturn retval;\n+\treturn opt->grep_filter.global_invert ? !retval : retval;\n }\n \n static inline int want_ancestry(const struct rev_info *revs)\n-- \n2.0.4\n"},{"id":"253826","messageId":"xmqqwq5o5e1j.fsf@gitster.dls.corp.google.com","threadId":"38198","inReplyTo":"1418955250-22402-1-git-send-email-ottxor@gentoo.org","subject":"Re: [PATCH] git-log: added --invert-grep option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-19T06:50:16Z","receivedAt":"2014-12-19T06:50:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christoph Junghans <ottxor@gentoo.org> writes:\n\n> Implements a inverted match for \"git log\", like in the case of\n> \"git grep -v\", which is useful from time to time to e.g. filter\n> FIXUP message out of \"git log\".\n>\n> Internally, a new bol 'global_invert' has been introduces as\n> revs->grep_filter.invert inverts the match line-wise, which cannot\n> work as i.e. empty line always not match the pattern given.\n\nWhile I am very sympathetic to those who feel the pain, i.e. the\nneed for something like this, I do not think this patch takes the\nright approach.\n\nThe pain is that \"git log --grep=...\" can express only a limited\nsubset of what \"git grep\" can express.  The latter supports a very\nrich set of logical operations, with operators like --and and --not,\nand grouping, e.g. \"git grep \\( -e foo --or -e bar \\) --and --not -e\nbaz\" (i.e. \"has either foo or bar but not baz\").  It also can turn\nthe list of top-level predicates into \"all of these predicates must\nmatch somewhere in the entire file\" with --all-match option.  None\nof this richness is available to \"git log --grep=...\".\n\nThe root cause of the pain comes from the fact that it needs to\nshare the command line parsing with the revision list commands to\ndrive the underlying \"grep\" machinery, so you cannot say\n\n    git log --not --grep=FIXUP\n\nbecause \"--not\" is taken as \"commits reachable from revs listed after\nthis point are to be excluded from the result\" and not passed to the\nunderlying grep machinery.\n\nThe right way to do this is to somehow find a way to allow you to\nexpress the full \"grep\" logical operations to the command line\nparser that is used by the \"log\" family of commands.  That would\nallow you to express something like \"Show only commits that has\neither foo or bar and does not have baz\".  I am not going to\nadvocate this exact syntax, but to illustrate the idea, if you had\nsomething like this supported:\n\n    git log --grep-begin \\\n    \t\\( -e foo --or -e bar \\) --and --not -e baz \\\n        --grep-end\n\nby stopping the revision.c parser between --grep-{begin,end} and\ninstead feeding the arguments to the grep expression builder, we may\nbe able to get the full expressiveness of the logical operations\noffered by the grep machinery.\n\nAnd you shouldn't need to add any new field to grep_opt for that.\nAll you need is a design of a new syntax and a tweak to the revision\nargument parser to understand the new syntax to redirect some\narguments to the grep command line parser.\n\nA new option that allows you to _only_ negate without allowing you\nto enable other richer logical operations of the underlying grep\nmachinery is going in a wrong direction, isn't it?\n\n> Signed-off-by: Christoph Junghans <ottxor@gentoo.org>\n> ---\n>  Documentation/rev-list-options.txt     | 4 ++++\n>  contrib/completion/git-completion.bash | 2 +-\n>  grep.h                                 | 3 ++-\n>  revision.c                             | 4 +++-\n>  4 files changed, 10 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\n> index afccfdc..6d4671f 100644\n> --- a/Documentation/rev-list-options.txt\n> +++ b/Documentation/rev-list-options.txt\n> @@ -66,6 +66,10 @@ if it is part of the log message.\n>  \tLimit the commits output to ones that match all given `--grep`,\n>  \tinstead of ones that match at least one.\n>  \n> +--invert-grep::\n> +\tLimit the commits output to ones with log message that do not\n> +\tmatch the pattern specified with `--grep=<pattern>`.\n> +\n>  -i::\n>  --regexp-ignore-case::\n>  \tMatch the regular expression limiting patterns without regard to letter\n> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> index 2fece98..914c317 100644\n> --- a/contrib/completion/git-completion.bash\n> +++ b/contrib/completion/git-completion.bash\n> @@ -1425,7 +1425,7 @@ __git_log_gitk_options=\"\n>  # Options that go well for log and shortlog (not gitk)\n>  __git_log_shortlog_options=\"\n>  \t--author= --committer= --grep=\n> -\t--all-match\n> +\t--all-match --invert-grep\n>  \"\n>  \n>  __git_log_pretty_formats=\"oneline short medium full fuller email raw format:\"\n> diff --git a/grep.h b/grep.h\n> index 95f197a..c137103 100644\n> --- a/grep.h\n> +++ b/grep.h\n> @@ -93,7 +93,8 @@ struct grep_opt {\n>  \tint prefix_length;\n>  \tregex_t regexp;\n>  \tint linenum;\n> -\tint invert;\n> +\tint invert; /** line-wise invert match */\n> +\tint global_invert; /** final global invert match */\n>  \tint ignore_case;\n>  \tint status_only;\n>  \tint name_only;\n> diff --git a/revision.c b/revision.c\n> index 75dda92..c8d4c49 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2011,6 +2011,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n>  \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n>  \t} else if (!strcmp(arg, \"--all-match\")) {\n>  \t\trevs->grep_filter.all_match = 1;\n> +\t} else if (!strcmp(arg, \"--invert-grep\")) {\n> +\t\trevs->grep_filter.global_invert = 1;\n>  \t} else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n>  \t\tif (strcmp(optarg, \"none\"))\n>  \t\t\tgit_log_output_encoding = xstrdup(optarg);\n> @@ -2909,7 +2911,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n>  \t\t\t\t     (char *)message, strlen(message));\n>  \tstrbuf_release(&buf);\n>  \tunuse_commit_buffer(commit, message);\n> -\treturn retval;\n> +\treturn opt->grep_filter.global_invert ? !retval : retval;\n>  }\n>  \n>  static inline int want_ancestry(const struct rev_info *revs)\n"},{"id":"254066","messageId":"1419390196-17222-1-git-send-email-ottxor@gentoo.org","threadId":"38198","inReplyTo":"xmqqwq5o5e1j.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] git-log: added --invert-grep option","fromName":"Christoph Junghans","fromEmail":"ottxor@gentoo.org","sentAt":"2014-12-24T03:03:15Z","receivedAt":"2014-12-24T03:03:15Z","isPatch":true,"sender":{"key":"ottxor@gentoo.org","avatar":"https://avatars.githubusercontent.com/u/439483?v=4"},"body":"Ok, I drafted a first version of the suggest --grep-begin ...\n--grep-end syntax.\n\nHowever, I could not find a good ways to invert the match on a commit\nbasis instead of the normal line-wise version. Any suggestions?\n"},{"id":"254067","messageId":"1419390196-17222-2-git-send-email-ottxor@gentoo.org","threadId":"38198","inReplyTo":"1419390196-17222-1-git-send-email-ottxor@gentoo.org","subject":"[PATCH] git-log: added --grep-begin .. --grep-end syntax","fromName":"Christoph Junghans","fromEmail":"ottxor@gentoo.org","sentAt":"2014-12-24T03:03:16Z","receivedAt":"2014-12-24T03:03:16Z","isPatch":true,"sender":{"key":"ottxor@gentoo.org","avatar":"https://avatars.githubusercontent.com/u/439483?v=4"},"body":"This is useful to specify more complicated pattern as with '--grep'.\n\nSigned-off-by: Christoph Junghans <ottxor@gentoo.org>\n---\n builtin/grep.c | 73 +++++-----------------------------------------------------\n grep.c         | 62 +++++++++++++++++++++++++++++++++++++++++++++++++\n grep.h         | 10 ++++++++\n revision.c     | 56 ++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 134 insertions(+), 67 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4063882..0127fa0 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -551,67 +551,6 @@ static int context_callback(const struct option *opt, const char *arg,\n \treturn 0;\n }\n \n-static int file_callback(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct grep_opt *grep_opt = opt->value;\n-\tint from_stdin = !strcmp(arg, \"-\");\n-\tFILE *patterns;\n-\tint lno = 0;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tpatterns = from_stdin ? stdin : fopen(arg, \"r\");\n-\tif (!patterns)\n-\t\tdie_errno(_(\"cannot open '%s'\"), arg);\n-\twhile (strbuf_getline(&sb, patterns, '\\n') == 0) {\n-\t\t/* ignore empty line like grep does */\n-\t\tif (sb.len == 0)\n-\t\t\tcontinue;\n-\n-\t\tappend_grep_pat(grep_opt, sb.buf, sb.len, arg, ++lno,\n-\t\t\t\tGREP_PATTERN);\n-\t}\n-\tif (!from_stdin)\n-\t\tfclose(patterns);\n-\tstrbuf_release(&sb);\n-\treturn 0;\n-}\n-\n-static int not_callback(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct grep_opt *grep_opt = opt->value;\n-\tappend_grep_pattern(grep_opt, \"--not\", \"command line\", 0, GREP_NOT);\n-\treturn 0;\n-}\n-\n-static int and_callback(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct grep_opt *grep_opt = opt->value;\n-\tappend_grep_pattern(grep_opt, \"--and\", \"command line\", 0, GREP_AND);\n-\treturn 0;\n-}\n-\n-static int open_callback(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct grep_opt *grep_opt = opt->value;\n-\tappend_grep_pattern(grep_opt, \"(\", \"command line\", 0, GREP_OPEN_PAREN);\n-\treturn 0;\n-}\n-\n-static int close_callback(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct grep_opt *grep_opt = opt->value;\n-\tappend_grep_pattern(grep_opt, \")\", \"command line\", 0, GREP_CLOSE_PAREN);\n-\treturn 0;\n-}\n-\n-static int pattern_callback(const struct option *opt, const char *arg,\n-\t\t\t    int unset)\n-{\n-\tstruct grep_opt *grep_opt = opt->value;\n-\tappend_grep_pattern(grep_opt, arg, \"-e option\", 0, GREP_PATTERN);\n-\treturn 0;\n-}\n-\n static int help_callback(const struct option *opt, const char *arg, int unset)\n {\n \treturn -1;\n@@ -710,21 +649,21 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\tN_(\"show the surrounding function\")),\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_CALLBACK('f', NULL, &opt, N_(\"file\"),\n-\t\t\tN_(\"read patterns from file\"), file_callback),\n+\t\t\tN_(\"read patterns from file\"), grep_file_callback),\n \t\t{ OPTION_CALLBACK, 'e', NULL, &opt, N_(\"pattern\"),\n-\t\t\tN_(\"match <pattern>\"), PARSE_OPT_NONEG, pattern_callback },\n+\t\t\tN_(\"match <pattern>\"), PARSE_OPT_NONEG, grep_pattern_callback },\n \t\t{ OPTION_CALLBACK, 0, \"and\", &opt, NULL,\n \t\t  N_(\"combine patterns specified with -e\"),\n-\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },\n+\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, grep_and_callback },\n \t\tOPT_BOOL(0, \"or\", &dummy, \"\"),\n \t\t{ OPTION_CALLBACK, 0, \"not\", &opt, NULL, \"\",\n-\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },\n+\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, grep_not_callback },\n \t\t{ OPTION_CALLBACK, '(', NULL, &opt, NULL, \"\",\n \t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,\n-\t\t  open_callback },\n+\t\t  grep_open_callback },\n \t\t{ OPTION_CALLBACK, ')', NULL, &opt, NULL, \"\",\n \t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,\n-\t\t  close_callback },\n+\t\t  grep_close_callback },\n \t\tOPT__QUIET(&opt.status_only,\n \t\t\t   N_(\"indicate hit with exit status without output\")),\n \t\tOPT_BOOL(0, \"all-match\", &opt.all_match,\ndiff --git a/grep.c b/grep.c\nindex 6e085f8..0c9a977 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -1796,3 +1796,65 @@ static int grep_source_is_binary(struct grep_source *gs)\n \n \treturn 0;\n }\n+\n+int grep_file_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct grep_opt *grep_opt = opt->value;\n+\tint from_stdin = !strcmp(arg, \"-\");\n+\tFILE *patterns;\n+\tint lno = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tpatterns = from_stdin ? stdin : fopen(arg, \"r\");\n+\tif (!patterns)\n+\t\tdie_errno(_(\"cannot open '%s'\"), arg);\n+\twhile (strbuf_getline(&sb, patterns, '\\n') == 0) {\n+\t\t/* ignore empty line like grep does */\n+\t\tif (sb.len == 0)\n+\t\t\tcontinue;\n+\n+\t\tappend_grep_pat(grep_opt, sb.buf, sb.len, arg, ++lno,\n+\t\t\t\tGREP_PATTERN);\n+\t}\n+\tif (!from_stdin)\n+\t\tfclose(patterns);\n+\tstrbuf_release(&sb);\n+\treturn 0;\n+}\n+\n+int grep_not_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct grep_opt *grep_opt = opt->value;\n+\tappend_grep_pattern(grep_opt, \"--not\", \"command line\", 0, GREP_NOT);\n+\treturn 0;\n+}\n+\n+int grep_and_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct grep_opt *grep_opt = opt->value;\n+\tappend_grep_pattern(grep_opt, \"--and\", \"command line\", 0, GREP_AND);\n+\treturn 0;\n+}\n+\n+int grep_open_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct grep_opt *grep_opt = opt->value;\n+\tappend_grep_pattern(grep_opt, \"(\", \"command line\", 0, GREP_OPEN_PAREN);\n+\treturn 0;\n+}\n+\n+int grep_close_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct grep_opt *grep_opt = opt->value;\n+\tappend_grep_pattern(grep_opt, \")\", \"command line\", 0, GREP_CLOSE_PAREN);\n+\treturn 0;\n+}\n+\n+int grep_pattern_callback(const struct option *opt, const char *arg,\n+\t\t\t    int unset)\n+{\n+\tstruct grep_opt *grep_opt = opt->value;\n+\tappend_grep_pattern(grep_opt, arg, \"-e option\", 0, GREP_PATTERN);\n+\treturn 0;\n+}\n+\ndiff --git a/grep.h b/grep.h\nindex 95f197a..d85fdb4 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -10,6 +10,7 @@ typedef int pcre_extra;\n #include \"kwset.h\"\n #include \"thread-utils.h\"\n #include \"userdiff.h\"\n+#include \"parse-options.h\"\n \n enum grep_pat_token {\n \tGREP_PATTERN,\n@@ -181,6 +182,15 @@ void grep_source_load_driver(struct grep_source *gs);\n \n int grep_source(struct grep_opt *opt, struct grep_source *gs);\n \n+\n+int grep_file_callback(const struct option *opt, const char *arg, int unset);\n+int grep_not_callback(const struct option *opt, const char *arg, int unset);\n+int grep_and_callback(const struct option *opt, const char *arg, int unset);\n+int grep_open_callback(const struct option *opt, const char *arg, int unset);\n+int grep_close_callback(const struct option *opt, const char *arg, int unset);\n+int grep_pattern_callback(const struct option *opt, const char *arg, int unset);\n+\n+\n extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);\n extern int grep_threads_ok(const struct grep_opt *opt);\n \ndiff --git a/revision.c b/revision.c\nindex 75dda92..4fe9085 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1998,6 +1998,62 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\treturn argcount;\n \t} else if (!strcmp(arg, \"--grep-debug\")) {\n \t\trevs->grep_filter.debug = 1;\n+\t} else if (!strcmp(arg, \"--grep-begin\")) {\n+\t\tif (argc <= 1)\n+\t\t\treturn error(\"--grep-begin requires an argument\");\n+\t\tint dummy;\n+\t\tstruct option options[] = {\n+\t\t\tOPT_CALLBACK('f', NULL, &revs->grep_filter, N_(\"file\"),\n+\t\t\t\tN_(\"read patterns from file\"), grep_file_callback),\n+\t\t\t{ OPTION_CALLBACK, 'e', NULL, &revs->grep_filter, N_(\"pattern\"),\n+\t\t\t\tN_(\"match <pattern>\"), PARSE_OPT_NONEG, grep_pattern_callback },\n+\t\t\t{ OPTION_CALLBACK, 0, \"and\", &revs->grep_filter, NULL,\n+\t\t\t  N_(\"combine patterns specified with -e\"),\n+\t\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, grep_and_callback },\n+\t\t\tOPT_BOOL(0, \"or\", &dummy, \"\"),\n+\t\t\t{ OPTION_CALLBACK, 0, \"not\", &revs->grep_filter, NULL, \"\",\n+\t\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, grep_not_callback },\n+\t\t\t{ OPTION_CALLBACK, '(', NULL, &revs->grep_filter, NULL, \"\",\n+\t\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,\n+\t\t\t  grep_open_callback },\n+\t\t\t{ OPTION_CALLBACK, ')', NULL, &revs->grep_filter, NULL, \"\",\n+\t\t\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,\n+\t\t\t  grep_close_callback },\n+\t\t\tOPT_END()\n+\t\t};\n+\t\tchar const * const grep_usage[] = {\n+\t\t\tN_(\"git log [log-options] --begin-grep [grep-options] --end-grep ...\"), \n+\t\t\tNULL \n+\t\t};\n+\t\tstruct parse_opt_ctx_t ctx;\n+\n+\t\tparse_options_start(&ctx, argc, argv, revs->prefix, options,\n+\t\t\t\tPARSE_OPT_STOP_AT_NON_OPTION | PARSE_OPT_NO_INTERNAL_HELP);\n+\t\tif (parse_options_step(&ctx, options, grep_usage) != PARSE_OPT_UNKNOWN) {\n+\t\t\terror(\"--grep-end expected\");\n+\t\t\tusage_with_options(grep_usage, options);\n+\t\t}\n+\t\tif(strcmp(ctx.argv[0], \"--grep-end\")) {\n+\t\t\tif (ctx.argv[0][1] == '-') {\n+\t\t\terror(\"unknown option `%s'\", ctx.argv[0] + 2);\n+\t\t\t} else if (isascii(*ctx.opt)) {\n+\t\t\t\terror(\"unknown switch `%c'\", *ctx.opt);\n+\t\t\t} else {\n+\t\t\terror(\"unknown non-ascii option in string: `%s'\",\n+\t\t\t      ctx.argv[0]);\n+\t\t\t}\t\n+\t\t\tusage_with_options(grep_usage, options);\n+\t\t}\n+\n+\t\tprecompose_argv(argc, argv);\n+\t\targcount = argc + 1 - parse_options_end(&ctx);\n+\t\tif (argcount == 2 ) {\n+\t\t\treturn error(\"There should be options between --grep-begin and --grep-end ;-)\");\n+\t\t}\n+\n+\t\tgrep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED, &revs->grep_filter);\n+\n+\t\treturn argcount;\n \t} else if (!strcmp(arg, \"--basic-regexp\")) {\n \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_BRE, &revs->grep_filter);\n \t} else if (!strcmp(arg, \"--extended-regexp\") || !strcmp(arg, \"-E\")) {\n-- \n2.0.5\n"},{"id":"254144","messageId":"xmqqy4pqz6fc.fsf@gitster.dls.corp.google.com","threadId":"38198","inReplyTo":"1419390196-17222-1-git-send-email-ottxor@gentoo.org","subject":"Re: [PATCH] git-log: added --invert-grep option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-29T17:56:07Z","receivedAt":"2014-12-29T17:56:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christoph Junghans <ottxor@gentoo.org> writes:\n\n> Ok, I drafted a first version of the suggest --grep-begin ...\n> --grep-end syntax.\n\nI am somewhat surprised that it was doable that cleanly.\n\nThe syntax, as I already said, is a bit too ugly to live in that\nform I suggested, though ;-).\n\n> However, I could not find a good ways to invert the match on a commit\n> basis instead of the normal line-wise version.\n\nGood point.\n\nThe only interface to tweak the way the individual matches are\nturned into file-level match (by default, we say \"any one of the\nclauses match, we find the file to be interesting\") is \"--all-match\"\n(\"all of the clauses have to trigger at least once for us to find\nthe file interesting\"), which, compared to the richer set of boolean\noperations at the line level, is a kludge.\n\nOffhand, short of overhauling that kludge to allow us to express\n\"The file has to have either 'atomic' or 'all-or-none' appear\nsomewhere, and also cannot have 'wip' anywhere\", I do not think of a\ngood way, other than adding yet another kludge on top of the\n\"--all-match\" that says \"none of the clauses must trigger\" to\nexpress what you want to see happen in \"git log\" to exclude ones\nthat are marked with \"wip\", perhaps naming it \"--none-match\" or\nsomething.\n\nThanks.\n"},{"id":"254274","messageId":"1420349268-13479-1-git-send-email-ottxor@gentoo.org","threadId":"38198","inReplyTo":"xmqqwq5o5e1j.fsf@gitster.dls.corp.google.com","subject":"[PATCH] git-log: added --none-match option","fromName":"Christoph Junghans","fromEmail":"ottxor@gentoo.org","sentAt":"2015-01-04T05:27:48Z","receivedAt":"2015-01-04T05:27:48Z","isPatch":true,"sender":{"key":"ottxor@gentoo.org","avatar":"https://avatars.githubusercontent.com/u/439483?v=4"},"body":"Implements a inverted match for \"git log\", like in the case of\n\"git grep -v\", which is useful from time to time to e.g. filter\nFIXUP message out of \"git log\".\n\nInternally, a new bol 'none_match' has been introduces as\nrevs->grep_filter.invert inverts the match line-wise, which cannot\nwork as i.e. empty line always not match the pattern given.\n\nSigned-off-by: Christoph Junghans <ottxor@gentoo.org>\n---\n Documentation/rev-list-options.txt     | 4 ++++\n contrib/completion/git-completion.bash | 2 +-\n grep.c                                 | 2 ++\n grep.h                                 | 1 +\n revision.c                             | 4 ++++\n 5 files changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex afccfdc..08e4ed8 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -66,6 +66,10 @@ if it is part of the log message.\n \tLimit the commits output to ones that match all given `--grep`,\n \tinstead of ones that match at least one.\n \n+--none-match::\n+\tLimit the commits output to ones that do not match any of the \n+\tgiven `--grep`, instead of ones that match at least one.\n+\n -i::\n --regexp-ignore-case::\n \tMatch the regular expression limiting patterns without regard to letter\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 23988ec..b0720e9 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1425,7 +1425,7 @@ __git_log_gitk_options=\"\n # Options that go well for log and shortlog (not gitk)\n __git_log_shortlog_options=\"\n \t--author= --committer= --grep=\n-\t--all-match\n+\t--all-match --none-match\n \"\n \n __git_log_pretty_formats=\"oneline short medium full fuller email raw format:\"\ndiff --git a/grep.c b/grep.c\nindex 6e085f8..eadf8d9 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -1622,6 +1622,8 @@ static int chk_hit_marker(struct grep_expr *x)\n \n int grep_source(struct grep_opt *opt, struct grep_source *gs)\n {\n+  \tif(opt->none_match)\n+\t\treturn !grep_source_1(opt, gs, 0);\t\n \t/*\n \t * we do not have to do the two-pass grep when we do not check\n \t * buffer-wide \"all-match\".\ndiff --git a/grep.h b/grep.h\nindex 95f197a..8e50c95 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -102,6 +102,7 @@ struct grep_opt {\n \tint word_regexp;\n \tint fixed;\n \tint all_match;\n+\tint none_match;\n \tint debug;\n #define GREP_BINARY_DEFAULT\t0\n #define GREP_BINARY_NOMATCH\t1\ndiff --git a/revision.c b/revision.c\nindex 75dda92..d43779e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2011,6 +2011,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n \t} else if (!strcmp(arg, \"--all-match\")) {\n \t\trevs->grep_filter.all_match = 1;\n+\t} else if (!strcmp(arg, \"--none-match\")) {\n+\t\trevs->grep_filter.none_match = 1;\n \t} else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n \t\tif (strcmp(optarg, \"none\"))\n \t\t\tgit_log_output_encoding = xstrdup(optarg);\n@@ -2333,6 +2335,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t\tdie(\"cannot combine --walk-reflogs with --graph\");\n \tif (!revs->reflog_info && revs->grep_filter.use_reflog_filter)\n \t\tdie(\"cannot use --grep-reflog without --walk-reflogs\");\n+\tif (revs->grep_filter.all_match && revs->grep_filter.none_match)\n+\t\tdie(\"cannot combine --all-match with --none-match\");\n \n \treturn left;\n }\n-- \n2.0.5\n"},{"id":"254378","messageId":"xmqq61cjo6lq.fsf@gitster.dls.corp.google.com","threadId":"38198","inReplyTo":"1420349268-13479-1-git-send-email-ottxor@gentoo.org","subject":"Re: [PATCH] git-log: added --none-match option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-06T23:02:41Z","receivedAt":"2015-01-06T23:02:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christoph Junghans <ottxor@gentoo.org> writes:\n\n> Implements a inverted match for \"git log\", like in the case of\n> \"git grep -v\", which is useful from time to time to e.g. filter\n> FIXUP message out of \"git log\".\n>\n> Internally, a new bol 'none_match' has been introduces as\n> revs->grep_filter.invert inverts the match line-wise, which cannot\n> work as i.e. empty line always not match the pattern given.\n>\n> Signed-off-by: Christoph Junghans <ottxor@gentoo.org>\n> ---\n\nThe patch itself looks like a good start, except that the above\ndescription no longer matches the implementation.\n\nI further suspect it would be better to rename all_match to\nall_or_none and then you can lose the \"these two are mutually\nincompatible\" check that is placed together with a wrong existing\ncomment.  I also notice that you forgot to update the \"git grep\"\nwhere the original \"--all-match\" came from.\n\nA partial fix-up may start like this on top of your version.  By\nrenaming the variable used in the existing code, the compiler will\nremind you that there are a few more places that your patch did not\ntouch that does something special for --all-match, which are a good\ncandidates you need to think if doing something similarly special\nfor the --none-match case is necessary.\n\nThanks.\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4063882..9ba4254 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -727,8 +727,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t  close_callback },\n \t\tOPT__QUIET(&opt.status_only,\n \t\t\t   N_(\"indicate hit with exit status without output\")),\n-\t\tOPT_BOOL(0, \"all-match\", &opt.all_match,\n-\t\t\tN_(\"show only matches from files that match all patterns\")),\n+\t\tOPT_SET_INT(0, \"all-match\", &opt.all_or_none,\n+\t\t\t    N_(\"show only matches from files that match all patterns\"),\n+\t\t\t    GREP_ALL_MATCH),\n+\t\tOPT_SET_INT(0, \"none-match\", &opt.all_or_none,\n+\t\t\t    N_(\"show only matches from files that match no patterns\"),\n+\t\t\t    GREP_NONE_MATCH),\n \t\t{ OPTION_SET_INT, 0, \"debug\", &opt.debug, NULL,\n \t\t  N_(\"show parse tree for grep expression\"),\n \t\t  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },\ndiff --git a/grep.c b/grep.c\nindex f486ee5..1ff5dea 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -1622,14 +1622,18 @@ static int chk_hit_marker(struct grep_expr *x)\n \n int grep_source(struct grep_opt *opt, struct grep_source *gs)\n {\n-\tif(opt->none_match)\n-\t\treturn !grep_source_1(opt, gs, 0);\n \t/*\n \t * we do not have to do the two-pass grep when we do not check\n-\t * buffer-wide \"all-match\".\n+\t * buffer-wide \"all-match\" or \"none-match\".\n \t */\n-\tif (!opt->all_match)\n+\tswitch (opt->all_or_none) {\n+\tcase GREP_ALL_MATCH:\n \t\treturn grep_source_1(opt, gs, 0);\n+\tcase GREP_NONE_MATCH:\n+\t\treturn !grep_source_1(opt, gs, 0);\n+\tdefault:\n+\t\tbreak;\n+\t}\n \n \t/* Otherwise the toplevel \"or\" terms hit a bit differently.\n \t * We first clear hit markers from them.\ndiff --git a/grep.h b/grep.h\nindex 8e50c95..2cdabf2 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -101,8 +101,9 @@ struct grep_opt {\n \tint count;\n \tint word_regexp;\n \tint fixed;\n-\tint all_match;\n-\tint none_match;\n+#define GREP_ALL_MATCH 1\n+#define GREP_NONE_MATCH 2\n+\tint all_or_none;\n \tint debug;\n #define GREP_BINARY_DEFAULT\t0\n #define GREP_BINARY_NOMATCH\t1\ndiff --git a/revision.c b/revision.c\nindex d43779e..b955848 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2010,9 +2010,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if (!strcmp(arg, \"--perl-regexp\")) {\n \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n \t} else if (!strcmp(arg, \"--all-match\")) {\n-\t\trevs->grep_filter.all_match = 1;\n+\t\trevs->grep_filter.all_or_none = GREP_ALL_MATCH;\n \t} else if (!strcmp(arg, \"--none-match\")) {\n-\t\trevs->grep_filter.none_match = 1;\n+\t\trevs->grep_filter.all_or_none = GREP_NONE_MATCH;\n \t} else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n \t\tif (strcmp(optarg, \"none\"))\n \t\t\tgit_log_output_encoding = xstrdup(optarg);\n@@ -2335,8 +2335,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t\tdie(\"cannot combine --walk-reflogs with --graph\");\n \tif (!revs->reflog_info && revs->grep_filter.use_reflog_filter)\n \t\tdie(\"cannot use --grep-reflog without --walk-reflogs\");\n-\tif (revs->grep_filter.all_match && revs->grep_filter.none_match)\n-\t\tdie(\"cannot combine --all-match with --none-match\");\n \n \treturn left;\n }\n"},{"id":"254514","messageId":"CANgp9kxXzt7x9JnnxjrcRLse4m86eDAgWyC4FwKw2U48NjV=ew@mail.gmail.com","threadId":"38198","inReplyTo":"xmqq61cjo6lq.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] git-log: added --none-match option","fromName":"Christoph Junghans","fromEmail":"ottxor@gentoo.org","sentAt":"2015-01-09T22:33:52Z","receivedAt":"2015-01-09T22:33:52Z","isPatch":true,"sender":{"key":"ottxor@gentoo.org","avatar":"https://avatars.githubusercontent.com/u/439483?v=4"},"body":"2015-01-06 16:02 GMT-07:00 Junio C Hamano <gitster@pobox.com>:\n> Christoph Junghans <ottxor@gentoo.org> writes:\n>\n>> Implements a inverted match for \"git log\", like in the case of\n>> \"git grep -v\", which is useful from time to time to e.g. filter\n>> FIXUP message out of \"git log\".\n>>\n>> Internally, a new bol 'none_match' has been introduces as\n>> revs->grep_filter.invert inverts the match line-wise, which cannot\n>> work as i.e. empty line always not match the pattern given.\n>>\n>> Signed-off-by: Christoph Junghans <ottxor@gentoo.org>\n>> ---\n>\n> The patch itself looks like a good start, except that the above\n> description no longer matches the implementation.\n>\n> I further suspect it would be better to rename all_match to\n> all_or_none and then you can lose the \"these two are mutually\n> incompatible\" check that is placed together with a wrong existing\n> comment.  I also notice that you forgot to update the \"git grep\"\n> where the original \"--all-match\" came from.\nThat was on purpose. I am not quite sure what would be the point of\n\"showing only matches from files that match no patterns\" (option\ndescription from your patch below).\nIf a file matches none of the patterns, what matches are there to show?\n\nThe only useful thing I could image is using it in conjunction with\n--files-with-matches, but that is what --files-without-match is for.\n\n>\n> A partial fix-up may start like this on top of your version.  By\n> renaming the variable used in the existing code, the compiler will\n> remind you that there are a few more places that your patch did not\n> touch that does something special for --all-match, which are a good\n> candidates you need to think if doing something similarly special\n> for the --none-match case is necessary.\n>\n> Thanks.\n>\n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 4063882..9ba4254 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -727,8 +727,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>                   close_callback },\n>                 OPT__QUIET(&opt.status_only,\n>                            N_(\"indicate hit with exit status without output\")),\n> -               OPT_BOOL(0, \"all-match\", &opt.all_match,\n> -                       N_(\"show only matches from files that match all patterns\")),\n> +               OPT_SET_INT(0, \"all-match\", &opt.all_or_none,\n> +                           N_(\"show only matches from files that match all patterns\"),\n> +                           GREP_ALL_MATCH),\n> +               OPT_SET_INT(0, \"none-match\", &opt.all_or_none,\n> +                           N_(\"show only matches from files that match no patterns\"),\n> +                           GREP_NONE_MATCH),\n>                 { OPTION_SET_INT, 0, \"debug\", &opt.debug, NULL,\n>                   N_(\"show parse tree for grep expression\"),\n>                   PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },\n> diff --git a/grep.c b/grep.c\n> index f486ee5..1ff5dea 100644\n> --- a/grep.c\n> +++ b/grep.c\n> @@ -1622,14 +1622,18 @@ static int chk_hit_marker(struct grep_expr *x)\n>\n>  int grep_source(struct grep_opt *opt, struct grep_source *gs)\n>  {\n> -       if(opt->none_match)\n> -               return !grep_source_1(opt, gs, 0);\n>         /*\n>          * we do not have to do the two-pass grep when we do not check\n> -        * buffer-wide \"all-match\".\n> +        * buffer-wide \"all-match\" or \"none-match\".\n>          */\n> -       if (!opt->all_match)\n> +       switch (opt->all_or_none) {\n> +       case GREP_ALL_MATCH:\n>                 return grep_source_1(opt, gs, 0);\n> +       case GREP_NONE_MATCH:\n> +               return !grep_source_1(opt, gs, 0);\n> +       default:\n> +               break;\n> +       }\n>\n>         /* Otherwise the toplevel \"or\" terms hit a bit differently.\n>          * We first clear hit markers from them.\n> diff --git a/grep.h b/grep.h\n> index 8e50c95..2cdabf2 100644\n> --- a/grep.h\n> +++ b/grep.h\n> @@ -101,8 +101,9 @@ struct grep_opt {\n>         int count;\n>         int word_regexp;\n>         int fixed;\n> -       int all_match;\n> -       int none_match;\n> +#define GREP_ALL_MATCH 1\n> +#define GREP_NONE_MATCH 2\n> +       int all_or_none;\n>         int debug;\n>  #define GREP_BINARY_DEFAULT    0\n>  #define GREP_BINARY_NOMATCH    1\n> diff --git a/revision.c b/revision.c\n> index d43779e..b955848 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2010,9 +2010,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n>         } else if (!strcmp(arg, \"--perl-regexp\")) {\n>                 grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n>         } else if (!strcmp(arg, \"--all-match\")) {\n> -               revs->grep_filter.all_match = 1;\n> +               revs->grep_filter.all_or_none = GREP_ALL_MATCH;\n>         } else if (!strcmp(arg, \"--none-match\")) {\n> -               revs->grep_filter.none_match = 1;\n> +               revs->grep_filter.all_or_none = GREP_NONE_MATCH;\n>         } else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n>                 if (strcmp(optarg, \"none\"))\n>                         git_log_output_encoding = xstrdup(optarg);\n> @@ -2335,8 +2335,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>                 die(\"cannot combine --walk-reflogs with --graph\");\n>         if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)\n>                 die(\"cannot use --grep-reflog without --walk-reflogs\");\n> -       if (revs->grep_filter.all_match && revs->grep_filter.none_match)\n> -               die(\"cannot combine --all-match with --none-match\");\n>\n>         return left;\n>  }\n>\n>\n>\n\n\n\n-- \nChristoph Junghans\nhttp://dev.gentoo.org/~ottxor/\n"},{"id":"254517","messageId":"xmqqk30vbm3r.fsf@gitster.dls.corp.google.com","threadId":"38198","inReplyTo":"CANgp9kxXzt7x9JnnxjrcRLse4m86eDAgWyC4FwKw2U48NjV=ew@mail.gmail.com","subject":"Re: [PATCH] git-log: added --none-match option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-09T22:55:20Z","receivedAt":"2015-01-09T22:55:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christoph Junghans <ottxor@gentoo.org> writes:\n\n> The only useful thing I could image is using it in conjunction with\n> --files-with-matches, but that is what --files-without-match is for.\n\nYes, \"-l\" was exactly what I had in mind and I was hoping that \"git\ngrep -l --no-match -e WIP -e TODO -e FIXME -e NEEDSWORK\" may be a\nway to find perfect files without needing any work.\n\nYou can say \"git grep -L -e WIP -e TODO -e FIXME -e NEEDSWORK\"\ninstead.  I missed that \"-L\" option.\n\nThanks.\n"},{"id":"254551","messageId":"1421026789-14932-1-git-send-email-ottxor@gentoo.org","threadId":"38198","inReplyTo":"xmqq61cjo6lq.fsf@gitster.dls.corp.google.com","subject":"[PATCH v2] git-log: added --none-match option","fromName":"Christoph Junghans","fromEmail":"ottxor@gentoo.org","sentAt":"2015-01-12T01:39:49Z","receivedAt":"2015-01-12T01:39:49Z","isPatch":true,"sender":{"key":"ottxor@gentoo.org","avatar":"https://avatars.githubusercontent.com/u/439483?v=4"},"body":"Implements a none match for git log, which is useful from time to\ntime to e.g. filter FIXUP message out of git log.\n\nInternally, the bol 'all_match' was changed to an int 'all_or_none'\ntaken the values 0, GREP_ALL_MATCH or GREP_NONE_MATCH.\n\nFor git grep a similar functionality can achieved by the existing\n--files-without-match option.\n\nSigned-off-by: Christoph Junghans <ottxor@gentoo.org>\n---\n Documentation/rev-list-options.txt     |  4 ++++\n builtin/grep.c                         |  5 +++--\n contrib/completion/git-completion.bash |  2 +-\n gitk-git/gitk                          |  1 +\n grep.c                                 | 24 ++++++++++++++++++------\n grep.h                                 |  4 +++-\n revision.c                             |  4 +++-\n 7 files changed, 33 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex afccfdc..08e4ed8 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -66,6 +66,10 @@ if it is part of the log message.\n \tLimit the commits output to ones that match all given `--grep`,\n \tinstead of ones that match at least one.\n \n+--none-match::\n+\tLimit the commits output to ones that do not match any of the \n+\tgiven `--grep`, instead of ones that match at least one.\n+\n -i::\n --regexp-ignore-case::\n \tMatch the regular expression limiting patterns without regard to letter\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4063882..1ec8ce1 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -727,8 +727,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t  close_callback },\n \t\tOPT__QUIET(&opt.status_only,\n \t\t\t   N_(\"indicate hit with exit status without output\")),\n-\t\tOPT_BOOL(0, \"all-match\", &opt.all_match,\n-\t\t\tN_(\"show only matches from files that match all patterns\")),\n+\t\tOPT_SET_INT(0, \"all-match\", &opt.all_or_none,\n+\t\t\t    N_(\"show only matches from files that match all patterns\"),\n+\t\t\t    GREP_ALL_MATCH),\n \t\t{ OPTION_SET_INT, 0, \"debug\", &opt.debug, NULL,\n \t\t  N_(\"show parse tree for grep expression\"),\n \t\t  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex cd76579..47ed970 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1425,7 +1425,7 @@ __git_log_gitk_options=\"\n # Options that go well for log and shortlog (not gitk)\n __git_log_shortlog_options=\"\n \t--author= --committer= --grep=\n-\t--all-match\n+\t--all-match --none-match\n \"\n \n __git_log_pretty_formats=\"oneline short medium full fuller email raw format:\"\ndiff --git a/gitk-git/gitk b/gitk-git/gitk\nindex 78358a7..c67674f 100755\n--- a/gitk-git/gitk\n+++ b/gitk-git/gitk\n@@ -3977,6 +3977,7 @@ set known_view_options {\n     {committer t15  .  \"--committer=*\"  {mc \"Committer:\"}}\n     {loginfo   t15  .. \"--grep=*\"       {mc \"Commit Message:\"}}\n     {allmatch  b    .. \"--all-match\"    {mc \"Matches all Commit Info criteria\"}}\n+    {nonematch b    .. \"--none-match\"   {mc \"Matches none Commit Info criteria\"}}\n     {changes_l l    +  {}               {mc \"Changes to Files:\"}}\n     {pickaxe_s r0   .  {}               {mc \"Fixed String\"}}\n     {pickaxe_t r1   .  \"--pickaxe-regex\"  {mc \"Regular Expression\"}}\ndiff --git a/grep.c b/grep.c\nindex 6e085f8..f6eb044 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -609,8 +609,14 @@ static void dump_grep_expression(struct grep_opt *opt)\n {\n \tstruct grep_expr *x = opt->pattern_expression;\n \n-\tif (opt->all_match)\n+\tswitch (opt->all_or_none) {\n+\tcase GREP_ALL_MATCH:\n \t\tfprintf(stderr, \"[all-match]\\n\");\n+\tcase GREP_NONE_MATCH:\n+\t\tfprintf(stderr, \"[none-match]\\n\");\n+\tdefault:\n+\t\tbreak;\n+\t}\n \tdump_grep_expression_1(x, 0);\n \tfflush(NULL);\n }\n@@ -713,7 +719,7 @@ static void compile_grep_patterns_real(struct grep_opt *opt)\n \t\t}\n \t}\n \n-\tif (opt->all_match || header_expr)\n+\tif ((opt->all_or_none == GREP_ALL_MATCH) || header_expr)\n \t\topt->extended = 1;\n \telse if (!opt->extended && !opt->debug)\n \t\treturn;\n@@ -729,13 +735,13 @@ static void compile_grep_patterns_real(struct grep_opt *opt)\n \n \tif (!opt->pattern_expression)\n \t\topt->pattern_expression = header_expr;\n-\telse if (opt->all_match)\n+\telse if (opt->all_or_none == GREP_ALL_MATCH)\n \t\topt->pattern_expression = grep_splice_or(header_expr,\n \t\t\t\t\t\t\t opt->pattern_expression);\n \telse\n \t\topt->pattern_expression = grep_or_expr(opt->pattern_expression,\n \t\t\t\t\t\t       header_expr);\n-\topt->all_match = 1;\n+\topt->all_or_none = GREP_ALL_MATCH;\n }\n \n void compile_grep_patterns(struct grep_opt *opt)\n@@ -1624,10 +1630,16 @@ int grep_source(struct grep_opt *opt, struct grep_source *gs)\n {\n \t/*\n \t * we do not have to do the two-pass grep when we do not check\n-\t * buffer-wide \"all-match\".\n+\t * buffer-wide \"all-match\" or check \"none-match\".\n \t */\n-\tif (!opt->all_match)\n+\tswitch (opt->all_or_none) {\n+\tcase GREP_NONE_MATCH:\n+\t\treturn !grep_source_1(opt, gs, 0);\n+\tcase GREP_ALL_MATCH:\n+\t\tbreak;\n+\tdefault:\n \t\treturn grep_source_1(opt, gs, 0);\n+\t}\n \n \t/* Otherwise the toplevel \"or\" terms hit a bit differently.\n \t * We first clear hit markers from them.\ndiff --git a/grep.h b/grep.h\nindex 95f197a..2cdabf2 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -101,7 +101,9 @@ struct grep_opt {\n \tint count;\n \tint word_regexp;\n \tint fixed;\n-\tint all_match;\n+#define GREP_ALL_MATCH 1\n+#define GREP_NONE_MATCH 2\n+\tint all_or_none;\n \tint debug;\n #define GREP_BINARY_DEFAULT\t0\n #define GREP_BINARY_NOMATCH\t1\ndiff --git a/revision.c b/revision.c\nindex 14e0e03..723b495 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2010,7 +2010,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if (!strcmp(arg, \"--perl-regexp\")) {\n \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n \t} else if (!strcmp(arg, \"--all-match\")) {\n-\t\trevs->grep_filter.all_match = 1;\n+\t\trevs->grep_filter.all_or_none = GREP_ALL_MATCH;\n+\t} else if (!strcmp(arg, \"--none-match\")) {\n+\t\trevs->grep_filter.all_or_none = GREP_NONE_MATCH;\n \t} else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n \t\tif (strcmp(optarg, \"none\"))\n \t\t\tgit_log_output_encoding = xstrdup(optarg);\n-- \n2.0.5\n"},{"id":"254559","messageId":"xmqqk30r7med.fsf@gitster.dls.corp.google.com","threadId":"38198","inReplyTo":"xmqqk30vbm3r.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] git-log: added --none-match option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-12T20:51:38Z","receivedAt":"2015-01-12T20:51:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Christoph Junghans <ottxor@gentoo.org> writes:\n>\n>> The only useful thing I could image is using it in conjunction with\n>> --files-with-matches, but that is what --files-without-match is for.\n>\n> Yes, \"-l\" was exactly what I had in mind and I was hoping that \"git\n> grep -l --no-match -e WIP -e TODO -e FIXME -e NEEDSWORK\" may be a\n> way to find perfect files without needing any work.\n>\n> You can say \"git grep -L -e WIP -e TODO -e FIXME -e NEEDSWORK\"\n> instead.  I missed that \"-L\" option.\n\nThanks for your patience.  I should have realized that this not only\ncan be \"log-only\" but _should_ be \"log-only\".  As you pointed out,\nwhen we already have \"-L\", trying to extend it to \"grep\" does not\nmake much sense.\n\nContinuing this line of thought, as we determined that it is\npointless to have this at \"grep\" level and it is needed only in the\n\"log\" family of commands, I would very much prefer the approach\ntaken by your original \"log --invert-grep\" patch.  I would further\nsay that I prefer not to touch grep_opt at all.\n\nThe new \"global-invert bit\" is about \"we'd run the usual grep thing\non the log message, and instead of filtering to only show the\ncommits with matching message, we only show the ones with messages\nthat do not match\".  That logically belongs to the revision struct\nthat is used to interpret what the underlying grep machinery figured\nout, and should not have to affect the way how underlying grep\nmachinery works.\n\nWe would want a few test to make sure that we do not break the\nfeature in the future changes.  Here is an attempt.  The patch would\napply any commit your original \"--invert-grep\" option would have\napplied.\n\nThanks again.\n\n-- >8 --\nSubject: log: teach --invert-grep option\n\n\"git log --grep=<string>\" shows only commits with messages that\nmatch the given string, but sometimes it is useful to be able to\nshow only commits that do *not* have certain messages (e.g. \"show me\nones that are not FIXUP commits\").\n\nSigned-off-by: Christoph Junghans <ottxor@gentoo.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n jc: Christoph originally had the invert-grep flag in grep_opt, but\n     because \"git grep --invert-grep\" does not make sense except in\n     conjunction with \"--files-with-matches\", which is already\n     covered by \"--files-without-matches\", I moved it to revisions\n     structure.  I think it expresses what the feature is about\n     better to have the flag there.\n\n     When the newly inserted two tests run, the history would have\n     commits with messages \"initial\", \"second\", \"third\", \"fourth\",\n     \"fifth\", \"sixth\" and Second\", committed in this order.  The\n     first commit that does not match either \"th\" or \"Sec\" is\n     \"second\", and \"initial\" is the one that does not match either\n     \"th\" or \"Sec\" case insensitively.\n\n Documentation/rev-list-options.txt     |  4 ++++\n contrib/completion/git-completion.bash |  2 +-\n revision.c                             |  4 +++-\n revision.h                             |  2 ++\n t/t4202-log.sh                         | 12 ++++++++++++\n 5 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex deb8cca..05aa997 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -66,6 +66,10 @@ if it is part of the log message.\n \tLimit the commits output to ones that match all given `--grep`,\n \tinstead of ones that match at least one.\n \n+--invert-grep::\n+\tLimit the commits output to ones with log message that do not\n+\tmatch the pattern specified with `--grep=<pattern>`.\n+\n -i::\n --regexp-ignore-case::\n \tMatch the regular expression limiting patterns without regard to letter\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 06bf262..53857f0 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1428,7 +1428,7 @@ __git_log_gitk_options=\"\n # Options that go well for log and shortlog (not gitk)\n __git_log_shortlog_options=\"\n \t--author= --committer= --grep=\n-\t--all-match\n+\t--all-match --invert-grep\n \"\n \n __git_log_pretty_formats=\"oneline short medium full fuller email raw format:\"\ndiff --git a/revision.c b/revision.c\nindex 615535c..84b33a3 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1952,6 +1952,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n \t} else if (!strcmp(arg, \"--all-match\")) {\n \t\trevs->grep_filter.all_match = 1;\n+\t} else if (!strcmp(arg, \"--invert-grep\")) {\n+\t\trevs->invert_grep = 1;\n \t} else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n \t\tif (strcmp(optarg, \"none\"))\n \t\t\tgit_log_output_encoding = xstrdup(optarg);\n@@ -2848,7 +2850,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \t\t\t\t     (char *)message, strlen(message));\n \tstrbuf_release(&buf);\n \tunuse_commit_buffer(commit, message);\n-\treturn retval;\n+\treturn opt->invert_grep ? !retval : retval;\n }\n \n static inline int want_ancestry(const struct rev_info *revs)\ndiff --git a/revision.h b/revision.h\nindex a620530..b0b82e7 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -168,6 +168,8 @@ struct rev_info {\n \n \t/* Filter by commit log message */\n \tstruct grep_opt\tgrep_filter;\n+\t/* Negate the match of grep_filter */\n+\tint invert_grep;\n \n \t/* Display history graph */\n \tstruct git_graph *graph;\ndiff --git a/t/t4202-log.sh b/t/t4202-log.sh\nindex 99ab7ca..1c9934e 100755\n--- a/t/t4202-log.sh\n+++ b/t/t4202-log.sh\n@@ -212,6 +212,18 @@ test_expect_success 'log --grep' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'log --invert-grep --grep' '\n+\techo second >expect &&\n+\tgit log -1 --pretty=\"tformat:%s\" --invert-grep --grep=th --grep=Sec >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'log --invert-grep --grep -i' '\n+\techo initial >expect &&\n+\tgit log -1 --pretty=\"tformat:%s\" --invert-grep -i --grep=th --grep=Sec >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'log --grep option parsing' '\n \techo second >expect &&\n \tgit log -1 --pretty=\"tformat:%s\" --grep sec >actual &&\n"},{"id":"254580","messageId":"1421112812-13578-1-git-send-email-ottxor@gentoo.org","threadId":"38198","inReplyTo":"xmqq61cjo6lq.fsf@gitster.dls.corp.google.com","subject":"[PATCH v2] log: teach --invert-grep option","fromName":"Christoph Junghans","fromEmail":"ottxor@gentoo.org","sentAt":"2015-01-13T01:33:32Z","receivedAt":"2015-01-13T01:33:32Z","isPatch":true,"sender":{"key":"ottxor@gentoo.org","avatar":"https://avatars.githubusercontent.com/u/439483?v=4"},"body":"\"git log --grep=<string>\" shows only commits with messages that\nmatch the given string, but sometimes it is useful to be able to\nshow only commits that do *not* have certain messages (e.g. \"show\nme ones that are not FIXUP commits\").\n\nOriginally, we had the invert-grep flag in grep_opt, but because\n\"git grep --invert-grep\" does not make sense except in conjunction\nwith \"--files-with-matches\", which is already covered by\n\"--files-without-matches\", it was moved it to revisions structure.\nTo have the flag there expresses the function to the feature better.\n\nWhen the newly inserted two tests run, the history would have commits\nwith messages \"initial\", \"second\", \"third\", \"fourth\", \"fifth\", \"sixth\"\nand \"Second\", committed in this order.  The commits that does not match\neither \"th\" or \"Sec\" is \"second\" and \"initial\". For the case insensitive\ncase only \"initial\" matches.\n\nSigned-off-by: Christoph Junghans <ottxor@gentoo.org>\n---\n Documentation/rev-list-options.txt     |  4 ++++\n contrib/completion/git-completion.bash |  2 +-\n gitk-git/gitk                          |  1 +\n revision.c                             |  4 +++-\n revision.h                             |  2 ++\n t/t4202-log.sh                         | 15 +++++++++++++++\n 6 files changed, 26 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex afccfdc..6d4671f 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -66,6 +66,10 @@ if it is part of the log message.\n \tLimit the commits output to ones that match all given `--grep`,\n \tinstead of ones that match at least one.\n \n+--invert-grep::\n+\tLimit the commits output to ones with log message that do not\n+\tmatch the pattern specified with `--grep=<pattern>`.\n+\n -i::\n --regexp-ignore-case::\n \tMatch the regular expression limiting patterns without regard to letter\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex cd76579..aaeac50 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1425,7 +1425,7 @@ __git_log_gitk_options=\"\n # Options that go well for log and shortlog (not gitk)\n __git_log_shortlog_options=\"\n \t--author= --committer= --grep=\n-\t--all-match\n+\t--all-match --invert-grep\n \"\n \n __git_log_pretty_formats=\"oneline short medium full fuller email raw format:\"\ndiff --git a/gitk-git/gitk b/gitk-git/gitk\nindex 78358a7..5a78432 100755\n--- a/gitk-git/gitk\n+++ b/gitk-git/gitk\n@@ -3977,6 +3977,7 @@ set known_view_options {\n     {committer t15  .  \"--committer=*\"  {mc \"Committer:\"}}\n     {loginfo   t15  .. \"--grep=*\"       {mc \"Commit Message:\"}}\n     {allmatch  b    .. \"--all-match\"    {mc \"Matches all Commit Info criteria\"}}\n+    {igrep     b    .. \"--invert-grep\"  {mc \"Matches none Commit Info criteria\"}}\n     {changes_l l    +  {}               {mc \"Changes to Files:\"}}\n     {pickaxe_s r0   .  {}               {mc \"Fixed String\"}}\n     {pickaxe_t r1   .  \"--pickaxe-regex\"  {mc \"Regular Expression\"}}\ndiff --git a/revision.c b/revision.c\nindex 14e0e03..4d03338 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2011,6 +2011,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\tgrep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);\n \t} else if (!strcmp(arg, \"--all-match\")) {\n \t\trevs->grep_filter.all_match = 1;\n+\t} else if (!strcmp(arg, \"--invert-grep\")) {\n+\t\trevs->invert_grep = 1;\n \t} else if ((argcount = parse_long_opt(\"encoding\", argv, &optarg))) {\n \t\tif (strcmp(optarg, \"none\"))\n \t\t\tgit_log_output_encoding = xstrdup(optarg);\n@@ -2909,7 +2911,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \t\t\t\t     (char *)message, strlen(message));\n \tstrbuf_release(&buf);\n \tunuse_commit_buffer(commit, message);\n-\treturn retval;\n+\treturn opt->invert_grep ? !retval : retval;\n }\n \n static inline int want_ancestry(const struct rev_info *revs)\ndiff --git a/revision.h b/revision.h\nindex 9cb5adc..bc27098 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -168,6 +168,8 @@ struct rev_info {\n \n \t/* Filter by commit log message */\n \tstruct grep_opt\tgrep_filter;\n+\t/* Negate the match of grep_filter */\n+\tint invert_grep;\n \n \t/* Display history graph */\n \tstruct git_graph *graph;\ndiff --git a/t/t4202-log.sh b/t/t4202-log.sh\nindex 99ab7ca..5f2b290 100755\n--- a/t/t4202-log.sh\n+++ b/t/t4202-log.sh\n@@ -212,6 +212,21 @@ test_expect_success 'log --grep' '\n \ttest_cmp expect actual\n '\n \n+cat > expect << EOF\n+second\n+initial\n+EOF\n+test_expect_success 'log --invert-grep --grep' '\n+\tgit log --pretty=\"tformat:%s\" --invert-grep --grep=th --grep=Sec >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'log --invert-grep --grep -i' '\n+\techo initial >expect &&\n+\tgit log --pretty=\"tformat:%s\" --invert-grep -i --grep=th --grep=Sec >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'log --grep option parsing' '\n \techo second >expect &&\n \tgit log -1 --pretty=\"tformat:%s\" --grep sec >actual &&\n-- \n2.0.5\n"},{"id":"254611","messageId":"xmqqfvbeedxf.fsf@gitster.dls.corp.google.com","threadId":"38198","inReplyTo":"1421112812-13578-1-git-send-email-ottxor@gentoo.org","subject":"Re: [PATCH v2] log: teach --invert-grep option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-13T18:25:00Z","receivedAt":"2015-01-13T18:25:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christoph Junghans <ottxor@gentoo.org> writes:\n\n> \"git log --grep=<string>\" shows only commits with messages that\n> match the given string, but sometimes it is useful to be able to\n> show only commits that do *not* have certain messages (e.g. \"show\n> me ones that are not FIXUP commits\").\n>\n> Originally, we had the invert-grep flag in grep_opt, but because\n> \"git grep --invert-grep\" does not make sense except in conjunction\n> with \"--files-with-matches\", which is already covered by\n> \"--files-without-matches\", it was moved it to revisions structure.\n> To have the flag there expresses the function to the feature better.\n>\n> When the newly inserted two tests run, the history would have commits\n> with messages \"initial\", \"second\", \"third\", \"fourth\", \"fifth\", \"sixth\"\n> and \"Second\", committed in this order.  The commits that does not match\n> either \"th\" or \"Sec\" is \"second\" and \"initial\". For the case insensitive\n> case only \"initial\" matches.\n\nI see you moved the two bits meant only to be useful during the\nreview to the commit message proper.  The reason why I omitted the\n\"Originally, ...\" part was because those who are reading \"git log\n-p\" output would not know what the earlier draft had.\n\nI do not mind it either way, though, so let's take this version.\n\nHowever, I'd have to drop gitk-git/ bit; that part of the tree is\nmaintained separately and comes from Paul's gitk repository.  I'll\nforward only that part of the patch to him when this feature\ngraduates to 'master'.\n\nThanks.\n"},{"id":"256138","messageId":"xmqqd25a71rt.fsf_-_@gitster.dls.corp.google.com","threadId":"38198","inReplyTo":"1421112812-13578-1-git-send-email-ottxor@gentoo.org","subject":"[PATCH] gitk: pass --invert-grep option down to \"git log\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-02-16T07:29:10Z","receivedAt":"2015-02-16T07:29:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"From: Christoph Junghans <ottxor@gentoo.org>\nDate: Mon, 12 Jan 2015 18:33:32 -0700\n\n\"git log --grep=<string>\" shows only commits with messages that\nmatch the given string, but sometimes it is useful to be able to\nshow only commits that do *not* have certain messages (e.g. \"show\nme ones that are not FIXUP commits\").\n\nNow the underlying \"git log\" learned the \"--invert-grep\" option.\nThe option syntactically behaves similar to \"--all-match\" that\nrequires that all of the grep strings to match and semantically\nbehaves the opposite---it requires that none of the grep strings to\nmatch.\n\nTeach \"gitk\" to allow users to pass it down to underlying \"git log\"\ncommand by adding it to the known_view_options array.\n\nSigned-off-by: Christoph Junghans <ottxor@gentoo.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * Split from Christoph's original patch ($gmane/262313).\n\n gitk | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git b/gitk a/gitk\nindex 3520bda..a95a93f 100755\n--- b/gitk\n+++ a/gitk\n@@ -4036,6 +4036,7 @@ set known_view_options {\n     {committer t15  .  \"--committer=*\"  {mc \"Committer:\"}}\n     {loginfo   t15  .. \"--grep=*\"       {mc \"Commit Message:\"}}\n     {allmatch  b    .. \"--all-match\"    {mc \"Matches all Commit Info criteria\"}}\n+    {igrep     b    .. \"--invert-grep\"  {mc \"Matches none Commit Info criteria\"}}\n     {changes_l l    +  {}               {mc \"Changes to Files:\"}}\n     {pickaxe_s r0   .  {}               {mc \"Fixed String\"}}\n     {pickaxe_t r1   .  \"--pickaxe-regex\"  {mc \"Regular Expression\"}}\n"},{"id":"258267","messageId":"20150322033940.GC14271@iris.ozlabs.ibm.com","threadId":"38198","inReplyTo":"xmqqd25a71rt.fsf_-_@gitster.dls.corp.google.com","subject":"Re: [PATCH] gitk: pass --invert-grep option down to \"git log\"","fromName":"Paul Mackerras","fromEmail":"paulus@samba.org","sentAt":"2015-03-22T03:39:40Z","receivedAt":"2015-03-22T03:39:40Z","isPatch":true,"sender":{"key":"paulus@samba.org","avatar":"https://avatars.githubusercontent.com/u/1606439?v=4"},"body":"On Sun, Feb 15, 2015 at 11:29:10PM -0800, Junio C Hamano wrote:\n> From: Christoph Junghans <ottxor@gentoo.org>\n> Date: Mon, 12 Jan 2015 18:33:32 -0700\n> \n> \"git log --grep=<string>\" shows only commits with messages that\n> match the given string, but sometimes it is useful to be able to\n> show only commits that do *not* have certain messages (e.g. \"show\n> me ones that are not FIXUP commits\").\n> \n> Now the underlying \"git log\" learned the \"--invert-grep\" option.\n> The option syntactically behaves similar to \"--all-match\" that\n> requires that all of the grep strings to match and semantically\n> behaves the opposite---it requires that none of the grep strings to\n> match.\n> \n> Teach \"gitk\" to allow users to pass it down to underlying \"git log\"\n> command by adding it to the known_view_options array.\n> \n> Signed-off-by: Christoph Junghans <ottxor@gentoo.org>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n\nThanks, applied.\n\nPaul.\n"}]}