Merge branch 'jk/pending-keep-tag-name'
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Dec 2015 21:58:03 +0000 (13:58 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Dec 2015 21:58:04 +0000 (13:58 -0800)
History traversal with "git log --source" that starts with an
annotated tag failed to report the tag as "source", due to an
old regression in the command line parser back in v2.2 days.

* jk/pending-keep-tag-name:
revision.c: propagate tag names from pending array

1  2 
revision.c
t/t4202-log.sh
diff --combined revision.c
index 9404a05eeb4adf7e8c1efeed7e16af2c2504aefa,0a0ef3ce5bb9a030b774dc68fc9c60533732a364..0a282f533b3e225a996221d614de8bdc960092a3
  #include "mailmap.h"
  #include "commit-slab.h"
  #include "dir.h"
 +#include "cache-tree.h"
 +#include "bisect.h"
  
  volatile show_early_output_fn_t show_early_output;
  
 +static const char *term_bad;
 +static const char *term_good;
 +
  char *path_name(const struct name_path *path, const char *name)
  {
        const struct name_path *p;
@@@ -38,7 -33,7 +38,7 @@@
        }
        n = xmalloc(len);
        m = n + len - (nlen + 1);
 -      strcpy(m, name);
 +      memcpy(m, name, nlen + 1);
        for (p = path; p; p = p->up) {
                if (p->elem_len) {
                        m -= p->elem_len + 1;
@@@ -86,11 -81,21 +86,11 @@@ void show_object_with_name(FILE *out, s
        leaf.elem = component;
        leaf.elem_len = strlen(component);
  
 -      fprintf(out, "%s ", sha1_to_hex(obj->sha1));
 +      fprintf(out, "%s ", oid_to_hex(&obj->oid));
        show_path_truncated(out, &leaf);
        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)
@@@ -106,10 -111,10 +106,10 @@@ static void mark_tree_contents_unintere
        struct name_entry entry;
        struct object *obj = &tree->object;
  
 -      if (!has_sha1_file(obj->sha1))
 +      if (!has_object_file(&obj->oid))
                return;
        if (parse_tree(tree) < 0)
 -              die("bad tree %s", sha1_to_hex(obj->sha1));
 +              die("bad tree %s", oid_to_hex(&obj->oid));
  
        init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
  
  void mark_tree_uninteresting(struct tree *tree)
  {
 -      struct object *obj = &tree->object;
 +      struct object *obj;
  
        if (!tree)
                return;
 +
 +      obj = &tree->object;
        if (obj->flags & UNINTERESTING)
                return;
        obj->flags |= UNINTERESTING;
@@@ -155,7 -158,10 +155,7 @@@ void mark_parents_uninteresting(struct 
                commit_list_insert(l->item, &parents);
  
        while (parents) {
 -              struct commit *commit = parents->item;
 -              l = parents;
 -              parents = parents->next;
 -              free(l);
 +              struct commit *commit = pop_commit(&parents);
  
                while (commit) {
                        /*
                         * it is popped next time around, we won't be trying
                         * to parse it and get an error.
                         */
 -                      if (!has_sha1_file(commit->object.sha1))
 +                      if (!has_object_file(&commit->object.oid))
                                commit->object.parsed = 1;
  
                        if (commit->object.flags & UNINTERESTING)
@@@ -284,19 -290,18 +284,18 @@@ static struct commit *handle_commit(str
                        add_pending_object(revs, object, tag->tag);
                if (!tag->tagged)
                        die("bad tag");
 -              object = parse_object(tag->tagged->sha1);
 +              object = parse_object(tag->tagged->oid.hash);
                if (!object) {
                        if (flags & UNINTERESTING)
                                return NULL;
 -                      die("bad object %s", sha1_to_hex(tag->tagged->sha1));
 +                      die("bad object %s", oid_to_hex(&tag->tagged->oid));
                }
                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.
+                * propagate path data from the tag's pending entry.
                 */
-               name = "";
                path = NULL;
                mode = 0;
        }
        die("%s is unknown object", name);
  }
  
 -static int everybody_uninteresting(struct commit_list *orig)
 +static int everybody_uninteresting(struct commit_list *orig,
 +                                 struct commit **interesting_cache)
  {
        struct commit_list *list = orig;
 +
 +      if (*interesting_cache) {
 +              struct commit *commit = *interesting_cache;
 +              if (!(commit->object.flags & UNINTERESTING))
 +                      return 0;
 +      }
 +
        while (list) {
                struct commit *commit = list->item;
                list = list->next;
                if (commit->object.flags & UNINTERESTING)
                        continue;
 +
 +              *interesting_cache = commit;
                return 0;
        }
        return 1;
@@@ -512,7 -507,7 +511,7 @@@ static int rev_compare_tree(struct rev_
  
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 -      if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
 +      if (diff_tree_sha1(t1->object.oid.hash, t2->object.oid.hash, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
        return tree_difference;
@@@ -528,7 -523,7 +527,7 @@@ static int rev_same_tree_as_empty(struc
  
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 -      retval = diff_tree_sha1(NULL, t1->object.sha1, "", &revs->pruning);
 +      retval = diff_tree_sha1(NULL, t1->object.oid.hash, "", &revs->pruning);
  
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
  }
@@@ -612,7 -607,7 +611,7 @@@ static unsigned update_treesame(struct 
  
                st = lookup_decoration(&revs->treesame, &commit->object);
                if (!st)
 -                      die("update_treesame %s", sha1_to_hex(commit->object.sha1));
 +                      die("update_treesame %s", oid_to_hex(&commit->object.oid));
                relevant_parents = 0;
                relevant_change = irrelevant_change = 0;
                for (p = commit->parents, n = 0; p; n++, p = p->next) {
@@@ -710,8 -705,8 +709,8 @@@ static void try_to_simplify_commit(stru
                }
                if (parse_commit(p) < 0)
                        die("cannot simplify commit %s (because of %s)",
 -                          sha1_to_hex(commit->object.sha1),
 -                          sha1_to_hex(p->object.sha1));
 +                          oid_to_hex(&commit->object.oid),
 +                          oid_to_hex(&p->object.oid));
                switch (rev_compare_tree(revs, p, commit)) {
                case REV_TREE_SAME:
                        if (!revs->simplify_history || !relevant_commit(p)) {
                                 */
                                if (parse_commit(p) < 0)
                                        die("cannot simplify commit %s (invalid %s)",
 -                                          sha1_to_hex(commit->object.sha1),
 -                                          sha1_to_hex(p->object.sha1));
 +                                          oid_to_hex(&commit->object.oid),
 +                                          oid_to_hex(&p->object.oid));
                                p->parents = NULL;
                        }
                /* fallthrough */
                                irrelevant_change = 1;
                        continue;
                }
 -              die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
 +              die("bad tree compare for commit %s", oid_to_hex(&commit->object.oid));
        }
  
        /*
@@@ -820,7 -815,7 +819,7 @@@ static int add_parents_to_list(struct r
                        parent = parent->next;
                        if (p)
                                p->object.flags |= UNINTERESTING;
 -                      if (parse_commit(p) < 0)
 +                      if (parse_commit_gently(p, 1) < 0)
                                continue;
                        if (p->parents)
                                mark_parents_uninteresting(p);
        for (parent = commit->parents; parent; parent = parent->next) {
                struct commit *p = parent->item;
  
 -              if (parse_commit(p) < 0)
 +              if (parse_commit_gently(p, revs->ignore_missing_links) < 0)
                        return -1;
                if (revs->show_source && !p->util)
                        p->util = commit->util;
@@@ -953,8 -948,7 +952,8 @@@ static void cherry_pick_list(struct com
  /* How many extra uninteresting commits we want to see.. */
  #define SLOP 5
  
 -static int still_interesting(struct commit_list *src, unsigned long date, int slop)
 +static int still_interesting(struct commit_list *src, unsigned long date, int slop,
 +                           struct commit **interesting_cache)
  {
        /*
         * No source list at all? We're definitely done..
         * Does the source list still have interesting commits in
         * it? Definitely not done..
         */
 -      if (!everybody_uninteresting(src))
 +      if (!everybody_uninteresting(src, interesting_cache))
                return SLOP;
  
        /* Ok, we're closing in.. */
@@@ -1092,7 -1086,6 +1091,7 @@@ static int limit_list(struct rev_info *
        struct commit_list *newlist = NULL;
        struct commit_list **p = &newlist;
        struct commit_list *bottom = NULL;
 +      struct commit *interesting_cache = NULL;
  
        if (revs->ancestry_path) {
                bottom = collect_bottom_commits(list);
        }
  
        while (list) {
 -              struct commit_list *entry = list;
 -              struct commit *commit = list->item;
 +              struct commit *commit = pop_commit(&list);
                struct object *obj = &commit->object;
                show_early_output_fn_t show;
  
 -              list = list->next;
 -              free(entry);
 +              if (commit == interesting_cache)
 +                      interesting_cache = NULL;
  
                if (revs->max_age != -1 && (commit->date < revs->max_age))
                        obj->flags |= UNINTERESTING;
                        mark_parents_uninteresting(commit);
                        if (revs->show_all)
                                p = &commit_list_insert(commit, p)->next;
 -                      slop = still_interesting(list, date, slop);
 +                      slop = still_interesting(list, date, slop, &interesting_cache);
                        if (slop)
                                continue;
                        /* If showing all, add the whole pending list to the end */
@@@ -1191,7 -1185,7 +1190,7 @@@ static void add_rev_cmdline_list(struc
  {
        while (commit_list) {
                struct object *object = &commit_list->item->object;
 -              add_rev_cmdline(revs, object, sha1_to_hex(object->sha1),
 +              add_rev_cmdline(revs, object, oid_to_hex(&object->oid),
                                whence, flags);
                commit_list = commit_list->next;
        }
@@@ -1217,8 -1211,7 +1216,8 @@@ int ref_excluded(struct string_list *re
        return 0;
  }
  
 -static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 +static int handle_one_ref(const char *path, const struct object_id *oid,
 +                        int flag, void *cb_data)
  {
        struct all_refs_cb *cb = cb_data;
        struct object *object;
        if (ref_excluded(cb->all_revs->ref_excludes, path))
            return 0;
  
 -      object = get_reference(cb->all_revs, path, sha1, cb->all_flags);
 +      object = get_reference(cb->all_revs, path, oid->hash, cb->all_flags);
        add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
 -      add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
 +      add_pending_sha1(cb->all_revs, path, oid->hash, cb->all_flags);
        return 0;
  }
  
@@@ -1292,8 -1285,7 +1291,8 @@@ static int handle_one_reflog_ent(unsign
        return 0;
  }
  
 -static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 +static int handle_one_reflog(const char *path, const struct object_id *oid,
 +                           int flag, void *cb_data)
  {
        struct all_refs_cb *cb = cb_data;
        cb->warned_bad_reflog = 0;
  void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
  {
        struct all_refs_cb cb;
 +
        cb.all_revs = revs;
        cb.all_flags = flags;
        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];
                        break;
                if (!((struct tag*)it)->tagged)
                        return 0;
 -              hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
 +              hashcpy(sha1, ((struct tag*)it)->tagged->oid.hash);
        }
        if (it->type != OBJ_COMMIT)
                return 0;
@@@ -1437,7 -1381,7 +1436,7 @@@ static void add_pending_commit_list(str
        while (commit_list) {
                struct object *object = &commit_list->item->object;
                object->flags |= flags;
 -              add_pending_object(revs, object, sha1_to_hex(object->sha1));
 +              add_pending_object(revs, object, oid_to_hex(&object->oid));
                commit_list = commit_list->next;
        }
  }
@@@ -1458,7 -1402,7 +1457,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);
@@@ -1557,13 -1501,13 +1556,13 @@@ int handle_revision_arg(const char *arg
  
                                a = (a_obj->type == OBJ_COMMIT
                                     ? (struct commit *)a_obj
 -                                   : lookup_commit_reference(a_obj->sha1));
 +                                   : lookup_commit_reference(a_obj->oid.hash));
                                b = (b_obj->type == OBJ_COMMIT
                                     ? (struct commit *)b_obj
 -                                   : lookup_commit_reference(b_obj->sha1));
 +                                   : lookup_commit_reference(b_obj->oid.hash));
                                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);
@@@ -1708,7 -1652,6 +1707,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, "--full-history")) {
                revs->simplify_history = 0;
        } else if (!strcmp(arg, "--relative-date")) {
 -              revs->date_mode = DATE_RELATIVE;
 +              revs->date_mode.type = DATE_RELATIVE;
                revs->date_mode_explicit = 1;
        } else if ((argcount = parse_long_opt("date", argv, &optarg))) {
 -              revs->date_mode = parse_date_format(optarg);
 +              parse_date_format(optarg, &revs->date_mode);
                revs->date_mode_explicit = 1;
                return argcount;
        } else if (!strcmp(arg, "--log-size")) {
                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);
@@@ -2075,23 -2010,14 +2074,23 @@@ void parse_revision_opt(struct rev_inf
        ctx->argc -= n;
  }
  
 +static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data, const char *term) {
 +      struct strbuf bisect_refs = STRBUF_INIT;
 +      int status;
 +      strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
 +      status = for_each_ref_in_submodule(submodule, bisect_refs.buf, fn, cb_data);
 +      strbuf_release(&bisect_refs);
 +      return status;
 +}
 +
  static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
  {
 -      return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
 +      return for_each_bisect_ref(submodule, fn, cb_data, term_bad);
  }
  
  static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
  {
 -      return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
 +      return for_each_bisect_ref(submodule, fn, cb_data, term_good);
  }
  
  static int handle_revision_pseudo_opt(const char *submodule,
                handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
 +              read_bisect_terms(&term_bad, &term_good);
                handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
                handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
                revs->bisect = 1;
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
                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")) {
        return 1;
  }
  
 +static void NORETURN diagnose_missing_default(const char *def)
 +{
 +      unsigned char sha1[20];
 +      int flags;
 +      const char *refname;
 +
 +      refname = resolve_ref_unsafe(def, 0, sha1, &flags);
 +      if (!refname || !(flags & REF_ISSYMREF) || (flags & REF_ISBROKEN))
 +              die(_("your current branch appears to be broken"));
 +
 +      skip_prefix(refname, "refs/heads/", &refname);
 +      die(_("your current branch '%s' does not have any commits yet"),
 +          refname);
 +}
 +
  /*
   * Parse revision information, filling in the "rev_info" structure,
   * and removing the used arguments from the argument list.
@@@ -2326,7 -2234,7 +2325,7 @@@ int setup_revisions(int argc, const cha
                struct object *object;
                struct object_context oc;
                if (get_sha1_with_context(revs->def, 0, sha1, &oc))
 -                      die("bad default revision '%s'", revs->def);
 +                      diagnose_missing_default(revs->def);
                object = get_reference(revs, revs->def, sha1, 0);
                add_pending_object_with_mode(revs, object, revs->def, oc.mode);
        }
  
        if (revs->reflog_info && revs->graph)
                die("cannot combine --walk-reflogs with --graph");
 +      if (revs->no_walk && revs->graph)
 +              die("cannot combine --no-walk with --graph");
        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;
  }
  
@@@ -2728,7 -2631,10 +2727,7 @@@ static void simplify_merges(struct rev_
                yet_to_do = NULL;
                tail = &yet_to_do;
                while (list) {
 -                      commit = list->item;
 -                      next = list->next;
 -                      free(list);
 -                      list = next;
 +                      commit = pop_commit(&list);
                        tail = simplify_one(revs, commit, tail);
                }
        }
        while (list) {
                struct merge_simplify_state *st;
  
 -              commit = list->item;
 -              next = list->next;
 -              free(list);
 -              list = next;
 +              commit = pop_commit(&list);
                st = locate_simplify_state(revs, commit);
                if (st->simplified == commit)
                        tail = &commit_list_insert(commit, tail)->next;
@@@ -2940,7 -2849,7 +2939,7 @@@ static int commit_match(struct commit *
        if (opt->show_notes) {
                if (!buf.len)
                        strbuf_addstr(&buf, message);
 -              format_display_notes(commit->object.sha1, &buf, encoding, 1);
 +              format_display_notes(commit->object.oid.hash, &buf, encoding, 1);
        }
  
        /*
                                     (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,7 -2879,7 +2969,7 @@@ enum commit_action get_commit_action(st
  {
        if (commit->object.flags & SHOWN)
                return commit_ignore;
 -      if (revs->unpacked && has_sha1_pack(commit->object.sha1))
 +      if (revs->unpacked && has_sha1_pack(commit->object.oid.hash))
                return commit_ignore;
        if (revs->show_all)
                return commit_show;
        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);
@@@ -3096,7 -2950,7 +3095,7 @@@ static void track_linear(struct rev_inf
                struct commit_list *p;
                for (p = revs->previous_parents; p; p = p->next)
                        if (p->item == NULL || /* first commit */
 -                          !hashcmp(p->item->object.sha1, commit->object.sha1))
 +                          !oidcmp(&p->item->object.oid, &commit->object.oid))
                                break;
                revs->linear = p != NULL;
        }
@@@ -3114,7 -2968,11 +3113,7 @@@ static struct commit *get_revision_1(st
                return NULL;
  
        do {
 -              struct commit_list *entry = revs->commits;
 -              struct commit *commit = entry->item;
 -
 -              revs->commits = entry->next;
 -              free(entry);
 +              struct commit *commit = pop_commit(&revs->commits);
  
                if (revs->reflog_info) {
                        save_parents(revs, commit);
                        if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
                                if (!revs->ignore_missing_links)
                                        die("Failed to traverse parents of commit %s",
 -                                              sha1_to_hex(commit->object.sha1));
 +                                              oid_to_hex(&commit->object.oid));
                        }
                }
  
                        continue;
                case commit_error:
                        die("Failed to simplify parents of commit %s",
 -                          sha1_to_hex(commit->object.sha1));
 +                          oid_to_hex(&commit->object.oid));
                default:
                        if (revs->track_linear)
                                track_linear(revs, commit);
@@@ -3361,3 -3219,54 +3360,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/t4202-log.sh
index 6ede0692f6de9f2da011dd8b7361bfc53aba7253,043c48e2992ab8ad3f4fc6c58961b078b307f53a..cb82eb7e66b4feb24086407019a812f46b715a34
@@@ -113,7 -113,11 +113,7 @@@ test_expect_success 'diff-filter=M' 
  
        actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) &&
        expect=$(echo second) &&
 -      test "$actual" = "$expect" || {
 -              echo Oops
 -              echo "Actual: $actual"
 -              false
 -      }
 +      verbose test "$actual" = "$expect"
  
  '
  
@@@ -121,7 -125,11 +121,7 @@@ test_expect_success 'diff-filter=D' 
  
        actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) &&
        expect=$(echo sixth ; echo third) &&
 -      test "$actual" = "$expect" || {
 -              echo Oops
 -              echo "Actual: $actual"
 -              false
 -      }
 +      verbose test "$actual" = "$expect"
  
  '
  
@@@ -129,7 -137,11 +129,7 @@@ test_expect_success 'diff-filter=R' 
  
        actual=$(git log -M --pretty="format:%s" --diff-filter=R HEAD) &&
        expect=$(echo third) &&
 -      test "$actual" = "$expect" || {
 -              echo Oops
 -              echo "Actual: $actual"
 -              false
 -      }
 +      verbose test "$actual" = "$expect"
  
  '
  
@@@ -137,7 -149,11 +137,7 @@@ test_expect_success 'diff-filter=C' 
  
        actual=$(git log -C -C --pretty="format:%s" --diff-filter=C HEAD) &&
        expect=$(echo fourth) &&
 -      test "$actual" = "$expect" || {
 -              echo Oops
 -              echo "Actual: $actual"
 -              false
 -      }
 +      verbose test "$actual" = "$expect"
  
  '
  
@@@ -145,31 -161,12 +145,31 @@@ test_expect_success 'git log --follow' 
  
        actual=$(git log --follow --pretty="format:%s" ichi) &&
        expect=$(echo third ; echo second ; echo initial) &&
 -      test "$actual" = "$expect" || {
 -              echo Oops
 -              echo "Actual: $actual"
 -              false
 -      }
 +      verbose test "$actual" = "$expect"
 +'
 +
 +test_expect_success 'git config log.follow works like --follow' '
 +      test_config log.follow true &&
 +      actual=$(git log --pretty="format:%s" ichi) &&
 +      expect=$(echo third ; echo second ; echo initial) &&
 +      verbose test "$actual" = "$expect"
 +'
 +
 +test_expect_success 'git config log.follow does not die with multiple paths' '
 +      test_config log.follow true &&
 +      git log --pretty="format:%s" ichi ein
 +'
  
 +test_expect_success 'git config log.follow does not die with no paths' '
 +      test_config log.follow true &&
 +      git log --
 +'
 +
 +test_expect_success 'git config log.follow is overridden by --no-follow' '
 +      test_config log.follow true &&
 +      actual=$(git log --no-follow --pretty="format:%s" ichi) &&
 +      expect="third" &&
 +      verbose test "$actual" = "$expect"
  '
  
  cat > expect << EOF
@@@ -215,21 -212,6 +215,21 @@@ test_expect_success 'log --grep' 
        test_cmp expect actual
  '
  
 +cat > expect << EOF
 +second
 +initial
 +EOF
 +test_expect_success 'log --invert-grep --grep' '
 +      git log --pretty="tformat:%s" --invert-grep --grep=th --grep=Sec >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'log --invert-grep --grep -i' '
 +      echo initial >expect &&
 +      git log --pretty="tformat:%s" --invert-grep -i --grep=th --grep=Sec >actual &&
 +      test_cmp expect actual
 +'
 +
  test_expect_success 'log --grep option parsing' '
        echo second >expect &&
        git log -1 --pretty="tformat:%s" --grep sec >actual &&
@@@ -484,7 -466,7 +484,7 @@@ test_expect_success 'log.decorate confi
        git log --oneline --no-decorate >actual &&
        test_cmp expect.none actual &&
        git log --oneline --decorate >actual &&
 -      test_cmp expect.short actual
 +      test_cmp expect.short actual &&
  
        test_unconfig log.decorate &&
        git log --pretty=raw >expect.raw &&
@@@ -890,22 -872,33 +890,51 @@@ test_expect_success GPG 'log --graph --
        grep "^| | gpg: Good signature" actual
  '
  
 +test_expect_success 'log --graph --no-walk is forbidden' '
 +      test_must_fail git log --graph --no-walk
 +'
 +
 +test_expect_success 'log diagnoses bogus HEAD' '
 +      git init empty &&
 +      test_must_fail git -C empty log 2>stderr &&
 +      test_i18ngrep does.not.have.any.commits stderr &&
 +      echo 1234abcd >empty/.git/refs/heads/master &&
 +      test_must_fail git -C empty log 2>stderr &&
 +      test_i18ngrep broken stderr &&
 +      echo "ref: refs/heads/invalid.lock" >empty/.git/HEAD &&
 +      test_must_fail git -C empty log 2>stderr &&
 +      test_i18ngrep broken stderr &&
 +      test_must_fail git -C empty log --default totally-bogus 2>stderr &&
 +      test_i18ngrep broken stderr
 +'
 +
+ test_expect_success 'set up --source tests' '
+       git checkout --orphan source-a &&
+       test_commit one &&
+       test_commit two &&
+       git checkout -b source-b HEAD^ &&
+       test_commit three
+ '
+ test_expect_success 'log --source paints branch names' '
+       cat >expect <<-\EOF &&
+       09e12a9 source-b three
+       8e393e1 source-a two
+       1ac6c77 source-b one
+       EOF
+       git log --oneline --source source-a source-b >actual &&
+       test_cmp expect actual
+ '
+ test_expect_success 'log --source paints tag names' '
+       git tag -m tagged source-tag &&
+       cat >expect <<-\EOF &&
+       09e12a9 source-tag three
+       8e393e1 source-a two
+       1ac6c77 source-tag one
+       EOF
+       git log --oneline --source source-tag source-a >actual &&
+       test_cmp expect actual
+ '
  test_done