Merge branch 'mh/reflife'
authorJunio C Hamano <gitster@pobox.com>
Fri, 14 Jun 2013 15:46:13 +0000 (08:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 14 Jun 2013 15:46:14 +0000 (08:46 -0700)
Define memory ownership and lifetime rules for what for-each-ref
feeds to its callbacks (in short, "you do not own it, so make a
copy if you want to keep it").

* mh/reflife: (25 commits)
refs: document the lifetime of the args passed to each_ref_fn
register_ref(): make a copy of the bad reference SHA-1
exclude_existing(): set existing_refs.strdup_strings
string_list_add_refs_by_glob(): add a comment about memory management
string_list_add_one_ref(): rename first parameter to "refname"
show_head_ref(): rename first parameter to "refname"
show_head_ref(): do not shadow name of argument
add_existing(): do not retain a reference to sha1
do_fetch(): clean up existing_refs before exiting
do_fetch(): reduce scope of peer_item
object_array_entry: fix memory handling of the name field
find_first_merges(): remove unnecessary code
find_first_merges(): initialize merges variable using initializer
fsck: don't put a void*-shaped peg in a char*-shaped hole
object_array_remove_duplicates(): rewrite to reduce copying
revision: use object_array_filter() in implementation of gc_boundary()
object_array: add function object_array_filter()
revision: split some overly-long lines
cmd_diff(): make it obvious which cases are exclusive of each other
cmd_diff(): rename local variable "list" -> "entry"
...

1  2 
builtin/describe.c
builtin/fetch.c
object.c
refs.h
revision.c
revision.h
submodule.c
diff --combined builtin/describe.c
index ad8471626ac4c6f4f4eda413114665fb4fcca018,3dc09eb8a2a4311163eb40df3f4edbe8faad8f0b..4e675c3d0d7b1daeb7f12ef425514a11a92a5c3f
@@@ -21,7 -21,6 +21,7 @@@ static int debug;     /* Display lots of ve
  static int all;       /* Any valid ref can be used */
  static int tags;      /* Allow lightweight tags */
  static int longformat;
 +static int first_parent;
  static int abbrev = -1; /* unspecified */
  static int max_candidates = 10;
  static struct hash_table names;
@@@ -43,7 -42,7 +43,7 @@@ struct commit_name 
        unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
        unsigned name_checked:1;
        unsigned char sha1[20];
-       const char *path;
+       char *path;
  };
  static const char *prio_names[] = {
        "head", "lightweight", "annotated",
@@@ -127,12 -126,14 +127,14 @@@ static void add_to_known_names(const ch
                        } else {
                                e->next = NULL;
                        }
+                       e->path = NULL;
                }
                e->tag = tag;
                e->prio = prio;
                e->name_checked = 0;
                hashcpy(e->sha1, sha1);
-               e->path = path;
+               free(e->path);
+               e->path = xstrdup(path);
        }
  }
  
@@@ -337,9 -338,6 +339,9 @@@ static void describe(const char *arg, i
                                commit_list_insert_by_date(p, &list);
                        p->object.flags |= c->object.flags;
                        parents = parents->next;
 +
 +                      if (first_parent)
 +                              break;
                }
        }
  
@@@ -408,7 -406,6 +410,7 @@@ int cmd_describe(int argc, const char *
                OPT_BOOLEAN(0, "all",        &all, N_("use any ref")),
                OPT_BOOLEAN(0, "tags",       &tags, N_("use any tag, even unannotated")),
                OPT_BOOLEAN(0, "long",       &longformat, N_("always use long format")),
 +              OPT_BOOLEAN(0, "first-parent", &first_parent, N_("only follow first parent")),
                OPT__ABBREV(&abbrev),
                OPT_SET_INT(0, "exact-match", &max_candidates,
                            N_("only output exact matches"), 0),
diff --combined builtin/fetch.c
index d15a7343d8384d05784aa5739dc758262f750a25,fa6fe441471912e7011db14bcb901ae3f8d18a4d..d784b2e6947f1b5ace0016d92258e29377e9d1a7
@@@ -119,7 -119,7 +119,7 @@@ static void add_merge_config(struct re
  
                for (rm = *head; rm; rm = rm->next) {
                        if (branch_merge_matches(branch, i, rm->name)) {
 -                              rm->merge = 1;
 +                              rm->fetch_head_status = FETCH_HEAD_MERGE;
                                break;
                        }
                }
                refspec.src = branch->merge[i]->src;
                get_fetch_map(remote_refs, &refspec, tail, 1);
                for (rm = *old_tail; rm; rm = rm->next)
 -                      rm->merge = 1;
 +                      rm->fetch_head_status = FETCH_HEAD_MERGE;
        }
  }
  
@@@ -160,8 -160,6 +160,8 @@@ static struct ref *get_ref_map(struct t
        const struct ref *remote_refs = transport_get_remote_refs(transport);
  
        if (ref_count || tags == TAGS_SET) {
 +              struct ref **old_tail;
 +
                for (i = 0; i < ref_count; i++) {
                        get_fetch_map(remote_refs, &refs[i], &tail, 0);
                        if (refs[i].dst && refs[i].dst[0])
                }
                /* Merge everything on the command line, but not --tags */
                for (rm = ref_map; rm; rm = rm->next)
 -                      rm->merge = 1;
 +                      rm->fetch_head_status = FETCH_HEAD_MERGE;
                if (tags == TAGS_SET)
                        get_fetch_map(remote_refs, tag_refspec, &tail, 0);
 +
 +              /*
 +               * For any refs that we happen to be fetching via command-line
 +               * arguments, take the opportunity to update their configured
 +               * counterparts. However, we do not want to mention these
 +               * entries in FETCH_HEAD at all, as they would simply be
 +               * duplicates of existing entries.
 +               */
 +              old_tail = tail;
 +              for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
 +                      get_fetch_map(ref_map, &transport->remote->fetch[i],
 +                                    &tail, 1);
 +              for (rm = *old_tail; rm; rm = rm->next)
 +                      rm->fetch_head_status = FETCH_HEAD_IGNORE;
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
                                        *autotags = 1;
                                if (!i && !has_merge && ref_map &&
                                    !remote->fetch[0].pattern)
 -                                      ref_map->merge = 1;
 +                                      ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        }
                        /*
                         * if the remote we're fetching from is the same
                        ref_map = get_remote_ref(remote_refs, "HEAD");
                        if (!ref_map)
                                die(_("Couldn't find remote ref HEAD"));
 -                      ref_map->merge = 1;
 +                      ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        tail = &ref_map->next;
                }
        }
@@@ -405,7 -389,7 +405,7 @@@ static int store_updated_refs(const cha
        const char *what, *kind;
        struct ref *rm;
        char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
 -      int want_merge;
 +      int want_status;
  
        fp = fopen(filename, "a");
        if (!fp)
        }
  
        /*
 -       * The first pass writes objects to be merged and then the
 -       * second pass writes the rest, in order to allow using
 -       * FETCH_HEAD as a refname to refer to the ref to be merged.
 +       * We do a pass for each fetch_head_status type in their enum order, so
 +       * merged entries are written before not-for-merge. That lets readers
 +       * use FETCH_HEAD as a refname to refer to the ref to be merged.
         */
 -      for (want_merge = 1; 0 <= want_merge; want_merge--) {
 +      for (want_status = FETCH_HEAD_MERGE;
 +           want_status <= FETCH_HEAD_IGNORE;
 +           want_status++) {
                for (rm = ref_map; rm; rm = rm->next) {
                        struct ref *ref = NULL;
 +                      const char *merge_status_marker = "";
  
                        commit = lookup_commit_reference_gently(rm->old_sha1, 1);
                        if (!commit)
 -                              rm->merge = 0;
 +                              rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
  
 -                      if (rm->merge != want_merge)
 +                      if (rm->fetch_head_status != want_status)
                                continue;
  
                        if (rm->peer_ref) {
                                        strbuf_addf(&note, "%s ", kind);
                                strbuf_addf(&note, "'%s' of ", what);
                        }
 -                      fprintf(fp, "%s\t%s\t%s",
 -                              sha1_to_hex(rm->old_sha1),
 -                              rm->merge ? "" : "not-for-merge",
 -                              note.buf);
 -                      for (i = 0; i < url_len; ++i)
 -                              if ('\n' == url[i])
 -                                      fputs("\\n", fp);
 -                              else
 -                                      fputc(url[i], fp);
 -                      fputc('\n', fp);
 +                      switch (rm->fetch_head_status) {
 +                      case FETCH_HEAD_NOT_FOR_MERGE:
 +                              merge_status_marker = "not-for-merge";
 +                              /* fall-through */
 +                      case FETCH_HEAD_MERGE:
 +                              fprintf(fp, "%s\t%s\t%s",
 +                                      sha1_to_hex(rm->old_sha1),
 +                                      merge_status_marker,
 +                                      note.buf);
 +                              for (i = 0; i < url_len; ++i)
 +                                      if ('\n' == url[i])
 +                                              fputs("\\n", fp);
 +                                      else
 +                                              fputc(url[i], fp);
 +                              fputc('\n', fp);
 +                              break;
 +                      default:
 +                              /* do not write anything to FETCH_HEAD */
 +                              break;
 +                      }
  
                        strbuf_reset(&note);
                        if (ref) {
@@@ -600,7 -571,8 +600,8 @@@ static int add_existing(const char *ref
  {
        struct string_list *list = (struct string_list *)cbdata;
        struct string_list_item *item = string_list_insert(list, refname);
-       item->util = (void *)sha1;
+       item->util = xmalloc(20);
+       hashcpy(item->util, sha1);
        return 0;
  }
  
@@@ -619,7 -591,7 +620,7 @@@ static void find_non_local_tags(struct 
                        struct ref **head,
                        struct ref ***tail)
  {
-       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct string_list remote_refs = STRING_LIST_INIT_NODUP;
        const struct ref *ref;
        struct string_list_item *item = NULL;
                item = string_list_insert(&remote_refs, ref->name);
                item->util = (void *)ref->old_sha1;
        }
-       string_list_clear(&existing_refs, 0);
+       string_list_clear(&existing_refs, 1);
  
        /*
         * We may have a final lightweight tag that needs to be
@@@ -722,11 -694,11 +723,11 @@@ static int truncate_fetch_head(void
  static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
  {
-       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
-       struct string_list_item *peer_item = NULL;
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct ref *ref_map;
        struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
+       int retcode = 0;
  
        for_each_ref(add_existing, &existing_refs);
  
  
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
-               int errcode = truncate_fetch_head();
-               if (errcode)
-                       return errcode;
+               retcode = truncate_fetch_head();
+               if (retcode)
+                       goto cleanup;
        }
  
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
  
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref) {
-                       peer_item = string_list_lookup(&existing_refs,
-                                                      rm->peer_ref->name);
+                       struct string_list_item *peer_item =
+                               string_list_lookup(&existing_refs,
+                                                  rm->peer_ref->name);
                        if (peer_item)
                                hashcpy(rm->peer_ref->old_sha1,
                                        peer_item->util);
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
        if (fetch_refs(transport, ref_map)) {
                free_refs(ref_map);
-               return 1;
+               retcode = 1;
+               goto cleanup;
        }
        if (prune) {
                /* If --tags was specified, pretend the user gave us the canonical tags refspec */
                free_refs(ref_map);
        }
  
-       return 0;
+  cleanup:
+       string_list_clear(&existing_refs, 1);
+       return retcode;
  }
  
  static void set_option(const char *name, const char *value)
diff --combined object.c
index 88d0bece8116862681ba9fee3f9e0dfdec7f8b17,243d694a58334cc45df6f38878d094ed419188ce..cbc7333a7ec083556ba9b46e1adafb695f83e0af
+++ b/object.c
@@@ -71,13 -71,13 +71,13 @@@ static unsigned int hashtable_index(con
  
  struct object *lookup_object(const unsigned char *sha1)
  {
 -      unsigned int i;
 +      unsigned int i, first;
        struct object *obj;
  
        if (!obj_hash)
                return NULL;
  
 -      i = hashtable_index(sha1);
 +      first = i = hashtable_index(sha1);
        while ((obj = obj_hash[i]) != NULL) {
                if (!hashcmp(sha1, obj->sha1))
                        break;
                if (i == obj_hash_size)
                        i = 0;
        }
 +      if (obj && i != first) {
 +              /*
 +               * Move object to where we started to look for it so
 +               * that we do not need to walk the hash table the next
 +               * time we look for it.
 +               */
 +              struct object *tmp = obj_hash[i];
 +              obj_hash[i] = obj_hash[first];
 +              obj_hash[first] = tmp;
 +      }
        return obj;
  }
  
@@@ -270,11 -260,18 +270,18 @@@ void add_object_array(struct object *ob
        add_object_array_with_mode(obj, name, array, S_IFINVALID);
  }
  
+ /*
+  * A zero-length string to which object_array_entry::name can be
+  * initialized without requiring a malloc/free.
+  */
+ static char object_array_slopbuf[1];
  void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode)
  {
        unsigned nr = array->nr;
        unsigned alloc = array->alloc;
        struct object_array_entry *objects = array->objects;
+       struct object_array_entry *entry;
  
        if (nr >= alloc) {
                alloc = (alloc + 32) * 2;
                array->alloc = alloc;
                array->objects = objects;
        }
-       objects[nr].item = obj;
-       objects[nr].name = name;
-       objects[nr].mode = mode;
+       entry = &objects[nr];
+       entry->item = obj;
+       if (!name)
+               entry->name = NULL;
+       else if (!*name)
+               /* Use our own empty string instead of allocating one: */
+               entry->name = object_array_slopbuf;
+       else
+               entry->name = xstrdup(name);
+       entry->mode = mode;
        array->nr = ++nr;
  }
  
- void object_array_remove_duplicates(struct object_array *array)
+ void object_array_filter(struct object_array *array,
+                        object_array_each_func_t want, void *cb_data)
  {
-       unsigned int ref, src, dst;
+       unsigned nr = array->nr, src, dst;
        struct object_array_entry *objects = array->objects;
  
-       for (ref = 0; ref + 1 < array->nr; ref++) {
-               for (src = ref + 1, dst = src;
-                    src < array->nr;
-                    src++) {
-                       if (!strcmp(objects[ref].name, objects[src].name))
-                               continue;
+       for (src = dst = 0; src < nr; src++) {
+               if (want(&objects[src], cb_data)) {
                        if (src != dst)
                                objects[dst] = objects[src];
                        dst++;
+               } else {
+                       if (objects[src].name != object_array_slopbuf)
+                               free(objects[src].name);
+               }
+       }
+       array->nr = dst;
+ }
+ /*
+  * Return true iff array already contains an entry with name.
+  */
+ static int contains_name(struct object_array *array, const char *name)
+ {
+       unsigned nr = array->nr, i;
+       struct object_array_entry *object = array->objects;
+       for (i = 0; i < nr; i++, object++)
+               if (!strcmp(object->name, name))
+                       return 1;
+       return 0;
+ }
+ void object_array_remove_duplicates(struct object_array *array)
+ {
+       unsigned nr = array->nr, src;
+       struct object_array_entry *objects = array->objects;
+       array->nr = 0;
+       for (src = 0; src < nr; src++) {
+               if (!contains_name(array, objects[src].name)) {
+                       if (src != array->nr)
+                               objects[array->nr] = objects[src];
+                       array->nr++;
+               } else {
+                       if (objects[src].name != object_array_slopbuf)
+                               free(objects[src].name);
                }
-               array->nr = dst;
        }
  }
  
diff --combined refs.h
index 8060ed831308fdde3a39c837f8b0953ea35baaef,122ec035954f8f9755c1ef374051997f51b1c26c..246bf6096d222a0d822d621de519ca5c49b0f641
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -10,31 -10,28 +10,41 @@@ struct ref_lock 
        int force_write;
  };
  
 +/*
 + * Bit values set in the flags argument passed to each_ref_fn():
 + */
 +
 +/* Reference is a symbolic reference. */
  #define REF_ISSYMREF 0x01
 +
 +/* Reference is a packed reference. */
  #define REF_ISPACKED 0x02
 +
 +/*
 + * Reference cannot be resolved to an object name: dangling symbolic
 + * reference (directly or indirectly), corrupt reference file, or
 + * symbolic reference refers to ill-formatted reference name.
 + */
  #define REF_ISBROKEN 0x04
  
  /*
-  * Calls the specified function for each ref file until it returns
-  * nonzero, and returns the value.  Please note that it is not safe to
-  * modify references while an iteration is in progress, unless the
-  * same callback function invocation that modifies the reference also
-  * returns a nonzero value to immediately stop the iteration.
+  * The signature for the callback function for the for_each_*()
+  * functions below.  The memory pointed to by the refname and sha1
+  * arguments is only guaranteed to be valid for the duration of a
+  * single callback invocation.
+  */
+ typedef int each_ref_fn(const char *refname,
+                       const unsigned char *sha1, int flags, void *cb_data);
+ /*
+  * The following functions invoke the specified callback function for
+  * each reference indicated.  If the function ever returns a nonzero
+  * value, stop the iteration and return that value.  Please note that
+  * it is not safe to modify references while an iteration is in
+  * progress, unless the same callback function invocation that
+  * modifies the reference also returns a nonzero value to immediately
+  * stop the iteration.
   */
- typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
  extern int head_ref(each_ref_fn, void *);
  extern int for_each_ref(each_ref_fn, void *);
  extern int for_each_ref_in(const char *, each_ref_fn, void *);
@@@ -72,30 -69,8 +82,30 @@@ extern void warn_dangling_symref(FILE *
   */
  extern void add_packed_ref(const char *refname, const unsigned char *sha1);
  
 +/*
 + * Flags for controlling behaviour of pack_refs()
 + * PACK_REFS_PRUNE: Prune loose refs after packing
 + * PACK_REFS_ALL:   Pack _all_ refs, not just tags and already packed refs
 + */
 +#define PACK_REFS_PRUNE 0x0001
 +#define PACK_REFS_ALL   0x0002
 +
 +/*
 + * Write a packed-refs file for the current repository.
 + * flags: Combination of the above PACK_REFS_* flags.
 + */
 +int pack_refs(unsigned int flags);
 +
  extern int ref_exists(const char *);
  
 +/*
 + * If refname is a non-symbolic reference that refers to a tag object,
 + * and the tag can be (recursively) dereferenced to a non-tag object,
 + * store the SHA1 of the referred-to object to sha1 and return 0.  If
 + * any of these conditions are not met, return a non-zero value.
 + * Symbolic references are considered unpeelable, even if they
 + * ultimately resolve to a peelable tag.
 + */
  extern int peel_ref(const char *refname, unsigned char *sha1);
  
  /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
diff --combined revision.c
index 9318b7d7637e366ff5600a30ba6468f3a5288aa5,4aeda334dbb7693563800da6da123d0094d72963..f1bb731fd71eb874344faa5376e7c375ac2fcf7f
@@@ -13,7 -13,6 +13,7 @@@
  #include "decorate.h"
  #include "log-tree.h"
  #include "string-list.h"
 +#include "line-log.h"
  #include "mailmap.h"
  
  volatile show_early_output_fn_t show_early_output;
@@@ -71,7 -70,8 +71,8 @@@ static int show_path_truncated(FILE *ou
        return ours || emitted;
  }
  
- void show_object_with_name(FILE *out, struct object *obj, const struct name_path *path, const char *component)
+ void show_object_with_name(FILE *out, struct object *obj,
+                          const struct name_path *path, const char *component)
  {
        struct name_path leaf;
        leaf.up = (struct name_path *)path;
@@@ -88,7 -88,9 +89,9 @@@ void add_object(struct object *obj
                struct name_path *path,
                const char *name)
  {
-       add_object_array(obj, path_name(path, name), p);
+       char *pn = path_name(path, name);
+       add_object_array(obj, pn, p);
+       free(pn);
  }
  
  static void mark_blob_uninteresting(struct blob *blob)
@@@ -187,7 -189,9 +190,9 @@@ void mark_parents_uninteresting(struct 
        }
  }
  
- static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
+ static void add_pending_object_with_mode(struct rev_info *revs,
+                                        struct object *obj,
+                                        const char *name, unsigned mode)
  {
        if (!obj)
                return;
        add_object_array_with_mode(obj, name, &revs->pending, mode);
  }
  
- void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+ void add_pending_object(struct rev_info *revs,
+                       struct object *obj, const char *name)
  {
        add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
  }
@@@ -227,7 -232,9 +233,9 @@@ void add_head_to_pending(struct rev_inf
        add_pending_object(revs, obj, "HEAD");
  }
  
- static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
+ static struct object *get_reference(struct rev_info *revs, const char *name,
+                                   const unsigned char *sha1,
+                                   unsigned int flags)
  {
        struct object *object;
  
@@@ -248,7 -255,8 +256,8 @@@ void add_pending_sha1(struct rev_info *
        add_pending_object(revs, object, name);
  }
  
- static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
+ static struct commit *handle_commit(struct rev_info *revs,
+                                   struct object *object, const char *name)
  {
        unsigned long flags = object->flags;
  
@@@ -333,80 -341,6 +342,80 @@@ static int everybody_uninteresting(stru
        return 1;
  }
  
 +/*
 + * A definition of "relevant" commit that we can use to simplify limited graphs
 + * by eliminating side branches.
 + *
 + * A "relevant" commit is one that is !UNINTERESTING (ie we are including it
 + * in our list), or that is a specified BOTTOM commit. Then after computing
 + * a limited list, during processing we can generally ignore boundary merges
 + * coming from outside the graph, (ie from irrelevant parents), and treat
 + * those merges as if they were single-parent. TREESAME is defined to consider
 + * only relevant parents, if any. If we are TREESAME to our on-graph parents,
 + * we don't care if we were !TREESAME to non-graph parents.
 + *
 + * Treating bottom commits as relevant ensures that a limited graph's
 + * connection to the actual bottom commit is not viewed as a side branch, but
 + * treated as part of the graph. For example:
 + *
 + *   ....Z...A---X---o---o---B
 + *        .     /
 + *         W---Y
 + *
 + * When computing "A..B", the A-X connection is at least as important as
 + * Y-X, despite A being flagged UNINTERESTING.
 + *
 + * And when computing --ancestry-path "A..B", the A-X connection is more
 + * important than Y-X, despite both A and Y being flagged UNINTERESTING.
 + */
 +static inline int relevant_commit(struct commit *commit)
 +{
 +      return (commit->object.flags & (UNINTERESTING | BOTTOM)) != UNINTERESTING;
 +}
 +
 +/*
 + * Return a single relevant commit from a parent list. If we are a TREESAME
 + * commit, and this selects one of our parents, then we can safely simplify to
 + * that parent.
 + */
 +static struct commit *one_relevant_parent(const struct rev_info *revs,
 +                                        struct commit_list *orig)
 +{
 +      struct commit_list *list = orig;
 +      struct commit *relevant = NULL;
 +
 +      if (!orig)
 +              return NULL;
 +
 +      /*
 +       * For 1-parent commits, or if first-parent-only, then return that
 +       * first parent (even if not "relevant" by the above definition).
 +       * TREESAME will have been set purely on that parent.
 +       */
 +      if (revs->first_parent_only || !orig->next)
 +              return orig->item;
 +
 +      /*
 +       * For multi-parent commits, identify a sole relevant parent, if any.
 +       * If we have only one relevant parent, then TREESAME will be set purely
 +       * with regard to that parent, and we can simplify accordingly.
 +       *
 +       * If we have more than one relevant parent, or no relevant parents
 +       * (and multiple irrelevant ones), then we can't select a parent here
 +       * and return NULL.
 +       */
 +      while (list) {
 +              struct commit *commit = list->item;
 +              list = list->next;
 +              if (relevant_commit(commit)) {
 +                      if (relevant)
 +                              return NULL;
 +                      relevant = commit;
 +              }
 +      }
 +      return relevant;
 +}
 +
  /*
   * The goal is to get REV_TREE_NEW as the result only if the
   * diff consists of all '+' (and no other changes), REV_TREE_OLD
@@@ -443,7 -377,8 +452,8 @@@ static void file_change(struct diff_opt
        DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
- static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct commit *commit)
+ 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;
@@@ -504,125 -439,10 +514,125 @@@ static int rev_same_tree_as_empty(struc
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
  }
  
 +struct treesame_state {
 +      unsigned int nparents;
 +      unsigned char treesame[FLEX_ARRAY];
 +};
 +
 +static struct treesame_state *initialise_treesame(struct rev_info *revs, struct commit *commit)
 +{
 +      unsigned n = commit_list_count(commit->parents);
 +      struct treesame_state *st = xcalloc(1, sizeof(*st) + n);
 +      st->nparents = n;
 +      add_decoration(&revs->treesame, &commit->object, st);
 +      return st;
 +}
 +
 +/*
 + * Must be called immediately after removing the nth_parent from a commit's
 + * parent list, if we are maintaining the per-parent treesame[] decoration.
 + * This does not recalculate the master TREESAME flag - update_treesame()
 + * should be called to update it after a sequence of treesame[] modifications
 + * that may have affected it.
 + */
 +static int compact_treesame(struct rev_info *revs, struct commit *commit, unsigned nth_parent)
 +{
 +      struct treesame_state *st;
 +      int old_same;
 +
 +      if (!commit->parents) {
 +              /*
 +               * Have just removed the only parent from a non-merge.
 +               * Different handling, as we lack decoration.
 +               */
 +              if (nth_parent != 0)
 +                      die("compact_treesame %u", nth_parent);
 +              old_same = !!(commit->object.flags & TREESAME);
 +              if (rev_same_tree_as_empty(revs, commit))
 +                      commit->object.flags |= TREESAME;
 +              else
 +                      commit->object.flags &= ~TREESAME;
 +              return old_same;
 +      }
 +
 +      st = lookup_decoration(&revs->treesame, &commit->object);
 +      if (!st || nth_parent >= st->nparents)
 +              die("compact_treesame %u", nth_parent);
 +
 +      old_same = st->treesame[nth_parent];
 +      memmove(st->treesame + nth_parent,
 +              st->treesame + nth_parent + 1,
 +              st->nparents - nth_parent - 1);
 +
 +      /*
 +       * If we've just become a non-merge commit, update TREESAME
 +       * immediately, and remove the no-longer-needed decoration.
 +       * If still a merge, defer update until update_treesame().
 +       */
 +      if (--st->nparents == 1) {
 +              if (commit->parents->next)
 +                      die("compact_treesame parents mismatch");
 +              if (st->treesame[0] && revs->dense)
 +                      commit->object.flags |= TREESAME;
 +              else
 +                      commit->object.flags &= ~TREESAME;
 +              free(add_decoration(&revs->treesame, &commit->object, NULL));
 +      }
 +
 +      return old_same;
 +}
 +
 +static unsigned update_treesame(struct rev_info *revs, struct commit *commit)
 +{
 +      if (commit->parents && commit->parents->next) {
 +              unsigned n;
 +              struct treesame_state *st;
 +              struct commit_list *p;
 +              unsigned relevant_parents;
 +              unsigned relevant_change, irrelevant_change;
 +
 +              st = lookup_decoration(&revs->treesame, &commit->object);
 +              if (!st)
 +                      die("update_treesame %s", sha1_to_hex(commit->object.sha1));
 +              relevant_parents = 0;
 +              relevant_change = irrelevant_change = 0;
 +              for (p = commit->parents, n = 0; p; n++, p = p->next) {
 +                      if (relevant_commit(p->item)) {
 +                              relevant_change |= !st->treesame[n];
 +                              relevant_parents++;
 +                      } else
 +                              irrelevant_change |= !st->treesame[n];
 +              }
 +              if (relevant_parents ? relevant_change : irrelevant_change)
 +                      commit->object.flags &= ~TREESAME;
 +              else
 +                      commit->object.flags |= TREESAME;
 +      }
 +
 +      return commit->object.flags & TREESAME;
 +}
 +
 +static inline int limiting_can_increase_treesame(const struct rev_info *revs)
 +{
 +      /*
 +       * TREESAME is irrelevant unless prune && dense;
 +       * if simplify_history is set, we can't have a mixture of TREESAME and
 +       *    !TREESAME INTERESTING parents (and we don't have treesame[]
 +       *    decoration anyway);
 +       * if first_parent_only is set, then the TREESAME flag is locked
 +       *    against the first parent (and again we lack treesame[] decoration).
 +       */
 +      return revs->prune && revs->dense &&
 +             !revs->simplify_history &&
 +             !revs->first_parent_only;
 +}
 +
  static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
  {
        struct commit_list **pp, *parent;
 -      int tree_changed = 0, tree_same = 0, nth_parent = 0;
 +      struct treesame_state *ts = NULL;
 +      int relevant_change = 0, irrelevant_change = 0;
 +      int relevant_parents, nth_parent;
  
        /*
         * If we don't do pruning, everything is interesting
        if (!revs->dense && !commit->parents->next)
                return;
  
 -      pp = &commit->parents;
 -      while ((parent = *pp) != NULL) {
 +      for (pp = &commit->parents, nth_parent = 0, relevant_parents = 0;
 +           (parent = *pp) != NULL;
 +           pp = &parent->next, nth_parent++) {
                struct commit *p = parent->item;
 +              if (relevant_commit(p))
 +                      relevant_parents++;
  
 -              /*
 -               * Do not compare with later parents when we care only about
 -               * the first parent chain, in order to avoid derailing the
 -               * traversal to follow a side branch that brought everything
 -               * in the path we are limited to by the pathspec.
 -               */
 -              if (revs->first_parent_only && nth_parent++)
 -                      break;
 +              if (nth_parent == 1) {
 +                      /*
 +                       * This our second loop iteration - so we now know
 +                       * we're dealing with a merge.
 +                       *
 +                       * Do not compare with later parents when we care only about
 +                       * the first parent chain, in order to avoid derailing the
 +                       * traversal to follow a side branch that brought everything
 +                       * in the path we are limited to by the pathspec.
 +                       */
 +                      if (revs->first_parent_only)
 +                              break;
 +                      /*
 +                       * If this will remain a potentially-simplifiable
 +                       * merge, remember per-parent treesame if needed.
 +                       * Initialise the array with the comparison from our
 +                       * first iteration.
 +                       */
 +                      if (revs->treesame.name &&
 +                          !revs->simplify_history &&
 +                          !(commit->object.flags & UNINTERESTING)) {
 +                              ts = initialise_treesame(revs, commit);
 +                              if (!(irrelevant_change || relevant_change))
 +                                      ts->treesame[0] = 1;
 +                      }
 +              }
                if (parse_commit(p) < 0)
                        die("cannot simplify commit %s (because of %s)",
                            sha1_to_hex(commit->object.sha1),
                            sha1_to_hex(p->object.sha1));
                switch (rev_compare_tree(revs, p, commit)) {
                case REV_TREE_SAME:
 -                      tree_same = 1;
 -                      if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
 +                      if (!revs->simplify_history || !relevant_commit(p)) {
                                /* Even if a merge with an uninteresting
                                 * side branch brought the entire change
                                 * we are interested in, we do not want
                                 * to lose the other branches of this
                                 * merge, so we just keep going.
                                 */
 -                              pp = &parent->next;
 +                              if (ts)
 +                                      ts->treesame[nth_parent] = 1;
                                continue;
                        }
                        parent->next = NULL;
                /* fallthrough */
                case REV_TREE_OLD:
                case REV_TREE_DIFFERENT:
 -                      tree_changed = 1;
 -                      pp = &parent->next;
 +                      if (relevant_commit(p))
 +                              relevant_change = 1;
 +                      else
 +                              irrelevant_change = 1;
                        continue;
                }
                die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
        }
 -      if (tree_changed && !tree_same)
 -              return;
 -      commit->object.flags |= TREESAME;
 +
 +      /*
 +       * TREESAME is straightforward for single-parent commits. For merge
 +       * commits, it is most useful to define it so that "irrelevant"
 +       * parents cannot make us !TREESAME - if we have any relevant
 +       * parents, then we only consider TREESAMEness with respect to them,
 +       * allowing irrelevant merges from uninteresting branches to be
 +       * simplified away. Only if we have only irrelevant parents do we
 +       * base TREESAME on them. Note that this logic is replicated in
 +       * update_treesame, which should be kept in sync.
 +       */
 +      if (relevant_parents ? !relevant_change : !irrelevant_change)
 +              commit->object.flags |= TREESAME;
  }
  
  static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
@@@ -1024,12 -811,16 +1034,12 @@@ static void limit_to_ancestry(struct co
   * to filter the result of "A..B" further to the ones that can actually
   * reach A.
   */
 -static struct commit_list *collect_bottom_commits(struct rev_info *revs)
 +static struct commit_list *collect_bottom_commits(struct commit_list *list)
  {
 -      struct commit_list *bottom = NULL;
 -      int i;
 -      for (i = 0; i < revs->cmdline.nr; i++) {
 -              struct rev_cmdline_entry *elem = &revs->cmdline.rev[i];
 -              if ((elem->flags & UNINTERESTING) &&
 -                  elem->item->type == OBJ_COMMIT)
 -                      commit_list_insert((struct commit *)elem->item, &bottom);
 -      }
 +      struct commit_list *elem, *bottom = NULL;
 +      for (elem = list; elem; elem = elem->next)
 +              if (elem->item->object.flags & BOTTOM)
 +                      commit_list_insert(elem->item, &bottom);
        return bottom;
  }
  
@@@ -1060,7 -851,7 +1070,7 @@@ static int limit_list(struct rev_info *
        struct commit_list *bottom = NULL;
  
        if (revs->ancestry_path) {
 -              bottom = collect_bottom_commits(revs);
 +              bottom = collect_bottom_commits(list);
                if (!bottom)
                        die("--ancestry-path given but there are no bottom commits");
        }
                free_commit_list(bottom);
        }
  
 +      /*
 +       * Check if any commits have become TREESAME by some of their parents
 +       * becoming UNINTERESTING.
 +       */
 +      if (limiting_can_increase_treesame(revs))
 +              for (list = newlist; list; list = list->next) {
 +                      struct commit *c = list->item;
 +                      if (c->object.flags & (UNINTERESTING | TREESAME))
 +                              continue;
 +                      update_treesame(revs, c);
 +              }
 +
        revs->commits = newlist;
        return 0;
  }
  
+ /*
+  * Add an entry to refs->cmdline with the specified information.
+  * *name is copied.
+  */
  static void add_rev_cmdline(struct rev_info *revs,
                            struct object *item,
                            const char *name,
  
        ALLOC_GROW(info->rev, nr + 1, info->alloc);
        info->rev[nr].item = item;
-       info->rev[nr].name = name;
+       info->rev[nr].name = xstrdup(name);
        info->rev[nr].whence = whence;
        info->rev[nr].flags = flags;
        info->nr++;
  }
  
 +static void add_rev_cmdline_list(struct rev_info *revs,
 +                               struct commit_list *commit_list,
 +                               int whence,
 +                               unsigned flags)
 +{
 +      while (commit_list) {
 +              struct object *object = &commit_list->item->object;
 +              add_rev_cmdline(revs, object, sha1_to_hex(object->sha1),
 +                              whence, flags);
 +              commit_list = commit_list->next;
 +      }
 +}
 +
  struct all_refs_cb {
        int all_flags;
        int warned_bad_reflog;
@@@ -1244,7 -1014,7 +1258,7 @@@ static int add_parents_only(struct rev_
        const char *arg = arg_;
  
        if (*arg == '^') {
 -              flags ^= UNINTERESTING;
 +              flags ^= UNINTERESTING | BOTTOM;
                arg++;
        }
        if (get_sha1_committish(arg, sha1))
@@@ -1336,8 -1106,7 +1350,8 @@@ static void prepare_show_merge(struct r
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
        bases = get_merge_bases(head, other, 1);
 -      add_pending_commit_list(revs, bases, UNINTERESTING);
 +      add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
 +      add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
        free_commit_list(bases);
        head->object.flags |= SYMMETRIC_LEFT;
  
@@@ -1373,15 -1142,13 +1387,15 @@@ int handle_revision_arg(const char *arg
        int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
        unsigned get_sha1_flags = 0;
  
 +      flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
 +
        dotdot = strstr(arg, "..");
        if (dotdot) {
                unsigned char from_sha1[20];
                const char *next = dotdot + 2;
                const char *this = arg;
                int symmetric = *next == '.';
 -              unsigned int flags_exclude = flags ^ UNINTERESTING;
 +              unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
                static const char head_by_default[] = "HEAD";
                unsigned int a_flags;
  
  
                        if (symmetric) {
                                exclude = get_merge_bases(a, b, 1);
 +                              add_rev_cmdline_list(revs, exclude,
 +                                                   REV_CMD_MERGE_BASE,
 +                                                   flags_exclude);
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
        dotdot = strstr(arg, "^!");
        if (dotdot && !dotdot[2]) {
                *dotdot = 0;
 -              if (!add_parents_only(revs, arg, flags ^ UNINTERESTING))
 +              if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM)))
                        *dotdot = '^';
        }
  
        local_flags = 0;
        if (*arg == '^') {
 -              local_flags = UNINTERESTING;
 +              local_flags = UNINTERESTING | BOTTOM;
                arg++;
        }
  
@@@ -1526,7 -1290,7 +1540,7 @@@ static void read_revisions_from_stdin(s
                        }
                        die("options not supported in --stdin mode");
                }
-               if (handle_revision_arg(xstrdup(sb.buf), revs, 0,
+               if (handle_revision_arg(sb.buf, revs, 0,
                                        REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", sb.buf);
        }
@@@ -1940,7 -1704,7 +1954,7 @@@ static int handle_revision_pseudo_opt(c
                handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
        } else if (!strcmp(arg, "--bisect")) {
                handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
 -              handle_refs(submodule, revs, *flags ^ UNINTERESTING, for_each_good_bisect_ref);
 +              handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
                handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
        } else if (!strcmp(arg, "--reflog")) {
                handle_reflog(revs, *flags);
        } else if (!strcmp(arg, "--not")) {
 -              *flags ^= UNINTERESTING;
 +              *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
        } else if (!prefixcmp(arg, "--no-walk=")) {
@@@ -2147,12 -1911,6 +2161,12 @@@ int setup_revisions(int argc, const cha
        if (revs->combine_merges)
                revs->ignore_merges = 0;
        revs->diffopt.abbrev = revs->abbrev;
 +
 +      if (revs->line_level_traverse) {
 +              revs->limited = 1;
 +              revs->topo_order = 1;
 +      }
 +
        diff_setup_done(&revs->diffopt);
  
        grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED,
@@@ -2186,32 -1944,28 +2200,32 @@@ static void add_child(struct rev_info *
        l->next = add_decoration(&revs->children, &parent->object, l);
  }
  
 -static int remove_duplicate_parents(struct commit *commit)
 +static int remove_duplicate_parents(struct rev_info *revs, struct commit *commit)
  {
 +      struct treesame_state *ts = lookup_decoration(&revs->treesame, &commit->object);
        struct commit_list **pp, *p;
        int surviving_parents;
  
        /* Examine existing parents while marking ones we have seen... */
        pp = &commit->parents;
 +      surviving_parents = 0;
        while ((p = *pp) != NULL) {
                struct commit *parent = p->item;
                if (parent->object.flags & TMP_MARK) {
                        *pp = p->next;
 +                      if (ts)
 +                              compact_treesame(revs, commit, surviving_parents);
                        continue;
                }
                parent->object.flags |= TMP_MARK;
 +              surviving_parents++;
                pp = &p->next;
        }
 -      /* count them while clearing the temporary mark */
 -      surviving_parents = 0;
 +      /* clear the temporary mark */
        for (p = commit->parents; p; p = p->next) {
                p->item->object.flags &= ~TMP_MARK;
 -              surviving_parents++;
        }
 +      /* no update_treesame() - removing duplicates can't affect TREESAME */
        return surviving_parents;
  }
  
@@@ -2231,157 -1985,9 +2245,157 @@@ static struct merge_simplify_state *loc
        return st;
  }
  
 +static int mark_redundant_parents(struct rev_info *revs, struct commit *commit)
 +{
 +      struct commit_list *h = reduce_heads(commit->parents);
 +      int i = 0, marked = 0;
 +      struct commit_list *po, *pn;
 +
 +      /* Want these for sanity-checking only */
 +      int orig_cnt = commit_list_count(commit->parents);
 +      int cnt = commit_list_count(h);
 +
 +      /*
 +       * Not ready to remove items yet, just mark them for now, based
 +       * on the output of reduce_heads(). reduce_heads outputs the reduced
 +       * set in its original order, so this isn't too hard.
 +       */
 +      po = commit->parents;
 +      pn = h;
 +      while (po) {
 +              if (pn && po->item == pn->item) {
 +                      pn = pn->next;
 +                      i++;
 +              } else {
 +                      po->item->object.flags |= TMP_MARK;
 +                      marked++;
 +              }
 +              po=po->next;
 +      }
 +
 +      if (i != cnt || cnt+marked != orig_cnt)
 +              die("mark_redundant_parents %d %d %d %d", orig_cnt, cnt, i, marked);
 +
 +      free_commit_list(h);
 +
 +      return marked;
 +}
 +
 +static int mark_treesame_root_parents(struct rev_info *revs, struct commit *commit)
 +{
 +      struct commit_list *p;
 +      int marked = 0;
 +
 +      for (p = commit->parents; p; p = p->next) {
 +              struct commit *parent = p->item;
 +              if (!parent->parents && (parent->object.flags & TREESAME)) {
 +                      parent->object.flags |= TMP_MARK;
 +                      marked++;
 +              }
 +      }
 +
 +      return marked;
 +}
 +
 +/*
 + * Awkward naming - this means one parent we are TREESAME to.
 + * cf mark_treesame_root_parents: root parents that are TREESAME (to an
 + * empty tree). Better name suggestions?
 + */
 +static int leave_one_treesame_to_parent(struct rev_info *revs, struct commit *commit)
 +{
 +      struct treesame_state *ts = lookup_decoration(&revs->treesame, &commit->object);
 +      struct commit *unmarked = NULL, *marked = NULL;
 +      struct commit_list *p;
 +      unsigned n;
 +
 +      for (p = commit->parents, n = 0; p; p = p->next, n++) {
 +              if (ts->treesame[n]) {
 +                      if (p->item->object.flags & TMP_MARK) {
 +                              if (!marked)
 +                                      marked = p->item;
 +                      } else {
 +                              if (!unmarked) {
 +                                      unmarked = p->item;
 +                                      break;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /*
 +       * If we are TREESAME to a marked-for-deletion parent, but not to any
 +       * unmarked parents, unmark the first TREESAME parent. This is the
 +       * parent that the default simplify_history==1 scan would have followed,
 +       * and it doesn't make sense to omit that path when asking for a
 +       * simplified full history. Retaining it improves the chances of
 +       * understanding odd missed merges that took an old version of a file.
 +       *
 +       * Example:
 +       *
 +       *   I--------*X       A modified the file, but mainline merge X used
 +       *    \       /        "-s ours", so took the version from I. X is
 +       *     `-*A--'         TREESAME to I and !TREESAME to A.
 +       *
 +       * Default log from X would produce "I". Without this check,
 +       * --full-history --simplify-merges would produce "I-A-X", showing
 +       * the merge commit X and that it changed A, but not making clear that
 +       * it had just taken the I version. With this check, the topology above
 +       * is retained.
 +       *
 +       * Note that it is possible that the simplification chooses a different
 +       * TREESAME parent from the default, in which case this test doesn't
 +       * activate, and we _do_ drop the default parent. Example:
 +       *
 +       *   I------X         A modified the file, but it was reverted in B,
 +       *    \    /          meaning mainline merge X is TREESAME to both
 +       *    *A-*B           parents.
 +       *
 +       * Default log would produce "I" by following the first parent;
 +       * --full-history --simplify-merges will produce "I-A-B". But this is a
 +       * reasonable result - it presents a logical full history leading from
 +       * I to X, and X is not an important merge.
 +       */
 +      if (!unmarked && marked) {
 +              marked->object.flags &= ~TMP_MARK;
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +static int remove_marked_parents(struct rev_info *revs, struct commit *commit)
 +{
 +      struct commit_list **pp, *p;
 +      int nth_parent, removed = 0;
 +
 +      pp = &commit->parents;
 +      nth_parent = 0;
 +      while ((p = *pp) != NULL) {
 +              struct commit *parent = p->item;
 +              if (parent->object.flags & TMP_MARK) {
 +                      parent->object.flags &= ~TMP_MARK;
 +                      *pp = p->next;
 +                      free(p);
 +                      removed++;
 +                      compact_treesame(revs, commit, nth_parent);
 +                      continue;
 +              }
 +              pp = &p->next;
 +              nth_parent++;
 +      }
 +
 +      /* Removing parents can only increase TREESAMEness */
 +      if (removed && !(commit->object.flags & TREESAME))
 +              update_treesame(revs, commit);
 +
 +      return nth_parent;
 +}
 +
  static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
  {
        struct commit_list *p;
 +      struct commit *parent;
        struct merge_simplify_state *st, *pst;
        int cnt;
  
        }
  
        /*
 -       * Rewrite our list of parents.
 +       * Rewrite our list of parents. Note that this cannot
 +       * affect our TREESAME flags in any way - a commit is
 +       * always TREESAME to its simplification.
         */
        for (p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
        if (revs->first_parent_only)
                cnt = 1;
        else
 -              cnt = remove_duplicate_parents(commit);
 +              cnt = remove_duplicate_parents(revs, 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.
 +       * in such a case, the immediate parent from that branch
 +       * will be rewritten to be the merge base.
         *
         *      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.
 +       * Further, a merge of an independent branch that doesn't
 +       * touch the path will reduce to a treesame root parent:
 +       *
 +       *  ----o----X          X: the commit we are looking at;
 +       *          /           o: a commit that touches the paths;
 +       *         r            r: a root commit not touching the paths
 +       *
 +       * Detect and simplify both cases.
         */
        if (1 < cnt) {
 -              struct commit_list *h = reduce_heads(commit->parents);
 -              cnt = commit_list_count(h);
 -              free_commit_list(commit->parents);
 -              commit->parents = h;
 +              int marked = mark_redundant_parents(revs, commit);
 +              marked += mark_treesame_root_parents(revs, commit);
 +              if (marked)
 +                      marked -= leave_one_treesame_to_parent(revs, commit);
 +              if (marked)
 +                      cnt = remove_marked_parents(revs, commit);
        }
  
        /*
         * 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
 +       * merge and its parents don't simplify to one relevant commit
         * (the first two cases are already handled at the beginning of
         * this function).
         *
 -       * Otherwise, it simplifies to what its sole parent simplifies to.
 +       * Otherwise, it simplifies to what its sole relevant parent
 +       * simplifies to.
         */
        if (!cnt ||
            (commit->object.flags & UNINTERESTING) ||
            !(commit->object.flags & TREESAME) ||
 -          (1 < cnt))
 +          (parent = one_relevant_parent(revs, commit->parents)) == NULL)
                st->simplified = commit;
        else {
 -              pst = locate_simplify_state(revs, commit->parents->item);
 +              pst = locate_simplify_state(revs, parent);
                st->simplified = pst->simplified;
        }
        return tail;
@@@ -2579,11 -2173,6 +2593,11 @@@ int prepare_revision_walk(struct rev_in
        if (!revs->leak_pending)
                free(list);
  
 +      /* Signal whether we need per-parent treesame decoration */
 +      if (revs->simplify_merges ||
 +          (revs->limited && limiting_can_increase_treesame(revs)))
 +              revs->treesame.name = "treesame";
 +
        if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
                commit_list_sort_by_date(&revs->commits);
        if (revs->no_walk)
                        return -1;
        if (revs->topo_order)
                sort_in_topological_order(&revs->commits, revs->lifo);
 +      if (revs->line_level_traverse)
 +              line_log_filter(revs);
        if (revs->simplify_merges)
                simplify_merges(revs);
        if (revs->children.name)
        return 0;
  }
  
 -enum rewrite_result {
 -      rewrite_one_ok,
 -      rewrite_one_noparents,
 -      rewrite_one_error
 -};
 -
  static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
  {
        struct commit_list *cache = NULL;
                if (!revs->limited)
                        if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
                                return rewrite_one_error;
 -              if (p->parents && p->parents->next)
 -                      return rewrite_one_ok;
                if (p->object.flags & UNINTERESTING)
                        return rewrite_one_ok;
                if (!(p->object.flags & TREESAME))
                        return rewrite_one_ok;
                if (!p->parents)
                        return rewrite_one_noparents;
 -              *pp = p->parents->item;
 +              if ((p = one_relevant_parent(revs, p->parents)) == NULL)
 +                      return rewrite_one_ok;
 +              *pp = p;
        }
  }
  
 -static int rewrite_parents(struct rev_info *revs, struct commit *commit)
 +int rewrite_parents(struct rev_info *revs, struct commit *commit,
 +      rewrite_parent_fn_t rewrite_parent)
  {
        struct commit_list **pp = &commit->parents;
        while (*pp) {
                struct commit_list *parent = *pp;
 -              switch (rewrite_one(revs, &parent->item)) {
 +              switch (rewrite_parent(revs, &parent->item)) {
                case rewrite_one_ok:
                        break;
                case rewrite_one_noparents:
                }
                pp = &parent->next;
        }
 -      remove_duplicate_parents(commit);
 +      remove_duplicate_parents(revs, commit);
        return 0;
  }
  
@@@ -2764,7 -2356,10 +2778,7 @@@ enum commit_action get_commit_action(st
        if (revs->min_age != -1 && (commit->date > revs->min_age))
                return commit_ignore;
        if (revs->min_parents || (revs->max_parents >= 0)) {
 -              int n = 0;
 -              struct commit_list *p;
 -              for (p = commit->parents; p; p = p->next)
 -                      n++;
 +              int n = commit_list_count(commit->parents);
                if ((n < revs->min_parents) ||
                    ((revs->max_parents >= 0) && (n > revs->max_parents)))
                        return commit_ignore;
        if (revs->prune && revs->dense) {
                /* Commit without changes? */
                if (commit->object.flags & TREESAME) {
 +                      int n;
 +                      struct commit_list *p;
                        /* drop merges unless we want parenthood */
                        if (!want_ancestry(revs))
                                return commit_ignore;
 -                      /* non-merge - always ignore it */
 -                      if (!commit->parents || !commit->parents->next)
 -                              return commit_ignore;
 +                      /*
 +                       * If we want ancestry, then need to keep any merges
 +                       * between relevant commits to tie together topology.
 +                       * For consistency with TREESAME and simplification
 +                       * use "relevant" here rather than just INTERESTING,
 +                       * to treat bottom commit(s) as part of the topology.
 +                       */
 +                      for (n = 0, p = commit->parents; p; p = p->next)
 +                              if (relevant_commit(p->item))
 +                                      if (++n >= 2)
 +                                              return commit_show;
 +                      return commit_ignore;
                }
        }
        return commit_show;
@@@ -2803,7 -2387,7 +2817,7 @@@ enum commit_action simplify_commit(stru
        if (action == commit_show &&
            !revs->show_all &&
            revs->prune && revs->dense && want_ancestry(revs)) {
 -              if (rewrite_parents(revs, commit) < 0)
 +              if (rewrite_parents(revs, commit, rewrite_one) < 0)
                        return commit_error;
        }
        return action;
@@@ -2853,25 -2437,23 +2867,23 @@@ static struct commit *get_revision_1(st
        return NULL;
  }
  
- static void gc_boundary(struct object_array *array)
+ /*
+  * Return true for entries that have not yet been shown.  (This is an
+  * object_array_each_func_t.)
+  */
+ static int entry_unshown(struct object_array_entry *entry, void *cb_data_unused)
  {
-       unsigned nr = array->nr;
-       unsigned alloc = array->alloc;
-       struct object_array_entry *objects = array->objects;
+       return !(entry->item->flags & SHOWN);
+ }
  
-       if (alloc <= nr) {
-               unsigned i, j;
-               for (i = j = 0; i < nr; i++) {
-                       if (objects[i].item->flags & SHOWN)
-                               continue;
-                       if (i != j)
-                               objects[j] = objects[i];
-                       j++;
-               }
-               for (i = j; i < nr; i++)
-                       objects[i].item = NULL;
-               array->nr = j;
-       }
+ /*
+  * If array is on the verge of a realloc, garbage-collect any entries
+  * that have already been shown to try to free up some space.
+  */
+ static void gc_boundary(struct object_array *array)
+ {
+       if (array->nr == array->alloc)
+               object_array_filter(array, entry_unshown, NULL);
  }
  
  static void create_boundary_commit_list(struct rev_info *revs)
diff --combined revision.h
index 24547b59f1d8f0d77bee6174600ea1bcb2e1b2a9,96284651dba13f5c8a737a582cf37004f71c8f3a..eeea6fba3c5408d5567eec238d0a331bf64ddc04
@@@ -15,8 -15,7 +15,8 @@@
  #define ADDED         (1u<<7) /* Parents already parsed and added? */
  #define SYMMETRIC_LEFT        (1u<<8)
  #define PATCHSAME     (1u<<9)
 -#define ALL_REV_FLAGS ((1u<<10)-1)
 +#define BOTTOM                (1u<<10)
 +#define ALL_REV_FLAGS ((1u<<11)-1)
  
  #define DECORATE_SHORT_REFS   1
  #define DECORATE_FULL_REFS    2
@@@ -36,7 -35,6 +36,7 @@@ struct rev_cmdline_info 
                        REV_CMD_PARENTS_ONLY,
                        REV_CMD_LEFT,
                        REV_CMD_RIGHT,
 +                      REV_CMD_MERGE_BASE,
                        REV_CMD_REV
                } whence;
                unsigned flags;
@@@ -98,8 -96,7 +98,8 @@@ struct rev_info 
                        cherry_mark:1,
                        bisect:1,
                        ancestry_path:1,
 -                      first_parent_only:1;
 +                      first_parent_only:1,
 +                      line_level_traverse:1;
  
        /* Diff flags */
        unsigned int    diff:1,
        struct reflog_walk_info *reflog_info;
        struct decoration children;
        struct decoration merge_simplification;
 +      struct decoration treesame;
  
        /* notes-specific options: which refs to show */
        struct display_notes_opt notes_opt;
        int count_left;
        int count_right;
        int count_same;
 +
 +      /* line level range that we are chasing */
 +      struct decoration line_log_data;
  };
  
  #define REV_TREE_SAME         0
@@@ -202,19 -195,23 +202,23 @@@ struct setup_revision_opt 
  };
  
  extern void init_revisions(struct rev_info *revs, const char *prefix);
- extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *);
+ extern int setup_revisions(int argc, const char **argv, struct rev_info *revs,
+                          struct setup_revision_opt *);
  extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
-                                const struct option *options,
-                                const char * const usagestr[]);
+                              const struct option *options,
+                              const char * const usagestr[]);
  #define REVARG_CANNOT_BE_FILENAME 01
  #define REVARG_COMMITTISH 02
- extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
+ extern int handle_revision_arg(const char *arg, struct rev_info *revs,
+                              int flags, unsigned revarg_opt);
  
  extern void reset_revision_walk(void);
  extern int prepare_revision_walk(struct rev_info *revs);
  extern struct commit *get_revision(struct rev_info *revs);
- extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
- extern void put_revision_mark(const struct rev_info *revs, const struct commit *commit);
+ extern char *get_revision_mark(const struct rev_info *revs,
+                              const struct commit *commit);
+ extern void put_revision_mark(const struct rev_info *revs,
+                             const struct commit *commit);
  
  extern void mark_parents_uninteresting(struct commit *commit);
  extern void mark_tree_uninteresting(struct tree *tree);
@@@ -227,15 -224,19 +231,19 @@@ struct name_path 
  
  char *path_name(const struct name_path *path, const char *name);
  
- extern void show_object_with_name(FILE *, struct object *, const struct name_path *, const char *);
+ extern void show_object_with_name(FILE *, struct object *,
+                                 const struct name_path *, const char *);
  
  extern void add_object(struct object *obj,
                       struct object_array *p,
                       struct name_path *path,
                       const char *name);
  
- extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
- extern void add_pending_sha1(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags);
+ extern void add_pending_object(struct rev_info *revs,
+                              struct object *obj, const char *name);
+ extern void add_pending_sha1(struct rev_info *revs,
+                            const char *name, const unsigned char *sha1,
+                            unsigned int flags);
  
  extern void add_head_to_pending(struct rev_info *);
  
@@@ -245,17 -246,9 +253,19 @@@ enum commit_action 
        commit_error
  };
  
- extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
- extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
+ extern enum commit_action get_commit_action(struct rev_info *revs,
+                                           struct commit *commit);
+ extern enum commit_action simplify_commit(struct rev_info *revs,
+                                         struct commit *commit);
  
 +enum rewrite_result {
 +      rewrite_one_ok,
 +      rewrite_one_noparents,
 +      rewrite_one_error
 +};
 +
 +typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct commit **pp);
 +
 +extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
 +      rewrite_parent_fn_t rewrite_parent);
  #endif
diff --combined submodule.c
index 1821a5b3160bd3a3e60f328b50b56a0208b2df9c,ad476ce5fb894bf62844ee6ca7413ce343f2933a..86854248983be78b3702f5045a659dfe83bf4446
@@@ -603,8 -603,9 +603,8 @@@ int fetch_populated_submodules(const st
        if (!work_tree)
                goto out;
  
 -      if (!the_index.initialized)
 -              if (read_cache() < 0)
 -                      die("index file corrupt");
 +      if (read_cache() < 0)
 +              die("index file corrupt");
  
        argv_array_push(&argv, "fetch");
        for (i = 0; i < options->argc; i++)
@@@ -845,7 -846,7 +845,7 @@@ static int find_first_merges(struct obj
                struct commit *a, struct commit *b)
  {
        int i, j;
-       struct object_array merges;
+       struct object_array merges = OBJECT_ARRAY_INIT;
        struct commit *commit;
        int contains_another;
  
        struct rev_info revs;
        struct setup_revision_opt rev_opts;
  
-       memset(&merges, 0, sizeof(merges));
        memset(result, 0, sizeof(struct object_array));
        memset(&rev_opts, 0, sizeof(rev_opts));
  
                }
  
                if (!contains_another)
-                       add_object_array(merges.objects[i].item,
-                                        merges.objects[i].name, result);
+                       add_object_array(merges.objects[i].item, NULL, result);
        }
  
        free(merges.objects);