Code

Merge branch 'lt/pack-object-memuse' into maint
authorJunio C Hamano <gitster@pobox.com>
Sun, 3 May 2009 22:02:40 +0000 (15:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 3 May 2009 22:02:40 +0000 (15:02 -0700)
* lt/pack-object-memuse:
  show_object(): push path_name() call further down
  process_{tree,blob}: show objects without buffering

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

diff --combined builtin-pack-objects.c
index 6cf5b86e15bd01d03aad4c3476a005923542a6f3,71041453f29715fdb18f2b74811f1ebfa6bf5311..b859cb147c707d76685e999284d8cc647977e6a0
@@@ -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 %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;
@@@ -1907,13 -1856,19 +1907,19 @@@ static void show_commit(struct commit *
        commit->object.flags |= OBJECT_ADDED;
  }
  
- static void show_object(struct object_array_entry *p)
+ 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");
                }
@@@ -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 436afa45f5b7569551aa8301aee8a0752009a900,aa3c962e56bd08e8e2a632b7170283d05efb69a2..0af7cd94f99728e08379441061098e1966e36b73
@@@ -100,14 -100,15 +100,14 @@@ static void show_commit(struct commit *
                        children = children->next;
                }
        }
 -      show_decorations(commit);
 +      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);
 +              struct strbuf buf = STRBUF_INIT;
                pretty_print_commit(revs.commit_format, commit,
                                    &buf, revs.abbrev, NULL, NULL,
                                    revs.date_mode, 0);
@@@ -168,27 -169,29 +168,29 @@@ static void finish_commit(struct commi
        commit->buffer = NULL;
  }
  
- static void finish_object(struct object_array_entry *p)
+ 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)
+ 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);
+       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)
@@@ -608,7 -611,6 +610,7 @@@ int cmd_rev_list(int argc, const char *
                if (!strcmp(arg, "--bisect-all")) {
                        bisect_list = 1;
                        bisect_find_all = 1;
 +                      revs.show_decorations = 1;
                        continue;
                }
                if (!strcmp(arg, "--bisect-vars")) {
diff --combined revision.c
index 34ee490ea0181091c375765141bd83e71ab2defe,69d5fd4784eb849221e3bd12d33ab48de45d431f..d7d345bdd9c71eb0f8d77179b4af1b79049d054c
  #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);
@@@ -1253,7 -1223,6 +1253,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")) {
@@@ -1386,179 -1355,6 +1386,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;
@@@ -1599,8 -1395,6 +1599,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;
@@@ -1633,6 -1427,26 +1633,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;
@@@ -1671,7 -1485,7 +1671,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;
@@@ -1724,16 -1538,14 +1724,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;
                }
@@@ -1821,6 -1633,26 +1821,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 66d211ac2e56be20fec686416dd6a2816b891239,e5b8908fde36554de8ee211e60fe7539dbe3953f..bfe27071bfff3dda4a468844b0cc9195c67b5625
@@@ -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;
  
@@@ -80,6 -75,9 +80,6 @@@
                        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;
  
        struct reflog_walk_info *reflog_info;
        struct decoration children;
 +      struct decoration merge_simplification;
  };
  
  #define REV_TREE_SAME         0
@@@ -144,6 -141,8 +144,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 19c24db643c0bceb6e1b960d411d657d2feb0f32,d8ce30654bb7df4e84f30f837aea0d16dd6b48af..b98b1d61c127de6368d24cc3c3b16a054724ed74
@@@ -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)
@@@ -78,20 -78,22 +78,22 @@@ static void show_commit(struct commit *
        commit->buffer = NULL;
  }
  
- static void show_object(struct object_array_entry *p)
+ 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)
@@@ -616,8 -618,6 +618,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];