Merge branch 'jc/merge-reduce-parents-early'
authorJunio C Hamano <gitster@pobox.com>
Fri, 27 Apr 2012 20:59:20 +0000 (13:59 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 27 Apr 2012 20:59:20 +0000 (13:59 -0700)
Octopus merge strategy did not reduce heads that are recorded in the
final commit correctly.

By Junio C Hamano (4) and MichaƂ Kiedrowicz (1)
* jc/merge-reduce-parents-early:
fmt-merge-msg: discard needless merge parents
builtin/merge.c: reduce parents early
builtin/merge.c: collect other parents early
builtin/merge.c: remove "remoteheads" global variable
merge tests: octopus with redundant parents

1  2 
builtin/fmt-merge-msg.c
builtin/merge.c
t/t7602-merge-octopus-many.sh
diff --combined builtin/fmt-merge-msg.c
index 1bc6b8b8c3a4f4ab174841f3c8507d784f237a1f,fc083e3c5cdddc1696c42846e3a624361fc26e5a..a517f1794a1c1bcc0939ad8b81d482356c20d2ba
@@@ -27,8 -27,6 +27,8 @@@ int fmt_merge_msg_config(const char *ke
                        merge_log_config = DEFAULT_MERGE_LOG_LEN;
        } else if (!strcmp(key, "merge.branchdesc")) {
                use_branch_desc = git_config_bool(key, value);
 +      } else {
 +              return git_default_config(key, value, cb);
        }
        return 0;
  }
@@@ -55,7 -53,48 +55,48 @@@ static void init_src_data(struct src_da
  static struct string_list srcs = STRING_LIST_INIT_DUP;
  static struct string_list origins = STRING_LIST_INIT_DUP;
  
- static int handle_line(char *line)
+ struct merge_parents {
+       int alloc, nr;
+       struct merge_parent {
+               unsigned char given[20];
+               unsigned char commit[20];
+               unsigned char used;
+       } *item;
+ };
+ /*
+  * I know, I know, this is inefficient, but you won't be pulling and merging
+  * hundreds of heads at a time anyway.
+  */
+ static struct merge_parent *find_merge_parent(struct merge_parents *table,
+                                             unsigned char *given,
+                                             unsigned char *commit)
+ {
+       int i;
+       for (i = 0; i < table->nr; i++) {
+               if (given && hashcmp(table->item[i].given, given))
+                       continue;
+               if (commit && hashcmp(table->item[i].commit, commit))
+                       continue;
+               return &table->item[i];
+       }
+       return NULL;
+ }
+ static void add_merge_parent(struct merge_parents *table,
+                            unsigned char *given,
+                            unsigned char *commit)
+ {
+       if (table->nr && find_merge_parent(table, given, commit))
+               return;
+       ALLOC_GROW(table->item, table->nr + 1, table->alloc);
+       hashcpy(table->item[table->nr].given, given);
+       hashcpy(table->item[table->nr].commit, commit);
+       table->item[table->nr].used = 0;
+       table->nr++;
+ }
+ static int handle_line(char *line, struct merge_parents *merge_parents)
  {
        int i, len = strlen(line);
        struct origin_data *origin_data;
        struct src_data *src_data;
        struct string_list_item *item;
        int pulling_head = 0;
+       unsigned char sha1[20];
  
        if (len < 43 || line[40] != '\t')
                return 1;
        if (line[41] != '\t')
                return 2;
  
-       line[40] = 0;
-       origin_data = xcalloc(1, sizeof(struct origin_data));
-       i = get_sha1(line, origin_data->sha1);
-       line[40] = '\t';
-       if (i) {
-               free(origin_data);
+       i = get_sha1_hex(line, sha1);
+       if (i)
                return 3;
-       }
+       if (!find_merge_parent(merge_parents, sha1, NULL))
+               return 0; /* subsumed by other parents */
+       origin_data = xcalloc(1, sizeof(struct origin_data));
+       hashcpy(origin_data->sha1, sha1);
  
        if (line[len - 1] == '\n')
                line[len - 1] = 0;
@@@ -182,101 -223,6 +225,101 @@@ static void add_branch_desc(struct strb
        strbuf_release(&desc);
  }
  
 +#define util_as_integral(elem) ((intptr_t)((elem)->util))
 +
 +static void record_person(int which, struct string_list *people,
 +                        struct commit *commit)
 +{
 +      char name_buf[MAX_GITNAME], *name, *name_end;
 +      struct string_list_item *elem;
 +      const char *field = (which == 'a') ? "\nauthor " : "\ncommitter ";
 +
 +      name = strstr(commit->buffer, field);
 +      if (!name)
 +              return;
 +      name += strlen(field);
 +      name_end = strchrnul(name, '<');
 +      if (*name_end)
 +              name_end--;
 +      while (isspace(*name_end) && name <= name_end)
 +              name_end--;
 +      if (name_end < name || name + MAX_GITNAME <= name_end)
 +              return;
 +      memcpy(name_buf, name, name_end - name + 1);
 +      name_buf[name_end - name + 1] = '\0';
 +
 +      elem = string_list_lookup(people, name_buf);
 +      if (!elem) {
 +              elem = string_list_insert(people, name_buf);
 +              elem->util = (void *)0;
 +      }
 +      elem->util = (void*)(util_as_integral(elem) + 1);
 +}
 +
 +static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
 +{
 +      const struct string_list_item *a = a_, *b = b_;
 +      return util_as_integral(b) - util_as_integral(a);
 +}
 +
 +static void add_people_count(struct strbuf *out, struct string_list *people)
 +{
 +      if (people->nr == 1)
 +              strbuf_addf(out, "%s", people->items[0].string);
 +      else if (people->nr == 2)
 +              strbuf_addf(out, "%s (%d) and %s (%d)",
 +                          people->items[0].string,
 +                          (int)util_as_integral(&people->items[0]),
 +                          people->items[1].string,
 +                          (int)util_as_integral(&people->items[1]));
 +      else if (people->nr)
 +              strbuf_addf(out, "%s (%d) and others",
 +                          people->items[0].string,
 +                          (int)util_as_integral(&people->items[0]));
 +}
 +
 +static void credit_people(struct strbuf *out,
 +                        struct string_list *them,
 +                        int kind)
 +{
 +      const char *label;
 +      const char *me;
 +
 +      if (kind == 'a') {
 +              label = "\nBy ";
 +              me = git_author_info(IDENT_NO_DATE);
 +      } else {
 +              label = "\nvia ";
 +              me = git_committer_info(IDENT_NO_DATE);
 +      }
 +
 +      if (!them->nr ||
 +          (them->nr == 1 &&
 +           me &&
 +           (me = skip_prefix(me, them->items->string)) != NULL &&
 +           skip_prefix(me, " <")))
 +              return;
 +      strbuf_addstr(out, label);
 +      add_people_count(out, them);
 +}
 +
 +static void add_people_info(struct strbuf *out,
 +                          struct string_list *authors,
 +                          struct string_list *committers)
 +{
 +      if (authors->nr)
 +              qsort(authors->items,
 +                    authors->nr, sizeof(authors->items[0]),
 +                    cmp_string_list_util_as_integral);
 +      if (committers->nr)
 +              qsort(committers->items,
 +                    committers->nr, sizeof(committers->items[0]),
 +                    cmp_string_list_util_as_integral);
 +
 +      credit_people(out, authors, 'a');
 +      credit_people(out, committers, 'c');
 +}
 +
  static void shortlog(const char *name,
                     struct origin_data *origin_data,
                     struct commit *head,
        struct commit *commit;
        struct object *branch;
        struct string_list subjects = STRING_LIST_INIT_DUP;
 +      struct string_list authors = STRING_LIST_INIT_DUP;
 +      struct string_list committers = STRING_LIST_INIT_DUP;
        int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
        struct strbuf sb = STRBUF_INIT;
        const unsigned char *sha1 = origin_data->sha1;
                return;
  
        setup_revisions(0, NULL, rev, NULL);
 -      rev->ignore_merges = 1;
        add_pending_object(rev, branch, name);
        add_pending_object(rev, &head->object, "^HEAD");
        head->object.flags |= UNINTERESTING;
        while ((commit = get_revision(rev)) != NULL) {
                struct pretty_print_context ctx = {0};
  
 -              /* ignore merges */
 -              if (commit->parents && commit->parents->next)
 +              if (commit->parents && commit->parents->next) {
 +                      /* do not list a merge but count committer */
 +                      record_person('c', &committers, commit);
                        continue;
 -
 +              }
 +              if (!count)
 +                      /* the 'tip' committer */
 +                      record_person('c', &committers, commit);
 +              record_person('a', &authors, commit);
                count++;
                if (subjects.nr > limit)
                        continue;
                        string_list_append(&subjects, strbuf_detach(&sb, NULL));
        }
  
 +      add_people_info(out, &authors, &committers);
        if (count > limit)
                strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
        else
        rev->commits = NULL;
        rev->pending.nr = 0;
  
 +      string_list_clear(&authors, 0);
 +      string_list_clear(&committers, 0);
        string_list_clear(&subjects, 0);
  }
  
@@@ -472,6 -409,67 +515,67 @@@ static void fmt_merge_msg_sigs(struct s
        strbuf_release(&tagbuf);
  }
  
+ static void find_merge_parents(struct merge_parents *result,
+                              struct strbuf *in, unsigned char *head)
+ {
+       struct commit_list *parents, *next;
+       struct commit *head_commit;
+       int pos = 0, i, j;
+       parents = NULL;
+       while (pos < in->len) {
+               int len;
+               char *p = in->buf + pos;
+               char *newline = strchr(p, '\n');
+               unsigned char sha1[20];
+               struct commit *parent;
+               struct object *obj;
+               len = newline ? newline - p : strlen(p);
+               pos += len + !!newline;
+               if (len < 43 ||
+                   get_sha1_hex(p, sha1) ||
+                   p[40] != '\t' ||
+                   p[41] != '\t')
+                       continue; /* skip not-for-merge */
+               /*
+                * Do not use get_merge_parent() here; we do not have
+                * "name" here and we do not want to contaminate its
+                * util field yet.
+                */
+               obj = parse_object(sha1);
+               parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
+               if (!parent)
+                       continue;
+               commit_list_insert(parent, &parents);
+               add_merge_parent(result, obj->sha1, parent->object.sha1);
+       }
+       head_commit = lookup_commit(head);
+       if (head_commit)
+               commit_list_insert(head_commit, &parents);
+       parents = reduce_heads(parents);
+       while (parents) {
+               for (i = 0; i < result->nr; i++)
+                       if (!hashcmp(result->item[i].commit,
+                                    parents->item->object.sha1))
+                               result->item[i].used = 1;
+               next = parents->next;
+               free(parents);
+               parents = next;
+       }
+       for (i = j = 0; i < result->nr; i++) {
+               if (result->item[i].used) {
+                       if (i != j)
+                               result->item[j] = result->item[i];
+                       j++;
+               }
+       }
+       result->nr = j;
+ }
  int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                  struct fmt_merge_msg_opts *opts)
  {
        unsigned char head_sha1[20];
        const char *current_branch;
        void *current_branch_to_free;
+       struct merge_parents merge_parents;
+       memset(&merge_parents, 0, sizeof(merge_parents));
  
        /* get current branch */
        current_branch = current_branch_to_free =
        if (!prefixcmp(current_branch, "refs/heads/"))
                current_branch += 11;
  
+       find_merge_parents(&merge_parents, in, head_sha1);
        /* get a line */
        while (pos < in->len) {
                int len;
                pos += len + !!newline;
                i++;
                p[len] = 0;
-               if (handle_line(p))
+               if (handle_line(p, &merge_parents))
                        die ("Error in line %d: %.*s", i, len, p);
        }
  
  
        strbuf_complete_line(out);
        free(current_branch_to_free);
+       free(merge_parents.item);
        return 0;
  }
  
diff --combined builtin/merge.c
index 08e01e8a60d7d7f9c60e9e7a3a2f8b4d66c12ce3,20aeca0b3c29f3c1c0e037b25f630eb0d9bbf34e..470fc57c5d6ceabedbd866a5db35d3123a764af0
@@@ -52,7 -52,6 +52,6 @@@ static int fast_forward_only, option_ed
  static int allow_trivial = 1, have_message;
  static int overwrite_ignore = 1;
  static struct strbuf merge_msg = STRBUF_INIT;
- static struct commit_list *remoteheads;
  static struct strategy **use_strategies;
  static size_t use_strategies_nr, use_strategies_alloc;
  static const char **xopts;
@@@ -318,7 -317,7 +317,7 @@@ static void finish_up_to_date(const cha
        drop_save();
  }
  
- static void squash_message(struct commit *commit)
+ static void squash_message(struct commit *commit, struct commit_list *remoteheads)
  {
        struct rev_info rev;
        struct strbuf out = STRBUF_INIT;
  }
  
  static void finish(struct commit *head_commit,
+                  struct commit_list *remoteheads,
                   const unsigned char *new_head, const char *msg)
  {
        struct strbuf reflog_message = STRBUF_INIT;
                        getenv("GIT_REFLOG_ACTION"), msg);
        }
        if (squash) {
-               squash_message(head_commit);
+               squash_message(head_commit, remoteheads);
        } else {
                if (verbosity >= 0 && !merge_msg.len)
                        printf(_("No merge message -- not updating HEAD\n"));
        if (new_head && show_diffstat) {
                struct diff_options opts;
                diff_setup(&opts);
 +              opts.stat_width = -1; /* use full terminal width */
 +              opts.stat_graph_width = -1; /* respect statGraphWidth config */
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
@@@ -683,6 -681,7 +683,7 @@@ int try_merge_command(const char *strat
  }
  
  static int try_merge_strategy(const char *strategy, struct commit_list *common,
+                             struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
  {
        int index_fd;
@@@ -876,14 -875,14 +877,14 @@@ static void read_merge_msg(struct strbu
                die_errno(_("Could not read from '%s'"), filename);
  }
  
- static void write_merge_state(void);
- static void abort_commit(const char *err_msg)
+ static void write_merge_state(struct commit_list *);
+ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
  {
        if (err_msg)
                error("%s", err_msg);
        fprintf(stderr,
                _("Not committing merge; use 'git commit' to complete the merge.\n"));
-       write_merge_state();
+       write_merge_state(remoteheads);
        exit(1);
  }
  
@@@ -894,7 -893,7 +895,7 @@@ N_("Please enter a commit message to ex
     "Lines starting with '#' will be ignored, and an empty message aborts\n"
     "the commit.\n");
  
- static void prepare_to_commit(void)
+ static void prepare_to_commit(struct commit_list *remoteheads)
  {
        struct strbuf msg = STRBUF_INIT;
        const char *comment = _(merge_editor_comment);
        write_merge_msg(&msg);
        run_hook(get_index_file(), "prepare-commit-msg",
                 git_path("MERGE_MSG"), "merge", NULL, NULL);
 -      if (option_edit) {
 +      if (0 < option_edit) {
                if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
-                       abort_commit(NULL);
+                       abort_commit(remoteheads, NULL);
        }
        read_merge_msg(&msg);
 -      stripspace(&msg, option_edit);
 +      stripspace(&msg, 0 < option_edit);
        if (!msg.len)
-               abort_commit(_("Empty commit message."));
+               abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
        strbuf_addbuf(&merge_msg, &msg);
        strbuf_release(&msg);
  }
  
- static int merge_trivial(struct commit *head)
+ 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));
        parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
-       prepare_to_commit();
+       prepare_to_commit(remoteheads);
        if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
                        sign_commit))
                die(_("failed to write commit object"));
-       finish(head, result_commit, "In-index merge");
+       finish(head, remoteheads, result_commit, "In-index merge");
        drop_save();
        return 0;
  }
  
  static int finish_automerge(struct commit *head,
+                           int head_subsumed,
                            struct commit_list *common,
+                           struct commit_list *remoteheads,
                            unsigned char *result_tree,
                            const char *wt_strategy)
  {
-       struct commit_list *parents = NULL, *j;
+       struct commit_list *parents = NULL;
        struct strbuf buf = STRBUF_INIT;
        unsigned char result_commit[20];
  
        free_commit_list(common);
-       if (allow_fast_forward) {
-               parents = remoteheads;
+       parents = remoteheads;
+       if (!head_subsumed || !allow_fast_forward)
                commit_list_insert(head, &parents);
-               parents = reduce_heads(parents);
-       } else {
-               struct commit_list **pptr = &parents;
-               pptr = &commit_list_insert(head,
-                               pptr)->next;
-               for (j = remoteheads; j; j = j->next)
-                       pptr = &commit_list_insert(j->item, pptr)->next;
-       }
        strbuf_addch(&merge_msg, '\n');
-       prepare_to_commit();
-       free_commit_list(remoteheads);
+       prepare_to_commit(remoteheads);
        if (commit_tree(&merge_msg, result_tree, parents, result_commit,
                        NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
-       finish(head, result_commit, buf.buf);
+       finish(head, remoteheads, result_commit, buf.buf);
        strbuf_release(&buf);
        drop_save();
        return 0;
@@@ -1072,7 -1063,7 +1065,7 @@@ static int setup_with_upstream(const ch
        return i;
  }
  
- static void write_merge_state(void)
+ static void write_merge_state(struct commit_list *remoteheads)
  {
        const char *filename;
        int fd;
@@@ -1137,6 -1128,39 +1130,39 @@@ static int default_edit_option(void
                st_stdin.st_mode == st_stdout.st_mode);
  }
  
+ static struct commit_list *collect_parents(struct commit *head_commit,
+                                          int *head_subsumed,
+                                          int argc, const char **argv)
+ {
+       int i;
+       struct commit_list *remoteheads = NULL, *parents, *next;
+       struct commit_list **remotes = &remoteheads;
+       if (head_commit)
+               remotes = &commit_list_insert(head_commit, remotes)->next;
+       for (i = 0; i < argc; i++) {
+               struct commit *commit = get_merge_parent(argv[i]);
+               if (!commit)
+                       die(_("%s - not something we can merge"), argv[i]);
+               remotes = &commit_list_insert(commit, remotes)->next;
+       }
+       *remotes = NULL;
+       parents = reduce_heads(remoteheads);
+       *head_subsumed = 1; /* we will flip this to 0 when we find it */
+       for (remoteheads = NULL, remotes = &remoteheads;
+            parents;
+            parents = next) {
+               struct commit *commit = parents->item;
+               next = parents->next;
+               if (commit == head_commit)
+                       *head_subsumed = 0;
+               else
+                       remotes = &commit_list_insert(commit, remotes)->next;
+       }
+       return remoteheads;
+ }
  
  int cmd_merge(int argc, const char **argv, const char *prefix)
  {
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
-       int flag, i, ret = 0;
+       int flag, i, ret = 0, head_subsumed;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
        const char *best_strategy = NULL, *wt_strategy = NULL;
-       struct commit_list **remotes = &remoteheads;
+       struct commit_list *remoteheads, *p;
        void *branch_to_free;
  
        if (argc == 2 && !strcmp(argv[1], "-h"))
                head_arg = argv[1];
                argv += 2;
                argc -= 2;
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
        } else if (!head_commit) {
                struct commit *remote_head;
                /*
                if (!allow_fast_forward)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
-               remote_head = get_merge_parent(argv[0]);
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
+               remote_head = remoteheads->item;
                if (!remote_head)
                        die(_("%s - not something we can merge"), argv[0]);
                read_empty(remote_head->object.sha1, 0);
                 * the standard merge summary message to be appended
                 * to the given message.
                 */
-               for (i = 0; i < argc; i++)
-                       merge_name(argv[i], &merge_names);
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
+               for (p = remoteheads; p; p = p->next)
+                       merge_name(merge_remote_util(p->item)->name, &merge_names);
  
                if (!have_message || shortlog_len) {
                        struct fmt_merge_msg_opts opts;
                        builtin_merge_options);
  
        strbuf_addstr(&buf, "merge");
-       for (i = 0; i < argc; i++)
-               strbuf_addf(&buf, " %s", argv[i]);
+       for (p = remoteheads; p; p = p->next)
+               strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
        setenv("GIT_REFLOG_ACTION", buf.buf, 0);
        strbuf_reset(&buf);
  
-       for (i = 0; i < argc; i++) {
-               struct commit *commit = get_merge_parent(argv[i]);
-               if (!commit)
-                       die(_("%s - not something we can merge"), argv[i]);
-               remotes = &commit_list_insert(commit, remotes)->next;
+       for (p = remoteheads; p; p = p->next) {
+               struct commit *commit = p->item;
                strbuf_addf(&buf, "GITHEAD_%s",
                            sha1_to_hex(commit->object.sha1));
-               setenv(buf.buf, argv[i], 1);
+               setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
                if (!fast_forward_only &&
                    merge_remote_util(commit) &&
                    merge_remote_util(commit)->obj &&
 -                  merge_remote_util(commit)->obj->type == OBJ_TAG) {
 -                      if (option_edit < 0)
 -                              option_edit = default_edit_option();
 +                  merge_remote_util(commit)->obj->type == OBJ_TAG)
                        allow_fast_forward = 0;
 -              }
        }
  
        if (option_edit < 0)
 -              option_edit = 0;
 +              option_edit = default_edit_option();
  
        if (!use_strategies) {
-               if (!remoteheads->next)
+               if (!remoteheads)
+                       ; /* already up-to-date */
+               else if (!remoteheads->next)
                        add_strategies(pull_twohead, DEFAULT_TWOHEAD);
                else
                        add_strategies(pull_octopus, DEFAULT_OCTOPUS);
                        allow_trivial = 0;
        }
  
-       if (!remoteheads->next)
+       if (!remoteheads)
+               ; /* already up-to-date */
+       else if (!remoteheads->next)
                common = get_merge_bases(head_commit, remoteheads->item, 1);
        else {
                struct commit_list *list = remoteheads;
        update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
                   NULL, 0, DIE_ON_ERR);
  
-       if (!common)
+       if (remoteheads && !common)
                ; /* No common ancestors found. We need a real merge. */
-       else if (!remoteheads->next && !common->next &&
-                       common->item == remoteheads->item) {
+       else if (!remoteheads ||
+                (!remoteheads->next && !common->next &&
+                 common->item == remoteheads->item)) {
                /*
                 * If head can reach all the merge then we are up to date.
                 * but first the most common case of merging one remote.
                        goto done;
                }
  
-               finish(head_commit, commit->object.sha1, msg.buf);
+               finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
                drop_save();
                goto done;
        } else if (!remoteheads->next && common->next)
                        if (!read_tree_trivial(common->item->object.sha1,
                                               head_commit->object.sha1,
                                               remoteheads->item->object.sha1)) {
-                               ret = merge_trivial(head_commit);
+                               ret = merge_trivial(head_commit, remoteheads);
                                goto done;
                        }
                        printf(_("Nope.\n"));
                wt_strategy = use_strategies[i]->name;
  
                ret = try_merge_strategy(use_strategies[i]->name,
-                                        common, head_commit, head_arg);
+                                        common, remoteheads,
+                                        head_commit, head_arg);
                if (!option_commit && !ret) {
                        merge_was_ok = 1;
                        /*
         * auto resolved the merge cleanly.
         */
        if (automerge_was_ok) {
-               ret = finish_automerge(head_commit, common, result_tree,
-                                      wt_strategy);
+               ret = finish_automerge(head_commit, head_subsumed,
+                                      common, remoteheads,
+                                      result_tree, wt_strategy);
                goto done;
        }
  
                restore_state(head_commit->object.sha1, stash);
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
-               try_merge_strategy(best_strategy, common, head_commit, head_arg);
+               try_merge_strategy(best_strategy, common, remoteheads,
+                                  head_commit, head_arg);
        }
  
        if (squash)
-               finish(head_commit, NULL, NULL);
+               finish(head_commit, remoteheads, NULL, NULL);
        else
-               write_merge_state();
+               write_merge_state(remoteheads);
  
        if (merge_was_ok)
                fprintf(stderr, _("Automatic merge went well; "
index bce0bd37cb6006f29b9c8c532863947c44509486,7117b57ccc9e20a2a6f4d10705b8a5665db8b30e..3b72c097ee0c9538d4e8003639ab7c44126c49d6
@@@ -66,21 -66,19 +66,19 @@@ EO
  test_expect_success 'merge output uses pretty names' '
        git reset --hard c1 &&
        git merge c2 c3 c4 >actual &&
 -      test_cmp actual expected
 +      test_i18ncmp expected actual
  '
  
  cat >expected <<\EOF
- Already up-to-date with c4
- Trying simple merge with c5
- Merge made by the 'octopus' strategy.
+ Merge made by the 'recursive' strategy.
   c5.c |    1 +
   1 file changed, 1 insertion(+)
   create mode 100644 c5.c
  EOF
  
- test_expect_success 'merge up-to-date output uses pretty names' '
-       git merge c4 c5 >actual &&
+ test_expect_success 'merge reduces irrelevant remote heads' '
+       GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
 -      test_cmp expected actual
 +      test_i18ncmp expected actual
  '
  
  cat >expected <<\EOF
@@@ -97,7 -95,7 +95,7 @@@ EO
  test_expect_success 'merge fast-forward output uses pretty names' '
        git reset --hard c0 &&
        git merge c1 c2 >actual &&
 -      test_cmp actual expected
 +      test_i18ncmp expected actual
  '
  
  test_done