Merge branch 'ma/unpack-trees-free-msgs'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 May 2018 12:51:29 +0000 (21:51 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 May 2018 12:51:29 +0000 (21:51 +0900)
Leak plugging.

* ma/unpack-trees-free-msgs:
unpack_trees_options: free messages when done
argv-array: return the pushed string from argv_push*()
merge-recursive: provide pair of `unpack_trees_{start,finish}()`
merge: setup `opts` later in `checkout_fast_forward()`

1  2 
argv-array.c
argv-array.h
builtin/checkout.c
merge-recursive.c
merge.c
unpack-trees.c
diff --combined argv-array.c
index cb5bcd2c064961919d965cb2cae79de653162fde,449dfc105a2e6a803f329ff4cf2f3a5a86ba82d7..f352ea9357d90e16c7506af9b5b047ac1e2721ac
@@@ -21,12 -21,13 +21,13 @@@ static void argv_array_push_nodup(struc
        array->argv[array->argc] = NULL;
  }
  
void argv_array_push(struct argv_array *array, const char *value)
const char *argv_array_push(struct argv_array *array, const char *value)
  {
        argv_array_push_nodup(array, xstrdup(value));
+       return array->argv[array->argc - 1];
  }
  
void argv_array_pushf(struct argv_array *array, const char *fmt, ...)
const char *argv_array_pushf(struct argv_array *array, const char *fmt, ...)
  {
        va_list ap;
        struct strbuf v = STRBUF_INIT;
@@@ -36,6 -37,7 +37,7 @@@
        va_end(ap);
  
        argv_array_push_nodup(array, strbuf_detach(&v, NULL));
+       return array->argv[array->argc - 1];
  }
  
  void argv_array_pushl(struct argv_array *array, ...)
@@@ -64,26 -66,6 +66,26 @@@ void argv_array_pop(struct argv_array *
        array->argc--;
  }
  
 +void argv_array_split(struct argv_array *array, const char *to_split)
 +{
 +      while (isspace(*to_split))
 +              to_split++;
 +      for (;;) {
 +              const char *p = to_split;
 +
 +              if (!*p)
 +                      break;
 +
 +              while (*p && !isspace(*p))
 +                      p++;
 +              argv_array_push_nodup(array, xstrndup(to_split, p - to_split));
 +
 +              while (isspace(*p))
 +                      p++;
 +              to_split = p;
 +      }
 +}
 +
  void argv_array_clear(struct argv_array *array)
  {
        if (array->argv != empty_argv) {
diff --combined argv-array.h
index 750c30d2f2cc7c7532ae67778da6396b4744708f,715c93b246c9878a42a45f94d4a1e4089676e899..a39ba43f57ce4969ef936646d30e5128ff28214f
@@@ -12,15 -12,13 +12,15 @@@ struct argv_array 
  #define ARGV_ARRAY_INIT { empty_argv, 0, 0 }
  
  void argv_array_init(struct argv_array *);
void argv_array_push(struct argv_array *, const char *);
const char *argv_array_push(struct argv_array *, const char *);
  __attribute__((format (printf,2,3)))
void argv_array_pushf(struct argv_array *, const char *fmt, ...);
const char *argv_array_pushf(struct argv_array *, const char *fmt, ...);
  LAST_ARG_MUST_BE_NULL
  void argv_array_pushl(struct argv_array *, ...);
  void argv_array_pushv(struct argv_array *, const char **);
  void argv_array_pop(struct argv_array *);
 +/* Splits by whitespace; does not handle quoted arguments! */
 +void argv_array_split(struct argv_array *, const char *);
  void argv_array_clear(struct argv_array *);
  const char **argv_array_detach(struct argv_array *);
  
diff --combined builtin/checkout.c
index 2b3b768effd75e5d12d2c1828eb080f15c91895f,5cebe170fc1937c4eafd912f7d09860f5b2ebff2..2e1d2376d24043a75adb9bbc69724591bcc2d5c3
@@@ -484,8 -484,7 +484,8 @@@ static int merge_working_tree(const str
  
        resolve_undo_clear();
        if (opts->force) {
 -              ret = reset_tree(new_branch_info->commit->tree, opts, 1, writeout_error);
 +              ret = reset_tree(get_commit_tree(new_branch_info->commit),
 +                               opts, 1, writeout_error);
                if (ret)
                        return ret;
        } else {
                init_tree_desc(&trees[1], tree->buffer, tree->size);
  
                ret = unpack_trees(2, trees, &topts);
+               clear_unpack_trees_porcelain(&topts);
                if (ret == -1) {
                        /*
                         * Unpack couldn't do a trivial merge; either
                        o.verbosity = 0;
                        work = write_tree_from_memory(&o);
  
 -                      ret = reset_tree(new_branch_info->commit->tree, opts, 1,
 +                      ret = reset_tree(get_commit_tree(new_branch_info->commit),
 +                                       opts, 1,
                                         writeout_error);
                        if (ret)
                                return ret;
                        o.ancestor = old_branch_info->name;
                        o.branch1 = new_branch_info->name;
                        o.branch2 = "local";
 -                      ret = merge_trees(&o, new_branch_info->commit->tree, work,
 -                              old_branch_info->commit->tree, &result);
 +                      ret = merge_trees(&o,
 +                                        get_commit_tree(new_branch_info->commit),
 +                                        work,
 +                                        get_commit_tree(old_branch_info->commit),
 +                                        &result);
                        if (ret < 0)
                                exit(128);
 -                      ret = reset_tree(new_branch_info->commit->tree, opts, 0,
 +                      ret = reset_tree(get_commit_tree(new_branch_info->commit),
 +                                       opts, 0,
                                         writeout_error);
                        strbuf_release(&o.obuf);
                        if (ret)
@@@ -1008,7 -1003,7 +1009,7 @@@ static int parse_branchname_arg(int arg
                *source_tree = parse_tree_indirect(rev);
        } else {
                parse_commit_or_die(new_branch_info->commit);
 -              *source_tree = new_branch_info->commit->tree;
 +              *source_tree = get_commit_tree(new_branch_info->commit);
        }
  
        if (!*source_tree)                   /* case (1): want a tree */
diff --combined merge-recursive.c
index cf5341828f53e5dbb4d3221e4db85b1295165b95,338f63a952b1f4a7a736ff5506362304760877b2..ac27abbd4cd708fdc2526c6508680b7d246773b6
@@@ -23,7 -23,6 +23,7 @@@
  #include "merge-recursive.h"
  #include "dir.h"
  #include "submodule.h"
 +#include "revision.h"
  
  struct path_hashmap_entry {
        struct hashmap_entry e;
@@@ -163,7 -162,7 +163,7 @@@ static struct commit *make_virtual_comm
        struct commit *commit = alloc_commit_node();
  
        set_merge_remote_desc(commit, comment, (struct object *)commit);
 -      commit->tree = tree;
 +      commit->maybe_tree = tree;
        commit->object.parsed = 1;
        return commit;
  }
@@@ -338,10 -337,10 +338,10 @@@ static void init_tree_desc_from_tree(st
        init_tree_desc(desc, tree->buffer, tree->size);
  }
  
- static int git_merge_trees(struct merge_options *o,
-                          struct tree *common,
-                          struct tree *head,
-                          struct tree *merge)
+ static int unpack_trees_start(struct merge_options *o,
+                             struct tree *common,
+                             struct tree *head,
+                             struct tree *merge)
  {
        int rc;
        struct tree_desc t[3];
        return rc;
  }
  
+ static void unpack_trees_finish(struct merge_options *o)
+ {
+       discard_index(&o->orig_index);
+       clear_unpack_trees_porcelain(&o->unpack_opts);
+ }
  struct tree *write_tree_from_memory(struct merge_options *o)
  {
        struct tree *result = NULL;
                                fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
                                        (int)ce_namelen(ce), ce->name);
                }
 -              die("BUG: unmerged index entries in merge-recursive.c");
 +              BUG("unmerged index entries in merge-recursive.c");
        }
  
        if (!active_cache_tree)
@@@ -1083,185 -1088,6 +1089,185 @@@ static int merge_3way(struct merge_opti
        return merge_status;
  }
  
 +static int find_first_merges(struct object_array *result, const char *path,
 +              struct commit *a, struct commit *b)
 +{
 +      int i, j;
 +      struct object_array merges = OBJECT_ARRAY_INIT;
 +      struct commit *commit;
 +      int contains_another;
 +
 +      char merged_revision[42];
 +      const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
 +                                 "--all", merged_revision, NULL };
 +      struct rev_info revs;
 +      struct setup_revision_opt rev_opts;
 +
 +      memset(result, 0, sizeof(struct object_array));
 +      memset(&rev_opts, 0, sizeof(rev_opts));
 +
 +      /* get all revisions that merge commit a */
 +      xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
 +                      oid_to_hex(&a->object.oid));
 +      init_revisions(&revs, NULL);
 +      rev_opts.submodule = path;
 +      /* FIXME: can't handle linked worktrees in submodules yet */
 +      revs.single_worktree = path != NULL;
 +      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))
 +              die("revision walk setup failed");
 +      while ((commit = get_revision(&revs)) != NULL) {
 +              struct object *o = &(commit->object);
 +              if (in_merge_bases(b, commit))
 +                      add_object_array(o, NULL, &merges);
 +      }
 +      reset_revision_walk();
 +
 +      /* Now we've got all merges that contain a and b. Prune all
 +       * merges that contain another found merge and save them in
 +       * result.
 +       */
 +      for (i = 0; i < merges.nr; i++) {
 +              struct commit *m1 = (struct commit *) merges.objects[i].item;
 +
 +              contains_another = 0;
 +              for (j = 0; j < merges.nr; j++) {
 +                      struct commit *m2 = (struct commit *) merges.objects[j].item;
 +                      if (i != j && in_merge_bases(m2, m1)) {
 +                              contains_another = 1;
 +                              break;
 +                      }
 +              }
 +
 +              if (!contains_another)
 +                      add_object_array(merges.objects[i].item, NULL, result);
 +      }
 +
 +      object_array_clear(&merges);
 +      return result->nr;
 +}
 +
 +static void print_commit(struct commit *commit)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      struct pretty_print_context ctx = {0};
 +      ctx.date_mode.type = DATE_NORMAL;
 +      format_commit_message(commit, " %h: %m %s", &sb, &ctx);
 +      fprintf(stderr, "%s\n", sb.buf);
 +      strbuf_release(&sb);
 +}
 +
 +static int merge_submodule(struct merge_options *o,
 +                         struct object_id *result, const char *path,
 +                         const struct object_id *base, const struct object_id *a,
 +                         const struct object_id *b)
 +{
 +      struct commit *commit_base, *commit_a, *commit_b;
 +      int parent_count;
 +      struct object_array merges;
 +
 +      int i;
 +      int search = !o->call_depth;
 +
 +      /* store a in result in case we fail */
 +      oidcpy(result, a);
 +
 +      /* we can not handle deletion conflicts */
 +      if (is_null_oid(base))
 +              return 0;
 +      if (is_null_oid(a))
 +              return 0;
 +      if (is_null_oid(b))
 +              return 0;
 +
 +      if (add_submodule_odb(path)) {
 +              output(o, 1, _("Failed to merge submodule %s (not checked out)"), path);
 +              return 0;
 +      }
 +
 +      if (!(commit_base = lookup_commit_reference(base)) ||
 +          !(commit_a = lookup_commit_reference(a)) ||
 +          !(commit_b = lookup_commit_reference(b))) {
 +              output(o, 1, _("Failed to merge submodule %s (commits not present)"), path);
 +              return 0;
 +      }
 +
 +      /* check whether both changes are forward */
 +      if (!in_merge_bases(commit_base, commit_a) ||
 +          !in_merge_bases(commit_base, commit_b)) {
 +              output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
 +              return 0;
 +      }
 +
 +      /* Case #1: a is contained in b or vice versa */
 +      if (in_merge_bases(commit_a, commit_b)) {
 +              oidcpy(result, b);
 +              if (show(o, 3)) {
 +                      output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
 +                      output_commit_title(o, commit_b);
 +              } else if (show(o, 2))
 +                      output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(b));
 +              else
 +                      ; /* no output */
 +
 +              return 1;
 +      }
 +      if (in_merge_bases(commit_b, commit_a)) {
 +              oidcpy(result, a);
 +              if (show(o, 3)) {
 +                      output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
 +                      output_commit_title(o, commit_a);
 +              } else if (show(o, 2))
 +                      output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(a));
 +              else
 +                      ; /* no output */
 +
 +              return 1;
 +      }
 +
 +      /*
 +       * Case #2: There are one or more merges that contain a and b in
 +       * the submodule. If there is only one, then present it as a
 +       * suggestion to the user, but leave it marked unmerged so the
 +       * user needs to confirm the resolution.
 +       */
 +
 +      /* Skip the search if makes no sense to the calling context.  */
 +      if (!search)
 +              return 0;
 +
 +      /* find commit which merges them */
 +      parent_count = find_first_merges(&merges, path, commit_a, commit_b);
 +      switch (parent_count) {
 +      case 0:
 +              output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
 +              break;
 +
 +      case 1:
 +              output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
 +              output(o, 2, _("Found a possible merge resolution for the submodule:\n"));
 +              print_commit((struct commit *) merges.objects[0].item);
 +              output(o, 2, _(
 +                      "If this is correct simply add it to the index "
 +                      "for example\n"
 +                      "by using:\n\n"
 +                      "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
 +                      "which will accept this suggestion.\n"),
 +                      oid_to_hex(&merges.objects[0].item->oid), path);
 +              break;
 +
 +      default:
 +              output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
 +              for (i = 0; i < merges.nr; i++)
 +                      print_commit((struct commit *) merges.objects[i].item);
 +      }
 +
 +      object_array_clear(&merges);
 +      return 0;
 +}
 +
  static int merge_file_1(struct merge_options *o,
                        const struct diff_filespec *one,
                        const struct diff_filespec *a,
                                return ret;
                        result->clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
 -                      result->clean = merge_submodule(&result->oid,
 +                      result->clean = merge_submodule(o, &result->oid,
                                                       one->path,
                                                       &one->oid,
                                                       &a->oid,
 -                                                     &b->oid,
 -                                                     !o->call_depth);
 +                                                     &b->oid);
                } else if (S_ISLNK(a->mode)) {
                        switch (o->recursive_variant) {
                        case MERGE_RECURSIVE_NORMAL:
                                break;
                        }
                } else
 -                      die("BUG: unsupported object type in the tree");
 +                      BUG("unsupported object type in the tree");
        }
  
        if (result->merge)
@@@ -2598,7 -2425,7 +2604,7 @@@ static int process_renames(struct merge
                        const char *ren2_dst = ren2->pair->two->path;
                        enum rename_type rename_type;
                        if (strcmp(ren1_src, ren2_src) != 0)
 -                              die("BUG: ren1_src != ren2_src");
 +                              BUG("ren1_src != ren2_src");
                        ren2->dst_entry->processed = 1;
                        ren2->processed = 1;
                        if (strcmp(ren1_dst, ren2_dst) != 0) {
                        ren2 = lookup->util;
                        ren2_dst = ren2->pair->two->path;
                        if (strcmp(ren1_dst, ren2_dst) != 0)
 -                              die("BUG: ren1_dst != ren2_dst");
 +                              BUG("ren1_dst != ren2_dst");
  
                        clean_merge = 0;
                        ren2->processed = 1;
@@@ -3236,7 -3063,7 +3242,7 @@@ static int process_entry(struct merge_o
                 */
                remove_file(o, 1, path, !a_mode);
        } else
 -              die("BUG: fatal merge failure, shouldn't happen.");
 +              BUG("fatal merge failure, shouldn't happen.");
  
        return clean_merge;
  }
@@@ -3267,13 -3094,14 +3273,14 @@@ int merge_trees(struct merge_options *o
                return 1;
        }
  
-       code = git_merge_trees(o, common, head, merge);
+       code = unpack_trees_start(o, common, head, merge);
  
        if (code != 0) {
                if (show(o, 4) || o->call_depth)
                        err(o, _("merging of trees %s and %s failed"),
                            oid_to_hex(&head->object.oid),
                            oid_to_hex(&merge->object.oid));
+               unpack_trees_finish(o);
                return -1;
        }
  
                for (i = 0; i < entries->nr; i++) {
                        struct stage_data *e = entries->items[i].util;
                        if (!e->processed)
 -                              die("BUG: unprocessed path??? %s",
 +                              BUG("unprocessed path??? %s",
                                    entries->items[i].string);
                }
  
@@@ -3326,20 -3154,15 +3333,15 @@@ cleanup
  
                hashmap_free(&o->current_file_dir_set, 1);
  
-               if (clean < 0)
+               if (clean < 0) {
+                       unpack_trees_finish(o);
                        return clean;
+               }
        }
        else
                clean = 1;
  
-       /* Free the extra index left from git_merge_trees() */
-       /*
-        * FIXME: Need to also free data allocated by
-        * setup_unpack_trees_porcelain() tucked away in o->unpack_opts.msgs,
-        * but the problem is that only half of it refers to dynamically
-        * allocated data, while the other half points at static strings.
-        */
-       discard_index(&o->orig_index);
+       unpack_trees_finish(o);
  
        if (o->call_depth && !(*result = write_tree_from_memory(o)))
                return -1;
@@@ -3434,8 -3257,7 +3436,8 @@@ int merge_recursive(struct merge_option
                read_cache();
  
        o->ancestor = "merged common ancestors";
 -      clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,
 +      clean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2),
 +                          get_commit_tree(merged_common_ancestors),
                            &mrtree);
        if (clean < 0) {
                flush_output(o);
diff --combined merge.c
index 5186cb6156f2d37dfeb287f959adbbaf4bb73afc,b433291d0c376ea3ba0a1c56b465cc1b91da4ddf..0783858739f84028df6eef85d3673c944fabb912
+++ b/merge.c
  
  static const char *merge_argument(struct commit *commit)
  {
 -      if (commit)
 -              return oid_to_hex(&commit->object.oid);
 -      else
 -              return EMPTY_TREE_SHA1_HEX;
 +      return oid_to_hex(commit ? &commit->object.oid : the_hash_algo->empty_tree);
  }
  
  int index_has_changes(struct strbuf *sb)
@@@ -91,8 -94,24 +91,24 @@@ int checkout_fast_forward(const struct 
                return -1;
  
        memset(&trees, 0, sizeof(trees));
-       memset(&opts, 0, sizeof(opts));
        memset(&t, 0, sizeof(t));
+       trees[nr_trees] = parse_tree_indirect(head);
+       if (!trees[nr_trees++]) {
+               rollback_lock_file(&lock_file);
+               return -1;
+       }
+       trees[nr_trees] = parse_tree_indirect(remote);
+       if (!trees[nr_trees++]) {
+               rollback_lock_file(&lock_file);
+               return -1;
+       }
+       for (i = 0; i < nr_trees; i++) {
+               parse_tree(trees[i]);
+               init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+       }
+       memset(&opts, 0, sizeof(opts));
        if (overwrite_ignore) {
                memset(&dir, 0, sizeof(dir));
                dir.flags |= DIR_SHOW_IGNORED;
        opts.fn = twoway_merge;
        setup_unpack_trees_porcelain(&opts, "merge");
  
-       trees[nr_trees] = parse_tree_indirect(head);
-       if (!trees[nr_trees++]) {
-               rollback_lock_file(&lock_file);
-               return -1;
-       }
-       trees[nr_trees] = parse_tree_indirect(remote);
-       if (!trees[nr_trees++]) {
-               rollback_lock_file(&lock_file);
-               return -1;
-       }
-       for (i = 0; i < nr_trees; i++) {
-               parse_tree(trees[i]);
-               init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
-       }
        if (unpack_trees(nr_trees, t, &opts)) {
                rollback_lock_file(&lock_file);
+               clear_unpack_trees_porcelain(&opts);
                return -1;
        }
+       clear_unpack_trees_porcelain(&opts);
        if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                return error(_("unable to write new index file"));
        return 0;
diff --combined unpack-trees.c
index d17f726e7572985c8c2fa5289bafe7c1b78eaa00,73a6dc1701fbc969a65efd2afb6eeab7130fa228..3a85a02a7733aeeee86da1a324319d026c32d787
@@@ -1,5 -1,6 +1,6 @@@
  #define NO_THE_INDEX_COMPATIBILITY_MACROS
  #include "cache.h"
+ #include "argv-array.h"
  #include "repository.h"
  #include "config.h"
  #include "dir.h"
@@@ -103,6 -104,8 +104,8 @@@ void setup_unpack_trees_porcelain(struc
        const char **msgs = opts->msgs;
        const char *msg;
  
+       argv_array_init(&opts->msgs_to_free);
        if (!strcmp(cmd, "checkout"))
                msg = advice_commit_before_merge
                      ? _("Your local changes to the following files would be overwritten by checkout:\n%%s"
                          "Please commit your changes or stash them before you %s.")
                      : _("Your local changes to the following files would be overwritten by %s:\n%%s");
        msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
-               xstrfmt(msg, cmd, cmd);
+               argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
  
        msgs[ERROR_NOT_UPTODATE_DIR] =
                _("Updating the following directories would lose untracked files in them:\n%s");
                      ? _("The following untracked working tree files would be removed by %s:\n%%s"
                          "Please move or remove them before you %s.")
                      : _("The following untracked working tree files would be removed by %s:\n%%s");
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, cmd, cmd);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] =
+               argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
  
        if (!strcmp(cmd, "checkout"))
                msg = advice_commit_before_merge
                      ? _("The following untracked working tree files would be overwritten by %s:\n%%s"
                          "Please move or remove them before you %s.")
                      : _("The following untracked working tree files would be overwritten by %s:\n%%s");
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, cmd, cmd);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] =
+               argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
  
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
                opts->unpack_rejects[i].strdup_strings = 1;
  }
  
+ void clear_unpack_trees_porcelain(struct unpack_trees_options *opts)
+ {
+       argv_array_clear(&opts->msgs_to_free);
+       memset(opts->msgs, 0, sizeof(opts->msgs));
+ }
  static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
                         unsigned int set, unsigned int clear)
  {
@@@ -290,7 -301,7 +301,7 @@@ static void load_gitmodules_file(struc
                if (!state && ce->ce_flags & CE_WT_REMOVE) {
                        repo_read_gitmodules(the_repository);
                } else if (state && (ce->ce_flags & CE_UPDATE)) {
 -                      submodule_free();
 +                      submodule_free(the_repository);
                        checkout_entry(ce, state, NULL);
                        repo_read_gitmodules(the_repository);
                }
@@@ -398,7 -409,7 +409,7 @@@ static int check_updates(struct unpack_
  
                if (ce->ce_flags & CE_UPDATE) {
                        if (ce->ce_flags & CE_WT_REMOVE)
 -                              die("BUG: both update and delete flags are set on %s",
 +                              BUG("both update and delete flags are set on %s",
                                    ce->name);
                        display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
@@@ -1284,21 -1295,10 +1295,21 @@@ int unpack_trees(unsigned len, struct t
        o->result.timestamp.sec = o->src_index->timestamp.sec;
        o->result.timestamp.nsec = o->src_index->timestamp.nsec;
        o->result.version = o->src_index->version;
 -      o->result.split_index = o->src_index->split_index;
 -      if (o->result.split_index)
 +      if (!o->src_index->split_index) {
 +              o->result.split_index = NULL;
 +      } else if (o->src_index == o->dst_index) {
 +              /*
 +               * o->dst_index (and thus o->src_index) will be discarded
 +               * and overwritten with o->result at the end of this function,
 +               * so just use src_index's split_index to avoid having to
 +               * create a new one.
 +               */
 +              o->result.split_index = o->src_index->split_index;
                o->result.split_index->refcount++;
 -      hashcpy(o->result.sha1, o->src_index->sha1);
 +      } else {
 +              o->result.split_index = init_split_index(&o->result);
 +      }
 +      oidcpy(&o->result.oid, &o->src_index->oid);
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
  
                }
        }
  
 -      o->src_index = NULL;
        ret = check_updates(o) ? (-2) : 0;
        if (o->dst_index) {
                if (!ret) {
                                                  WRITE_TREE_SILENT |
                                                  WRITE_TREE_REPAIR);
                }
 -              move_index_extensions(&o->result, o->dst_index);
 +              move_index_extensions(&o->result, o->src_index);
                discard_index(o->dst_index);
                *o->dst_index = o->result;
        } else {
                discard_index(&o->result);
        }
 +      o->src_index = NULL;
  
  done:
        clear_exclude_list(&el);