Merge branch 'jc/merge-bases'
authorJunio C Hamano <gitster@pobox.com>
Wed, 7 Jan 2015 20:55:05 +0000 (12:55 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 7 Jan 2015 20:55:05 +0000 (12:55 -0800)
The get_merge_bases*() API was easy to misuse by careless
copy&paste coders, leaving object flags tainted in the commits that
needed to be traversed.

* jc/merge-bases:
get_merge_bases(): always clean-up object flags
bisect: clean flags after checking merge bases

1  2 
bisect.c
builtin/merge.c
builtin/rev-parse.c
commit.c
commit.h
merge-recursive.c
notes-merge.c
revision.c
sha1_name.c
submodule.c
diff --combined bisect.c
index df09cbc8cabe9dcb87a0023445babcf14c3d7691,1d6b393f366069735727c08bbec85433dd7d7838..8c6d843699ab04bc0fe952268d14965943446a17
+++ b/bisect.c
@@@ -215,12 -215,11 +215,12 @@@ static struct commit_list *best_bisecti
        }
        qsort(array, cnt, sizeof(*array), compare_commit_dist);
        for (p = list, i = 0; i < cnt; i++) {
 -              struct name_decoration *r = xmalloc(sizeof(*r) + 100);
 +              char buf[100]; /* enough for dist=%d */
                struct object *obj = &(array[i].commit->object);
  
 -              sprintf(r->name, "dist=%d", array[i].distance);
 -              r->next = add_decoration(&name_decoration, obj, r);
 +              snprintf(buf, sizeof(buf), "dist=%d", array[i].distance);
 +              add_name_decoration(DECORATION_NONE, buf, obj);
 +
                p->item = array[i].commit;
                p = p->next;
        }
@@@ -777,7 -776,7 +777,7 @@@ static void check_merge_bases(int no_ch
        int rev_nr;
        struct commit **rev = get_bad_and_good_commits(&rev_nr);
  
-       result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
+       result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
  
        for (; result; result = result->next) {
                const unsigned char *mb = result->item->object.sha1;
diff --combined builtin/merge.c
index 215d4856e507f671139f41c73b57e661d8d1bf87,fbdbd78da18c2f6ac134a3ea5705ca1ac4da9a27..9effed7ff15d087a6d6768044ffaa70db1ddac73
@@@ -9,7 -9,6 +9,7 @@@
  #include "cache.h"
  #include "parse-options.h"
  #include "builtin.h"
 +#include "lockfile.h"
  #include "run-command.h"
  #include "diff.h"
  #include "refs.h"
@@@ -29,7 -28,6 +29,7 @@@
  #include "remote.h"
  #include "fmt-merge-msg.h"
  #include "gpg-interface.h"
 +#include "sequencer.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -65,7 -63,7 +65,7 @@@ static int verbosity
  static int allow_rerere_auto;
  static int abort_current_merge;
  static int show_progress = -1;
 -static int default_to_upstream;
 +static int default_to_upstream = 1;
  static const char *sign_commit;
  
  static struct strategy all_strategy[] = {
@@@ -239,10 -237,11 +239,10 @@@ static void drop_save(void
  static int save_state(unsigned char *stash)
  {
        int len;
 -      struct child_process cp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf buffer = STRBUF_INIT;
        const char *argv[] = {"stash", "create", NULL};
  
 -      memset(&cp, 0, sizeof(cp));
        cp.argv = argv;
        cp.out = -1;
        cp.git_cmd = 1;
@@@ -399,7 -398,7 +399,7 @@@ static void finish(struct commit *head_
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
                        update_ref(reflog_message.buf, "HEAD",
                                new_head, head, 0,
 -                              DIE_ON_ERR);
 +                              UPDATE_REFS_DIE_ON_ERR);
                        /*
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
@@@ -558,7 -557,7 +558,7 @@@ static void parse_branch_merge_options(
        if (argc < 0)
                die(_("Bad branch.%s.mergeoptions string: %s"), branch,
                    split_cmdline_strerror(argc));
 -      argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
 +      REALLOC_ARRAY(argv, argc + 2);
        memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
        argc++;
        argv[0] = "branch.*.mergeoptions";
@@@ -658,18 -657,22 +658,18 @@@ static int try_merge_strategy(const cha
                              struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
  {
 -      int index_fd;
 -      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +      static struct lock_file lock;
  
 -      index_fd = hold_locked_index(lock, 1);
 +      hold_locked_index(&lock, 1);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
 -                      (write_cache(index_fd, active_cache, active_nr) ||
 -                       commit_locked_index(lock)))
 +          write_locked_index(&the_index, &lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
 -      rollback_lock_file(lock);
 +      rollback_lock_file(&lock);
  
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
                int clean, x;
                struct commit *result;
 -              struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 -              int index_fd;
                struct commit_list *reversed = NULL;
                struct merge_options o;
                struct commit_list *j;
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
  
 -              index_fd = hold_locked_index(lock, 1);
 +              hold_locked_index(&lock, 1);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (active_cache_changed &&
 -                              (write_cache(index_fd, active_cache, active_nr) ||
 -                               commit_locked_index(lock)))
 +                  write_locked_index(&the_index, &lock, COMMIT_LOCK))
                        die (_("unable to write %s"), get_index_file());
 -              rollback_lock_file(lock);
 +              rollback_lock_file(&lock);
                return clean ? 0 : 1;
        } else {
                return try_merge_command(strategy, xopts_nr, xopts,
@@@ -839,14 -843,16 +839,14 @@@ static void prepare_to_commit(struct co
  static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
  {
        unsigned char result_tree[20], result_commit[20];
 -      struct commit_list *parent = xmalloc(sizeof(*parent));
 +      struct commit_list *parents, **pptr = &parents;
  
        write_tree_trivial(result_tree);
        printf(_("Wonderful.\n"));
 -      parent->item = head;
 -      parent->next = xmalloc(sizeof(*parent->next));
 -      parent->next->item = remoteheads->item;
 -      parent->next->next = NULL;
 +      pptr = commit_list_append(head, pptr);
 +      pptr = commit_list_append(remoteheads->item, pptr);
        prepare_to_commit(remoteheads);
 -      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parent,
 +      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
                        result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, result_commit, "In-index merge");
@@@ -881,19 -887,28 +881,19 @@@ static int finish_automerge(struct comm
        return 0;
  }
  
 -static int suggest_conflicts(int renormalizing)
 +static int suggest_conflicts(void)
  {
        const char *filename;
        FILE *fp;
 -      int pos;
 +      struct strbuf msgbuf = STRBUF_INIT;
  
        filename = git_path("MERGE_MSG");
        fp = fopen(filename, "a");
        if (!fp)
                die_errno(_("Could not open '%s' for writing"), filename);
 -      fprintf(fp, "\nConflicts:\n");
 -      for (pos = 0; pos < active_nr; pos++) {
 -              const struct cache_entry *ce = active_cache[pos];
 -
 -              if (ce_stage(ce)) {
 -                      fprintf(fp, "\t%s\n", ce->name);
 -                      while (pos + 1 < active_nr &&
 -                                      !strcmp(ce->name,
 -                                              active_cache[pos + 1]->name))
 -                              pos++;
 -              }
 -      }
 +
 +      append_conflicts_hint(&msgbuf);
 +      fputs(msgbuf.buf, fp);
        fclose(fp);
        rerere(allow_rerere_auto);
        printf(_("Automatic merge failed; "
@@@ -1093,7 -1108,7 +1093,7 @@@ int cmd_merge(int argc, const char **ar
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
         */
 -      branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
 +      branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
        if (branch && starts_with(branch, "refs/heads/"))
                branch += 11;
        if (!branch || is_null_sha1(head_sha1))
                 */
                if (advice_resolve_conflict)
                        die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
 -                                "Please, commit your changes before you can merge."));
 +                                "Please, commit your changes before you merge."));
                else
                        die(_("You have not concluded your merge (MERGE_HEAD exists)."));
        }
        if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
                if (advice_resolve_conflict)
                        die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 -                          "Please, commit your changes before you can merge."));
 +                          "Please, commit your changes before you merge."));
                else
                        die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
        }
                        die(_("%s - not something we can merge"), argv[0]);
                read_empty(remote_head->object.sha1, 0);
                update_ref("initial pull", "HEAD", remote_head->object.sha1,
 -                         NULL, 0, DIE_ON_ERR);
 +                         NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
                                printf(_("Commit %s has a good GPG signature by %s\n"),
                                       hex, signature_check.signer);
  
 -                      free(signature_check.gpg_output);
 -                      free(signature_check.gpg_status);
 -                      free(signature_check.signer);
 -                      free(signature_check.key);
 +                      signature_check_clear(&signature_check);
                }
        }
  
        if (!remoteheads)
                ; /* already up-to-date */
        else if (!remoteheads->next)
-               common = get_merge_bases(head_commit, remoteheads->item, 1);
+               common = get_merge_bases(head_commit, remoteheads->item);
        else {
                struct commit_list *list = remoteheads;
                commit_list_insert(head_commit, &list);
        }
  
        update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
 -                 NULL, 0, DIE_ON_ERR);
 +                 NULL, 0, UPDATE_REFS_DIE_ON_ERR);
  
        if (remoteheads && !common)
                ; /* No common ancestors found. We need a real merge. */
                         * merge_bases again, otherwise "git merge HEAD^
                         * HEAD^^" would be missed.
                         */
-                       common_one = get_merge_bases(head_commit, j->item, 1);
+                       common_one = get_merge_bases(head_commit, j->item);
                        if (hashcmp(common_one->item->object.sha1,
                                j->item->object.sha1)) {
                                up_to_date = 0;
                fprintf(stderr, _("Automatic merge went well; "
                        "stopped before committing as requested\n"));
        else
 -              ret = suggest_conflicts(option_renormalize);
 +              ret = suggest_conflicts();
  
  done:
        free(branch_to_free);
diff --combined builtin/rev-parse.c
index 35d3c43ed656bf28b78501426f4502c0dff1e7b0,ed6db3731afd69f6477f3c77f59c58cded0fb2d6..95328b80d930260915ad86a30a21ecd3c1b7d1f4
@@@ -11,7 -11,6 +11,7 @@@
  #include "parse-options.h"
  #include "diff.h"
  #include "revision.h"
 +#include "split-index.h"
  
  #define DO_REVS               1
  #define DO_NOREV      2
@@@ -151,7 -150,6 +151,7 @@@ static void show_rev(int type, const un
                                error("refname '%s' is ambiguous", name);
                                break;
                        }
 +                      free(full);
                } else {
                        show_with_type(type, name);
                }
@@@ -279,7 -277,7 +279,7 @@@ static int try_difference(const char *a
                        struct commit *a, *b;
                        a = lookup_commit_reference(sha1);
                        b = lookup_commit_reference(end);
-                       exclude = get_merge_bases(a, b, 1);
+                       exclude = get_merge_bases(a, b);
                        while (exclude) {
                                struct commit_list *n = exclude->next;
                                show_rev(REVERSED,
@@@ -508,9 -506,7 +508,9 @@@ int cmd_rev_parse(int argc, const char 
        int has_dashdash = 0;
        int output_prefix = 0;
        unsigned char sha1[20];
 +      unsigned int flags = 0;
        const char *name = NULL;
 +      struct object_context unused;
  
        if (argc > 1 && !strcmp("--parseopt", argv[1]))
                return cmd_parseopt(argc - 1, argv + 1, prefix);
                        }
                        if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
                                quiet = 1;
 +                              flags |= GET_SHA1_QUIETLY;
                                continue;
                        }
                        if (!strcmp(arg, "--short") ||
                        }
                        if (!strcmp(arg, "--git-dir")) {
                                const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
 -                              static char cwd[PATH_MAX];
 +                              char *cwd;
                                int len;
                                if (gitdir) {
                                        puts(gitdir);
                                        puts(".git");
                                        continue;
                                }
 -                              if (!getcwd(cwd, PATH_MAX))
 -                                      die_errno("unable to get current working directory");
 +                              cwd = xgetcwd();
                                len = strlen(cwd);
                                printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
 +                              free(cwd);
                                continue;
                        }
                        if (!strcmp(arg, "--resolve-git-dir")) {
                                                : "false");
                                continue;
                        }
 +                      if (!strcmp(arg, "--shared-index-path")) {
 +                              if (read_cache() < 0)
 +                                      die(_("Could not read the index"));
 +                              if (the_index.split_index) {
 +                                      const unsigned char *sha1 = the_index.split_index->base_sha1;
 +                                      puts(git_path("sharedindex.%s", sha1_to_hex(sha1)));
 +                              }
 +                              continue;
 +                      }
                        if (starts_with(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
                        name++;
                        type = REVERSED;
                }
 -              if (!get_sha1(name, sha1)) {
 +              if (!get_sha1_with_context(name, flags, sha1, &unused)) {
                        if (verify)
                                revs_count++;
                        else
diff --combined commit.c
index a54cb9a454fdce3ccf9dff58338e43d73945d728,8f9f37e3e6d3372207b63191fe3853377099a2f1..a8c7577d28a4b2a0b5fc13420f8a141871626087
+++ b/commit.c
@@@ -430,7 -430,12 +430,7 @@@ struct commit_list *copy_commit_list(st
        struct commit_list *head = NULL;
        struct commit_list **pp = &head;
        while (list) {
 -              struct commit_list *new;
 -              new = xmalloc(sizeof(struct commit_list));
 -              new->item = list->item;
 -              new->next = NULL;
 -              *pp = new;
 -              pp = &new->next;
 +              pp = commit_list_append(list->item, pp);
                list = list->next;
        }
        return head;
@@@ -584,19 -589,26 +584,19 @@@ define_commit_slab(author_date_slab, un
  static void record_author_date(struct author_date_slab *author_date,
                               struct commit *commit)
  {
 -      const char *buf, *line_end, *ident_line;
        const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
 +      const char *ident_line;
 +      size_t ident_len;
        char *date_end;
        unsigned long date;
  
 -      for (buf = buffer; buf; buf = line_end + 1) {
 -              line_end = strchrnul(buf, '\n');
 -              ident_line = skip_prefix(buf, "author ");
 -              if (!ident_line) {
 -                      if (!line_end[0] || line_end[1] == '\n')
 -                              return; /* end of header */
 -                      continue;
 -              }
 -              if (split_ident_line(&ident,
 -                                   ident_line, line_end - ident_line) ||
 -                  !ident.date_begin || !ident.date_end)
 -                      goto fail_exit; /* malformed "author" line */
 -              break;
 -      }
 +      ident_line = find_commit_header(buffer, "author", &ident_len);
 +      if (!ident_line)
 +              goto fail_exit; /* no author line */
 +      if (split_ident_line(&ident, ident_line, ident_len) ||
 +          !ident.date_begin || !ident.date_end)
 +              goto fail_exit; /* malformed "author" line */
  
        date = strtoul(ident.date_begin, &date_end, 10);
        if (date_end != ident.date_end)
@@@ -758,41 -770,45 +758,41 @@@ void sort_in_topological_order(struct c
  
  static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
  
 -static struct commit *interesting(struct commit_list *list)
 +static int queue_has_nonstale(struct prio_queue *queue)
  {
 -      while (list) {
 -              struct commit *commit = list->item;
 -              list = list->next;
 -              if (commit->object.flags & STALE)
 -                      continue;
 -              return commit;
 +      int i;
 +      for (i = 0; i < queue->nr; i++) {
 +              struct commit *commit = queue->array[i].data;
 +              if (!(commit->object.flags & STALE))
 +                      return 1;
        }
 -      return NULL;
 +      return 0;
  }
  
  /* all input commits in one and twos[] must have been parsed! */
  static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
  {
 -      struct commit_list *list = NULL;
 +      struct prio_queue queue = { compare_commits_by_commit_date };
        struct commit_list *result = NULL;
        int i;
  
        one->object.flags |= PARENT1;
 -      commit_list_insert_by_date(one, &list);
 -      if (!n)
 -              return list;
 +      if (!n) {
 +              commit_list_append(one, &result);
 +              return result;
 +      }
 +      prio_queue_put(&queue, one);
 +
        for (i = 0; i < n; i++) {
                twos[i]->object.flags |= PARENT2;
 -              commit_list_insert_by_date(twos[i], &list);
 +              prio_queue_put(&queue, twos[i]);
        }
  
 -      while (interesting(list)) {
 -              struct commit *commit;
 +      while (queue_has_nonstale(&queue)) {
 +              struct commit *commit = prio_queue_get(&queue);
                struct commit_list *parents;
 -              struct commit_list *next;
                int flags;
  
 -              commit = list->item;
 -              next = list->next;
 -              free(list);
 -              list = next;
 -
                flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
                if (flags == (PARENT1 | PARENT2)) {
                        if (!(commit->object.flags & RESULT)) {
                        if (parse_commit(p))
                                return NULL;
                        p->object.flags |= flags;
 -                      commit_list_insert_by_date(p, &list);
 +                      prio_queue_put(&queue, p);
                }
        }
  
 -      free_commit_list(list);
 +      clear_prio_queue(&queue);
        return result;
  }
  
@@@ -867,7 -883,7 +867,7 @@@ struct commit_list *get_octopus_merge_b
  
                for (j = ret; j; j = j->next) {
                        struct commit_list *bases;
-                       bases = get_merge_bases(i->item, j->item, 1);
+                       bases = get_merge_bases(i->item, j->item);
                        if (!new)
                                new = bases;
                        else
@@@ -936,10 -952,10 +936,10 @@@ static int remove_redundant(struct comm
        return filled;
  }
  
- struct commit_list *get_merge_bases_many(struct commit *one,
-                                        int n,
-                                        struct commit **twos,
-                                        int cleanup)
+ static struct commit_list *get_merge_bases_many_0(struct commit *one,
+                                                 int n,
+                                                 struct commit **twos,
+                                                 int cleanup)
  {
        struct commit_list *list;
        struct commit **rslt;
        }
  
        /* There are more than one */
 -      cnt = 0;
 -      list = result;
 -      while (list) {
 -              list = list->next;
 -              cnt++;
 -      }
 +      cnt = commit_list_count(result);
        rslt = xcalloc(cnt, sizeof(*rslt));
        for (list = result, i = 0; list; list = list->next)
                rslt[i++] = list->item;
        return result;
  }
  
- struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
-                                   int cleanup)
+ struct commit_list *get_merge_bases_many(struct commit *one,
+                                        int n,
+                                        struct commit **twos)
+ {
+       return get_merge_bases_many_0(one, n, twos, 1);
+ }
+ struct commit_list *get_merge_bases_many_dirty(struct commit *one,
+                                              int n,
+                                              struct commit **twos)
+ {
+       return get_merge_bases_many_0(one, n, twos, 0);
+ }
+ struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
  {
-       return get_merge_bases_many(one, 1, &two, cleanup);
+       return get_merge_bases_many_0(one, 1, &two, 1);
  }
  
  /*
@@@ -1140,40 -1174,6 +1153,40 @@@ int parse_signed_commit(const struct co
        return saw_signature;
  }
  
 +int remove_signature(struct strbuf *buf)
 +{
 +      const char *line = buf->buf;
 +      const char *tail = buf->buf + buf->len;
 +      int in_signature = 0;
 +      const char *sig_start = NULL;
 +      const char *sig_end = NULL;
 +
 +      while (line < tail) {
 +              const char *next = memchr(line, '\n', tail - line);
 +              next = next ? next + 1 : tail;
 +
 +              if (in_signature && line[0] == ' ')
 +                      sig_end = next;
 +              else if (starts_with(line, gpg_sig_header) &&
 +                       line[gpg_sig_header_len] == ' ') {
 +                      sig_start = line;
 +                      sig_end = next;
 +                      in_signature = 1;
 +              } else {
 +                      if (*line == '\n')
 +                              /* dump the whole remainder of the buffer */
 +                              next = tail;
 +                      in_signature = 0;
 +              }
 +              line = next;
 +      }
 +
 +      if (sig_start)
 +              strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
 +
 +      return sig_start != NULL;
 +}
 +
  static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
  {
        struct merge_remote_desc *desc;
@@@ -1214,7 -1214,44 +1227,7 @@@ free_return
        free(buf);
  }
  
 -static struct {
 -      char result;
 -      const char *check;
 -} sigcheck_gpg_status[] = {
 -      { 'G', "\n[GNUPG:] GOODSIG " },
 -      { 'B', "\n[GNUPG:] BADSIG " },
 -      { 'U', "\n[GNUPG:] TRUST_NEVER" },
 -      { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
 -};
 -
 -static void parse_gpg_output(struct signature_check *sigc)
 -{
 -      const char *buf = sigc->gpg_status;
 -      int i;
 -
 -      /* Iterate over all search strings */
 -      for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
 -              const char *found, *next;
 -
 -              found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
 -              if (!found) {
 -                      found = strstr(buf, sigcheck_gpg_status[i].check);
 -                      if (!found)
 -                              continue;
 -                      found += strlen(sigcheck_gpg_status[i].check);
 -              }
 -              sigc->result = sigcheck_gpg_status[i].result;
 -              /* The trust messages are not followed by key/signer information */
 -              if (sigc->result != 'U') {
 -                      sigc->key = xmemdupz(found, 16);
 -                      found += 17;
 -                      next = strchrnul(found, '\n');
 -                      sigc->signer = xmemdupz(found, next - found);
 -              }
 -      }
 -}
 -
 -void check_commit_signature(const struct commit* commit, struct signature_check *sigc)
 +void check_commit_signature(const struct commit *commit, struct signature_check *sigc)
  {
        struct strbuf payload = STRBUF_INIT;
        struct strbuf signature = STRBUF_INIT;
                                      &gpg_output, &gpg_status);
        if (status && !gpg_output.len)
                goto out;
 +      sigc->payload = strbuf_detach(&payload, NULL);
        sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
        sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
        parse_gpg_output(sigc);
@@@ -1276,19 -1312,6 +1289,19 @@@ struct commit_extra_header *read_commit
        return extra;
  }
  
 +void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
 +{
 +      struct commit_extra_header *extra, *to_free;
 +
 +      to_free = read_commit_extra_headers(commit, NULL);
 +      for (extra = to_free; extra; extra = extra->next) {
 +              if (strcmp(extra->key, "mergetag"))
 +                      continue; /* not a merge tag */
 +              fn(commit, extra, data);
 +      }
 +      free_commit_extra_headers(to_free);
 +}
 +
  static inline int standard_header_field(const char *field, size_t len)
  {
        return ((len == 4 && !memcmp(field, "tree ", 5)) ||
@@@ -1618,71 -1641,3 +1631,71 @@@ void print_commit_list(struct commit_li
                printf(format, sha1_to_hex(list->item->object.sha1));
        }
  }
 +
 +const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
 +{
 +      int key_len = strlen(key);
 +      const char *line = msg;
 +
 +      while (line) {
 +              const char *eol = strchrnul(line, '\n');
 +
 +              if (line == eol)
 +                      return NULL;
 +
 +              if (eol - line > key_len &&
 +                  !strncmp(line, key, key_len) &&
 +                  line[key_len] == ' ') {
 +                      *out_len = eol - line - key_len - 1;
 +                      return line + key_len + 1;
 +              }
 +              line = *eol ? eol + 1 : NULL;
 +      }
 +      return NULL;
 +}
 +
 +/*
 + * Inspect sb and determine the true "end" of the log message, in
 + * order to find where to put a new Signed-off-by: line.  Ignored are
 + * trailing comment lines and blank lines, and also the traditional
 + * "Conflicts:" block that is not commented out, so that we can use
 + * "git commit -s --amend" on an existing commit that forgot to remove
 + * it.
 + *
 + * Returns the number of bytes from the tail to ignore, to be fed as
 + * the second parameter to append_signoff().
 + */
 +int ignore_non_trailer(struct strbuf *sb)
 +{
 +      int boc = 0;
 +      int bol = 0;
 +      int in_old_conflicts_block = 0;
 +
 +      while (bol < sb->len) {
 +              char *next_line;
 +
 +              if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol)))
 +                      next_line = sb->buf + sb->len;
 +              else
 +                      next_line++;
 +
 +              if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') {
 +                      /* is this the first of the run of comments? */
 +                      if (!boc)
 +                              boc = bol;
 +                      /* otherwise, it is just continuing */
 +              } else if (starts_with(sb->buf + bol, "Conflicts:\n")) {
 +                      in_old_conflicts_block = 1;
 +                      if (!boc)
 +                              boc = bol;
 +              } else if (in_old_conflicts_block && sb->buf[bol] == '\t') {
 +                      ; /* a pathname in the conflicts block */
 +              } else if (boc) {
 +                      /* the previous was not trailing comment */
 +                      boc = 0;
 +                      in_old_conflicts_block = 0;
 +              }
 +              bol = next_line - sb->buf;
 +      }
 +      return boc ? sb->len - boc : 0;
 +}
diff --combined commit.h
index cd35ac150acd076047eed2e3200d1be12e4e7d5e,7cd45035818ed615230d333f653d3e1c1d6e387c..5cc1e7ec9ee80965d1669e0f96dfeab03f3b0798
+++ b/commit.h
@@@ -26,25 -26,13 +26,25 @@@ extern int save_commit_buffer
  extern const char *commit_type;
  
  /* While we can decorate any object with a name, it's only used for commits.. */
 -extern struct decoration name_decoration;
  struct name_decoration {
        struct name_decoration *next;
        int type;
 -      char name[1];
 +      char name[FLEX_ARRAY];
  };
  
 +enum decoration_type {
 +      DECORATION_NONE = 0,
 +      DECORATION_REF_LOCAL,
 +      DECORATION_REF_REMOTE,
 +      DECORATION_REF_TAG,
 +      DECORATION_REF_STASH,
 +      DECORATION_REF_HEAD,
 +      DECORATION_GRAFTED,
 +};
 +
 +void add_name_decoration(enum decoration_type type, const char *name, struct object *obj);
 +const struct name_decoration *get_name_decoration(const struct object *obj);
 +
  struct commit *lookup_commit(const unsigned char *sha1);
  struct commit *lookup_commit_reference(const unsigned char *sha1);
  struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
@@@ -171,7 -159,6 +171,7 @@@ extern void get_commit_format(const cha
  extern const char *format_subject(struct strbuf *sb, const char *msg,
                                  const char *line_separator);
  extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
 +extern int commit_format_is_empty(enum cmit_fmt);
  extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
@@@ -236,10 -223,13 +236,13 @@@ struct commit_graft *read_graft_line(ch
  int register_commit_graft(struct commit_graft *, int);
  struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
  
- extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
- extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
+ extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2);
+ extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos);
  extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
  
+ /* To be used only when object flags after this call no longer matter */
+ extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos);
  /* largest positive number a signed 32-bit integer can contain */
  #define INFINITE_DEPTH 0x7fffffff
  
@@@ -284,7 -274,6 +287,7 @@@ extern void assign_shallow_commits_to_r
                                           int *ref_status);
  extern int delayed_reachability_test(struct shallow_info *si, int c);
  extern void prune_shallow(int show_only);
 +extern struct trace_key trace_shallow;
  
  int is_descendant_of(struct commit *, struct commit_list *);
  int in_merge_bases(struct commit *, struct commit *);
@@@ -326,25 -315,6 +329,25 @@@ extern struct commit_extra_header *read
  
  extern void free_commit_extra_headers(struct commit_extra_header *extra);
  
 +/*
 + * Search the commit object contents given by "msg" for the header "key".
 + * Returns a pointer to the start of the header contents, or NULL. The length
 + * of the header, up to the first newline, is returned via out_len.
 + *
 + * Note that some headers (like mergetag) may be multi-line. It is the caller's
 + * responsibility to parse further in this case!
 + */
 +extern const char *find_commit_header(const char *msg, const char *key,
 +                                    size_t *out_len);
 +
 +/* Find the end of the log message, the right place for a new trailer. */
 +extern int ignore_non_trailer(struct strbuf *sb);
 +
 +typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
 +                               void *cb_data);
 +
 +extern void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data);
 +
  struct merge_remote_desc {
        struct object *obj; /* the named object, could be a tag */
        const char *name;
@@@ -360,8 -330,6 +363,8 @@@ struct commit *get_merge_parent(const c
  
  extern int parse_signed_commit(const struct commit *commit,
                               struct strbuf *message, struct strbuf *signature);
 +extern int remove_signature(struct strbuf *buf);
 +
  extern void print_commit_list(struct commit_list *list,
                              const char *format_cur,
                              const char *format_last);
   * at all.  This may allocate memory for sig->gpg_output, sig->gpg_status,
   * sig->signer and sig->key.
   */
 -extern void check_commit_signature(const struct commitcommit, struct signature_check *sigc);
 +extern void check_commit_signature(const struct commit *commit, struct signature_check *sigc);
  
  int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
  
diff --combined merge-recursive.c
index 25c067e4a71f1ed2c5d4971d41ab6b4e1108e9e4,0b7bb6d547b16ff124bac111d6d338bd3214b2e2..771f5e21b09aac6976297b8a6e9028f90787a764
@@@ -3,9 -3,8 +3,9 @@@
   * Fredrik Kuivinen.
   * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
   */
 -#include "advice.h"
  #include "cache.h"
 +#include "advice.h"
 +#include "lockfile.h"
  #include "cache-tree.h"
  #include "commit.h"
  #include "blob.h"
@@@ -164,13 -163,15 +164,13 @@@ static void output(struct merge_option
        if (!show(o, v))
                return;
  
 -      strbuf_grow(&o->obuf, o->call_depth * 2 + 2);
 -      memset(o->obuf.buf + o->obuf.len, ' ', o->call_depth * 2);
 -      strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2);
 +      strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
  
        va_start(ap, fmt);
        strbuf_vaddf(&o->obuf, fmt, ap);
        va_end(ap);
  
 -      strbuf_add(&o->obuf, "\n", 1);
 +      strbuf_addch(&o->obuf, '\n');
        if (!o->buffer_output)
                flush_output(o);
  }
@@@ -266,7 -267,9 +266,7 @@@ struct tree *write_tree_from_memory(str
                active_cache_tree = cache_tree();
  
        if (!cache_tree_fully_valid(active_cache_tree) &&
 -          cache_tree_update(active_cache_tree,
 -                            (const struct cache_entry * const *)active_cache,
 -                            active_nr, 0) < 0)
 +          cache_tree_update(&the_index, 0) < 0)
                die(_("error building trees"));
  
        result = lookup_tree(active_cache_tree->sha1);
  }
  
  static int save_files_dirs(const unsigned char *sha1,
 -              const char *base, int baselen, const char *path,
 +              struct strbuf *base, const char *path,
                unsigned int mode, int stage, void *context)
  {
 -      int len = strlen(path);
 -      char *newpath = xmalloc(baselen + len + 1);
 +      int baselen = base->len;
        struct merge_options *o = context;
  
 -      memcpy(newpath, base, baselen);
 -      memcpy(newpath + baselen, path, len);
 -      newpath[baselen + len] = '\0';
 +      strbuf_addstr(base, path);
  
        if (S_ISDIR(mode))
 -              string_list_insert(&o->current_directory_set, newpath);
 +              string_list_insert(&o->current_directory_set, base->buf);
        else
 -              string_list_insert(&o->current_file_set, newpath);
 -      free(newpath);
 +              string_list_insert(&o->current_file_set, base->buf);
  
 +      strbuf_setlen(base, baselen);
        return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
  }
  
@@@ -597,36 -603,25 +597,36 @@@ static int remove_file(struct merge_opt
        return 0;
  }
  
 +/* add a string to a strbuf, but converting "/" to "_" */
 +static void add_flattened_path(struct strbuf *out, const char *s)
 +{
 +      size_t i = out->len;
 +      strbuf_addstr(out, s);
 +      for (; i < out->len; i++)
 +              if (out->buf[i] == '/')
 +                      out->buf[i] = '_';
 +}
 +
  static char *unique_path(struct merge_options *o, const char *path, const char *branch)
  {
 -      char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
 +      struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
        struct stat st;
 -      char *p = newpath + strlen(path);
 -      strcpy(newpath, path);
 -      *(p++) = '~';
 -      strcpy(p, branch);
 -      for (; *p; ++p)
 -              if ('/' == *p)
 -                      *p = '_';
 -      while (string_list_has_string(&o->current_file_set, newpath) ||
 -             string_list_has_string(&o->current_directory_set, newpath) ||
 -             lstat(newpath, &st) == 0)
 -              sprintf(p, "_%d", suffix++);
 -
 -      string_list_insert(&o->current_file_set, newpath);
 -      return newpath;
 +      size_t base_len;
 +
 +      strbuf_addf(&newpath, "%s~", path);
 +      add_flattened_path(&newpath, branch);
 +
 +      base_len = newpath.len;
 +      while (string_list_has_string(&o->current_file_set, newpath.buf) ||
 +             string_list_has_string(&o->current_directory_set, newpath.buf) ||
 +             lstat(newpath.buf, &st) == 0) {
 +              strbuf_setlen(&newpath, base_len);
 +              strbuf_addf(&newpath, "_%d", suffix++);
 +      }
 +
 +      string_list_insert(&o->current_file_set, newpath.buf);
 +      return strbuf_detach(&newpath, NULL);
  }
  
  static int dir_in_way(const char *path, int check_working_copy)
@@@ -976,10 -971,14 +976,10 @@@ merge_file_special_markers(struct merge
        char *side2 = NULL;
        struct merge_file_info mfi;
  
 -      if (filename1) {
 -              side1 = xmalloc(strlen(branch1) + strlen(filename1) + 2);
 -              sprintf(side1, "%s:%s", branch1, filename1);
 -      }
 -      if (filename2) {
 -              side2 = xmalloc(strlen(branch2) + strlen(filename2) + 2);
 -              sprintf(side2, "%s:%s", branch2, filename2);
 -      }
 +      if (filename1)
 +              side1 = xstrfmt("%s:%s", branch1, filename1);
 +      if (filename2)
 +              side2 = xstrfmt("%s:%s", branch2, filename2);
  
        mfi = merge_file_1(o, one, a, b,
                           side1 ? side1 : branch1, side2 ? side2 : branch2);
@@@ -1553,7 -1552,7 +1553,7 @@@ static int blob_unchanged(const unsigne
         * unchanged since their sha1s have already been compared.
         */
        if (renormalize_buffer(path, o.buf, o.len, &o) |
 -          renormalize_buffer(path, a.buf, o.len, &a))
 +          renormalize_buffer(path, a.buf, a.len, &a))
                ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
  
  error_return:
@@@ -1684,6 -1683,10 +1684,6 @@@ static int merge_content(struct merge_o
  static int process_entry(struct merge_options *o,
                         const char *path, struct stage_data *entry)
  {
 -      /*
 -      printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
 -      print_index_entry("\tpath: ", entry);
 -      */
        int clean_merge = 1;
        int normalize = o->renormalize;
        unsigned o_mode = entry->stages[1].mode;
@@@ -1901,7 -1904,7 +1901,7 @@@ int merge_recursive(struct merge_option
        }
  
        if (!ca) {
-               ca = get_merge_bases(h1, h2, 1);
+               ca = get_merge_bases(h1, h2);
                ca = reverse_commit_list(ca);
        }
  
@@@ -1991,7 -1994,7 +1991,7 @@@ int merge_recursive_generic(struct merg
                            const unsigned char **base_list,
                            struct commit **result)
  {
 -      int clean, index_fd;
 +      int clean;
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        struct commit *head_commit = get_ref(head, o->branch1);
        struct commit *next_commit = get_ref(merge, o->branch2);
                }
        }
  
 -      index_fd = hold_locked_index(lock, 1);
 +      hold_locked_index(lock, 1);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (active_cache_changed &&
 -                      (write_cache(index_fd, active_cache, active_nr) ||
 -                       commit_locked_index(lock)))
 +          write_locked_index(&the_index, lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
  
        return clean ? 0 : 1;
  }
  
 -static int merge_recursive_config(const char *var, const char *value, void *cb)
 +static void merge_recursive_config(struct merge_options *o)
  {
 -      struct merge_options *o = cb;
 -      if (!strcmp(var, "merge.verbosity")) {
 -              o->verbosity = git_config_int(var, value);
 -              return 0;
 -      }
 -      if (!strcmp(var, "diff.renamelimit")) {
 -              o->diff_rename_limit = git_config_int(var, value);
 -              return 0;
 -      }
 -      if (!strcmp(var, "merge.renamelimit")) {
 -              o->merge_rename_limit = git_config_int(var, value);
 -              return 0;
 -      }
 -      return git_xmerge_config(var, value, cb);
 +      git_config_get_int("merge.verbosity", &o->verbosity);
 +      git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
 +      git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
 +      git_config(git_xmerge_config, NULL);
  }
  
  void init_merge_options(struct merge_options *o)
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
        o->renormalize = 0;
 -      git_config(merge_recursive_config, o);
 +      merge_recursive_config(o);
        if (getenv("GIT_MERGE_VERBOSITY"))
                o->verbosity =
                        strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
        if (o->verbosity >= 5)
                o->buffer_output = 0;
        strbuf_init(&o->obuf, 0);
 -      memset(&o->current_file_set, 0, sizeof(struct string_list));
 -      o->current_file_set.strdup_strings = 1;
 -      memset(&o->current_directory_set, 0, sizeof(struct string_list));
 -      o->current_directory_set.strdup_strings = 1;
 -      memset(&o->df_conflict_file_set, 0, sizeof(struct string_list));
 -      o->df_conflict_file_set.strdup_strings = 1;
 +      string_list_init(&o->current_file_set, 1);
 +      string_list_init(&o->current_directory_set, 1);
 +      string_list_init(&o->df_conflict_file_set, 1);
  }
  
  int parse_merge_opt(struct merge_options *o, const char *s)
  {
 +      const char *arg;
 +
        if (!s || !*s)
                return -1;
        if (!strcmp(s, "ours"))
                o->recursive_variant = MERGE_RECURSIVE_THEIRS;
        else if (!strcmp(s, "subtree"))
                o->subtree_shift = "";
 -      else if (starts_with(s, "subtree="))
 -              o->subtree_shift = s + strlen("subtree=");
 +      else if (skip_prefix(s, "subtree=", &arg))
 +              o->subtree_shift = arg;
        else if (!strcmp(s, "patience"))
                o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
                o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
 -      else if (starts_with(s, "diff-algorithm=")) {
 -              long value = parse_algorithm_value(s + strlen("diff-algorithm="));
 +      else if (skip_prefix(s, "diff-algorithm=", &arg)) {
 +              long value = parse_algorithm_value(arg);
                if (value < 0)
                        return -1;
                /* clear out previous settings */
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
 -      else if (starts_with(s, "rename-threshold=")) {
 -              const char *score = s + strlen("rename-threshold=");
 -              if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
 +      else if (skip_prefix(s, "rename-threshold=", &arg)) {
 +              if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
                        return -1;
        }
        else
diff --combined notes-merge.c
index 7eb9d7a0103ad8447e4fde50976a5669a75d8cdd,c6de891e499f0c37452d186649ed09953542d6a2..109ff4ef410b77a814cca282f728f1b88948e4ec
@@@ -549,7 -549,7 +549,7 @@@ int notes_merge(struct notes_merge_opti
               o->local_ref, o->remote_ref);
  
        /* Dereference o->local_ref into local_sha1 */
 -      if (read_ref_full(o->local_ref, local_sha1, 0, NULL))
 +      if (read_ref_full(o->local_ref, 0, local_sha1, NULL))
                die("Failed to resolve local notes ref '%s'", o->local_ref);
        else if (!check_refname_format(o->local_ref, 0) &&
                is_null_sha1(local_sha1))
        assert(local && remote);
  
        /* Find merge bases */
-       bases = get_merge_bases(local, remote, 1);
+       bases = get_merge_bases(local, remote);
        if (!bases) {
                base_sha1 = null_sha1;
                base_tree_sha1 = EMPTY_TREE_SHA1_BIN;
diff --combined revision.c
index 75dda928ea6be1dacd2fbeba5c0ce207ea46dd25,07a8ca1ac682ce06cd0fc3ec796990937fcb65df..14e0e0358cf19dc95a5509ec441335a70a277503
@@@ -17,7 -17,6 +17,7 @@@
  #include "mailmap.h"
  #include "commit-slab.h"
  #include "dir.h"
 +#include "cache-tree.h"
  
  volatile show_early_output_fn_t show_early_output;
  
@@@ -87,6 -86,16 +87,6 @@@ void show_object_with_name(FILE *out, s
        fputc('\n', out);
  }
  
 -void add_object(struct object *obj,
 -              struct object_array *p,
 -              struct name_path *path,
 -              const char *name)
 -{
 -      char *pn = path_name(path, name);
 -      add_object_array(obj, pn, p);
 -      free(pn);
 -}
 -
  static void mark_blob_uninteresting(struct blob *blob)
  {
        if (!blob)
@@@ -189,10 -198,9 +189,10 @@@ void mark_parents_uninteresting(struct 
        }
  }
  
 -static void add_pending_object_with_mode(struct rev_info *revs,
 +static void add_pending_object_with_path(struct rev_info *revs,
                                         struct object *obj,
 -                                       const char *name, unsigned mode)
 +                                       const char *name, unsigned mode,
 +                                       const char *path)
  {
        if (!obj)
                return;
                if (st)
                        return;
        }
 -      add_object_array_with_mode(obj, name, &revs->pending, mode);
 +      add_object_array_with_path(obj, name, &revs->pending, mode, path);
 +}
 +
 +static void add_pending_object_with_mode(struct rev_info *revs,
 +                                       struct object *obj,
 +                                       const char *name, unsigned mode)
 +{
 +      add_pending_object_with_path(revs, obj, name, mode, NULL);
  }
  
  void add_pending_object(struct rev_info *revs,
@@@ -264,12 -265,8 +264,12 @@@ void add_pending_sha1(struct rev_info *
  }
  
  static struct commit *handle_commit(struct rev_info *revs,
 -                                  struct object *object, const char *name)
 +                                  struct object_array_entry *entry)
  {
 +      struct object *object = entry->item;
 +      const char *name = entry->name;
 +      const char *path = entry->path;
 +      unsigned int mode = entry->mode;
        unsigned long flags = object->flags;
  
        /*
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
                }
                object->flags |= flags;
 +              /*
 +               * We'll handle the tagged object by looping or dropping
 +               * through to the non-tag handlers below. Do not
 +               * propagate data from the tag's pending entry.
 +               */
 +              name = "";
 +              path = NULL;
 +              mode = 0;
        }
  
        /*
                        revs->limited = 1;
                }
                if (revs->show_source && !commit->util)
 -                      commit->util = (void *) name;
 +                      commit->util = xstrdup(name);
                return commit;
        }
  
                        mark_tree_contents_uninteresting(tree);
                        return NULL;
                }
 -              add_pending_object(revs, object, "");
 +              add_pending_object_with_path(revs, object, name, mode, path);
                return NULL;
        }
  
                        return NULL;
                if (flags & UNINTERESTING)
                        return NULL;
 -              add_pending_object(revs, object, "");
 +              add_pending_object_with_path(revs, object, name, mode, path);
                return NULL;
        }
        die("%s is unknown object", name);
@@@ -484,7 -473,7 +484,7 @@@ static int rev_compare_tree(struct rev_
                 * 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))
 +              if (get_name_decoration(&commit->object))
                        return REV_TREE_DIFFERENT;
                /*
                 * A commit that is not pointed by a tag is uninteresting
@@@ -1286,7 -1275,7 +1286,7 @@@ static int handle_one_reflog(const cha
        return 0;
  }
  
 -static void handle_reflog(struct rev_info *revs, unsigned flags)
 +void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
  {
        struct all_refs_cb cb;
        cb.all_revs = revs;
        for_each_reflog(handle_one_reflog, &cb);
  }
  
 +static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
 +                         struct strbuf *path)
 +{
 +      size_t baselen = path->len;
 +      int i;
 +
 +      if (it->entry_count >= 0) {
 +              struct tree *tree = lookup_tree(it->sha1);
 +              add_pending_object_with_path(revs, &tree->object, "",
 +                                           040000, path->buf);
 +      }
 +
 +      for (i = 0; i < it->subtree_nr; i++) {
 +              struct cache_tree_sub *sub = it->down[i];
 +              strbuf_addf(path, "%s%s", baselen ? "/" : "", sub->name);
 +              add_cache_tree(sub->cache_tree, revs, path);
 +              strbuf_setlen(path, baselen);
 +      }
 +
 +}
 +
 +void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
 +{
 +      int i;
 +
 +      read_cache();
 +      for (i = 0; i < active_nr; i++) {
 +              struct cache_entry *ce = active_cache[i];
 +              struct blob *blob;
 +
 +              if (S_ISGITLINK(ce->ce_mode))
 +                      continue;
 +
 +              blob = lookup_blob(ce->sha1);
 +              if (!blob)
 +                      die("unable to add index blob to traversal");
 +              add_pending_object_with_path(revs, &blob->object, "",
 +                                           ce->ce_mode, ce->name);
 +      }
 +
 +      if (active_cache_tree) {
 +              struct strbuf path = STRBUF_INIT;
 +              add_cache_tree(active_cache_tree, revs, &path);
 +              strbuf_release(&path);
 +      }
 +}
 +
  static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
  {
        unsigned char sha1[20];
@@@ -1441,7 -1383,7 +1441,7 @@@ static void prepare_show_merge(struct r
        other = lookup_commit_or_die(sha1, "MERGE_HEAD");
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
-       bases = get_merge_bases(head, other, 1);
+       bases = get_merge_bases(head, other);
        add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
        add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
        free_commit_list(bases);
                        continue;
                if (ce_path_match(ce, &revs->prune_data, NULL)) {
                        prune_num++;
 -                      prune = xrealloc(prune, sizeof(*prune) * prune_num);
 +                      REALLOC_ARRAY(prune, prune_num);
                        prune[prune_num-2] = ce->name;
                        prune[prune_num-1] = NULL;
                }
@@@ -1546,7 -1488,7 +1546,7 @@@ int handle_revision_arg(const char *arg
                                     : lookup_commit_reference(b_obj->sha1));
                                if (!a || !b)
                                        goto missing;
-                               exclude = get_merge_bases(a, b, 1);
+                               exclude = get_merge_bases(a, b);
                                add_rev_cmdline_list(revs, exclude,
                                                     REV_CMD_MERGE_BASE,
                                                     flags_exclude);
@@@ -1691,7 -1633,6 +1691,7 @@@ static int handle_revision_opt(struct r
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
            !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
 +          !strcmp(arg, "--indexed-objects") ||
            starts_with(arg, "--exclude=") ||
            starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
            starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        } else if (!strcmp(arg, "--pretty")) {
                revs->verbose_header = 1;
                revs->pretty_given = 1;
 -              get_commit_format(arg+8, revs);
 +              get_commit_format(NULL, revs);
        } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) {
                /*
                 * Detached form ("--pretty X" as opposed to "--pretty=X")
@@@ -2120,9 -2061,7 +2120,9 @@@ static int handle_revision_pseudo_opt(c
                for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
 -              handle_reflog(revs, *flags);
 +              add_reflogs_to_pending(revs, *flags);
 +      } else if (!strcmp(arg, "--indexed-objects")) {
 +              add_index_objects_to_pending(revs, *flags);
        } else if (!strcmp(arg, "--not")) {
                *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
@@@ -2717,26 -2656,26 +2717,26 @@@ void reset_revision_walk(void
  
  int prepare_revision_walk(struct rev_info *revs)
  {
 -      int nr = revs->pending.nr;
 -      struct object_array_entry *e, *list;
 +      int i;
 +      struct object_array old_pending;
        struct commit_list **next = &revs->commits;
  
 -      e = list = revs->pending.objects;
 +      memcpy(&old_pending, &revs->pending, sizeof(old_pending));
        revs->pending.nr = 0;
        revs->pending.alloc = 0;
        revs->pending.objects = NULL;
 -      while (--nr >= 0) {
 -              struct commit *commit = handle_commit(revs, e->item, e->name);
 +      for (i = 0; i < old_pending.nr; i++) {
 +              struct object_array_entry *e = old_pending.objects + i;
 +              struct commit *commit = handle_commit(revs, e);
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
                                next = commit_list_append(commit, next);
                        }
                }
 -              e++;
        }
        if (!revs->leak_pending)
 -              free(list);
 +              object_array_clear(&old_pending);
  
        /* Signal whether we need per-parent treesame decoration */
        if (revs->simplify_merges ||
diff --combined sha1_name.c
index cb88170252a26c37d90f5c03263136439ec60e54,3e9e86cbee6738312421ff2418c04d402c350bff..cf2a83b14381ae02e737e6135561aab401f1f7e8
@@@ -372,10 -372,10 +372,10 @@@ const char *find_unique_abbrev(const un
        int status, exists;
        static char hex[41];
  
 -      exists = has_sha1_file(sha1);
        memcpy(hex, sha1_to_hex(sha1), 40);
        if (len == 40 || !len)
                return hex;
 +      exists = has_sha1_file(sha1);
        while (len < 40) {
                unsigned char sha1_ret[20];
                status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
@@@ -432,8 -432,7 +432,8 @@@ static inline int upstream_mark(const c
  static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
  static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
  
 -static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 +static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 +                        unsigned int flags)
  {
        static const char *warn_msg = "refname '%.*s' is ambiguous.";
        static const char *object_name_msg = N_(
        if (!refs_found)
                return -1;
  
 -      if (warn_ambiguous_refs &&
 +      if (warn_ambiguous_refs && !(flags & GET_SHA1_QUIETLY) &&
            (refs_found > 1 ||
             !get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
                warning(warn_msg, len, str);
                        char *tmp = xstrndup(str + at + 2, reflog_len);
                        at_time = approxidate_careful(tmp, &errors);
                        free(tmp);
 -                      if (errors)
 +                      if (errors) {
 +                              free(real_ref);
                                return -1;
 +                      }
                }
 -              if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
 +              if (read_ref_at(real_ref, flags, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
                                if (starts_with(real_ref, "refs/heads/")) {
                                        len = 4;
                                }
                        }
 -                      if (at_time)
 -                              warning("Log for '%.*s' only goes "
 -                                      "back to %s.", len, str,
 -                                      show_date(co_time, co_tz, DATE_RFC2822));
 -                      else {
 +                      if (at_time) {
 +                              if (!(flags & GET_SHA1_QUIETLY)) {
 +                                      warning("Log for '%.*s' only goes "
 +                                              "back to %s.", len, str,
 +                                              show_date(co_time, co_tz, DATE_RFC2822));
 +                              }
 +                      } else {
 +                              if (flags & GET_SHA1_QUIETLY) {
 +                                      exit(128);
 +                              }
                                die("Log for '%.*s' only has %d entries.",
                                    len, str, co_cnt);
                        }
@@@ -807,7 -799,7 +807,7 @@@ static int get_sha1_1(const char *name
        if (!ret)
                return 0;
  
 -      ret = get_sha1_basic(name, len, sha1);
 +      ret = get_sha1_basic(name, len, sha1, lookup_flags);
        if (!ret)
                return 0;
  
@@@ -845,7 -837,7 +845,7 @@@ static int handle_one_ref(const char *p
        }
        if (object->type != OBJ_COMMIT)
                return 0;
 -      commit_list_insert_by_date((struct commit *)object, list);
 +      commit_list_insert((struct commit *)object, list);
        return 0;
  }
  
@@@ -909,8 -901,10 +909,8 @@@ static int grab_nth_branch_switch(unsig
        const char *match = NULL, *target = NULL;
        size_t len;
  
 -      if (starts_with(message, "checkout: moving from ")) {
 -              match = message + strlen("checkout: moving from ");
 +      if (skip_prefix(message, "checkout: moving from ", &match))
                target = strstr(match, " to ");
 -      }
  
        if (!match || !target)
                return 0;
@@@ -954,7 -948,7 +954,7 @@@ static int interpret_nth_prior_checkout
        retval = 0;
        if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
                strbuf_reset(buf);
 -              strbuf_add(buf, cb.buf.buf, cb.buf.len);
 +              strbuf_addbuf(buf, &cb.buf);
                retval = brace - name + 1;
        }
  
@@@ -993,7 -987,7 +993,7 @@@ int get_sha1_mb(const char *name, unsig
        two = lookup_commit_reference_gently(sha1_tmp, 0);
        if (!two)
                return -1;
-       mbs = get_merge_bases(one, two, 1);
+       mbs = get_merge_bases(one, two);
        if (!mbs || mbs->next)
                st = -1;
        else {
@@@ -1248,7 -1242,10 +1248,7 @@@ static void diagnose_invalid_sha1_path(
                die("Path '%s' exists on disk, but not in '%.*s'.",
                    filename, object_name_len, object_name);
        if (errno == ENOENT || errno == ENOTDIR) {
 -              char *fullname = xmalloc(strlen(filename)
 -                                           + strlen(prefix) + 1);
 -              strcpy(fullname, prefix);
 -              strcat(fullname, filename);
 +              char *fullname = xstrfmt("%s%s", prefix, filename);
  
                if (!get_tree_entry(tree_sha1, fullname,
                                    sha1, &mode)) {
@@@ -1372,7 -1369,6 +1372,7 @@@ static int get_sha1_with_context_1(cons
                if (!only_to_die && namelen > 2 && name[1] == '/') {
                        struct commit_list *list = NULL;
                        for_each_ref(handle_one_ref, &list);
 +                      commit_list_sort_by_date(&list);
                        return get_sha1_oneline(name + 2, sha1, list);
                }
                if (namelen < 3 ||
diff --combined submodule.c
index 0690dc50d07e9fd30242e41afe149476a34fc105,10b2ddb870f6a80cca6ebba977238c88f3aaf767..d37d400b2227c58f352795b9f6ede20a9cd54631
@@@ -301,7 -301,7 +301,7 @@@ static int prepare_submodule_summary(st
        left->object.flags |= SYMMETRIC_LEFT;
        add_pending_object(rev, &left->object, path);
        add_pending_object(rev, &right->object, path);
-       merge_bases = get_merge_bases(left, right, 1);
+       merge_bases = get_merge_bases(left, right);
        if (merge_bases) {
                if (merge_bases->item == left)
                        *fast_forward = 1;
@@@ -433,12 -433,13 +433,12 @@@ static int submodule_needs_pushing(cons
                return 0;
  
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 -              struct child_process cp;
 +              struct child_process cp = CHILD_PROCESS_INIT;
                const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL};
                struct strbuf buf = STRBUF_INIT;
                int needs_pushing = 0;
  
                argv[1] = sha1_to_hex(sha1);
 -              memset(&cp, 0, sizeof(cp));
                cp.argv = argv;
                cp.env = local_repo_env;
                cp.git_cmd = 1;
@@@ -523,9 -524,10 +523,9 @@@ static int push_submodule(const char *p
                return 1;
  
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 -              struct child_process cp;
 +              struct child_process cp = CHILD_PROCESS_INIT;
                const char *argv[] = {"push", NULL};
  
 -              memset(&cp, 0, sizeof(cp));
                cp.argv = argv;
                cp.env = local_repo_env;
                cp.git_cmd = 1;
  int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
  {
        int i, ret = 1;
 -      struct string_list needs_pushing;
 -
 -      memset(&needs_pushing, 0, sizeof(struct string_list));
 -      needs_pushing.strdup_strings = 1;
 +      struct string_list needs_pushing = STRING_LIST_INIT_DUP;
  
        if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
                return 1;
@@@ -567,11 -572,12 +567,11 @@@ static int is_submodule_commit_present(
        if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) {
                /* Even if the submodule is checked out and the commit is
                 * present, make sure it is reachable from a ref. */
 -              struct child_process cp;
 +              struct child_process cp = CHILD_PROCESS_INIT;
                const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL};
                struct strbuf buf = STRBUF_INIT;
  
                argv[3] = sha1_to_hex(sha1);
 -              memset(&cp, 0, sizeof(cp));
                cp.argv = argv;
                cp.env = local_repo_env;
                cp.git_cmd = 1;
@@@ -692,7 -698,7 +692,7 @@@ int fetch_populated_submodules(const st
                               int quiet)
  {
        int i, result = 0;
 -      struct child_process cp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct string_list_item *name_for_path;
        const char *work_tree = get_git_work_tree();
        argv_array_push(&argv, "--recurse-submodules-default");
        /* default value, "--submodule-prefix" and its value are added later */
  
 -      memset(&cp, 0, sizeof(cp));
        cp.env = local_repo_env;
        cp.git_cmd = 1;
        cp.no_stdin = 1;
@@@ -790,7 -797,7 +790,7 @@@ out
  unsigned is_submodule_modified(const char *path, int ignore_untracked)
  {
        ssize_t len;
 -      struct child_process cp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
        const char *argv[] = {
                "status",
                "--porcelain",
        if (ignore_untracked)
                argv[2] = "-uno";
  
 -      memset(&cp, 0, sizeof(cp));
        cp.argv = argv;
        cp.env = local_repo_env;
        cp.git_cmd = 1;
  
  int submodule_uses_gitfile(const char *path)
  {
 -      struct child_process cp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
        const char *argv[] = {
                "submodule",
                "foreach",
        strbuf_release(&buf);
  
        /* Now test that all nested submodules use a gitfile too */
 -      memset(&cp, 0, sizeof(cp));
        cp.argv = argv;
        cp.env = local_repo_env;
        cp.git_cmd = 1;
@@@ -895,7 -904,7 +895,7 @@@ int ok_to_remove_submodule(const char *
  {
        struct stat st;
        ssize_t len;
 -      struct child_process cp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
        const char *argv[] = {
                "status",
                "--porcelain",
        if (!submodule_uses_gitfile(path))
                return 0;
  
 -      memset(&cp, 0, sizeof(cp));
        cp.argv = argv;
        cp.env = local_repo_env;
        cp.git_cmd = 1;
@@@ -955,7 -965,7 +955,7 @@@ static int find_first_merges(struct obj
                        sha1_to_hex(a->object.sha1));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
 -      setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
 +      setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
  
        /* save all revisions from the above list that contain b */
        if (prepare_revision_walk(&revs))