Merge branch 'lt/pack-object-memuse'
authorJunio C Hamano <gitster@pobox.com>
Sat, 18 Apr 2009 21:46:17 +0000 (14:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 18 Apr 2009 21:46:17 +0000 (14:46 -0700)
* lt/pack-object-memuse:
  show_object(): push path_name() call further down
  process_{tree,blob}: show objects without buffering

Conflicts:
builtin-pack-objects.c
builtin-rev-list.c
list-objects.c
list-objects.h
upload-pack.c

1  2 
builtin-pack-objects.c
builtin-rev-list.c
list-objects.c
list-objects.h
revision.c
revision.h
upload-pack.c

diff --combined builtin-pack-objects.c
index d3360ac1f8bf13a00f90d9f385d69b628471a95d,71041453f29715fdb18f2b74811f1ebfa6bf5311..419f29aa1ac2912af15fa340c88967fc0c26ffba
@@@ -78,7 -78,7 +78,7 @@@ static int progress = 1
  static int window = 10;
  static uint32_t pack_size_limit, pack_size_limit_cfg;
  static int depth = 50;
 -static int delta_search_threads = 1;
 +static int delta_search_threads;
  static int pack_to_stdout;
  static int num_preferred_base;
  static struct progress *progress_state;
@@@ -195,16 -195,16 +195,16 @@@ static int check_pack_inflate(struct pa
        int st;
  
        memset(&stream, 0, sizeof(stream));
 -      inflateInit(&stream);
 +      git_inflate_init(&stream);
        do {
                in = use_pack(p, w_curs, offset, &stream.avail_in);
                stream.next_in = in;
                stream.next_out = fakebuf;
                stream.avail_out = sizeof(fakebuf);
 -              st = inflate(&stream, Z_FINISH);
 +              st = git_inflate(&stream, Z_FINISH);
                offset += stream.next_in - in;
        } while (st == Z_OK || st == Z_BUF_ERROR);
 -      inflateEnd(&stream);
 +      git_inflate_end(&stream);
        return (st == Z_STREAM_END &&
                stream.total_out == expect &&
                stream.total_in == len) ? 0 : -1;
@@@ -286,7 -286,6 +286,7 @@@ static unsigned long write_object(struc
                                 */
  
        if (!to_reuse) {
 +              no_reuse:
                if (!usable_delta) {
                        buf = read_sha1_file(entry->idx.sha1, &type, &size);
                        if (!buf)
                struct revindex_entry *revidx;
                off_t offset;
  
 -              if (entry->delta) {
 +              if (entry->delta)
                        type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
 -                      reused_delta++;
 -              }
                hdrlen = encode_header(type, entry->size, header);
 +
                offset = entry->in_pack_offset;
                revidx = find_pack_revindex(p, offset);
                datalen = revidx[1].offset - offset;
                if (!pack_to_stdout && p->index_version > 1 &&
 -                  check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
 -                      die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
 +                  check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
 +                      error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
 +                      unuse_pack(&w_curs);
 +                      goto no_reuse;
 +              }
 +
                offset += entry->in_pack_header_size;
                datalen -= entry->in_pack_header_size;
 +              if (!pack_to_stdout && p->index_version == 1 &&
 +                  check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
 +                      error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
 +                      unuse_pack(&w_curs);
 +                      goto no_reuse;
 +              }
 +
                if (type == OBJ_OFS_DELTA) {
                        off_t ofs = entry->idx.offset - entry->delta->idx.offset;
                        unsigned pos = sizeof(dheader) - 1;
                        dheader[pos] = ofs & 127;
                        while (ofs >>= 7)
                                dheader[--pos] = 128 | (--ofs & 127);
 -                      if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
 +                      if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
 +                              unuse_pack(&w_curs);
                                return 0;
 +                      }
                        sha1write(f, header, hdrlen);
                        sha1write(f, dheader + pos, sizeof(dheader) - pos);
                        hdrlen += sizeof(dheader) - pos;
 +                      reused_delta++;
                } else if (type == OBJ_REF_DELTA) {
 -                      if (limit && hdrlen + 20 + datalen + 20 >= limit)
 +                      if (limit && hdrlen + 20 + datalen + 20 >= limit) {
 +                              unuse_pack(&w_curs);
                                return 0;
 +                      }
                        sha1write(f, header, hdrlen);
                        sha1write(f, entry->delta->idx.sha1, 20);
                        hdrlen += 20;
 +                      reused_delta++;
                } else {
 -                      if (limit && hdrlen + datalen + 20 >= limit)
 +                      if (limit && hdrlen + datalen + 20 >= limit) {
 +                              unuse_pack(&w_curs);
                                return 0;
 +                      }
                        sha1write(f, header, hdrlen);
                }
 -
 -              if (!pack_to_stdout && p->index_version == 1 &&
 -                  check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
 -                      die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
                copy_pack_data(f, p, &w_curs, offset, datalen);
                unuse_pack(&w_curs);
                reused++;
@@@ -488,8 -473,9 +488,8 @@@ static void write_pack_file(void
                } else {
                        char tmpname[PATH_MAX];
                        int fd;
 -                      snprintf(tmpname, sizeof(tmpname),
 -                               "%s/pack/tmp_pack_XXXXXX", get_object_directory());
 -                      fd = xmkstemp(tmpname);
 +                      fd = odb_mkstemp(tmpname, sizeof(tmpname),
 +                                       "pack/tmp_pack_XXXXXX");
                        pack_tmp_name = xstrdup(tmpname);
                        f = sha1fd(fd, pack_tmp_name);
                }
@@@ -1031,11 -1017,9 +1031,11 @@@ static void check_object(struct object_
                 * We want in_pack_type even if we do not reuse delta
                 * since non-delta representations could still be reused.
                 */
 -              used = unpack_object_header_gently(buf, avail,
 +              used = unpack_object_header_buffer(buf, avail,
                                                   &entry->in_pack_type,
                                                   &entry->size);
 +              if (used == 0)
 +                      goto give_up;
  
                /*
                 * Determine if this is a delta and if so whether we can
                        /* Not a delta hence we've already got all we need. */
                        entry->type = entry->in_pack_type;
                        entry->in_pack_header_size = used;
 +                      if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
 +                              goto give_up;
                        unuse_pack(&w_curs);
                        return;
                case OBJ_REF_DELTA:
                        ofs = c & 127;
                        while (c & 128) {
                                ofs += 1;
 -                              if (!ofs || MSB(ofs, 7))
 -                                      die("delta base offset overflow in pack for %s",
 -                                          sha1_to_hex(entry->idx.sha1));
 +                              if (!ofs || MSB(ofs, 7)) {
 +                                      error("delta base offset overflow in pack for %s",
 +                                            sha1_to_hex(entry->idx.sha1));
 +                                      goto give_up;
 +                              }
                                c = buf[used_0++];
                                ofs = (ofs << 7) + (c & 127);
                        }
 -                      if (ofs >= entry->in_pack_offset)
 -                              die("delta base offset out of bound for %s",
 -                                  sha1_to_hex(entry->idx.sha1));
                        ofs = entry->in_pack_offset - ofs;
 +                      if (ofs <= 0 || ofs >= entry->in_pack_offset) {
 +                              error("delta base offset out of bound for %s",
 +                                    sha1_to_hex(entry->idx.sha1));
 +                              goto give_up;
 +                      }
                        if (reuse_delta && !entry->preferred_base) {
                                struct revindex_entry *revidx;
                                revidx = find_pack_revindex(p, ofs);
 +                              if (!revidx)
 +                                      goto give_up;
                                base_ref = nth_packed_object_sha1(p, revidx->nr);
                        }
                        entry->in_pack_header_size = used + used_0;
                         */
                        entry->type = entry->in_pack_type;
                        entry->delta = base_entry;
 +                      entry->delta_size = entry->size;
                        entry->delta_sibling = base_entry->delta_child;
                        base_entry->delta_child = entry;
                        unuse_pack(&w_curs);
                         */
                        entry->size = get_size_from_delta(p, &w_curs,
                                        entry->in_pack_offset + entry->in_pack_header_size);
 +                      if (entry->size == 0)
 +                              goto give_up;
                        unuse_pack(&w_curs);
                        return;
                }
                 * with sha1_object_info() to find about the object type
                 * at this point...
                 */
 +              give_up:
                unuse_pack(&w_curs);
        }
  
@@@ -1412,10 -1384,12 +1412,10 @@@ static void find_deltas(struct object_e
                        int window, int depth, unsigned *processed)
  {
        uint32_t i, idx = 0, count = 0;
 -      unsigned int array_size = window * sizeof(struct unpacked);
        struct unpacked *array;
        unsigned long mem_usage = 0;
  
 -      array = xmalloc(array_size);
 -      memset(array, 0, array_size);
 +      array = xcalloc(window, sizeof(struct unpacked));
  
        for (;;) {
                struct object_entry *entry;
@@@ -1611,18 -1585,11 +1611,18 @@@ static void ll_find_deltas(struct objec
                find_deltas(list, &list_size, window, depth, processed);
                return;
        }
 +      if (progress > pack_to_stdout)
 +              fprintf(stderr, "Delta compression using up to %d threads.\n",
 +                              delta_search_threads);
  
        /* Partition the work amongst work threads. */
        for (i = 0; i < delta_search_threads; i++) {
                unsigned sub_size = list_size / (delta_search_threads - i);
  
 +              /* don't use too small segments or no deltas will be found */
 +              if (sub_size < 2*window && i+1 < delta_search_threads)
 +                      sub_size = 0;
 +
                p[i].window = window;
                p[i].depth = depth;
                p[i].processed = processed;
@@@ -1748,16 -1715,6 +1748,16 @@@ static void prepare_pack(int window, in
  
        get_object_details();
  
 +      /*
 +       * If we're locally repacking then we need to be doubly careful
 +       * from now on in order to make sure no stealth corruption gets
 +       * propagated to the new pack.  Clients receiving streamed packs
 +       * should validate everything they get anyway so no need to incur
 +       * the additional cost here in that case.
 +       */
 +      if (!pack_to_stdout)
 +              do_check_packed_object_crc = 1;
 +
        if (!nr_objects || !window || !depth)
                return;
  
                        if (entry->type < 0)
                                die("unable to get type of object %s",
                                    sha1_to_hex(entry->idx.sha1));
 +              } else {
 +                      if (entry->type < 0) {
 +                              /*
 +                               * This object is not found, but we
 +                               * don't have to include it anyway.
 +                               */
 +                              continue;
 +                      }
                }
  
                delta_list[n++] = entry;
@@@ -1901,19 -1850,25 +1901,25 @@@ static void read_object_list_from_stdin
  
  #define OBJECT_ADDED (1u<<20)
  
 -static void show_commit(struct commit *commit)
 +static void show_commit(struct commit *commit, void *data)
  {
        add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
  }
  
- static void show_object(struct object_array_entry *p, void *data)
+ static void show_object(struct object *obj, const struct name_path *path, const char *last)
  {
-       add_preferred_base_object(p->name);
-       add_object_entry(p->item->sha1, p->item->type, p->name, 0);
-       p->item->flags |= OBJECT_ADDED;
-       free((char *)p->name);
-       p->name = NULL;
+       char *name = path_name(path, last);
+       add_preferred_base_object(name);
+       add_object_entry(obj->sha1, obj->type, name, 0);
+       obj->flags |= OBJECT_ADDED;
+       /*
+        * We will have generated the hash from the name,
+        * but not saved a pointer to it - we can free it
+        */
+       free((char *)name);
  }
  
  static void show_edge(struct commit *commit)
@@@ -1968,7 -1923,11 +1974,7 @@@ static void add_objects_in_unpacked_pac
                const unsigned char *sha1;
                struct object *o;
  
 -              for (i = 0; i < revs->num_ignore_packed; i++) {
 -                      if (matches_pack_name(p, revs->ignore_packed[i]))
 -                              break;
 -              }
 -              if (revs->num_ignore_packed <= i)
 +              if (!p->pack_local || p->pack_keep)
                        continue;
                if (open_pack_index(p))
                        die("cannot open pack index");
        free(in_pack.array);
  }
  
 +static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 +{
 +      static struct packed_git *last_found = (void *)1;
 +      struct packed_git *p;
 +
 +      p = (last_found != (void *)1) ? last_found : packed_git;
 +
 +      while (p) {
 +              if ((!p->pack_local || p->pack_keep) &&
 +                      find_pack_entry_one(sha1, p)) {
 +                      last_found = p;
 +                      return 1;
 +              }
 +              if (p == last_found)
 +                      p = packed_git;
 +              else
 +                      p = p->next;
 +              if (p == last_found)
 +                      p = p->next;
 +      }
 +      return 0;
 +}
 +
  static void loosen_unused_packed_objects(struct rev_info *revs)
  {
        struct packed_git *p;
        const unsigned char *sha1;
  
        for (p = packed_git; p; p = p->next) {
 -              for (i = 0; i < revs->num_ignore_packed; i++) {
 -                      if (matches_pack_name(p, revs->ignore_packed[i]))
 -                              break;
 -              }
 -              if (revs->num_ignore_packed <= i)
 +              if (!p->pack_local || p->pack_keep)
                        continue;
  
                if (open_pack_index(p))
  
                for (i = 0; i < p->num_objects; i++) {
                        sha1 = nth_packed_object_sha1(p, i);
 -                      if (!locate_object_entry(sha1))
 +                      if (!locate_object_entry(sha1) &&
 +                              !has_sha1_pack_kept_or_nonlocal(sha1))
                                if (force_object_loose(sha1, p->mtime))
                                        die("unable to force loose object");
                }
@@@ -2073,7 -2012,7 +2079,7 @@@ static void get_object_list(int ac, con
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
 -      traverse_commit_list(&revs, show_commit, show_object);
 +      traverse_commit_list(&revs, show_commit, show_object, NULL);
  
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
@@@ -2226,6 -2165,7 +2232,6 @@@ int cmd_pack_objects(int argc, const ch
                        continue;
                }
                if (!strcmp("--unpacked", arg) ||
 -                  !prefixcmp(arg, "--unpacked=") ||
                    !strcmp("--reflog", arg) ||
                    !strcmp("--all", arg)) {
                        use_internal_rev_list = 1;
diff --combined builtin-rev-list.c
index 193993cf4494aca98d5e57ce80bc2c99b5cba948,aa3c962e56bd08e8e2a632b7170283d05efb69a2..38a8f234de8120d15eaff9ee0d342e334ee006ca
@@@ -1,12 -1,20 +1,12 @@@
  #include "cache.h"
 -#include "refs.h"
 -#include "tag.h"
  #include "commit.h"
 -#include "tree.h"
 -#include "blob.h"
 -#include "tree-walk.h"
  #include "diff.h"
  #include "revision.h"
  #include "list-objects.h"
  #include "builtin.h"
  #include "log-tree.h"
  #include "graph.h"
 -
 -/* bits #0-15 in revision.h */
 -
 -#define COUNTED               (1u<<16)
 +#include "bisect.h"
  
  static const char rev_list_usage[] =
  "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
  "    --bisect-all"
  ;
  
 -static struct rev_info revs;
 -
 -static int bisect_list;
 -static int show_timestamp;
 -static int hdr_termination;
 -static const char *header_prefix;
 -
 -static void finish_commit(struct commit *commit);
 -static void show_commit(struct commit *commit)
 +static void finish_commit(struct commit *commit, void *data);
 +static void show_commit(struct commit *commit, void *data)
  {
 -      graph_show_commit(revs.graph);
 +      struct rev_list_info *info = data;
 +      struct rev_info *revs = info->revs;
 +
 +      graph_show_commit(revs->graph);
  
 -      if (show_timestamp)
 +      if (info->show_timestamp)
                printf("%lu ", commit->date);
 -      if (header_prefix)
 -              fputs(header_prefix, stdout);
 +      if (info->header_prefix)
 +              fputs(info->header_prefix, stdout);
  
 -      if (!revs.graph) {
 +      if (!revs->graph) {
                if (commit->object.flags & BOUNDARY)
                        putchar('-');
                else if (commit->object.flags & UNINTERESTING)
                        putchar('^');
 -              else if (revs.left_right) {
 +              else if (revs->left_right) {
                        if (commit->object.flags & SYMMETRIC_LEFT)
                                putchar('<');
                        else
                                putchar('>');
                }
        }
 -      if (revs.abbrev_commit && revs.abbrev)
 -              fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
 +      if (revs->abbrev_commit && revs->abbrev)
 +              fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
                      stdout);
        else
                fputs(sha1_to_hex(commit->object.sha1), stdout);
 -      if (revs.print_parents) {
 +      if (revs->print_parents) {
                struct commit_list *parents = commit->parents;
                while (parents) {
                        printf(" %s", sha1_to_hex(parents->item->object.sha1));
                        parents = parents->next;
                }
        }
 -      if (revs.children.name) {
 +      if (revs->children.name) {
                struct commit_list *children;
  
 -              children = lookup_decoration(&revs.children, &commit->object);
 +              children = lookup_decoration(&revs->children, &commit->object);
                while (children) {
                        printf(" %s", sha1_to_hex(children->item->object.sha1));
                        children = children->next;
                }
        }
 -      show_decorations(commit);
 -      if (revs.commit_format == CMIT_FMT_ONELINE)
 +      show_decorations(revs, commit);
 +      if (revs->commit_format == CMIT_FMT_ONELINE)
                putchar(' ');
        else
                putchar('\n');
  
 -      if (revs.verbose_header && commit->buffer) {
 -              struct strbuf buf;
 -              strbuf_init(&buf, 0);
 -              pretty_print_commit(revs.commit_format, commit,
 -                                  &buf, revs.abbrev, NULL, NULL,
 -                                  revs.date_mode, 0);
 -              if (revs.graph) {
 +      if (revs->verbose_header && commit->buffer) {
 +              struct strbuf buf = STRBUF_INIT;
 +              pretty_print_commit(revs->commit_format, commit,
 +                                  &buf, revs->abbrev, NULL, NULL,
 +                                  revs->date_mode, 0);
 +              if (revs->graph) {
                        if (buf.len) {
 -                              if (revs.commit_format != CMIT_FMT_ONELINE)
 -                                      graph_show_oneline(revs.graph);
 +                              if (revs->commit_format != CMIT_FMT_ONELINE)
 +                                      graph_show_oneline(revs->graph);
  
 -                              graph_show_commit_msg(revs.graph, &buf);
 +                              graph_show_commit_msg(revs->graph, &buf);
  
                                /*
                                 * Add a newline after the commit message.
                                 * format doesn't explicitly end in a newline.)
                                 */
                                if (buf.len && buf.buf[buf.len - 1] == '\n')
 -                                      graph_show_padding(revs.graph);
 +                                      graph_show_padding(revs->graph);
                                putchar('\n');
                        } else {
                                /*
                                 * the rest of the graph output for this
                                 * commit.
                                 */
 -                              if (graph_show_remainder(revs.graph))
 +                              if (graph_show_remainder(revs->graph))
                                        putchar('\n');
                        }
                } else {
                        if (buf.len)
 -                              printf("%s%c", buf.buf, hdr_termination);
 +                              printf("%s%c", buf.buf, info->hdr_termination);
                }
                strbuf_release(&buf);
        } else {
 -              if (graph_show_remainder(revs.graph))
 +              if (graph_show_remainder(revs->graph))
                        putchar('\n');
        }
        maybe_flush_or_die(stdout, "stdout");
 -      finish_commit(commit);
 +      finish_commit(commit, data);
  }
  
 -static void finish_commit(struct commit *commit)
 +static void finish_commit(struct commit *commit, void *data)
  {
        if (commit->parents) {
                free_commit_list(commit->parents);
        commit->buffer = NULL;
  }
  
- static void finish_object(struct object_array_entry *p, void *data)
+ static void finish_object(struct object *obj, const struct name_path *path, const char *name)
  {
-       if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
-               die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
+       if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1))
+               die("missing blob object '%s'", sha1_to_hex(obj->sha1));
  }
  
- static void show_object(struct object_array_entry *p, void *data)
+ static void show_object(struct object *obj, const struct name_path *path, const char *component)
  {
+       char *name = path_name(path, component);
        /* An object with name "foo\n0000000..." can be used to
         * confuse downstream "git pack-objects" very badly.
         */
-       const char *ep = strchr(p->name, '\n');
+       const char *ep = strchr(name, '\n');
  
-       finish_object(p, data);
+       finish_object(obj, path, name);
        if (ep) {
-               printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
-                      (int) (ep - p->name),
-                      p->name);
+               printf("%s %.*s\n", sha1_to_hex(obj->sha1),
+                      (int) (ep - name),
+                      name);
        }
        else
-               printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
+               printf("%s %s\n", sha1_to_hex(obj->sha1), name);
+       free(name);
  }
  
  static void show_edge(struct commit *commit)
        printf("-%s\n", sha1_to_hex(commit->object.sha1));
  }
  
 -/*
 - * This is a truly stupid algorithm, but it's only
 - * used for bisection, and we just don't care enough.
 - *
 - * We care just barely enough to avoid recursing for
 - * non-merge entries.
 - */
 -static int count_distance(struct commit_list *entry)
 -{
 -      int nr = 0;
 -
 -      while (entry) {
 -              struct commit *commit = entry->item;
 -              struct commit_list *p;
 -
 -              if (commit->object.flags & (UNINTERESTING | COUNTED))
 -                      break;
 -              if (!(commit->object.flags & TREESAME))
 -                      nr++;
 -              commit->object.flags |= COUNTED;
 -              p = commit->parents;
 -              entry = p;
 -              if (p) {
 -                      p = p->next;
 -                      while (p) {
 -                              nr += count_distance(p);
 -                              p = p->next;
 -                      }
 -              }
 -      }
 -
 -      return nr;
 -}
 -
 -static void clear_distance(struct commit_list *list)
 +static inline int log2i(int n)
  {
 -      while (list) {
 -              struct commit *commit = list->item;
 -              commit->object.flags &= ~COUNTED;
 -              list = list->next;
 -      }
 -}
 +      int log2 = 0;
  
 -#define DEBUG_BISECT 0
 +      for (; n > 1; n >>= 1)
 +              log2++;
  
 -static inline int weight(struct commit_list *elem)
 -{
 -      return *((int*)(elem->item->util));
 +      return log2;
  }
  
 -static inline void weight_set(struct commit_list *elem, int weight)
 +static inline int exp2i(int n)
  {
 -      *((int*)(elem->item->util)) = weight;
 +      return 1 << n;
  }
  
 -static int count_interesting_parents(struct commit *commit)
 +/*
 + * Estimate the number of bisect steps left (after the current step)
 + *
 + * For any x between 0 included and 2^n excluded, the probability for
 + * n - 1 steps left looks like:
 + *
 + * P(2^n + x) == (2^n - x) / (2^n + x)
 + *
 + * and P(2^n + x) < 0.5 means 2^n < 3x
 + */
 +static int estimate_bisect_steps(int all)
  {
 -      struct commit_list *p;
 -      int count;
 +      int n, x, e;
  
 -      for (count = 0, p = commit->parents; p; p = p->next) {
 -              if (p->item->object.flags & UNINTERESTING)
 -                      continue;
 -              count++;
 -      }
 -      return count;
 -}
 -
 -static inline int halfway(struct commit_list *p, int nr)
 -{
 -      /*
 -       * Don't short-cut something we are not going to return!
 -       */
 -      if (p->item->object.flags & TREESAME)
 +      if (all < 3)
                return 0;
 -      if (DEBUG_BISECT)
 -              return 0;
 -      /*
 -       * 2 and 3 are halfway of 5.
 -       * 3 is halfway of 6 but 2 and 4 are not.
 -       */
 -      switch (2 * weight(p) - nr) {
 -      case -1: case 0: case 1:
 -              return 1;
 -      default:
 -              return 0;
 -      }
 -}
 -
 -#if !DEBUG_BISECT
 -#define show_list(a,b,c,d) do { ; } while (0)
 -#else
 -static void show_list(const char *debug, int counted, int nr,
 -                    struct commit_list *list)
 -{
 -      struct commit_list *p;
 -
 -      fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
 -
 -      for (p = list; p; p = p->next) {
 -              struct commit_list *pp;
 -              struct commit *commit = p->item;
 -              unsigned flags = commit->object.flags;
 -              enum object_type type;
 -              unsigned long size;
 -              char *buf = read_sha1_file(commit->object.sha1, &type, &size);
 -              char *ep, *sp;
 -
 -              fprintf(stderr, "%c%c%c ",
 -                      (flags & TREESAME) ? ' ' : 'T',
 -                      (flags & UNINTERESTING) ? 'U' : ' ',
 -                      (flags & COUNTED) ? 'C' : ' ');
 -              if (commit->util)
 -                      fprintf(stderr, "%3d", weight(p));
 -              else
 -                      fprintf(stderr, "---");
 -              fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
 -              for (pp = commit->parents; pp; pp = pp->next)
 -                      fprintf(stderr, " %.*s", 8,
 -                              sha1_to_hex(pp->item->object.sha1));
 -
 -              sp = strstr(buf, "\n\n");
 -              if (sp) {
 -                      sp += 2;
 -                      for (ep = sp; *ep && *ep != '\n'; ep++)
 -                              ;
 -                      fprintf(stderr, " %.*s", (int)(ep - sp), sp);
 -              }
 -              fprintf(stderr, "\n");
 -      }
 -}
 -#endif /* DEBUG_BISECT */
 -
 -static struct commit_list *best_bisection(struct commit_list *list, int nr)
 -{
 -      struct commit_list *p, *best;
 -      int best_distance = -1;
 -
 -      best = list;
 -      for (p = list; p; p = p->next) {
 -              int distance;
 -              unsigned flags = p->item->object.flags;
 -
 -              if (flags & TREESAME)
 -                      continue;
 -              distance = weight(p);
 -              if (nr - distance < distance)
 -                      distance = nr - distance;
 -              if (distance > best_distance) {
 -                      best = p;
 -                      best_distance = distance;
 -              }
 -      }
 -
 -      return best;
 -}
 -
 -struct commit_dist {
 -      struct commit *commit;
 -      int distance;
 -};
  
 -static int compare_commit_dist(const void *a_, const void *b_)
 -{
 -      struct commit_dist *a, *b;
 +      n = log2i(all);
 +      e = exp2i(n);
 +      x = all - e;
  
 -      a = (struct commit_dist *)a_;
 -      b = (struct commit_dist *)b_;
 -      if (a->distance != b->distance)
 -              return b->distance - a->distance; /* desc sort */
 -      return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
 +      return (e < 3 * x) ? n : n - 1;
  }
  
 -static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
 +static void show_tried_revs(struct commit_list *tried, int stringed)
  {
 -      struct commit_list *p;
 -      struct commit_dist *array = xcalloc(nr, sizeof(*array));
 -      int cnt, i;
 -
 -      for (p = list, cnt = 0; p; p = p->next) {
 -              int distance;
 -              unsigned flags = p->item->object.flags;
 -
 -              if (flags & TREESAME)
 -                      continue;
 -              distance = weight(p);
 -              if (nr - distance < distance)
 -                      distance = nr - distance;
 -              array[cnt].commit = p->item;
 -              array[cnt].distance = distance;
 -              cnt++;
 -      }
 -      qsort(array, cnt, sizeof(*array), compare_commit_dist);
 -      for (p = list, i = 0; i < cnt; i++) {
 -              struct name_decoration *r = xmalloc(sizeof(*r) + 100);
 -              struct object *obj = &(array[i].commit->object);
 -
 -              sprintf(r->name, "dist=%d", array[i].distance);
 -              r->next = add_decoration(&name_decoration, obj, r);
 -              p->item = array[i].commit;
 -              p = p->next;
 +      printf("bisect_tried='");
 +      for (;tried; tried = tried->next) {
 +              char *format = tried->next ? "%s|" : "%s";
 +              printf(format, sha1_to_hex(tried->item->object.sha1));
        }
 -      if (p)
 -              p->next = NULL;
 -      free(array);
 -      return list;
 +      printf(stringed ? "' &&\n" : "'\n");
  }
  
 -/*
 - * zero or positive weight is the number of interesting commits it can
 - * reach, including itself.  Especially, weight = 0 means it does not
 - * reach any tree-changing commits (e.g. just above uninteresting one
 - * but traversal is with pathspec).
 - *
 - * weight = -1 means it has one parent and its distance is yet to
 - * be computed.
 - *
 - * weight = -2 means it has more than one parent and its distance is
 - * unknown.  After running count_distance() first, they will get zero
 - * or positive distance.
 - */
 -static struct commit_list *do_find_bisection(struct commit_list *list,
 -                                           int nr, int *weights,
 -                                           int find_all)
 +int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
  {
 -      int n, counted;
 -      struct commit_list *p;
 -
 -      counted = 0;
 -
 -      for (n = 0, p = list; p; p = p->next) {
 -              struct commit *commit = p->item;
 -              unsigned flags = commit->object.flags;
 -
 -              p->item->util = &weights[n++];
 -              switch (count_interesting_parents(commit)) {
 -              case 0:
 -                      if (!(flags & TREESAME)) {
 -                              weight_set(p, 1);
 -                              counted++;
 -                              show_list("bisection 2 count one",
 -                                        counted, nr, list);
 -                      }
 -                      /*
 -                       * otherwise, it is known not to reach any
 -                       * tree-changing commit and gets weight 0.
 -                       */
 -                      break;
 -              case 1:
 -                      weight_set(p, -1);
 -                      break;
 -              default:
 -                      weight_set(p, -2);
 -                      break;
 -              }
 -      }
 +      int cnt, flags = info->bisect_show_flags;
 +      char hex[41] = "", *format;
 +      struct commit_list *tried;
 +      struct rev_info *revs = info->revs;
  
 -      show_list("bisection 2 initialize", counted, nr, list);
 +      if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
 +              return 1;
 +
 +      revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL);
  
        /*
 -       * If you have only one parent in the resulting set
 -       * then you can reach one commit more than that parent
 -       * can reach.  So we do not have to run the expensive
 -       * count_distance() for single strand of pearls.
 -       *
 -       * However, if you have more than one parents, you cannot
 -       * just add their distance and one for yourself, since
 -       * they usually reach the same ancestor and you would
 -       * end up counting them twice that way.
 -       *
 -       * So we will first count distance of merges the usual
 -       * way, and then fill the blanks using cheaper algorithm.
 +       * revs->commits can reach "reaches" commits among
 +       * "all" commits.  If it is good, then there are
 +       * (all-reaches) commits left to be bisected.
 +       * On the other hand, if it is bad, then the set
 +       * to bisect is "reaches".
 +       * A bisect set of size N has (N-1) commits further
 +       * to test, as we already know one bad one.
         */
 -      for (p = list; p; p = p->next) {
 -              if (p->item->object.flags & UNINTERESTING)
 -                      continue;
 -              if (weight(p) != -2)
 -                      continue;
 -              weight_set(p, count_distance(p));
 -              clear_distance(list);
 -
 -              /* Does it happen to be at exactly half-way? */
 -              if (!find_all && halfway(p, nr))
 -                      return p;
 -              counted++;
 -      }
 +      cnt = all - reaches;
 +      if (cnt < reaches)
 +              cnt = reaches;
  
 -      show_list("bisection 2 count_distance", counted, nr, list);
 +      if (revs->commits)
 +              strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
  
 -      while (counted < nr) {
 -              for (p = list; p; p = p->next) {
 -                      struct commit_list *q;
 -                      unsigned flags = p->item->object.flags;
 -
 -                      if (0 <= weight(p))
 -                              continue;
 -                      for (q = p->item->parents; q; q = q->next) {
 -                              if (q->item->object.flags & UNINTERESTING)
 -                                      continue;
 -                              if (0 <= weight(q))
 -                                      break;
 -                      }
 -                      if (!q)
 -                              continue;
 -
 -                      /*
 -                       * weight for p is unknown but q is known.
 -                       * add one for p itself if p is to be counted,
 -                       * otherwise inherit it from q directly.
 -                       */
 -                      if (!(flags & TREESAME)) {
 -                              weight_set(p, weight(q)+1);
 -                              counted++;
 -                              show_list("bisection 2 count one",
 -                                        counted, nr, list);
 -                      }
 -                      else
 -                              weight_set(p, weight(q));
 -
 -                      /* Does it happen to be at exactly half-way? */
 -                      if (!find_all && halfway(p, nr))
 -                              return p;
 -              }
 +      if (flags & BISECT_SHOW_ALL) {
 +              traverse_commit_list(revs, show_commit, show_object, info);
 +              printf("------\n");
        }
  
 -      show_list("bisection 2 counted all", counted, nr, list);
 -
 -      if (!find_all)
 -              return best_bisection(list, nr);
 -      else
 -              return best_bisection_sorted(list, nr);
 -}
 -
 -static struct commit_list *find_bisection(struct commit_list *list,
 -                                        int *reaches, int *all,
 -                                        int find_all)
 -{
 -      int nr, on_list;
 -      struct commit_list *p, *best, *next, *last;
 -      int *weights;
 -
 -      show_list("bisection 2 entry", 0, 0, list);
 +      if (flags & BISECT_SHOW_TRIED)
 +              show_tried_revs(tried, flags & BISECT_SHOW_STRINGED);
 +      format = (flags & BISECT_SHOW_STRINGED) ?
 +              "bisect_rev=%s &&\n"
 +              "bisect_nr=%d &&\n"
 +              "bisect_good=%d &&\n"
 +              "bisect_bad=%d &&\n"
 +              "bisect_all=%d &&\n"
 +              "bisect_steps=%d\n"
 +              :
 +              "bisect_rev=%s\n"
 +              "bisect_nr=%d\n"
 +              "bisect_good=%d\n"
 +              "bisect_bad=%d\n"
 +              "bisect_all=%d\n"
 +              "bisect_steps=%d\n";
 +      printf(format,
 +             hex,
 +             cnt - 1,
 +             all - reaches - 1,
 +             reaches - 1,
 +             all,
 +             estimate_bisect_steps(all));
  
 -      /*
 -       * Count the number of total and tree-changing items on the
 -       * list, while reversing the list.
 -       */
 -      for (nr = on_list = 0, last = NULL, p = list;
 -           p;
 -           p = next) {
 -              unsigned flags = p->item->object.flags;
 -
 -              next = p->next;
 -              if (flags & UNINTERESTING)
 -                      continue;
 -              p->next = last;
 -              last = p;
 -              if (!(flags & TREESAME))
 -                      nr++;
 -              on_list++;
 -      }
 -      list = last;
 -      show_list("bisection 2 sorted", 0, nr, list);
 -
 -      *all = nr;
 -      weights = xcalloc(on_list, sizeof(*weights));
 -
 -      /* Do the real work of finding bisection commit. */
 -      best = do_find_bisection(list, nr, weights, find_all);
 -      if (best) {
 -              if (!find_all)
 -                      best->next = NULL;
 -              *reaches = weight(best);
 -      }
 -      free(weights);
 -      return best;
 +      return 0;
  }
  
  int cmd_rev_list(int argc, const char **argv, const char *prefix)
  {
 -      struct commit_list *list;
 +      struct rev_info revs;
 +      struct rev_list_info info;
        int i;
        int read_from_stdin = 0;
 +      int bisect_list = 0;
        int bisect_show_vars = 0;
        int bisect_find_all = 0;
        int quiet = 0;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
        argc = setup_revisions(argc, argv, &revs, NULL);
  
 +      memset(&info, 0, sizeof(info));
 +      info.revs = &revs;
 +
        quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
                        continue;
                }
                if (!strcmp(arg, "--timestamp")) {
 -                      show_timestamp = 1;
 +                      info.show_timestamp = 1;
                        continue;
                }
                if (!strcmp(arg, "--bisect")) {
                if (!strcmp(arg, "--bisect-all")) {
                        bisect_list = 1;
                        bisect_find_all = 1;
 +                      info.bisect_show_flags = BISECT_SHOW_ALL;
 +                      revs.show_decorations = 1;
                        continue;
                }
                if (!strcmp(arg, "--bisect-vars")) {
        }
        if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
                /* The command line has a --pretty  */
 -              hdr_termination = '\n';
 +              info.hdr_termination = '\n';
                if (revs.commit_format == CMIT_FMT_ONELINE)
 -                      header_prefix = "";
 +                      info.header_prefix = "";
                else
 -                      header_prefix = "commit ";
 +                      info.header_prefix = "commit ";
        }
        else if (revs.verbose_header)
                /* Only --header was specified */
                revs.commit_format = CMIT_FMT_RAW;
  
 -      list = revs.commits;
 -
 -      if ((!list &&
 +      if ((!revs.commits &&
             (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
              !revs.pending.nr)) ||
            revs.diff)
  
                revs.commits = find_bisection(revs.commits, &reaches, &all,
                                              bisect_find_all);
 -              if (bisect_show_vars) {
 -                      int cnt;
 -                      char hex[41];
 -                      if (!revs.commits)
 -                              return 1;
 -                      /*
 -                       * revs.commits can reach "reaches" commits among
 -                       * "all" commits.  If it is good, then there are
 -                       * (all-reaches) commits left to be bisected.
 -                       * On the other hand, if it is bad, then the set
 -                       * to bisect is "reaches".
 -                       * A bisect set of size N has (N-1) commits further
 -                       * to test, as we already know one bad one.
 -                       */
 -                      cnt = all - reaches;
 -                      if (cnt < reaches)
 -                              cnt = reaches;
 -                      strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
 -
 -                      if (bisect_find_all) {
 -                              traverse_commit_list(&revs, show_commit, show_object);
 -                              printf("------\n");
 -                      }
  
 -                      printf("bisect_rev=%s\n"
 -                             "bisect_nr=%d\n"
 -                             "bisect_good=%d\n"
 -                             "bisect_bad=%d\n"
 -                             "bisect_all=%d\n",
 -                             hex,
 -                             cnt - 1,
 -                             all - reaches - 1,
 -                             reaches - 1,
 -                             all);
 -                      return 0;
 -              }
 +              if (bisect_show_vars)
 +                      return show_bisect_vars(&info, reaches, all);
        }
  
        traverse_commit_list(&revs,
 -              quiet ? finish_commit : show_commit,
 -              quiet ? finish_object : show_object);
 +                           quiet ? finish_commit : show_commit,
 +                           quiet ? finish_object : show_object,
 +                           &info);
  
        return 0;
  }
diff --combined list-objects.c
index 433394a107fe682b6adfcb122ef182321c4f5947,30ded3d4dd9d41432eba317ccc8765746b7d5c95..8953548c07bb36f20798c7ca344d07960c22618c
@@@ -10,7 -10,7 +10,7 @@@
  
  static void process_blob(struct rev_info *revs,
                         struct blob *blob,
-                        struct object_array *p,
+                        show_object_fn show,
                         struct name_path *path,
                         const char *name)
  {
@@@ -23,7 -23,7 +23,7 @@@
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
        obj->flags |= SEEN;
-       add_object(obj, p, path, name);
+       show(obj, path, name);
  }
  
  /*
@@@ -50,7 -50,7 +50,7 @@@
   */
  static void process_gitlink(struct rev_info *revs,
                            const unsigned char *sha1,
-                           struct object_array *p,
+                           show_object_fn show,
                            struct name_path *path,
                            const char *name)
  {
@@@ -59,7 -59,7 +59,7 @@@
  
  static void process_tree(struct rev_info *revs,
                         struct tree *tree,
-                        struct object_array *p,
+                        show_object_fn show,
                         struct name_path *path,
                         const char *name)
  {
@@@ -77,7 -77,7 +77,7 @@@
        if (parse_tree(tree) < 0)
                die("bad tree object %s", sha1_to_hex(obj->sha1));
        obj->flags |= SEEN;
-       add_object(obj, p, path, name);
+       show(obj, path, name);
        me.up = path;
        me.elem = name;
        me.elem_len = strlen(name);
                if (S_ISDIR(entry.mode))
                        process_tree(revs,
                                     lookup_tree(entry.sha1),
-                                    p, &me, entry.path);
+                                    show, &me, entry.path);
                else if (S_ISGITLINK(entry.mode))
                        process_gitlink(revs, entry.sha1,
-                                       p, &me, entry.path);
+                                       show, &me, entry.path);
                else
                        process_blob(revs,
                                     lookup_blob(entry.sha1),
-                                    p, &me, entry.path);
+                                    show, &me, entry.path);
        }
        free(tree->buffer);
        tree->buffer = NULL;
@@@ -134,18 -134,21 +134,22 @@@ void mark_edges_uninteresting(struct co
        }
  }
  
+ static void add_pending_tree(struct rev_info *revs, struct tree *tree)
+ {
+       add_pending_object(revs, &tree->object, "");
+ }
  void traverse_commit_list(struct rev_info *revs,
                          show_commit_fn show_commit,
 -                        show_object_fn show_object)
 +                        show_object_fn show_object,
 +                        void *data)
  {
        int i;
        struct commit *commit;
-       struct object_array objects = { 0, 0, NULL };
  
        while ((commit = get_revision(revs)) != NULL) {
-               process_tree(revs, commit->tree, &objects, NULL, "");
+               add_pending_tree(revs, commit->tree);
 -              show_commit(commit);
 +              show_commit(commit, data);
        }
        for (i = 0; i < revs->pending.nr; i++) {
                struct object_array_entry *pending = revs->pending.objects + i;
                        continue;
                if (obj->type == OBJ_TAG) {
                        obj->flags |= SEEN;
-                       add_object_array(obj, name, &objects);
+                       show_object(obj, NULL, name);
                        continue;
                }
                if (obj->type == OBJ_TREE) {
-                       process_tree(revs, (struct tree *)obj, &objects,
+                       process_tree(revs, (struct tree *)obj, show_object,
                                     NULL, name);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
-                       process_blob(revs, (struct blob *)obj, &objects,
+                       process_blob(revs, (struct blob *)obj, show_object,
                                     NULL, name);
                        continue;
                }
                die("unknown pending object %s (%s)",
                    sha1_to_hex(obj->sha1), name);
        }
-       for (i = 0; i < objects.nr; i++)
-               show_object(&objects.objects[i], data);
-       free(objects.objects);
        if (revs->pending.nr) {
                free(revs->pending.objects);
                revs->pending.nr = 0;
diff --combined list-objects.h
index 47fae2e4683e604b81c530a80494033a2e36ca35,0b2de64301b59b5e27ca738242c92109332f9e07..d65dbf03e657facb29a2846144eda2fa3687bc2f
@@@ -1,11 -1,11 +1,11 @@@
  #ifndef LIST_OBJECTS_H
  #define LIST_OBJECTS_H
  
 -typedef void (*show_commit_fn)(struct commit *);
 +typedef void (*show_commit_fn)(struct commit *, void *);
- typedef void (*show_object_fn)(struct object_array_entry *, void *);
+ typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *);
  typedef void (*show_edge_fn)(struct commit *);
  
 -void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn);
 +void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
  
  void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
  
diff --combined revision.c
index b6215cc72c77baa3f72d652e8457d3ace6e5f439,69d5fd4784eb849221e3bd12d33ab48de45d431f..bd0ea34af0c229f0e874503cb3146957f1c61853
  #include "reflog-walk.h"
  #include "patch-ids.h"
  #include "decorate.h"
 +#include "log-tree.h"
  
  volatile show_early_output_fn_t show_early_output;
  
static char *path_name(struct name_path *path, const char *name)
char *path_name(const struct name_path *path, const char *name)
  {
-       struct name_path *p;
+       const struct name_path *p;
        char *n, *m;
        int nlen = strlen(name);
        int len = nlen + 1;
@@@ -183,11 -182,8 +183,11 @@@ static struct commit *handle_commit(str
                if (!tag->tagged)
                        die("bad tag");
                object = parse_object(tag->tagged->sha1);
 -              if (!object)
 +              if (!object) {
 +                      if (flags & UNINTERESTING)
 +                              return NULL;
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
 +              }
        }
  
        /*
                        mark_parents_uninteresting(commit);
                        revs->limited = 1;
                }
 +              if (revs->show_source && !commit->util)
 +                      commit->util = (void *) name;
                return commit;
        }
  
@@@ -298,31 -292,10 +298,31 @@@ static void file_change(struct diff_opt
        DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
 -static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
 +static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct commit *commit)
  {
 +      struct tree *t1 = parent->tree;
 +      struct tree *t2 = commit->tree;
 +
        if (!t1)
                return REV_TREE_NEW;
 +
 +      if (revs->simplify_by_decoration) {
 +              /*
 +               * 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))
 +                      return REV_TREE_DIFFERENT;
 +              /*
 +               * A commit that is not pointed by a tag is uninteresting
 +               * if we are not limited by path.  This means that you will
 +               * see the usual "commits that touch the paths" plus any
 +               * tagged commit by specifying both --simplify-by-decoration
 +               * and pathspec.
 +               */
 +              if (!revs->prune_data)
 +                      return REV_TREE_SAME;
 +      }
        if (!t2)
                return REV_TREE_DIFFERENT;
        tree_difference = REV_TREE_SAME;
        return tree_difference;
  }
  
 -static int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
 +static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
  {
        int retval;
        void *tree;
        unsigned long size;
        struct tree_desc empty, real;
 +      struct tree *t1 = commit->tree;
  
        if (!t1)
                return 0;
@@@ -373,7 -345,7 +373,7 @@@ static void try_to_simplify_commit(stru
                return;
  
        if (!commit->parents) {
 -              if (rev_same_tree_as_empty(revs, commit->tree))
 +              if (rev_same_tree_as_empty(revs, commit))
                        commit->object.flags |= TREESAME;
                return;
        }
                        die("cannot simplify commit %s (because of %s)",
                            sha1_to_hex(commit->object.sha1),
                            sha1_to_hex(p->object.sha1));
 -              switch (rev_compare_tree(revs, p->tree, commit->tree)) {
 +              switch (rev_compare_tree(revs, p, commit)) {
                case REV_TREE_SAME:
                        tree_same = 1;
                        if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
  
                case REV_TREE_NEW:
                        if (revs->remove_empty_trees &&
 -                          rev_same_tree_as_empty(revs, p->tree)) {
 +                          rev_same_tree_as_empty(revs, p)) {
                                /* We are adding all the specified
                                 * paths from this parent, so the
                                 * history beyond this parent is not
@@@ -482,10 -454,9 +482,10 @@@ static int add_parents_to_list(struct r
                while (parent) {
                        struct commit *p = parent->item;
                        parent = parent->next;
 +                      if (p)
 +                              p->object.flags |= UNINTERESTING;
                        if (parse_commit(p) < 0)
 -                              return -1;
 -                      p->object.flags |= UNINTERESTING;
 +                              continue;
                        if (p->parents)
                                mark_parents_uninteresting(p);
                        if (p->object.flags & SEEN)
  
                if (parse_commit(p) < 0)
                        return -1;
 +              if (revs->show_source && !p->util)
 +                      p->util = commit->util;
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
                        insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
 -              if(revs->first_parent_only)
 +              if (revs->first_parent_only)
                        break;
        }
        return 0;
@@@ -994,6 -963,16 +994,6 @@@ static void add_message_grep(struct rev
        add_grep(revs, pattern, GREP_PATTERN_BODY);
  }
  
 -static void add_ignore_packed(struct rev_info *revs, const char *name)
 -{
 -      int num = ++revs->num_ignore_packed;
 -
 -      revs->ignore_packed = xrealloc(revs->ignore_packed,
 -                                     sizeof(const char *) * (num + 1));
 -      revs->ignore_packed[num-1] = name;
 -      revs->ignore_packed[num] = NULL;
 -}
 -
  static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
                               int *unkc, const char **unkv)
  {
        } else if (!strcmp(arg, "--topo-order")) {
                revs->lifo = 1;
                revs->topo_order = 1;
 +      } else if (!strcmp(arg, "--simplify-merges")) {
 +              revs->simplify_merges = 1;
 +              revs->rewrite_parents = 1;
 +              revs->simplify_history = 0;
 +              revs->limited = 1;
 +      } else if (!strcmp(arg, "--simplify-by-decoration")) {
 +              revs->simplify_merges = 1;
 +              revs->rewrite_parents = 1;
 +              revs->simplify_history = 0;
 +              revs->simplify_by_decoration = 1;
 +              revs->limited = 1;
 +              revs->prune = 1;
 +              load_ref_decorations();
        } else if (!strcmp(arg, "--date-order")) {
                revs->lifo = 0;
                revs->topo_order = 1;
                revs->edge_hint = 1;
        } else if (!strcmp(arg, "--unpacked")) {
                revs->unpacked = 1;
 -              free(revs->ignore_packed);
 -              revs->ignore_packed = NULL;
 -              revs->num_ignore_packed = 0;
        } else if (!prefixcmp(arg, "--unpacked=")) {
 -              revs->unpacked = 1;
 -              add_ignore_packed(revs, arg+11);
 +              die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
                DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
        } else if (!strcmp(arg, "--pretty")) {
                revs->verbose_header = 1;
                get_commit_format(arg+8, revs);
 -      } else if (!prefixcmp(arg, "--pretty=")) {
 +      } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
                revs->verbose_header = 1;
                get_commit_format(arg+9, revs);
 +      } else if (!strcmp(arg, "--oneline")) {
 +              revs->verbose_header = 1;
 +              get_commit_format("oneline", revs);
 +              revs->abbrev_commit = 1;
        } else if (!strcmp(arg, "--graph")) {
                revs->topo_order = 1;
                revs->rewrite_parents = 1;
@@@ -1257,7 -1223,6 +1257,7 @@@ int setup_revisions(int argc, const cha
  
                        if (!strcmp(arg, "--all")) {
                                handle_refs(revs, flags, for_each_ref);
 +                              handle_refs(revs, flags, head_ref);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
@@@ -1390,179 -1355,6 +1390,179 @@@ static void add_child(struct rev_info *
        l->next = add_decoration(&revs->children, &parent->object, l);
  }
  
 +static int remove_duplicate_parents(struct commit *commit)
 +{
 +      struct commit_list **pp, *p;
 +      int surviving_parents;
 +
 +      /* Examine existing parents while marking ones we have seen... */
 +      pp = &commit->parents;
 +      while ((p = *pp) != NULL) {
 +              struct commit *parent = p->item;
 +              if (parent->object.flags & TMP_MARK) {
 +                      *pp = p->next;
 +                      continue;
 +              }
 +              parent->object.flags |= TMP_MARK;
 +              pp = &p->next;
 +      }
 +      /* count them while clearing the temporary mark */
 +      surviving_parents = 0;
 +      for (p = commit->parents; p; p = p->next) {
 +              p->item->object.flags &= ~TMP_MARK;
 +              surviving_parents++;
 +      }
 +      return surviving_parents;
 +}
 +
 +struct merge_simplify_state {
 +      struct commit *simplified;
 +};
 +
 +static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs, struct commit *commit)
 +{
 +      struct merge_simplify_state *st;
 +
 +      st = lookup_decoration(&revs->merge_simplification, &commit->object);
 +      if (!st) {
 +              st = xcalloc(1, sizeof(*st));
 +              add_decoration(&revs->merge_simplification, &commit->object, st);
 +      }
 +      return st;
 +}
 +
 +static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
 +{
 +      struct commit_list *p;
 +      struct merge_simplify_state *st, *pst;
 +      int cnt;
 +
 +      st = locate_simplify_state(revs, commit);
 +
 +      /*
 +       * Have we handled this one?
 +       */
 +      if (st->simplified)
 +              return tail;
 +
 +      /*
 +       * An UNINTERESTING commit simplifies to itself, so does a
 +       * root commit.  We do not rewrite parents of such commit
 +       * anyway.
 +       */
 +      if ((commit->object.flags & UNINTERESTING) || !commit->parents) {
 +              st->simplified = commit;
 +              return tail;
 +      }
 +
 +      /*
 +       * Do we know what commit all of our parents should be rewritten to?
 +       * Otherwise we are not ready to rewrite this one yet.
 +       */
 +      for (cnt = 0, p = commit->parents; p; p = p->next) {
 +              pst = locate_simplify_state(revs, p->item);
 +              if (!pst->simplified) {
 +                      tail = &commit_list_insert(p->item, tail)->next;
 +                      cnt++;
 +              }
 +      }
 +      if (cnt) {
 +              tail = &commit_list_insert(commit, tail)->next;
 +              return tail;
 +      }
 +
 +      /*
 +       * Rewrite our list of parents.
 +       */
 +      for (p = commit->parents; p; p = p->next) {
 +              pst = locate_simplify_state(revs, p->item);
 +              p->item = pst->simplified;
 +      }
 +      cnt = remove_duplicate_parents(commit);
 +
 +      /*
 +       * It is possible that we are a merge and one side branch
 +       * does not have any commit that touches the given paths;
 +       * in such a case, the immediate parents will be rewritten
 +       * to different commits.
 +       *
 +       *      o----X          X: the commit we are looking at;
 +       *     /    /           o: a commit that touches the paths;
 +       * ---o----'
 +       *
 +       * Further reduce the parents by removing redundant parents.
 +       */
 +      if (1 < cnt) {
 +              struct commit_list *h = reduce_heads(commit->parents);
 +              cnt = commit_list_count(h);
 +              free_commit_list(commit->parents);
 +              commit->parents = h;
 +      }
 +
 +      /*
 +       * A commit simplifies to itself if it is a root, if it is
 +       * UNINTERESTING, if it touches the given paths, or if it is a
 +       * merge and its parents simplifies to more than one commits
 +       * (the first two cases are already handled at the beginning of
 +       * this function).
 +       *
 +       * Otherwise, it simplifies to what its sole parent simplifies to.
 +       */
 +      if (!cnt ||
 +          (commit->object.flags & UNINTERESTING) ||
 +          !(commit->object.flags & TREESAME) ||
 +          (1 < cnt))
 +              st->simplified = commit;
 +      else {
 +              pst = locate_simplify_state(revs, commit->parents->item);
 +              st->simplified = pst->simplified;
 +      }
 +      return tail;
 +}
 +
 +static void simplify_merges(struct rev_info *revs)
 +{
 +      struct commit_list *list;
 +      struct commit_list *yet_to_do, **tail;
 +
 +      if (!revs->topo_order)
 +              sort_in_topological_order(&revs->commits, revs->lifo);
 +      if (!revs->prune)
 +              return;
 +
 +      /* feed the list reversed */
 +      yet_to_do = NULL;
 +      for (list = revs->commits; list; list = list->next)
 +              commit_list_insert(list->item, &yet_to_do);
 +      while (yet_to_do) {
 +              list = yet_to_do;
 +              yet_to_do = NULL;
 +              tail = &yet_to_do;
 +              while (list) {
 +                      struct commit *commit = list->item;
 +                      struct commit_list *next = list->next;
 +                      free(list);
 +                      list = next;
 +                      tail = simplify_one(revs, commit, tail);
 +              }
 +      }
 +
 +      /* clean up the result, removing the simplified ones */
 +      list = revs->commits;
 +      revs->commits = NULL;
 +      tail = &revs->commits;
 +      while (list) {
 +              struct commit *commit = list->item;
 +              struct commit_list *next = list->next;
 +              struct merge_simplify_state *st;
 +              free(list);
 +              list = next;
 +              st = locate_simplify_state(revs, commit);
 +              if (st->simplified == commit)
 +                      tail = &commit_list_insert(commit, tail)->next;
 +      }
 +}
 +
  static void set_children(struct rev_info *revs)
  {
        struct commit_list *l;
@@@ -1603,8 -1395,6 +1603,8 @@@ int prepare_revision_walk(struct rev_in
                        return -1;
        if (revs->topo_order)
                sort_in_topological_order(&revs->commits, revs->lifo);
 +      if (revs->simplify_merges)
 +              simplify_merges(revs);
        if (revs->children.name)
                set_children(revs);
        return 0;
@@@ -1637,6 -1427,26 +1637,6 @@@ static enum rewrite_result rewrite_one(
        }
  }
  
 -static void remove_duplicate_parents(struct commit *commit)
 -{
 -      struct commit_list **pp, *p;
 -
 -      /* Examine existing parents while marking ones we have seen... */
 -      pp = &commit->parents;
 -      while ((p = *pp) != NULL) {
 -              struct commit *parent = p->item;
 -              if (parent->object.flags & TMP_MARK) {
 -                      *pp = p->next;
 -                      continue;
 -              }
 -              parent->object.flags |= TMP_MARK;
 -              pp = &p->next;
 -      }
 -      /* ... and clear the temporary mark */
 -      for (p = commit->parents; p; p = p->next)
 -              p->item->object.flags &= ~TMP_MARK;
 -}
 -
  static int rewrite_parents(struct rev_info *revs, struct commit *commit)
  {
        struct commit_list **pp = &commit->parents;
@@@ -1675,7 -1485,7 +1675,7 @@@ enum commit_action simplify_commit(stru
  {
        if (commit->object.flags & SHOWN)
                return commit_ignore;
 -      if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
 +      if (revs->unpacked && has_sha1_pack(commit->object.sha1))
                return commit_ignore;
        if (revs->show_all)
                return commit_show;
@@@ -1728,16 -1538,14 +1728,16 @@@ static struct commit *get_revision_1(st
                            (commit->date < revs->max_age))
                                continue;
                        if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0)
 -                              return NULL;
 +                              die("Failed to traverse parents of commit %s",
 +                                  sha1_to_hex(commit->object.sha1));
                }
  
                switch (simplify_commit(revs, commit)) {
                case commit_ignore:
                        continue;
                case commit_error:
 -                      return NULL;
 +                      die("Failed to simplify parents of commit %s",
 +                          sha1_to_hex(commit->object.sha1));
                default:
                        return commit;
                }
@@@ -1825,6 -1633,26 +1825,6 @@@ static struct commit *get_revision_inte
                return c;
        }
  
 -      if (revs->reverse) {
 -              int limit = -1;
 -
 -              if (0 <= revs->max_count) {
 -                      limit = revs->max_count;
 -                      if (0 < revs->skip_count)
 -                              limit += revs->skip_count;
 -              }
 -              l = NULL;
 -              while ((c = get_revision_1(revs))) {
 -                      commit_list_insert(c, &l);
 -                      if ((0 < limit) && !--limit)
 -                              break;
 -              }
 -              revs->commits = l;
 -              revs->reverse = 0;
 -              revs->max_count = -1;
 -              c = NULL;
 -      }
 -
        /*
         * Now pick up what they want to give us
         */
  
  struct commit *get_revision(struct rev_info *revs)
  {
 -      struct commit *c = get_revision_internal(revs);
 +      struct commit *c;
 +      struct commit_list *reversed;
 +
 +      if (revs->reverse) {
 +              reversed = NULL;
 +              while ((c = get_revision_internal(revs))) {
 +                      commit_list_insert(c, &reversed);
 +              }
 +              revs->commits = reversed;
 +              revs->reverse = 0;
 +              revs->reverse_output_stage = 1;
 +      }
 +
 +      if (revs->reverse_output_stage)
 +              return pop_commit(&revs->commits);
 +
 +      c = get_revision_internal(revs);
        if (c && revs->graph)
                graph_update(revs->graph, c);
        return c;
diff --combined revision.h
index 5adfc9140544429254bd67ff10504730c7424e1b,e5b8908fde36554de8ee211e60fe7539dbe3953f..be39e7d38643817cced4e07a89b6db5201f2b747
@@@ -42,22 -42,17 +42,22 @@@ struct rev_info 
                        simplify_history:1,
                        lifo:1,
                        topo_order:1,
 +                      simplify_merges:1,
 +                      simplify_by_decoration:1,
                        tag_objects:1,
                        tree_objects:1,
                        blob_objects:1,
                        edge_hint:1,
                        limited:1,
 -                      unpacked:1, /* see also ignore_packed below */
 +                      unpacked:1,
                        boundary:2,
                        left_right:1,
                        rewrite_parents:1,
                        print_parents:1,
 +                      show_source:1,
 +                      show_decorations:1,
                        reverse:1,
 +                      reverse_output_stage:1,
                        cherry_pick:1,
                        first_parent_only:1;
  
                        missing_newline:1;
        enum date_mode date_mode;
  
 -      const char **ignore_packed; /* pretend objects in these are unpacked */
 -      int num_ignore_packed;
 -
        unsigned int    abbrev;
        enum cmit_fmt   commit_format;
        struct log_info *loginfo;
        int             nr, total;
        const char      *mime_boundary;
 +      const char      *patch_suffix;
 +      int             numbered_files;
        char            *message_id;
 -      const char      *ref_message_id;
 +      struct string_list *ref_message_ids;
        const char      *add_signoff;
        const char      *extra_headers;
        const char      *log_reencode;
  
        struct reflog_walk_info *reflog_info;
        struct decoration children;
 +      struct decoration merge_simplification;
  };
  
  #define REV_TREE_SAME         0
@@@ -146,6 -141,8 +146,8 @@@ struct name_path 
        const char *elem;
  };
  
+ char *path_name(const struct name_path *path, const char *name);
  extern void add_object(struct object *obj,
                       struct object_array *p,
                       struct name_path *path,
diff --combined upload-pack.c
index 495c99f80a9de2e28f0875560d920e5bb4155d7c,d8ce30654bb7df4e84f30f837aea0d16dd6b48af..edc78612289c5bd774e18fe5f8e1b9ea80378a5f
@@@ -11,7 -11,7 +11,7 @@@
  #include "list-objects.h"
  #include "run-command.h"
  
 -static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
 +static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=nn] <dir>";
  
  /* bits #0..7 in revision.h, #8..10 in commit.c */
  #define THEY_HAVE     (1u << 11)
@@@ -66,7 -66,7 +66,7 @@@ static ssize_t send_client_data(int fd
  }
  
  static FILE *pack_pipe = NULL;
 -static void show_commit(struct commit *commit)
 +static void show_commit(struct commit *commit, void *data)
  {
        if (commit->object.flags & BOUNDARY)
                fputc('-', pack_pipe);
        commit->buffer = NULL;
  }
  
- static void show_object(struct object_array_entry *p, void *data)
+ static void show_object(struct object *obj, const struct name_path *path, const char *component)
  {
        /* An object with name "foo\n0000000..." can be used to
         * confuse downstream git-pack-objects very badly.
         */
-       const char *ep = strchr(p->name, '\n');
+       const char *name = path_name(path, component);
+       const char *ep = strchr(name, '\n');
        if (ep) {
-               fprintf(pack_pipe, "%s %.*s\n", sha1_to_hex(p->item->sha1),
-                      (int) (ep - p->name),
-                      p->name);
+               fprintf(pack_pipe, "%s %.*s\n", sha1_to_hex(obj->sha1),
+                      (int) (ep - name),
+                      name);
        }
        else
                fprintf(pack_pipe, "%s %s\n",
-                               sha1_to_hex(p->item->sha1), p->name);
+                               sha1_to_hex(obj->sha1), name);
+       free((char *)name);
  }
  
  static void show_edge(struct commit *commit)
@@@ -134,7 -136,7 +136,7 @@@ static int do_rev_list(int fd, void *cr
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
 -      traverse_commit_list(&revs, show_commit, show_object);
 +      traverse_commit_list(&revs, show_commit, show_object, NULL);
        fflush(pack_pipe);
        fclose(pack_pipe);
        return 0;
@@@ -397,11 -399,12 +399,11 @@@ static int get_common_commits(void
        static char line[1000];
        unsigned char sha1[20];
        char hex[41], last_hex[41];
 -      int len;
  
        save_commit_buffer = 0;
  
        for(;;) {
 -              len = packet_read_line(0, line, sizeof(line));
 +              int len = packet_read_line(0, line, sizeof(line));
                reset_timeout();
  
                if (!len) {
                                packet_write(1, "NAK\n");
                        continue;
                }
 -              len = strip(line, len);
 +              strip(line, len);
                if (!prefixcmp(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
@@@ -615,8 -618,6 +617,8 @@@ int main(int argc, char **argv
        int i;
        int strict = 0;
  
 +      git_extract_argv0_path(argv[0]);
 +
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
  
        dir = argv[i];
  
        if (!enter_repo(dir, strict))
 -              die("'%s': unable to chdir or not a git archive", dir);
 +              die("'%s' does not appear to be a git repository", dir);
        if (is_repository_shallow())
                die("attempt to fetch/clone from a shallow repository");
        if (getenv("GIT_DEBUG_SEND_PACK"))