Merge branch 'jk/squelch-missing-link-warning-for-unreachable'
authorJunio C Hamano <gitster@pobox.com>
Thu, 11 Jun 2015 16:29:58 +0000 (09:29 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 11 Jun 2015 16:29:59 +0000 (09:29 -0700)
Recent "git prune" traverses young unreachable objects to safekeep
old objects in the reachability chain from them, which sometimes
caused error messages that are unnecessarily alarming.

* jk/squelch-missing-link-warning-for-unreachable:
suppress errors on missing UNINTERESTING links
silence broken link warnings with revs->ignore_missing_links
add quieter versions of parse_{tree,commit}

1  2 
commit.c
commit.h
list-objects.c
revision.c
tree.c
tree.h
diff --combined commit.c
index 2d9de807aeb230c97e74928c1b446544186bc4e1,388819c4e97813e89ebc1e0cd597f9d2652c2599..6e2103cef60e8a0c2df18685280dbb5fda5c133f
+++ b/commit.c
@@@ -55,12 -55,12 +55,12 @@@ struct commit *lookup_commit(const unsi
  
  struct commit *lookup_commit_reference_by_name(const char *name)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct commit *commit;
  
 -      if (get_sha1_committish(name, sha1))
 +      if (get_sha1_committish(name, oid.hash))
                return NULL;
 -      commit = lookup_commit_reference(sha1);
 +      commit = lookup_commit_reference(oid.hash);
        if (parse_commit(commit))
                return NULL;
        return commit;
@@@ -99,7 -99,7 +99,7 @@@ static int commit_graft_alloc, commit_g
  static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
  {
        struct commit_graft **commit_graft_table = table;
 -      return commit_graft_table[index]->sha1;
 +      return commit_graft_table[index]->oid.hash;
  }
  
  static int commit_graft_pos(const unsigned char *sha1)
  
  int register_commit_graft(struct commit_graft *graft, int ignore_dups)
  {
 -      int pos = commit_graft_pos(graft->sha1);
 +      int pos = commit_graft_pos(graft->oid.hash);
  
        if (0 <= pos) {
                if (ignore_dups)
@@@ -138,23 -138,22 +138,23 @@@ struct commit_graft *read_graft_line(ch
        /* The format is just "Commit Parent1 Parent2 ...\n" */
        int i;
        struct commit_graft *graft = NULL;
 +      const int entry_size = GIT_SHA1_HEXSZ + 1;
  
        while (len && isspace(buf[len-1]))
                buf[--len] = '\0';
        if (buf[0] == '#' || buf[0] == '\0')
                return NULL;
 -      if ((len + 1) % 41)
 +      if ((len + 1) % entry_size)
                goto bad_graft_data;
 -      i = (len + 1) / 41 - 1;
 -      graft = xmalloc(sizeof(*graft) + 20 * i);
 +      i = (len + 1) / entry_size - 1;
 +      graft = xmalloc(sizeof(*graft) + GIT_SHA1_RAWSZ * i);
        graft->nr_parent = i;
 -      if (get_sha1_hex(buf, graft->sha1))
 +      if (get_oid_hex(buf, &graft->oid))
                goto bad_graft_data;
 -      for (i = 40; i < len; i += 41) {
 +      for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) {
                if (buf[i] != ' ')
                        goto bad_graft_data;
 -              if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
 +              if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash))
                        goto bad_graft_data;
        }
        return graft;
@@@ -303,42 -302,39 +303,42 @@@ int parse_commit_buffer(struct commit *
  {
        const char *tail = buffer;
        const char *bufptr = buffer;
 -      unsigned char parent[20];
 +      struct object_id parent;
        struct commit_list **pptr;
        struct commit_graft *graft;
 +      const int tree_entry_len = GIT_SHA1_HEXSZ + 5;
 +      const int parent_entry_len = GIT_SHA1_HEXSZ + 7;
  
        if (item->object.parsed)
                return 0;
        item->object.parsed = 1;
        tail += size;
 -      if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n')
 +      if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
 +                      bufptr[tree_entry_len] != '\n')
                return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
 -      if (get_sha1_hex(bufptr + 5, parent) < 0)
 +      if (get_sha1_hex(bufptr + 5, parent.hash) < 0)
                return error("bad tree pointer in commit %s",
                             sha1_to_hex(item->object.sha1));
 -      item->tree = lookup_tree(parent);
 -      bufptr += 46; /* "tree " + "hex sha1" + "\n" */
 +      item->tree = lookup_tree(parent.hash);
 +      bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
  
        graft = lookup_commit_graft(item->object.sha1);
 -      while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) {
 +      while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
                struct commit *new_parent;
  
 -              if (tail <= bufptr + 48 ||
 -                  get_sha1_hex(bufptr + 7, parent) ||
 -                  bufptr[47] != '\n')
 +              if (tail <= bufptr + parent_entry_len + 1 ||
 +                  get_sha1_hex(bufptr + 7, parent.hash) ||
 +                  bufptr[parent_entry_len] != '\n')
                        return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
 -              bufptr += 48;
 +              bufptr += parent_entry_len + 1;
                /*
                 * The clone is shallow if nr_parent < 0, and we must
                 * not traverse its real parents even when we unhide them.
                 */
                if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
                        continue;
 -              new_parent = lookup_commit(parent);
 +              new_parent = lookup_commit(parent.hash);
                if (new_parent)
                        pptr = &commit_list_insert(new_parent, pptr)->next;
        }
                int i;
                struct commit *new_parent;
                for (i = 0; i < graft->nr_parent; i++) {
 -                      new_parent = lookup_commit(graft->parent[i]);
 +                      new_parent = lookup_commit(graft->parent[i].hash);
                        if (!new_parent)
                                continue;
                        pptr = &commit_list_insert(new_parent, pptr)->next;
        return 0;
  }
  
- int parse_commit(struct commit *item)
+ int parse_commit_gently(struct commit *item, int quiet_on_missing)
  {
        enum object_type type;
        void *buffer;
                return 0;
        buffer = read_sha1_file(item->object.sha1, &type, &size);
        if (!buffer)
-               return error("Could not read %s",
+               return quiet_on_missing ? -1 :
+                       error("Could not read %s",
                             sha1_to_hex(item->object.sha1));
        if (type != OBJ_COMMIT) {
                free(buffer);
@@@ -871,7 -868,7 +872,7 @@@ struct commit_list *get_octopus_merge_b
  
                for (j = ret; j; j = j->next) {
                        struct commit_list *bases;
 -                      bases = get_merge_bases(i->item, j->item, 1);
 +                      bases = get_merge_bases(i->item, j->item);
                        if (!new)
                                new = bases;
                        else
@@@ -940,10 -937,10 +941,10 @@@ static int remove_redundant(struct comm
        return filled;
  }
  
 -struct commit_list *get_merge_bases_many(struct commit *one,
 -                                       int n,
 -                                       struct commit **twos,
 -                                       int cleanup)
 +static struct commit_list *get_merge_bases_many_0(struct commit *one,
 +                                                int n,
 +                                                struct commit **twos,
 +                                                int cleanup)
  {
        struct commit_list *list;
        struct commit **rslt;
        return result;
  }
  
 -struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
 -                                  int cleanup)
 +struct commit_list *get_merge_bases_many(struct commit *one,
 +                                       int n,
 +                                       struct commit **twos)
  {
 -      return get_merge_bases_many(one, 1, &two, cleanup);
 +      return get_merge_bases_many_0(one, n, twos, 1);
 +}
 +
 +struct commit_list *get_merge_bases_many_dirty(struct commit *one,
 +                                             int n,
 +                                             struct commit **twos)
 +{
 +      return get_merge_bases_many_0(one, n, twos, 0);
 +}
 +
 +struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
 +{
 +      return get_merge_bases_many_0(one, 1, &two, 1);
  }
  
  /*
@@@ -1584,10 -1568,10 +1585,10 @@@ struct commit *get_merge_parent(const c
  {
        struct object *obj;
        struct commit *commit;
 -      unsigned char sha1[20];
 -      if (get_sha1(name, sha1))
 +      struct object_id oid;
 +      if (get_sha1(name, oid.hash))
                return NULL;
 -      obj = parse_object(sha1);
 +      obj = parse_object(oid.hash);
        commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
        if (commit && !commit->util) {
                struct merge_remote_desc *desc;
@@@ -1657,49 -1641,3 +1658,49 @@@ const char *find_commit_header(const ch
        }
        return NULL;
  }
 +
 +/*
 + * Inspect sb and determine the true "end" of the log message, in
 + * order to find where to put a new Signed-off-by: line.  Ignored are
 + * trailing comment lines and blank lines, and also the traditional
 + * "Conflicts:" block that is not commented out, so that we can use
 + * "git commit -s --amend" on an existing commit that forgot to remove
 + * it.
 + *
 + * Returns the number of bytes from the tail to ignore, to be fed as
 + * the second parameter to append_signoff().
 + */
 +int ignore_non_trailer(struct strbuf *sb)
 +{
 +      int boc = 0;
 +      int bol = 0;
 +      int in_old_conflicts_block = 0;
 +
 +      while (bol < sb->len) {
 +              char *next_line;
 +
 +              if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol)))
 +                      next_line = sb->buf + sb->len;
 +              else
 +                      next_line++;
 +
 +              if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') {
 +                      /* is this the first of the run of comments? */
 +                      if (!boc)
 +                              boc = bol;
 +                      /* otherwise, it is just continuing */
 +              } else if (starts_with(sb->buf + bol, "Conflicts:\n")) {
 +                      in_old_conflicts_block = 1;
 +                      if (!boc)
 +                              boc = bol;
 +              } else if (in_old_conflicts_block && sb->buf[bol] == '\t') {
 +                      ; /* a pathname in the conflicts block */
 +              } else if (boc) {
 +                      /* the previous was not trailing comment */
 +                      boc = 0;
 +                      in_old_conflicts_block = 0;
 +              }
 +              bol = next_line - sb->buf;
 +      }
 +      return boc ? sb->len - boc : 0;
 +}
diff --combined commit.h
index ed3a1d59a553b498b20d28f4999effde78e51c3c,61bda25b348756af94659e27b94499e96a4c2f24..9a1fa961d2ba0e3ec3eae9096ef49bc7155cdd92
+++ b/commit.h
@@@ -59,7 -59,11 +59,11 @@@ struct commit *lookup_commit_reference_
  struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name);
  
  int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
- int parse_commit(struct commit *item);
+ int parse_commit_gently(struct commit *item, int quiet_on_missing);
+ static inline int parse_commit(struct commit *item)
+ {
+       return parse_commit_gently(item, 0);
+ }
  void parse_commit_or_die(struct commit *item);
  
  /*
@@@ -226,9 -230,9 +230,9 @@@ enum rev_sort_order 
  void sort_in_topological_order(struct commit_list **, enum rev_sort_order);
  
  struct commit_graft {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int nr_parent; /* < 0 if shallow commit */
 -      unsigned char parent[FLEX_ARRAY][20]; /* more */
 +      struct object_id parent[FLEX_ARRAY]; /* more */
  };
  typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
  
@@@ -236,13 -240,10 +240,13 @@@ struct commit_graft *read_graft_line(ch
  int register_commit_graft(struct commit_graft *, int);
  struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
  
 -extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
 -extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
 +extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2);
 +extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos);
  extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
  
 +/* To be used only when object flags after this call no longer matter */
 +extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos);
 +
  /* largest positive number a signed 32-bit integer can contain */
  #define INFINITE_DEPTH 0x7fffffff
  
@@@ -254,6 -255,7 +258,6 @@@ extern int for_each_commit_graft(each_c
  extern int is_repository_shallow(void);
  extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
 -extern void check_shallow_file_for_update(void);
  extern void set_alternate_shallow_file(const char *path, int override);
  extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
                                 const struct sha1_array *extra);
@@@ -339,9 -341,6 +343,9 @@@ extern void free_commit_extra_headers(s
  extern const char *find_commit_header(const char *msg, const char *key,
                                      size_t *out_len);
  
 +/* Find the end of the log message, the right place for a new trailer. */
 +extern int ignore_non_trailer(struct strbuf *sb);
 +
  typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                 void *cb_data);
  
diff --combined list-objects.c
index 2a139b6ced68809f3a324f493325ec62c3a44044,90c21abc5cf2ee2d3f77a35277855fd964f91db4..41736d23727e2ff246d7000ee74a47ce80ea189d
@@@ -81,7 -81,7 +81,7 @@@ static void process_tree(struct rev_inf
                die("bad tree object");
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
-       if (parse_tree(tree) < 0) {
+       if (parse_tree_gently(tree, revs->ignore_missing_links) < 0) {
                if (revs->ignore_missing_links)
                        return;
                die("bad tree object %s", sha1_to_hex(obj->sha1));
@@@ -157,7 -157,7 +157,7 @@@ void mark_edges_uninteresting(struct re
  
                if (commit->object.flags & UNINTERESTING) {
                        mark_tree_uninteresting(commit->tree);
 -                      if (revs->edge_hint && !(commit->object.flags & SHOWN)) {
 +                      if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
                                commit->object.flags |= SHOWN;
                                show_edge(commit);
                        }
                }
                mark_edge_parents_uninteresting(commit, revs, show_edge);
        }
 -      if (revs->edge_hint) {
 +      if (revs->edge_hint_aggressive) {
                for (i = 0; i < revs->cmdline.nr; i++) {
                        struct object *obj = revs->cmdline.rev[i].item;
                        struct commit *commit = (struct commit *)obj;
diff --combined revision.c
index 1d903cf3115b46ab204a877924dded50e7122111,9f5476dc707ab4d95f66ae219538d87114d002f0..3ff8723da463b87268146ce3c7680d691f5bba4f
@@@ -345,24 -345,14 +345,24 @@@ static struct commit *handle_commit(str
        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;
 +              if (interesting_cache)
 +                      *interesting_cache = commit;
                return 0;
        }
        return 1;
@@@ -817,7 -807,7 +817,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;
@@@ -950,8 -940,7 +950,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.. */
@@@ -1089,7 -1078,6 +1089,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);
                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;
                if (add_parents_to_list(revs, commit, &list, NULL) < 0)
                        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 */
@@@ -1218,8 -1203,7 +1218,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;
  }
  
@@@ -1293,8 -1277,7 +1293,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);
@@@ -1459,7 -1441,7 +1459,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);
@@@ -1564,7 -1546,7 +1564,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);
@@@ -1871,12 -1853,6 +1871,12 @@@ static int handle_revision_opt(struct r
                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);
@@@ -2357,14 -2331,9 +2357,14 @@@ int setup_revisions(int argc, const cha
  
        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;
  }
  
@@@ -2940,7 -2909,7 +2940,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)
@@@ -2993,61 -2962,6 +2993,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);
@@@ -3347,3 -3261,54 +3347,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 tree.c
index 58ebfce1bca609b0f7119eb210345c2065d02f40,194a84074d0d6675c75de9031e4aa16abc90ee58..413a5b1fa617df2f407d32ffdf78e58d9c42de58
--- 1/tree.c
--- 2/tree.c
+++ b/tree.c
@@@ -30,12 -30,9 +30,12 @@@ static int read_one_entry_opt(const uns
        return add_cache_entry(ce, opt);
  }
  
 -static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *context)
 +static int read_one_entry(const unsigned char *sha1, struct strbuf *base,
 +                        const char *pathname, unsigned mode, int stage,
 +                        void *context)
  {
 -      return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
 +      return read_one_entry_opt(sha1, base->buf, base->len, pathname,
 +                                mode, stage,
                                  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
  }
  
   * This is used when the caller knows there is no existing entries at
   * the stage that will conflict with the entry being added.
   */
 -static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *context)
 +static int read_one_entry_quick(const unsigned char *sha1, struct strbuf *base,
 +                              const char *pathname, unsigned mode, int stage,
 +                              void *context)
  {
 -      return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
 +      return read_one_entry_opt(sha1, base->buf, base->len, pathname,
 +                                mode, stage,
                                  ADD_CACHE_JUST_APPEND);
  }
  
@@@ -76,7 -70,7 +76,7 @@@ static int read_tree_1(struct tree *tre
                                continue;
                }
  
 -              switch (fn(entry.sha1, base->buf, base->len,
 +              switch (fn(entry.sha1, base,
                           entry.path, entry.mode, stage, context)) {
                case 0:
                        continue;
@@@ -204,7 -198,7 +204,7 @@@ int parse_tree_buffer(struct tree *item
        return 0;
  }
  
- int parse_tree(struct tree *item)
+ int parse_tree_gently(struct tree *item, int quiet_on_missing)
  {
         enum object_type type;
         void *buffer;
                return 0;
        buffer = read_sha1_file(item->object.sha1, &type, &size);
        if (!buffer)
-               return error("Could not read %s",
+               return quiet_on_missing ? -1 :
+                       error("Could not read %s",
                             sha1_to_hex(item->object.sha1));
        if (type != OBJ_TREE) {
                free(buffer);
diff --combined tree.h
index d24125f84f01cddaecbd047b24e3a710014560a4,35332fba87172f59c20b8651089b075264471025..d24786cba2ca91d0bffd63490b4f71ac4a58de13
--- 1/tree.h
--- 2/tree.h
+++ b/tree.h
@@@ -4,7 -4,6 +4,7 @@@
  #include "object.h"
  
  extern const char *tree_type;
 +struct strbuf;
  
  struct tree {
        struct object object;
@@@ -16,14 -15,18 +16,18 @@@ struct tree *lookup_tree(const unsigne
  
  int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
  
- int parse_tree(struct tree *tree);
+ int parse_tree_gently(struct tree *tree, int quiet_on_missing);
+ static inline int parse_tree(struct tree *tree)
+ {
+       return parse_tree_gently(tree, 0);
+ }
  void free_tree_buffer(struct tree *tree);
  
  /* Parses and returns the tree in the given ent, chasing tags and commits. */
  struct tree *parse_tree_indirect(const unsigned char *sha1);
  
  #define READ_TREE_RECURSIVE 1
 -typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
 +typedef int (*read_tree_fn_t)(const unsigned char *, struct strbuf *, const char *, unsigned int, int, void *);
  
  extern int read_tree_recursive(struct tree *tree,
                               const char *base, int baselen,