Merge branch 'cj/log-invert-grep'
authorJunio C Hamano <gitster@pobox.com>
Wed, 11 Feb 2015 21:42:39 +0000 (13:42 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 11 Feb 2015 21:42:39 +0000 (13:42 -0800)
"git log --invert-grep --grep=WIP" will show only commits that do
not have the string "WIP" in their messages.

* cj/log-invert-grep:
log: teach --invert-grep option

1  2 
Documentation/rev-list-options.txt
contrib/completion/git-completion.bash
revision.c
revision.h
index 2984f407a9f3084235fd0207a82ea44a424942ed,05aa997fd1037d9a26cb5cf6ab05874293f1a3ef..0f3d460d34a6285687792c22c66a24921f17baa7
@@@ -66,6 -66,10 +66,10 @@@ if it is part of the log message
        Limit the commits output to ones that match all given `--grep`,
        instead of ones that match at least one.
  
+ --invert-grep::
+       Limit the commits output to ones with log message that do not
+       match the pattern specified with `--grep=<pattern>`.
  -i::
  --regexp-ignore-case::
        Match the regular expression limiting patterns without regard to letter
@@@ -160,7 -164,7 +164,7 @@@ parents) and `--max-parents=-1` (negati
        consider. Repetitions of this option accumulate exclusion patterns
        up to the next `--all`, `--branches`, `--tags`, `--remotes`, or
        `--glob` option (other options or arguments do not clear
 -      accumlated patterns).
 +      accumulated patterns).
  +
  The patterns given should not begin with `refs/heads`, `refs/tags`, or
  `refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,
@@@ -168,15 -172,6 +172,15 @@@ respectively, and they must begin with 
  or `--all`. If a trailing '/{asterisk}' is intended, it must be given
  explicitly.
  
 +--reflog::
 +      Pretend as if all objects mentioned by reflogs are listed on the
 +      command line as `<commit>`.
 +
 +--indexed-objects::
 +      Pretend as if all trees and blobs used by the index are listed
 +      on the command line.  Note that you probably want to use
 +      `--objects`, too.
 +
  --ignore-missing::
        Upon seeing an invalid object name in the input, pretend as if
        the bad input was not given.
@@@ -653,15 -648,10 +657,15 @@@ These options are mostly targeted for p
  --objects-edge::
        Similar to `--objects`, but also print the IDs of excluded
        commits prefixed with a ``-'' character.  This is used by
 -      linkgit:git-pack-objects[1] to build ``thin'' pack, which records
 +      linkgit:git-pack-objects[1] to build ``thin'' pack, which records
        objects in deltified form based on objects contained in these
        excluded commits to reduce network traffic.
  
 +--objects-edge-aggressive::
 +      Similar to `--objects-edge`, but it tries harder to find excluded
 +      commits at the cost of increased time.  This is used instead of
 +      `--objects-edge` to build ``thin'' packs for shallow repositories.
 +
  --unpacked::
        Only useful with `--objects`; print the object IDs that are not
        in packs.
@@@ -691,7 -681,7 +695,7 @@@ include::pretty-options.txt[
  --relative-date::
        Synonym for `--date=relative`.
  
 ---date=(relative|local|default|iso|rfc|short|raw)::
 +--date=(relative|local|default|iso|iso-strict|rfc|short|raw)::
        Only takes effect for dates shown in human-readable format, such
        as when using `--pretty`. `log.date` config variable sets a default
        value for the log command's `--date` option.
@@@ -701,16 -691,7 +705,16 @@@ e.g. ``2 hours ago''
  +
  `--date=local` shows timestamps in user's local time zone.
  +
 -`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
 +`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.
 +The differences to the strict ISO 8601 format are:
 +
 +      - a space instead of the `T` date/time delimiter
 +      - a space between time and time zone
 +      - no colon between hours and minutes of the time zone
 +
 ++
 +`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict
 +ISO 8601 format.
  +
  `--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
  format, often found in email messages.
index 8cfee95f88006269e4227f98701b9af8587af0bd,53857f0bbf608ca9c8d24e5c8dce328de7c825cc..c21190d7510c173482b7784f9e8c4bf0b56cb1e1
@@@ -16,9 -16,9 +16,9 @@@
  #
  # To use these routines:
  #
 -#    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
 +#    1) Copy this file to somewhere (e.g. ~/.git-completion.bash).
  #    2) Add the following line to your .bashrc/.zshrc:
 -#        source ~/.git-completion.sh
 +#        source ~/.git-completion.bash
  #    3) Consider changing your PS1 to also show the current branch,
  #       see git-prompt.sh for details.
  #
@@@ -281,12 -281,16 +281,12 @@@ __gitcomp_file (
  # argument, and using the options specified in the second argument.
  __git_ls_files_helper ()
  {
 -      (
 -              test -n "${CDPATH+set}" && unset CDPATH
 -              cd "$1"
 -              if [ "$2" == "--committable" ]; then
 -                      git diff-index --name-only --relative HEAD
 -              else
 -                      # NOTE: $2 is not quoted in order to support multiple options
 -                      git ls-files --exclude-standard $2
 -              fi
 -      ) 2>/dev/null
 +      if [ "$2" == "--committable" ]; then
 +              git -C "$1" diff-index --name-only --relative HEAD
 +      else
 +              # NOTE: $2 is not quoted in order to support multiple options
 +              git -C "$1" ls-files --exclude-standard $2
 +      fi 2>/dev/null
  }
  
  
@@@ -384,8 -388,7 +384,8 @@@ __git_refs (
                ;;
        *)
                echo "HEAD"
 -              git for-each-ref --format="%(refname:short)" -- "refs/remotes/$dir/" | sed -e "s#^$dir/##"
 +              git for-each-ref --format="%(refname:short)" -- \
 +                      "refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
                ;;
        esac
  }
@@@ -519,7 -522,7 +519,7 @@@ __git_complete_index_file (
                ;;
        esac
  
 -      __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
 +      __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_"
  }
  
  __git_complete_file ()
@@@ -1172,8 -1175,8 +1172,8 @@@ __git_diff_common_options="--stat --num
                        --full-index --binary --abbrev --diff-filter=
                        --find-copies-harder
                        --text --ignore-space-at-eol --ignore-space-change
 -                      --ignore-all-space --exit-code --quiet --ext-diff
 -                      --no-ext-diff
 +                      --ignore-all-space --ignore-blank-lines --exit-code
 +                      --quiet --ext-diff --no-ext-diff
                        --no-prefix --src-prefix= --dst-prefix=
                        --inter-hunk-context=
                        --patience --histogram --minimal
@@@ -1204,7 -1207,7 +1204,7 @@@ _git_diff (
  }
  
  __git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
 -                      tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3 codecompare
 +                      tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
  "
  
  _git_difftool ()
@@@ -1425,7 -1428,7 +1425,7 @@@ __git_log_gitk_options=
  # Options that go well for log and shortlog (not gitk)
  __git_log_shortlog_options="
        --author= --committer= --grep=
-       --all-match
+       --all-match --invert-grep
  "
  
  __git_log_pretty_formats="oneline short medium full fuller email raw format:"
@@@ -1464,7 -1467,6 +1464,7 @@@ _git_log (
                        --abbrev-commit --abbrev=
                        --relative-date --date=
                        --pretty= --format= --oneline
 +                      --show-signature
                        --cherry-pick
                        --graph
                        --decorate --decorate=
@@@ -1693,7 -1695,6 +1693,7 @@@ _git_rebase (
                        --committer-date-is-author-date --ignore-date
                        --ignore-whitespace --whitespace=
                        --autosquash --fork-point --no-fork-point
 +                      --autostash
                        "
  
                return
@@@ -1876,10 -1877,6 +1876,10 @@@ _git_config (
                __gitcomp "$__git_send_email_suppresscc_options"
                return
                ;;
 +      sendemail.transferencoding)
 +              __gitcomp "7bit 8bit quoted-printable base64"
 +              return
 +              ;;
        --get|--get-all|--unset|--unset-all)
                __gitcomp_nl "$(__git_config_get_set_variables)"
                return
@@@ -2347,7 -2344,6 +2347,7 @@@ _git_show (
                ;;
        --*)
                __gitcomp "--pretty= --format= --abbrev-commit --oneline
 +                      --show-signature
                        $__git_diff_common_options
                        "
                return
@@@ -2553,16 -2549,6 +2553,16 @@@ _git_tag (
                __gitcomp_nl "$(__git_refs)"
                ;;
        esac
 +
 +      case "$cur" in
 +      --*)
 +              __gitcomp "
 +                      --list --delete --verify --annotate --message --file
 +                      --sign --cleanup --local-user --force --column --sort
 +                      --contains --points-at
 +                      "
 +              ;;
 +      esac
  }
  
  _git_whatchanged ()
diff --combined revision.c
index 86406a26a2d4599ea401d1faa9a817cadb3b7bcd,84b33a33ca1100e39220f68b637cfebdab640a60..4bc851c070e40fbdc7aedf5edc9db78f808f328e
@@@ -17,7 -17,6 +17,7 @@@
  #include "mailmap.h"
  #include "commit-slab.h"
  #include "dir.h"
 +#include "cache-tree.h"
  
  volatile show_early_output_fn_t show_early_output;
  
@@@ -87,6 -86,16 +87,6 @@@ void show_object_with_name(FILE *out, s
        fputc('\n', out);
  }
  
 -void add_object(struct object *obj,
 -              struct object_array *p,
 -              struct name_path *path,
 -              const char *name)
 -{
 -      char *pn = path_name(path, name);
 -      add_object_array(obj, pn, p);
 -      free(pn);
 -}
 -
  static void mark_blob_uninteresting(struct blob *blob)
  {
        if (!blob)
@@@ -189,10 -198,9 +189,10 @@@ void mark_parents_uninteresting(struct 
        }
  }
  
 -static void add_pending_object_with_mode(struct rev_info *revs,
 +static void add_pending_object_with_path(struct rev_info *revs,
                                         struct object *obj,
 -                                       const char *name, unsigned mode)
 +                                       const char *name, unsigned mode,
 +                                       const char *path)
  {
        if (!obj)
                return;
                if (st)
                        return;
        }
 -      add_object_array_with_mode(obj, name, &revs->pending, mode);
 +      add_object_array_with_path(obj, name, &revs->pending, mode, path);
 +}
 +
 +static void add_pending_object_with_mode(struct rev_info *revs,
 +                                       struct object *obj,
 +                                       const char *name, unsigned mode)
 +{
 +      add_pending_object_with_path(revs, obj, name, mode, NULL);
  }
  
  void add_pending_object(struct rev_info *revs,
@@@ -264,12 -265,8 +264,12 @@@ void add_pending_sha1(struct rev_info *
  }
  
  static struct commit *handle_commit(struct rev_info *revs,
 -                                  struct object *object, const char *name)
 +                                  struct object_array_entry *entry)
  {
 +      struct object *object = entry->item;
 +      const char *name = entry->name;
 +      const char *path = entry->path;
 +      unsigned int mode = entry->mode;
        unsigned long flags = object->flags;
  
        /*
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
                }
                object->flags |= flags;
 +              /*
 +               * We'll handle the tagged object by looping or dropping
 +               * through to the non-tag handlers below. Do not
 +               * propagate data from the tag's pending entry.
 +               */
 +              name = "";
 +              path = NULL;
 +              mode = 0;
        }
  
        /*
                        revs->limited = 1;
                }
                if (revs->show_source && !commit->util)
 -                      commit->util = (void *) name;
 +                      commit->util = xstrdup(name);
                return commit;
        }
  
                        mark_tree_contents_uninteresting(tree);
                        return NULL;
                }
 -              add_pending_object(revs, object, "");
 +              add_pending_object_with_path(revs, object, name, mode, path);
                return NULL;
        }
  
                        return NULL;
                if (flags & UNINTERESTING)
                        return NULL;
 -              add_pending_object(revs, object, "");
 +              add_pending_object_with_path(revs, object, name, mode, path);
                return NULL;
        }
        die("%s is unknown object", name);
@@@ -484,7 -473,7 +484,7 @@@ static int rev_compare_tree(struct rev_
                 * If we are simplifying by decoration, then the commit
                 * is worth showing if it has a tag pointing at it.
                 */
 -              if (lookup_decoration(&name_decoration, &commit->object))
 +              if (get_name_decoration(&commit->object))
                        return REV_TREE_DIFFERENT;
                /*
                 * A commit that is not pointed by a tag is uninteresting
@@@ -1286,7 -1275,7 +1286,7 @@@ static int handle_one_reflog(const cha
        return 0;
  }
  
 -static void handle_reflog(struct rev_info *revs, unsigned flags)
 +void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
  {
        struct all_refs_cb cb;
        cb.all_revs = revs;
        for_each_reflog(handle_one_reflog, &cb);
  }
  
 +static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
 +                         struct strbuf *path)
 +{
 +      size_t baselen = path->len;
 +      int i;
 +
 +      if (it->entry_count >= 0) {
 +              struct tree *tree = lookup_tree(it->sha1);
 +              add_pending_object_with_path(revs, &tree->object, "",
 +                                           040000, path->buf);
 +      }
 +
 +      for (i = 0; i < it->subtree_nr; i++) {
 +              struct cache_tree_sub *sub = it->down[i];
 +              strbuf_addf(path, "%s%s", baselen ? "/" : "", sub->name);
 +              add_cache_tree(sub->cache_tree, revs, path);
 +              strbuf_setlen(path, baselen);
 +      }
 +
 +}
 +
 +void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
 +{
 +      int i;
 +
 +      read_cache();
 +      for (i = 0; i < active_nr; i++) {
 +              struct cache_entry *ce = active_cache[i];
 +              struct blob *blob;
 +
 +              if (S_ISGITLINK(ce->ce_mode))
 +                      continue;
 +
 +              blob = lookup_blob(ce->sha1);
 +              if (!blob)
 +                      die("unable to add index blob to traversal");
 +              add_pending_object_with_path(revs, &blob->object, "",
 +                                           ce->ce_mode, ce->name);
 +      }
 +
 +      if (active_cache_tree) {
 +              struct strbuf path = STRBUF_INIT;
 +              add_cache_tree(active_cache_tree, revs, &path);
 +              strbuf_release(&path);
 +      }
 +}
 +
  static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
  {
        unsigned char sha1[20];
@@@ -1441,7 -1383,7 +1441,7 @@@ static void prepare_show_merge(struct r
        other = lookup_commit_or_die(sha1, "MERGE_HEAD");
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
 -      bases = get_merge_bases(head, other, 1);
 +      bases = get_merge_bases(head, other);
        add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
        add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
        free_commit_list(bases);
                        continue;
                if (ce_path_match(ce, &revs->prune_data, NULL)) {
                        prune_num++;
 -                      prune = xrealloc(prune, sizeof(*prune) * prune_num);
 +                      REALLOC_ARRAY(prune, prune_num);
                        prune[prune_num-2] = ce->name;
                        prune[prune_num-1] = NULL;
                }
@@@ -1546,7 -1488,7 +1546,7 @@@ int handle_revision_arg(const char *arg
                                     : lookup_commit_reference(b_obj->sha1));
                                if (!a || !b)
                                        goto missing;
 -                              exclude = get_merge_bases(a, b, 1);
 +                              exclude = get_merge_bases(a, b);
                                add_rev_cmdline_list(revs, exclude,
                                                     REV_CMD_MERGE_BASE,
                                                     flags_exclude);
@@@ -1691,7 -1633,6 +1691,7 @@@ static int handle_revision_opt(struct r
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
            !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
 +          !strcmp(arg, "--indexed-objects") ||
            starts_with(arg, "--exclude=") ||
            starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
            starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
                revs->tree_objects = 1;
                revs->blob_objects = 1;
                revs->edge_hint = 1;
 +      } else if (!strcmp(arg, "--objects-edge-aggressive")) {
 +              revs->tag_objects = 1;
 +              revs->tree_objects = 1;
 +              revs->blob_objects = 1;
 +              revs->edge_hint = 1;
 +              revs->edge_hint_aggressive = 1;
        } else if (!strcmp(arg, "--verify-objects")) {
                revs->tag_objects = 1;
                revs->tree_objects = 1;
                grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
+       } else if (!strcmp(arg, "--invert-grep")) {
+               revs->invert_grep = 1;
        } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
                if (strcmp(optarg, "none"))
                        git_log_output_encoding = xstrdup(optarg);
@@@ -2126,9 -2063,7 +2128,9 @@@ static int handle_revision_pseudo_opt(c
                for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
 -              handle_reflog(revs, *flags);
 +              add_reflogs_to_pending(revs, *flags);
 +      } else if (!strcmp(arg, "--indexed-objects")) {
 +              add_index_objects_to_pending(revs, *flags);
        } else if (!strcmp(arg, "--not")) {
                *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
@@@ -2723,26 -2658,26 +2725,26 @@@ void reset_revision_walk(void
  
  int prepare_revision_walk(struct rev_info *revs)
  {
 -      int nr = revs->pending.nr;
 -      struct object_array_entry *e, *list;
 +      int i;
 +      struct object_array old_pending;
        struct commit_list **next = &revs->commits;
  
 -      e = list = revs->pending.objects;
 +      memcpy(&old_pending, &revs->pending, sizeof(old_pending));
        revs->pending.nr = 0;
        revs->pending.alloc = 0;
        revs->pending.objects = NULL;
 -      while (--nr >= 0) {
 -              struct commit *commit = handle_commit(revs, e->item, e->name);
 +      for (i = 0; i < old_pending.nr; i++) {
 +              struct object_array_entry *e = old_pending.objects + i;
 +              struct commit *commit = handle_commit(revs, e);
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
                                next = commit_list_append(commit, next);
                        }
                }
 -              e++;
        }
        if (!revs->leak_pending)
 -              free(list);
 +              object_array_clear(&old_pending);
  
        /* Signal whether we need per-parent treesame decoration */
        if (revs->simplify_merges ||
@@@ -2915,7 -2850,7 +2917,7 @@@ static int commit_match(struct commit *
                                     (char *)message, strlen(message));
        strbuf_release(&buf);
        unuse_commit_buffer(commit, message);
-       return retval;
+       return opt->invert_grep ? !retval : retval;
  }
  
  static inline int want_ancestry(const struct rev_info *revs)
diff --combined revision.h
index 033a24460e71b723f95ef91f4b6f0dbfd8c2c390,b0b82e766eba38e4881757e841229e5d8b1c3744..17ebafc4c76de4ac74f5b977101af1aedd1a1ae9
@@@ -93,7 -93,6 +93,7 @@@ struct rev_info 
                        blob_objects:1,
                        verify_objects:1,
                        edge_hint:1,
 +                      edge_hint_aggressive:1,
                        limited:1,
                        unpacked:1,
                        boundary:2,
  
        /* 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;
@@@ -265,6 -266,11 +267,6 @@@ char *path_name(const struct name_path 
  extern void show_object_with_name(FILE *, struct object *,
                                  const struct name_path *, const char *);
  
 -extern void add_object(struct object *obj,
 -                     struct object_array *p,
 -                     struct name_path *path,
 -                     const char *name);
 -
  extern void add_pending_object(struct rev_info *revs,
                               struct object *obj, const char *name);
  extern void add_pending_sha1(struct rev_info *revs,
                             unsigned int flags);
  
  extern void add_head_to_pending(struct rev_info *);
 +extern void add_reflogs_to_pending(struct rev_info *, unsigned int flags);
 +extern void add_index_objects_to_pending(struct rev_info *, unsigned int flags);
  
  enum commit_action {
        commit_ignore,