Code

Merge branch 'jc/maint-1.6.6-pathspec-stdin-and-cmdline'
authorJunio C Hamano <gitster@pobox.com>
Wed, 11 May 2011 21:57:45 +0000 (14:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 11 May 2011 21:57:45 +0000 (14:57 -0700)
Update the fix for 1.7.4 maintenance track.

* jc/maint-1.6.6-pathspec-stdin-and-cmdline:
  setup_revisions(): take pathspec from command line and --stdin correctly

1  2 
revision.c

diff --combined revision.c
index 7b9eaefae4ed03e994c2122453144b3c09591b9c,047e0170056e236e4d096f45de4a56cc461308c6..073173eb9c48ec08a8fb30adf6bbd8b6ce54b1cc
@@@ -12,7 -12,6 +12,7 @@@
  #include "patch-ids.h"
  #include "decorate.h"
  #include "log-tree.h"
 +#include "string-list.h"
  
  volatile show_early_output_fn_t show_early_output;
  
@@@ -135,20 -134,10 +135,20 @@@ static void add_pending_object_with_mod
  {
        if (revs->no_walk && (obj->flags & UNINTERESTING))
                revs->no_walk = 0;
 -      if (revs->reflog_info && obj->type == OBJ_COMMIT &&
 -                      add_reflog_for_walk(revs->reflog_info,
 -                              (struct commit *)obj, name))
 -              return;
 +      if (revs->reflog_info && obj->type == OBJ_COMMIT) {
 +              struct strbuf buf = STRBUF_INIT;
 +              int len = interpret_branch_name(name, &buf);
 +              int st;
 +
 +              if (0 < len && name[len] && buf.len)
 +                      strbuf_addstr(&buf, name + len);
 +              st = add_reflog_for_walk(revs->reflog_info,
 +                                       (struct commit *)obj,
 +                                       buf.buf[0] ? buf.buf: name);
 +              strbuf_release(&buf);
 +              if (st)
 +                      return;
 +      }
        add_object_array_with_mode(obj, name, &revs->pending, mode);
  }
  
@@@ -279,7 -268,7 +279,7 @@@ static int tree_difference = REV_TREE_S
  static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
 -                  const char *fullpath)
 +                  const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
  
@@@ -292,8 -281,7 +292,8 @@@ static void file_change(struct diff_opt
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
 -               const char *fullpath)
 +               const char *fullpath,
 +               unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
        tree_difference = REV_TREE_DIFFERENT;
        DIFF_OPT_SET(options, HAS_CHANGES);
@@@ -444,15 -432,15 +444,15 @@@ static void try_to_simplify_commit(stru
        commit->object.flags |= TREESAME;
  }
  
 -static void insert_by_date_cached(struct commit *p, struct commit_list **head,
 +static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
                    struct commit_list *cached_base, struct commit_list **cache)
  {
        struct commit_list *new_entry;
  
        if (cached_base && p->date < cached_base->item->date)
 -              new_entry = insert_by_date(p, &cached_base->next);
 +              new_entry = commit_list_insert_by_date(p, &cached_base->next);
        else
 -              new_entry = insert_by_date(p, head);
 +              new_entry = commit_list_insert_by_date(p, head);
  
        if (cache && (!*cache || p->date < (*cache)->item->date))
                *cache = new_entry;
@@@ -494,7 -482,7 +494,7 @@@ static int add_parents_to_list(struct r
                        if (p->object.flags & SEEN)
                                continue;
                        p->object.flags |= SEEN;
 -                      insert_by_date_cached(p, list, cached_base, cache_ptr);
 +                      commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                return 0;
        }
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
 -                      insert_by_date_cached(p, list, cached_base, cache_ptr);
 +                      commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                if (revs->first_parent_only)
                        break;
@@@ -548,9 -536,6 +548,9 @@@ static void cherry_pick_list(struct com
                        right_count++;
        }
  
 +      if (!left_count || !right_count)
 +              return;
 +
        left_first = left_count < right_count;
        init_patch_ids(&ids);
        if (revs->diffopt.nr_paths) {
@@@ -646,93 -631,6 +646,93 @@@ static int still_interesting(struct com
        return slop-1;
  }
  
 +/*
 + * "rev-list --ancestry-path A..B" computes commits that are ancestors
 + * of B but not ancestors of A but further limits the result to those
 + * that are descendants of A.  This takes the list of bottom commits and
 + * the result of "A..B" without --ancestry-path, and limits the latter
 + * further to the ones that can reach one of the commits in "bottom".
 + */
 +static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
 +{
 +      struct commit_list *p;
 +      struct commit_list *rlist = NULL;
 +      int made_progress;
 +
 +      /*
 +       * Reverse the list so that it will be likely that we would
 +       * process parents before children.
 +       */
 +      for (p = list; p; p = p->next)
 +              commit_list_insert(p->item, &rlist);
 +
 +      for (p = bottom; p; p = p->next)
 +              p->item->object.flags |= TMP_MARK;
 +
 +      /*
 +       * Mark the ones that can reach bottom commits in "list",
 +       * in a bottom-up fashion.
 +       */
 +      do {
 +              made_progress = 0;
 +              for (p = rlist; p; p = p->next) {
 +                      struct commit *c = p->item;
 +                      struct commit_list *parents;
 +                      if (c->object.flags & (TMP_MARK | UNINTERESTING))
 +                              continue;
 +                      for (parents = c->parents;
 +                           parents;
 +                           parents = parents->next) {
 +                              if (!(parents->item->object.flags & TMP_MARK))
 +                                      continue;
 +                              c->object.flags |= TMP_MARK;
 +                              made_progress = 1;
 +                              break;
 +                      }
 +              }
 +      } while (made_progress);
 +
 +      /*
 +       * NEEDSWORK: decide if we want to remove parents that are
 +       * not marked with TMP_MARK from commit->parents for commits
 +       * in the resulting list.  We may not want to do that, though.
 +       */
 +
 +      /*
 +       * The ones that are not marked with TMP_MARK are uninteresting
 +       */
 +      for (p = list; p; p = p->next) {
 +              struct commit *c = p->item;
 +              if (c->object.flags & TMP_MARK)
 +                      continue;
 +              c->object.flags |= UNINTERESTING;
 +      }
 +
 +      /* We are done with the TMP_MARK */
 +      for (p = list; p; p = p->next)
 +              p->item->object.flags &= ~TMP_MARK;
 +      for (p = bottom; p; p = p->next)
 +              p->item->object.flags &= ~TMP_MARK;
 +      free_commit_list(rlist);
 +}
 +
 +/*
 + * Before walking the history, keep the set of "negative" refs the
 + * caller has asked to exclude.
 + *
 + * This is used to compute "rev-list --ancestry-path A..B", as we need
 + * to filter the result of "A..B" further to the ones that can actually
 + * reach A.
 + */
 +static struct commit_list *collect_bottom_commits(struct commit_list *list)
 +{
 +      struct commit_list *elem, *bottom = NULL;
 +      for (elem = list; elem; elem = elem->next)
 +              if (elem->item->object.flags & UNINTERESTING)
 +                      commit_list_insert(elem->item, &bottom);
 +      return bottom;
 +}
 +
  static int limit_list(struct rev_info *revs)
  {
        int slop = SLOP;
        struct commit_list *list = revs->commits;
        struct commit_list *newlist = NULL;
        struct commit_list **p = &newlist;
 +      struct commit_list *bottom = NULL;
 +
 +      if (revs->ancestry_path) {
 +              bottom = collect_bottom_commits(list);
 +              if (!bottom)
 +                      die("--ancestry-path given but there are no bottom commits");
 +      }
  
        while (list) {
                struct commit_list *entry = list;
        if (revs->cherry_pick)
                cherry_pick_list(newlist, revs);
  
 +      if (bottom) {
 +              limit_to_ancestry(bottom, newlist);
 +              free_commit_list(bottom);
 +      }
 +
        revs->commits = newlist;
        return 0;
  }
@@@ -813,19 -699,13 +813,19 @@@ static int handle_one_ref(const char *p
        return 0;
  }
  
 -static void handle_refs(struct rev_info *revs, unsigned flags,
 -              int (*for_each)(each_ref_fn, void *))
 +static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
 +      unsigned flags)
 +{
 +      cb->all_revs = revs;
 +      cb->all_flags = flags;
 +}
 +
 +static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
 +              int (*for_each)(const char *, each_ref_fn, void *))
  {
        struct all_refs_cb cb;
 -      cb.all_revs = revs;
 -      cb.all_flags = flags;
 -      for_each(handle_one_ref, &cb);
 +      init_all_refs_cb(&cb, revs, flags);
 +      for_each(submodule, handle_one_ref, &cb);
  }
  
  static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@@ -911,7 -791,7 +911,7 @@@ void init_revisions(struct rev_info *re
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
        DIFF_OPT_SET(&revs->pruning, RECURSIVE);
 -      DIFF_OPT_SET(&revs->pruning, QUIET);
 +      DIFF_OPT_SET(&revs->pruning, QUICK);
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
        revs->lifo = 1;
  
        revs->grep_filter.status_only = 1;
        revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
 +      revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
        revs->grep_filter.regflags = REG_NEWLINE;
  
        diff_setup(&revs->diffopt);
@@@ -1074,35 -953,34 +1074,34 @@@ int handle_revision_arg(const char *arg
        return 0;
  }
  
- static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb, const char ***prune_data)
- {
-       const char **prune = *prune_data;
-       int prune_nr;
      int prune_alloc;
+ struct cmdline_pathspec {
+       int alloc;
+       int nr;
+       const char **path;
};
  
-       /* count existing ones */
-       if (!prune)
-               prune_nr = 0;
-       else
-               for (prune_nr = 0; prune[prune_nr]; prune_nr++)
-                       ;
-       prune_alloc = prune_nr; /* not really, but we do not know */
+ static void append_prune_data(struct cmdline_pathspec *prune, const char **av)
+ {
+       while (*av) {
+               ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
+               prune->path[prune->nr++] = *(av++);
+       }
+ }
  
+ static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
+                                    struct cmdline_pathspec *prune)
+ {
        while (strbuf_getwholeline(sb, stdin, '\n') != EOF) {
                int len = sb->len;
                if (len && sb->buf[len - 1] == '\n')
                        sb->buf[--len] = '\0';
-               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
-               prune[prune_nr++] = xstrdup(sb->buf);
+               ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
+               prune->path[prune->nr++] = xstrdup(sb->buf);
        }
-       if (prune) {
-               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
-               prune[prune_nr] = NULL;
-       }
-       *prune_data = prune;
  }
  
- static void read_revisions_from_stdin(struct rev_info *revs, const char ***prune)
+ static void read_revisions_from_stdin(struct rev_info *revs,
+                                     struct cmdline_pathspec *prune)
  {
        struct strbuf sb;
        int seen_dashdash = 0;
@@@ -1148,8 -1026,6 +1147,8 @@@ static int handle_revision_opt(struct r
                               int *unkc, const char **unkv)
  {
        const char *arg = argv[0];
 +      const char *optarg;
 +      int argcount;
  
        /* pseudo revision arguments */
        if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
                return 1;
        }
  
 -      if (!prefixcmp(arg, "--max-count=")) {
 -              revs->max_count = atoi(arg + 12);
 -      } else if (!prefixcmp(arg, "--skip=")) {
 -              revs->skip_count = atoi(arg + 7);
 +      if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
 +              revs->max_count = atoi(optarg);
 +              revs->no_walk = 0;
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
 +              revs->skip_count = atoi(optarg);
 +              return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
        /* accept -<digit>, like traditional "head" */
                revs->max_count = atoi(arg + 1);
 +              revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
                        return error("-n requires an argument");
                revs->max_count = atoi(argv[1]);
 +              revs->no_walk = 0;
                return 2;
        } else if (!prefixcmp(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
 -      } else if (!prefixcmp(arg, "--max-age=")) {
 -              revs->max_age = atoi(arg + 10);
 -      } else if (!prefixcmp(arg, "--since=")) {
 -              revs->max_age = approxidate(arg + 8);
 -      } else if (!prefixcmp(arg, "--after=")) {
 -              revs->max_age = approxidate(arg + 8);
 -      } else if (!prefixcmp(arg, "--min-age=")) {
 -              revs->min_age = atoi(arg + 10);
 -      } else if (!prefixcmp(arg, "--before=")) {
 -              revs->min_age = approxidate(arg + 9);
 -      } else if (!prefixcmp(arg, "--until=")) {
 -              revs->min_age = approxidate(arg + 8);
 +              revs->no_walk = 0;
 +      } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
 +              revs->max_age = atoi(optarg);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
 +              revs->max_age = approxidate(optarg);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("after", argv, &optarg))) {
 +              revs->max_age = approxidate(optarg);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
 +              revs->min_age = atoi(optarg);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
 +              revs->min_age = approxidate(optarg);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("until", argv, &optarg))) {
 +              revs->min_age = approxidate(optarg);
 +              return argcount;
        } else if (!strcmp(arg, "--first-parent")) {
                revs->first_parent_only = 1;
 +      } else if (!strcmp(arg, "--ancestry-path")) {
 +              revs->ancestry_path = 1;
 +              revs->simplify_history = 0;
 +              revs->limited = 1;
        } else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
                init_reflog_walk(&revs->reflog_info);
        } else if (!strcmp(arg, "--default")) {
                revs->boundary = 1;
        } else if (!strcmp(arg, "--left-right")) {
                revs->left_right = 1;
 +      } else if (!strcmp(arg, "--count")) {
 +              revs->count = 1;
        } else if (!strcmp(arg, "--cherry-pick")) {
                revs->cherry_pick = 1;
                revs->limited = 1;
                revs->verbose_header = 1;
        } else if (!strcmp(arg, "--pretty")) {
                revs->verbose_header = 1;
 +              revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
        } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
 +              /*
 +               * Detached form ("--pretty X" as opposed to "--pretty=X")
 +               * not allowed, since the argument is optional.
 +               */
                revs->verbose_header = 1;
 +              revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
 +      } else if (!strcmp(arg, "--show-notes")) {
 +              revs->show_notes = 1;
 +              revs->show_notes_given = 1;
 +      } else if (!prefixcmp(arg, "--show-notes=")) {
 +              struct strbuf buf = STRBUF_INIT;
 +              revs->show_notes = 1;
 +              revs->show_notes_given = 1;
 +              if (!revs->notes_opt.extra_notes_refs)
 +                      revs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));
 +              if (!prefixcmp(arg+13, "refs/"))
 +                      /* happy */;
 +              else if (!prefixcmp(arg+13, "notes/"))
 +                      strbuf_addstr(&buf, "refs/");
 +              else
 +                      strbuf_addstr(&buf, "refs/notes/");
 +              strbuf_addstr(&buf, arg+13);
 +              string_list_append(revs->notes_opt.extra_notes_refs,
 +                                 strbuf_detach(&buf, NULL));
 +      } else if (!strcmp(arg, "--no-notes")) {
 +              revs->show_notes = 0;
 +              revs->show_notes_given = 1;
 +      } else if (!strcmp(arg, "--standard-notes")) {
 +              revs->show_notes_given = 1;
 +              revs->notes_opt.suppress_default_notes = 0;
 +      } else if (!strcmp(arg, "--no-standard-notes")) {
 +              revs->notes_opt.suppress_default_notes = 1;
        } else if (!strcmp(arg, "--oneline")) {
                revs->verbose_header = 1;
                get_commit_format("oneline", revs);
 +              revs->pretty_given = 1;
                revs->abbrev_commit = 1;
        } else if (!strcmp(arg, "--graph")) {
                revs->topo_order = 1;
        } else if (!strcmp(arg, "--relative-date")) {
                revs->date_mode = DATE_RELATIVE;
                revs->date_mode_explicit = 1;
 -      } else if (!strncmp(arg, "--date=", 7)) {
 -              revs->date_mode = parse_date_format(arg + 7);
 +      } else if ((argcount = parse_long_opt("date", argv, &optarg))) {
 +              revs->date_mode = parse_date_format(optarg);
                revs->date_mode_explicit = 1;
 +              return argcount;
        } else if (!strcmp(arg, "--log-size")) {
                revs->show_log_size = 1;
        }
        /*
         * Grepping the commit log
         */
 -      else if (!prefixcmp(arg, "--author=")) {
 -              add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
 -      } else if (!prefixcmp(arg, "--committer=")) {
 -              add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
 -      } else if (!prefixcmp(arg, "--grep=")) {
 -              add_message_grep(revs, arg+7);
 +      else if ((argcount = parse_long_opt("author", argv, &optarg))) {
 +              add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
 +              add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
 +              add_message_grep(revs, optarg);
 +              return argcount;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
 -      } else if (!prefixcmp(arg, "--encoding=")) {
 -              arg += 11;
 -              if (strcmp(arg, "none"))
 -                      git_log_output_encoding = xstrdup(arg);
 +      } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
 +              if (strcmp(optarg, "none"))
 +                      git_log_output_encoding = xstrdup(optarg);
                else
                        git_log_output_encoding = "";
 +              return argcount;
        } else if (!strcmp(arg, "--reverse")) {
                revs->reverse ^= 1;
        } else if (!strcmp(arg, "--children")) {
@@@ -1435,44 -1256,16 +1434,16 @@@ void parse_revision_opt(struct rev_inf
        ctx->argc -= n;
  }
  
 -static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data)
 +static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
  {
 -      return for_each_ref_in("refs/bisect/bad", fn, cb_data);
 +      return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
  }
  
 -static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data)
 +static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
  {
 -      return for_each_ref_in("refs/bisect/good", fn, cb_data);
 +      return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
  }
  
- static void append_prune_data(const char ***prune_data, const char **av)
- {
-       const char **prune = *prune_data;
-       int prune_nr;
-       int prune_alloc;
-       if (!prune) {
-               *prune_data = av;
-               return;
-       }
-       /* count existing ones */
-       for (prune_nr = 0; prune[prune_nr]; prune_nr++)
-               ;
-       prune_alloc = prune_nr; /* not really, but we do not know */
-       while (*av) {
-               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
-               prune[prune_nr++] = *av;
-               av++;
-       }
-       if (prune) {
-               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
-               prune[prune_nr] = NULL;
-       }
-       *prune_data = prune;
- }
  /*
   * Parse revision information, filling in the "rev_info" structure,
   * and removing the used arguments from the argument list.
   * Returns the number of arguments left that weren't recognized
   * (which are also moved to the head of the argument list)
   */
 -int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
 +int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
  {
 -      int i, flags, left, seen_dashdash, read_from_stdin;
 +      int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
-       const char **prune_data = NULL;
+       struct cmdline_pathspec prune_data;
 +      const char *submodule = NULL;
 +      const char *optarg;
 +      int argcount;
  
+       memset(&prune_data, 0, sizeof(prune_data));
 +      if (opt)
 +              submodule = opt->submodule;
 +
        /* First, search for "--" */
        seen_dashdash = 0;
        for (i = 1; i < argc; i++) {
                argv[i] = NULL;
                argc = i;
                if (argv[i + 1])
-                       prune_data = argv + i + 1;
+                       append_prune_data(&prune_data, argv + i + 1);
                seen_dashdash = 1;
                break;
        }
                        int opts;
  
                        if (!strcmp(arg, "--all")) {
 -                              handle_refs(revs, flags, for_each_ref);
 -                              handle_refs(revs, flags, head_ref);
 +                              handle_refs(submodule, revs, flags, for_each_ref_submodule);
 +                              handle_refs(submodule, revs, flags, head_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
 -                              handle_refs(revs, flags, for_each_branch_ref);
 +                              handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
 -                              handle_refs(revs, flags, for_each_bad_bisect_ref);
 -                              handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
 +                              handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
 +                              handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
                                revs->bisect = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
 -                              handle_refs(revs, flags, for_each_tag_ref);
 +                              handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
 -                              handle_refs(revs, flags, for_each_remote_ref);
 +                              handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
 +                              continue;
 +                      }
 +                      if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
 +                              struct all_refs_cb cb;
 +                              i += argcount - 1;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref(handle_one_ref, optarg, &cb);
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--branches=")) {
 +                              struct all_refs_cb cb;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--tags=")) {
 +                              struct all_refs_cb cb;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--remotes=")) {
 +                              struct all_refs_cb cb;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
                                continue;
                        }
                        if (!strcmp(arg, "--reflog")) {
                        append_prune_data(&prune_data, argv + i);
                        break;
                }
 +              else
 +                      got_rev_arg = 1;
        }
  
-       if (prune_data)
-               revs->prune_data = get_pathspec(revs->prefix, prune_data);
+       if (prune_data.nr) {
+               ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
+               prune_data.path[prune_data.nr++] = NULL;
+               revs->prune_data = get_pathspec(revs->prefix, prune_data.path);
+       }
  
        if (revs->def == NULL)
 -              revs->def = def;
 +              revs->def = opt ? opt->def : NULL;
 +      if (opt && opt->tweak)
 +              opt->tweak(revs, opt);
        if (revs->show_merge)
                prepare_show_merge(revs);
 -      if (revs->def && !revs->pending.nr) {
 +      if (revs->def && !revs->pending.nr && !got_rev_arg) {
                unsigned char sha1[20];
                struct object *object;
                unsigned mode;
                if (!revs->full_diff)
                        diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
        }
 -      if (revs->combine_merges) {
 +      if (revs->combine_merges)
                revs->ignore_merges = 0;
 -              if (revs->dense_combined_merges && !revs->diffopt.output_format)
 -                      revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 -      }
        revs->diffopt.abbrev = revs->abbrev;
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
@@@ -1891,7 -1657,7 +1867,7 @@@ int prepare_revision_walk(struct rev_in
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
 -                              insert_by_date(commit, &revs->commits);
 +                              commit_list_insert_by_date(commit, &revs->commits);
                        }
                }
                e++;
  enum rewrite_result {
        rewrite_one_ok,
        rewrite_one_noparents,
 -      rewrite_one_error,
 +      rewrite_one_error
  };
  
  static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
@@@ -1961,7 -1727,7 +1937,7 @@@ static int rewrite_parents(struct rev_i
  
  static int commit_match(struct commit *commit, struct rev_info *opt)
  {
 -      if (!opt->grep_filter.pattern_list)
 +      if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
                return 1;
        return grep_buffer(&opt->grep_filter,
                           NULL, /* we say nothing, not even filename */
@@@ -2030,10 -1796,8 +2006,10 @@@ static struct commit *get_revision_1(st
                revs->commits = entry->next;
                free(entry);
  
 -              if (revs->reflog_info)
 +              if (revs->reflog_info) {
                        fake_reflog_parent(revs->reflog_info, commit);
 +                      commit->object.flags &= ~(ADDED | SEEN | SHOWN);
 +              }
  
                /*
                 * If we haven't done the list limiting, we need to look at