Merge branch 'kd/rev-list-bisect-first-parent'
authorJunio C Hamano <gitster@pobox.com>
Wed, 25 Mar 2015 19:54:21 +0000 (12:54 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 25 Mar 2015 19:54:21 +0000 (12:54 -0700)
"git rev-list --bisect --first-parent" does not work (yet) and can
even cause SEGV; forbid it. "git log --bisect --first-parent"
would not be useful until "git bisect --first-parent" materializes,
so it is also forbidden for now.

* kd/rev-list-bisect-first-parent:
rev-list: refuse --first-parent combined with --bisect

1  2 
Documentation/rev-list-options.txt
revision.c
t/t6000-rev-list-misc.sh
index 4ed8587c846d78a03e154ec6a4716d78882840b5,9f78fca3f6e7133896a01bc4f535f456ec94d0c5..e2de7891cb37cb38605dab2e0b904d38701c37f5
@@@ -66,10 -66,6 +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
@@@ -123,7 -119,8 +123,8 @@@ parents) and `--max-parents=-1` (negati
        because merges into a topic branch tend to be only about
        adjusting to updated upstream from time to time, and
        this option allows you to ignore the individual commits
-       brought in to your history by such a merge.
+       brought in to your history by such a merge. Cannot be
+       combined with --bisect.
  
  --not::
        Reverses the meaning of the '{caret}' prefix (or lack thereof)
        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`,
@@@ -172,10 -169,6 +173,10 @@@ 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>`.
 +
  --ignore-missing::
        Upon seeing an invalid object name in the input, pretend as if
        the bad input was not given.
@@@ -185,7 -178,7 +186,7 @@@ ifndef::git-rev-list[
        Pretend as if the bad bisection ref `refs/bisect/bad`
        was listed and as if it was followed by `--not` and the good
        bisection refs `refs/bisect/good-*` on the command
-       line.
+       line. Cannot be combined with --first-parent.
  endif::git-rev-list[]
  
  --stdin::
@@@ -566,7 -559,7 +567,7 @@@ outputs 'midpoint', the output of the t
  would be of roughly the same length.  Finding the change which
  introduces a regression is thus reduced to a binary search: repeatedly
  generate and test new 'midpoint's until the commit chain is of length
- one.
+ one. Cannot be combined with --first-parent.
  
  --bisect-vars::
        This calculates the same as `--bisect`, except that refs in
@@@ -643,7 -636,6 +644,7 @@@ Object Traversa
  
  These options are mostly targeted for packing of Git repositories.
  
 +ifdef::git-rev-list[]
  --objects::
        Print the object IDs of any object referenced by the listed
        commits.  `--objects foo ^bar` thus means ``send me
  --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.
 +
 +--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.
 +
  --unpacked::
        Only useful with `--objects`; print the object IDs that are not
        in packs.
 +endif::git-rev-list[]
  
  --no-walk[=(sorted|unsorted)]::
        Only show the given commits, but do not traverse their ancestors.
@@@ -697,7 -678,7 +698,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.
@@@ -707,16 -688,7 +708,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.
diff --combined revision.c
index 66520c671ee1141b1c0edb7ab776dd9d9df04726,161cf73b108748f15fee83878ff1953c31ef26cc..ed3f6e967b32a4b489cd32c7f9f01b732250ae3b
@@@ -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;
        } else if (!strcmp(arg, "--pretty")) {
                revs->verbose_header = 1;
                revs->pretty_given = 1;
 -              get_commit_format(arg+8, revs);
 +              get_commit_format(NULL, revs);
        } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) {
                /*
                 * Detached form ("--pretty X" as opposed to "--pretty=X")
                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);
@@@ -2128,9 -2061,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")) {
@@@ -2342,6 -2273,9 +2342,9 @@@ int setup_revisions(int argc, const cha
        if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
                die("cannot use --grep-reflog without --walk-reflogs");
  
+       if (revs->first_parent_only && revs->bisect)
+               die(_("--first-parent is incompatible with --bisect"));
        return left;
  }
  
@@@ -2725,26 -2659,26 +2728,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 ||
@@@ -2917,7 -2851,7 +2920,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)
@@@ -2970,61 -2904,6 +2973,61 @@@ enum commit_action get_commit_action(st
        return commit_show;
  }
  
 +define_commit_slab(saved_parents, struct commit_list *);
 +
 +#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
 +
 +/*
 + * You may only call save_parents() once per commit (this is checked
 + * for non-root commits).
 + */
 +static void save_parents(struct rev_info *revs, struct commit *commit)
 +{
 +      struct commit_list **pp;
 +
 +      if (!revs->saved_parents_slab) {
 +              revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents));
 +              init_saved_parents(revs->saved_parents_slab);
 +      }
 +
 +      pp = saved_parents_at(revs->saved_parents_slab, commit);
 +
 +      /*
 +       * When walking with reflogs, we may visit the same commit
 +       * several times: once for each appearance in the reflog.
 +       *
 +       * In this case, save_parents() will be called multiple times.
 +       * We want to keep only the first set of parents.  We need to
 +       * store a sentinel value for an empty (i.e., NULL) parent
 +       * list to distinguish it from a not-yet-saved list, however.
 +       */
 +      if (*pp)
 +              return;
 +      if (commit->parents)
 +              *pp = copy_commit_list(commit->parents);
 +      else
 +              *pp = EMPTY_PARENT_LIST;
 +}
 +
 +static void free_saved_parents(struct rev_info *revs)
 +{
 +      if (revs->saved_parents_slab)
 +              clear_saved_parents(revs->saved_parents_slab);
 +}
 +
 +struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
 +{
 +      struct commit_list *parents;
 +
 +      if (!revs->saved_parents_slab)
 +              return commit->parents;
 +
 +      parents = *saved_parents_at(revs->saved_parents_slab, commit);
 +      if (parents == EMPTY_PARENT_LIST)
 +              return NULL;
 +      return parents;
 +}
 +
  enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
  {
        enum commit_action action = get_commit_action(revs, commit);
@@@ -3324,3 -3203,54 +3327,3 @@@ void put_revision_mark(const struct rev
        fputs(mark, stdout);
        putchar(' ');
  }
 -
 -define_commit_slab(saved_parents, struct commit_list *);
 -
 -#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
 -
 -void save_parents(struct rev_info *revs, struct commit *commit)
 -{
 -      struct commit_list **pp;
 -
 -      if (!revs->saved_parents_slab) {
 -              revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents));
 -              init_saved_parents(revs->saved_parents_slab);
 -      }
 -
 -      pp = saved_parents_at(revs->saved_parents_slab, commit);
 -
 -      /*
 -       * When walking with reflogs, we may visit the same commit
 -       * several times: once for each appearance in the reflog.
 -       *
 -       * In this case, save_parents() will be called multiple times.
 -       * We want to keep only the first set of parents.  We need to
 -       * store a sentinel value for an empty (i.e., NULL) parent
 -       * list to distinguish it from a not-yet-saved list, however.
 -       */
 -      if (*pp)
 -              return;
 -      if (commit->parents)
 -              *pp = copy_commit_list(commit->parents);
 -      else
 -              *pp = EMPTY_PARENT_LIST;
 -}
 -
 -struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
 -{
 -      struct commit_list *parents;
 -
 -      if (!revs->saved_parents_slab)
 -              return commit->parents;
 -
 -      parents = *saved_parents_at(revs->saved_parents_slab, commit);
 -      if (parents == EMPTY_PARENT_LIST)
 -              return NULL;
 -      return parents;
 -}
 -
 -void free_saved_parents(struct rev_info *revs)
 -{
 -      if (revs->saved_parents_slab)
 -              clear_saved_parents(revs->saved_parents_slab);
 -}
diff --combined t/t6000-rev-list-misc.sh
index 2602086303edeae192606d82d7fd1e71ac76534f,97a13a574e09edb7b70782e357d5b1231747e053..1f58b46e1f1300b7e893f16b92c40f4c2a09b014
@@@ -73,27 -73,8 +73,31 @@@ test_expect_success 'symleft flag bit i
        test_cmp expect actual
  '
  
 +test_expect_success 'rev-list can show index objects' '
 +      # Of the blobs and trees in the index, note:
 +      #
 +      #   - we do not show two/three, because it is the
 +      #     same blob as "one", and we show objects only once
 +      #
 +      #   - we do show the tree "two", because it has a valid cache tree
 +      #     from the last commit
 +      #
 +      #   - we do not show the root tree; since we updated the index, it
 +      #     does not have a valid cache tree
 +      #
 +      cat >expect <<-\EOF
 +      8e4020bb5a8d8c873b25de15933e75cc0fc275df one
 +      d9d3a7417b9605cfd88ee6306b28dadc29e6ab08 only-in-index
 +      9200b628cf9dc883a85a7abc8d6e6730baee589c two
 +      EOF
 +      echo only-in-index >only-in-index &&
 +      git add only-in-index &&
 +      git rev-list --objects --indexed-objects >actual &&
 +      test_cmp expect actual
 +'
 +
+ test_expect_success '--bisect and --first-parent can not be combined' '
+       test_must_fail git rev-list --bisect --first-parent HEAD
+ '
  test_done