Deprecate support for .git/info/grafts
[gitweb.git] / merge-recursive.c
index 2cbdb9939a79ae1d0fe6eab72170d0be3bac25df..0c0d48624da1d6162f0804e4a105841d965bb2c2 100644 (file)
@@ -49,67 +49,6 @@ static unsigned int path_hash(const char *path)
        return ignore_case ? strihash(path) : strhash(path);
 }
 
-static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,
-                                                     char *dir)
-{
-       struct dir_rename_entry key;
-
-       if (dir == NULL)
-               return NULL;
-       hashmap_entry_init(&key, strhash(dir));
-       key.dir = dir;
-       return hashmap_get(hashmap, &key, NULL);
-}
-
-static int dir_rename_cmp(const void *unused_cmp_data,
-                         const void *entry,
-                         const void *entry_or_key,
-                         const void *unused_keydata)
-{
-       const struct dir_rename_entry *e1 = entry;
-       const struct dir_rename_entry *e2 = entry_or_key;
-
-       return strcmp(e1->dir, e2->dir);
-}
-
-static void dir_rename_init(struct hashmap *map)
-{
-       hashmap_init(map, dir_rename_cmp, NULL, 0);
-}
-
-static void dir_rename_entry_init(struct dir_rename_entry *entry,
-                                 char *directory)
-{
-       hashmap_entry_init(entry, strhash(directory));
-       entry->dir = directory;
-       entry->non_unique_new_dir = 0;
-       strbuf_init(&entry->new_dir, 0);
-       string_list_init(&entry->possible_new_dirs, 0);
-}
-
-static struct collision_entry *collision_find_entry(struct hashmap *hashmap,
-                                                   char *target_file)
-{
-       struct collision_entry key;
-
-       hashmap_entry_init(&key, strhash(target_file));
-       key.target_file = target_file;
-       return hashmap_get(hashmap, &key, NULL);
-}
-
-static int collision_cmp(void *unused_cmp_data,
-                        const struct collision_entry *e1,
-                        const struct collision_entry *e2,
-                        const void *unused_keydata)
-{
-       return strcmp(e1->target_file, e2->target_file);
-}
-
-static void collision_init(struct hashmap *map)
-{
-       hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);
-}
-
 static void flush_output(struct merge_options *o)
 {
        if (o->buffer_output < 2 && o->obuf.len) {
@@ -289,7 +228,7 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
                strbuf_addf(&o->obuf, "virtual %s\n",
                        merge_remote_util(commit)->name);
        else {
-               strbuf_add_unique_abbrev(&o->obuf, commit->object.oid.hash,
+               strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,
                                         DEFAULT_ABBREV);
                strbuf_addch(&o->obuf, ' ');
                if (parse_commit(commit) != 0)
@@ -396,7 +335,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
        return result;
 }
 
-static int save_files_dirs(const unsigned char *sha1,
+static int save_files_dirs(const struct object_id *oid,
                struct strbuf *base, const char *path,
                unsigned int mode, int stage, void *context)
 {
@@ -431,12 +370,12 @@ static struct stage_data *insert_stage_data(const char *path,
 {
        struct string_list_item *item;
        struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
-       get_tree_entry(o->object.oid.hash, path,
-                       e->stages[1].oid.hash, &e->stages[1].mode);
-       get_tree_entry(a->object.oid.hash, path,
-                       e->stages[2].oid.hash, &e->stages[2].mode);
-       get_tree_entry(b->object.oid.hash, path,
-                       e->stages[3].oid.hash, &e->stages[3].mode);
+       get_tree_entry(&o->object.oid, path,
+                       &e->stages[1].oid, &e->stages[1].mode);
+       get_tree_entry(&a->object.oid, path,
+                       &e->stages[2].oid, &e->stages[2].mode);
+       get_tree_entry(&b->object.oid, path,
+                       &e->stages[3].oid, &e->stages[3].mode);
        item = string_list_insert(entries, path);
        item->util = e;
        return e;
@@ -598,6 +537,75 @@ struct rename {
        unsigned processed:1;
 };
 
+/*
+ * Get information of all renames which occurred between 'o_tree' and
+ * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and
+ * 'b_tree') to be able to associate the correct cache entries with
+ * the rename information. 'tree' is always equal to either a_tree or b_tree.
+ */
+static struct string_list *get_renames(struct merge_options *o,
+                                      struct tree *tree,
+                                      struct tree *o_tree,
+                                      struct tree *a_tree,
+                                      struct tree *b_tree,
+                                      struct string_list *entries)
+{
+       int i;
+       struct string_list *renames;
+       struct diff_options opts;
+
+       renames = xcalloc(1, sizeof(struct string_list));
+       if (!o->detect_rename)
+               return renames;
+
+       diff_setup(&opts);
+       opts.flags.recursive = 1;
+       opts.flags.rename_empty = 0;
+       opts.detect_rename = DIFF_DETECT_RENAME;
+       opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
+                           o->diff_rename_limit >= 0 ? o->diff_rename_limit :
+                           1000;
+       opts.rename_score = o->rename_score;
+       opts.show_rename_progress = o->show_rename_progress;
+       opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+       diff_setup_done(&opts);
+       diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
+       diffcore_std(&opts);
+       if (opts.needed_rename_limit > o->needed_rename_limit)
+               o->needed_rename_limit = opts.needed_rename_limit;
+       for (i = 0; i < diff_queued_diff.nr; ++i) {
+               struct string_list_item *item;
+               struct rename *re;
+               struct diff_filepair *pair = diff_queued_diff.queue[i];
+               if (pair->status != 'R') {
+                       diff_free_filepair(pair);
+                       continue;
+               }
+               re = xmalloc(sizeof(*re));
+               re->processed = 0;
+               re->pair = pair;
+               item = string_list_lookup(entries, re->pair->one->path);
+               if (!item)
+                       re->src_entry = insert_stage_data(re->pair->one->path,
+                                       o_tree, a_tree, b_tree, entries);
+               else
+                       re->src_entry = item->util;
+
+               item = string_list_lookup(entries, re->pair->two->path);
+               if (!item)
+                       re->dst_entry = insert_stage_data(re->pair->two->path,
+                                       o_tree, a_tree, b_tree, entries);
+               else
+                       re->dst_entry = item->util;
+               item = string_list_insert(renames, pair->one->path);
+               item->util = re;
+       }
+       opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+       diff_queued_diff.nr = 0;
+       diff_flush(&opts);
+       return renames;
+}
+
 static int update_stages(struct merge_options *opt, const char *path,
                         const struct diff_filespec *o,
                         const struct diff_filespec *a,
@@ -834,7 +842,7 @@ static int update_file_flags(struct merge_options *o,
                        goto update_index;
                }
 
-               buf = read_sha1_file(oid->hash, &type, &size);
+               buf = read_object_file(oid, &type, &size);
                if (!buf)
                        return err(o, _("cannot read object %s '%s'"), oid_to_hex(oid), path);
                if (type != OBJ_BLOB) {
@@ -1020,8 +1028,9 @@ static int merge_file_1(struct merge_options *o,
                        if ((merge_status < 0) || !result_buf.ptr)
                                ret = err(o, _("Failed to execute internal merge"));
 
-                       if (!ret && write_sha1_file(result_buf.ptr, result_buf.size,
-                                                   blob_type, result->oid.hash))
+                       if (!ret &&
+                           write_object_file(result_buf.ptr, result_buf.size,
+                                             blob_type, &result->oid))
                                ret = err(o, _("Unable to add %s to database"),
                                          a->path);
 
@@ -1037,10 +1046,19 @@ static int merge_file_1(struct merge_options *o,
                                                       &b->oid,
                                                       !o->call_depth);
                } else if (S_ISLNK(a->mode)) {
-                       oidcpy(&result->oid, &a->oid);
-
-                       if (!oid_eq(&a->oid, &b->oid))
-                               result->clean = 0;
+                       switch (o->recursive_variant) {
+                       case MERGE_RECURSIVE_NORMAL:
+                               oidcpy(&result->oid, &a->oid);
+                               if (!oid_eq(&a->oid, &b->oid))
+                                       result->clean = 0;
+                               break;
+                       case MERGE_RECURSIVE_OURS:
+                               oidcpy(&result->oid, &a->oid);
+                               break;
+                       case MERGE_RECURSIVE_THEIRS:
+                               oidcpy(&result->oid, &b->oid);
+                               break;
+                       }
                } else
                        die("BUG: unsupported object type in the tree");
        }
@@ -1372,486 +1390,6 @@ static int conflict_rename_rename_2to1(struct merge_options *o,
        return ret;
 }
 
-/*
- * Get the diff_filepairs changed between o_tree and tree.
- */
-static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
-                                              struct tree *o_tree,
-                                              struct tree *tree)
-{
-       struct diff_queue_struct *ret;
-       struct diff_options opts;
-
-       diff_setup(&opts);
-       opts.flags.recursive = 1;
-       opts.flags.rename_empty = 0;
-       opts.detect_rename = DIFF_DETECT_RENAME;
-       opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
-                           o->diff_rename_limit >= 0 ? o->diff_rename_limit :
-                           1000;
-       opts.rename_score = o->rename_score;
-       opts.show_rename_progress = o->show_rename_progress;
-       opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       diff_setup_done(&opts);
-       diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
-       diffcore_std(&opts);
-       if (opts.needed_rename_limit > o->needed_rename_limit)
-               o->needed_rename_limit = opts.needed_rename_limit;
-
-       ret = xmalloc(sizeof(*ret));
-       *ret = diff_queued_diff;
-
-       opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       diff_queued_diff.nr = 0;
-       diff_queued_diff.queue = NULL;
-       diff_flush(&opts);
-       return ret;
-}
-
-static int tree_has_path(struct tree *tree, const char *path)
-{
-       unsigned char hashy[GIT_MAX_RAWSZ];
-       unsigned int mode_o;
-
-       return !get_tree_entry(tree->object.oid.hash, path,
-                              hashy, &mode_o);
-}
-
-/*
- * Return a new string that replaces the beginning portion (which matches
- * entry->dir), with entry->new_dir.  In perl-speak:
- *   new_path_name = (old_path =~ s/entry->dir/entry->new_dir/);
- * NOTE:
- *   Caller must ensure that old_path starts with entry->dir + '/'.
- */
-static char *apply_dir_rename(struct dir_rename_entry *entry,
-                             const char *old_path)
-{
-       struct strbuf new_path = STRBUF_INIT;
-       int oldlen, newlen;
-
-       if (entry->non_unique_new_dir)
-               return NULL;
-
-       oldlen = strlen(entry->dir);
-       newlen = entry->new_dir.len + (strlen(old_path) - oldlen) + 1;
-       strbuf_grow(&new_path, newlen);
-       strbuf_addbuf(&new_path, &entry->new_dir);
-       strbuf_addstr(&new_path, &old_path[oldlen]);
-
-       return strbuf_detach(&new_path, NULL);
-}
-
-static void get_renamed_dir_portion(const char *old_path, const char *new_path,
-                                   char **old_dir, char **new_dir)
-{
-       char *end_of_old, *end_of_new;
-       int old_len, new_len;
-
-       *old_dir = NULL;
-       *new_dir = NULL;
-
-       /*
-        * For
-        *    "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
-        * the "e/foo.c" part is the same, we just want to know that
-        *    "a/b/c/d" was renamed to "a/b/some/thing/else"
-        * so, for this example, this function returns "a/b/c/d" in
-        * *old_dir and "a/b/some/thing/else" in *new_dir.
-        *
-        * Also, if the basename of the file changed, we don't care.  We
-        * want to know which portion of the directory, if any, changed.
-        */
-       end_of_old = strrchr(old_path, '/');
-       end_of_new = strrchr(new_path, '/');
-
-       if (end_of_old == NULL || end_of_new == NULL)
-               return;
-       while (*--end_of_new == *--end_of_old &&
-              end_of_old != old_path &&
-              end_of_new != new_path)
-               ; /* Do nothing; all in the while loop */
-       /*
-        * We've found the first non-matching character in the directory
-        * paths.  That means the current directory we were comparing
-        * represents the rename.  Move end_of_old and end_of_new back
-        * to the full directory name.
-        */
-       if (*end_of_old == '/')
-               end_of_old++;
-       if (*end_of_old != '/')
-               end_of_new++;
-       end_of_old = strchr(end_of_old, '/');
-       end_of_new = strchr(end_of_new, '/');
-
-       /*
-        * It may have been the case that old_path and new_path were the same
-        * directory all along.  Don't claim a rename if they're the same.
-        */
-       old_len = end_of_old - old_path;
-       new_len = end_of_new - new_path;
-
-       if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
-               *old_dir = xstrndup(old_path, old_len);
-               *new_dir = xstrndup(new_path, new_len);
-       }
-}
-
-static void remove_hashmap_entries(struct hashmap *dir_renames,
-                                  struct string_list *items_to_remove)
-{
-       int i;
-       struct dir_rename_entry *entry;
-
-       for (i = 0; i < items_to_remove->nr; i++) {
-               entry = items_to_remove->items[i].util;
-               hashmap_remove(dir_renames, entry, NULL);
-       }
-       string_list_clear(items_to_remove, 0);
-}
-
-/*
- * There are a couple things we want to do at the directory level:
- *   1. Check for both sides renaming to the same thing, in order to avoid
- *      implicit renaming of files that should be left in place.  (See
- *      testcase 6b in t6043 for details.)
- *   2. Prune directory renames if there are still files left in the
- *      the original directory.  These represent a partial directory rename,
- *      i.e. a rename where only some of the files within the directory
- *      were renamed elsewhere.  (Technically, this could be done earlier
- *      in get_directory_renames(), except that would prevent us from
- *      doing the previous check and thus failing testcase 6b.)
- *   3. Check for rename/rename(1to2) conflicts (at the directory level).
- *      In the future, we could potentially record this info as well and
- *      omit reporting rename/rename(1to2) conflicts for each path within
- *      the affected directories, thus cleaning up the merge output.
- *   NOTE: We do NOT check for rename/rename(2to1) conflicts at the
- *         directory level, because merging directories is fine.  If it
- *         causes conflicts for files within those merged directories, then
- *         that should be detected at the individual path level.
- */
-static void handle_directory_level_conflicts(struct merge_options *o,
-                                            struct hashmap *dir_re_head,
-                                            struct tree *head,
-                                            struct hashmap *dir_re_merge,
-                                            struct tree *merge)
-{
-       struct hashmap_iter iter;
-       struct dir_rename_entry *head_ent;
-       struct dir_rename_entry *merge_ent;
-
-       struct string_list remove_from_head = STRING_LIST_INIT_NODUP;
-       struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;
-
-       hashmap_iter_init(dir_re_head, &iter);
-       while ((head_ent = hashmap_iter_next(&iter))) {
-               merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);
-               if (merge_ent &&
-                   !head_ent->non_unique_new_dir &&
-                   !merge_ent->non_unique_new_dir &&
-                   !strbuf_cmp(&head_ent->new_dir, &merge_ent->new_dir)) {
-                       /* 1. Renamed identically; remove it from both sides */
-                       string_list_append(&remove_from_head,
-                                          head_ent->dir)->util = head_ent;
-                       strbuf_release(&head_ent->new_dir);
-                       string_list_append(&remove_from_merge,
-                                          merge_ent->dir)->util = merge_ent;
-                       strbuf_release(&merge_ent->new_dir);
-               } else if (tree_has_path(head, head_ent->dir)) {
-                       /* 2. This wasn't a directory rename after all */
-                       string_list_append(&remove_from_head,
-                                          head_ent->dir)->util = head_ent;
-                       strbuf_release(&head_ent->new_dir);
-               }
-       }
-
-       remove_hashmap_entries(dir_re_head, &remove_from_head);
-       remove_hashmap_entries(dir_re_merge, &remove_from_merge);
-
-       hashmap_iter_init(dir_re_merge, &iter);
-       while ((merge_ent = hashmap_iter_next(&iter))) {
-               head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);
-               if (tree_has_path(merge, merge_ent->dir)) {
-                       /* 2. This wasn't a directory rename after all */
-                       string_list_append(&remove_from_merge,
-                                          merge_ent->dir)->util = merge_ent;
-               } else if (head_ent &&
-                          !head_ent->non_unique_new_dir &&
-                          !merge_ent->non_unique_new_dir) {
-                       /* 3. rename/rename(1to2) */
-                       /*
-                        * We can assume it's not rename/rename(1to1) because
-                        * that was case (1), already checked above.  So we
-                        * know that head_ent->new_dir and merge_ent->new_dir
-                        * are different strings.
-                        */
-                       output(o, 1, _("CONFLICT (rename/rename): "
-                                      "Rename directory %s->%s in %s. "
-                                      "Rename directory %s->%s in %s"),
-                              head_ent->dir, head_ent->new_dir.buf, o->branch1,
-                              head_ent->dir, merge_ent->new_dir.buf, o->branch2);
-                       string_list_append(&remove_from_head,
-                                          head_ent->dir)->util = head_ent;
-                       strbuf_release(&head_ent->new_dir);
-                       string_list_append(&remove_from_merge,
-                                          merge_ent->dir)->util = merge_ent;
-                       strbuf_release(&merge_ent->new_dir);
-               }
-       }
-
-       remove_hashmap_entries(dir_re_head, &remove_from_head);
-       remove_hashmap_entries(dir_re_merge, &remove_from_merge);
-}
-
-static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
-                                            struct tree *tree)
-{
-       struct hashmap *dir_renames;
-       struct hashmap_iter iter;
-       struct dir_rename_entry *entry;
-       int i;
-
-       /*
-        * Typically, we think of a directory rename as all files from a
-        * certain directory being moved to a target directory.  However,
-        * what if someone first moved two files from the original
-        * directory in one commit, and then renamed the directory
-        * somewhere else in a later commit?  At merge time, we just know
-        * that files from the original directory went to two different
-        * places, and that the bulk of them ended up in the same place.
-        * We want each directory rename to represent where the bulk of the
-        * files from that directory end up; this function exists to find
-        * where the bulk of the files went.
-        *
-        * The first loop below simply iterates through the list of file
-        * renames, finding out how often each directory rename pair
-        * possibility occurs.
-        */
-       dir_renames = xmalloc(sizeof(struct hashmap));
-       dir_rename_init(dir_renames);
-       for (i = 0; i < pairs->nr; ++i) {
-               struct string_list_item *item;
-               int *count;
-               struct diff_filepair *pair = pairs->queue[i];
-               char *old_dir, *new_dir;
-
-               /* File not part of directory rename if it wasn't renamed */
-               if (pair->status != 'R')
-                       continue;
-
-               get_renamed_dir_portion(pair->one->path, pair->two->path,
-                                       &old_dir,        &new_dir);
-               if (!old_dir)
-                       /* Directory didn't change at all; ignore this one. */
-                       continue;
-
-               entry = dir_rename_find_entry(dir_renames, old_dir);
-               if (!entry) {
-                       entry = xmalloc(sizeof(struct dir_rename_entry));
-                       dir_rename_entry_init(entry, old_dir);
-                       hashmap_put(dir_renames, entry);
-               } else {
-                       free(old_dir);
-               }
-               item = string_list_lookup(&entry->possible_new_dirs, new_dir);
-               if (!item) {
-                       item = string_list_insert(&entry->possible_new_dirs,
-                                                 new_dir);
-                       item->util = xcalloc(1, sizeof(int));
-               } else {
-                       free(new_dir);
-               }
-               count = item->util;
-               *count += 1;
-       }
-
-       /*
-        * For each directory with files moved out of it, we find out which
-        * target directory received the most files so we can declare it to
-        * be the "winning" target location for the directory rename.  This
-        * winner gets recorded in new_dir.  If there is no winner
-        * (multiple target directories received the same number of files),
-        * we set non_unique_new_dir.  Once we've determined the winner (or
-        * that there is no winner), we no longer need possible_new_dirs.
-        */
-       hashmap_iter_init(dir_renames, &iter);
-       while ((entry = hashmap_iter_next(&iter))) {
-               int max = 0;
-               int bad_max = 0;
-               char *best = NULL;
-
-               for (i = 0; i < entry->possible_new_dirs.nr; i++) {
-                       int *count = entry->possible_new_dirs.items[i].util;
-
-                       if (*count == max)
-                               bad_max = max;
-                       else if (*count > max) {
-                               max = *count;
-                               best = entry->possible_new_dirs.items[i].string;
-                       }
-               }
-               if (bad_max == max)
-                       entry->non_unique_new_dir = 1;
-               else {
-                       assert(entry->new_dir.len == 0);
-                       strbuf_addstr(&entry->new_dir, best);
-               }
-               /*
-                * The relevant directory sub-portion of the original full
-                * filepaths were xstrndup'ed before inserting into
-                * possible_new_dirs, and instead of manually iterating the
-                * list and free'ing each, just lie and tell
-                * possible_new_dirs that it did the strdup'ing so that it
-                * will free them for us.
-                */
-               entry->possible_new_dirs.strdup_strings = 1;
-               string_list_clear(&entry->possible_new_dirs, 1);
-       }
-
-       return dir_renames;
-}
-
-static struct dir_rename_entry *check_dir_renamed(const char *path,
-                                                 struct hashmap *dir_renames)
-{
-       char temp[PATH_MAX];
-       char *end;
-       struct dir_rename_entry *entry;
-
-       strcpy(temp, path);
-       while ((end = strrchr(temp, '/'))) {
-               *end = '\0';
-               entry = dir_rename_find_entry(dir_renames, temp);
-               if (entry)
-                       return entry;
-       }
-       return NULL;
-}
-
-static void compute_collisions(struct hashmap *collisions,
-                              struct hashmap *dir_renames,
-                              struct diff_queue_struct *pairs)
-{
-       int i;
-
-       /*
-        * Multiple files can be mapped to the same path due to directory
-        * renames done by the other side of history.  Since that other
-        * side of history could have merged multiple directories into one,
-        * if our side of history added the same file basename to each of
-        * those directories, then all N of them would get implicitly
-        * renamed by the directory rename detection into the same path,
-        * and we'd get an add/add/.../add conflict, and all those adds
-        * from *this* side of history.  This is not representable in the
-        * index, and users aren't going to easily be able to make sense of
-        * it.  So we need to provide a good warning about what's
-        * happening, and fall back to no-directory-rename detection
-        * behavior for those paths.
-        *
-        * See testcases 9e and all of section 5 from t6043 for examples.
-        */
-       collision_init(collisions);
-
-       for (i = 0; i < pairs->nr; ++i) {
-               struct dir_rename_entry *dir_rename_ent;
-               struct collision_entry *collision_ent;
-               char *new_path;
-               struct diff_filepair *pair = pairs->queue[i];
-
-               if (pair->status == 'D')
-                       continue;
-               dir_rename_ent = check_dir_renamed(pair->two->path,
-                                                  dir_renames);
-               if (!dir_rename_ent)
-                       continue;
-
-               new_path = apply_dir_rename(dir_rename_ent, pair->two->path);
-               if (!new_path)
-                       /*
-                        * dir_rename_ent->non_unique_new_path is true, which
-                        * means there is no directory rename for us to use,
-                        * which means it won't cause us any additional
-                        * collisions.
-                        */
-                       continue;
-               collision_ent = collision_find_entry(collisions, new_path);
-               if (!collision_ent) {
-                       collision_ent = xcalloc(1,
-                                               sizeof(struct collision_entry));
-                       hashmap_entry_init(collision_ent, strhash(new_path));
-                       hashmap_put(collisions, collision_ent);
-                       collision_ent->target_file = new_path;
-               } else {
-                       free(new_path);
-               }
-               string_list_insert(&collision_ent->source_files,
-                                  pair->two->path);
-       }
-}
-
-/*
- * Get information of all renames which occurred in 'pairs', making use of
- * any implicit directory renames inferred from the other side of history.
- * We need the three trees in the merge ('o_tree', 'a_tree' and 'b_tree')
- * to be able to associate the correct cache entries with the rename
- * information; tree is always equal to either a_tree or b_tree.
- */
-static struct string_list *get_renames(struct merge_options *o,
-                                      struct diff_queue_struct *pairs,
-                                      struct hashmap *dir_renames,
-                                      struct tree *tree,
-                                      struct tree *o_tree,
-                                      struct tree *a_tree,
-                                      struct tree *b_tree,
-                                      struct string_list *entries)
-{
-       int i;
-       struct hashmap collisions;
-       struct hashmap_iter iter;
-       struct collision_entry *e;
-       struct string_list *renames;
-
-       compute_collisions(&collisions, dir_renames, pairs);
-       renames = xcalloc(1, sizeof(struct string_list));
-
-       for (i = 0; i < pairs->nr; ++i) {
-               struct string_list_item *item;
-               struct rename *re;
-               struct diff_filepair *pair = pairs->queue[i];
-
-               if (pair->status != 'R') {
-                       diff_free_filepair(pair);
-                       continue;
-               }
-               re = xmalloc(sizeof(*re));
-               re->processed = 0;
-               re->pair = pair;
-               item = string_list_lookup(entries, re->pair->one->path);
-               if (!item)
-                       re->src_entry = insert_stage_data(re->pair->one->path,
-                                       o_tree, a_tree, b_tree, entries);
-               else
-                       re->src_entry = item->util;
-
-               item = string_list_lookup(entries, re->pair->two->path);
-               if (!item)
-                       re->dst_entry = insert_stage_data(re->pair->two->path,
-                                       o_tree, a_tree, b_tree, entries);
-               else
-                       re->dst_entry = item->util;
-               item = string_list_insert(renames, pair->one->path);
-               item->util = re;
-       }
-
-       hashmap_iter_init(&collisions, &iter);
-       while ((e = hashmap_iter_next(&iter))) {
-               free(e->target_file);
-               string_list_clear(&e->source_files, 0);
-       }
-       hashmap_free(&collisions, 1);
-       return renames;
-}
-
 static int process_renames(struct merge_options *o,
                           struct string_list *a_renames,
                           struct string_list *b_renames)
@@ -2107,98 +1645,6 @@ static int process_renames(struct merge_options *o,
        return clean_merge;
 }
 
-struct rename_info {
-       struct string_list *head_renames;
-       struct string_list *merge_renames;
-};
-
-static void initial_cleanup_rename(struct diff_queue_struct *pairs,
-                                  struct hashmap *dir_renames)
-{
-       struct hashmap_iter iter;
-       struct dir_rename_entry *e;
-
-       hashmap_iter_init(dir_renames, &iter);
-       while ((e = hashmap_iter_next(&iter))) {
-               free(e->dir);
-               strbuf_release(&e->new_dir);
-               /* possible_new_dirs already cleared in get_directory_renames */
-       }
-       hashmap_free(dir_renames, 1);
-       free(dir_renames);
-
-       free(pairs->queue);
-       free(pairs);
-}
-
-static int handle_renames(struct merge_options *o,
-                         struct tree *common,
-                         struct tree *head,
-                         struct tree *merge,
-                         struct string_list *entries,
-                         struct rename_info *ri)
-{
-       struct diff_queue_struct *head_pairs, *merge_pairs;
-       struct hashmap *dir_re_head, *dir_re_merge;
-       int clean;
-
-       ri->head_renames = NULL;
-       ri->merge_renames = NULL;
-
-       if (!o->detect_rename)
-               return 1;
-
-       head_pairs = get_diffpairs(o, common, head);
-       merge_pairs = get_diffpairs(o, common, merge);
-
-       dir_re_head = get_directory_renames(head_pairs, head);
-       dir_re_merge = get_directory_renames(merge_pairs, merge);
-
-       handle_directory_level_conflicts(o,
-                                        dir_re_head, head,
-                                        dir_re_merge, merge);
-
-       ri->head_renames  = get_renames(o, head_pairs,
-                                       dir_re_merge, head,
-                                       common, head, merge, entries);
-       ri->merge_renames = get_renames(o, merge_pairs,
-                                       dir_re_head, merge,
-                                        common, head, merge, entries);
-       clean = process_renames(o, ri->head_renames, ri->merge_renames);
-
-       /*
-        * Some cleanup is deferred until cleanup_renames() because the
-        * data structures are still needed and referenced in
-        * process_entry().  But there are a few things we can free now.
-        */
-       initial_cleanup_rename(head_pairs, dir_re_head);
-       initial_cleanup_rename(merge_pairs, dir_re_merge);
-
-       return clean;
-}
-
-static void final_cleanup_rename(struct string_list *rename)
-{
-       const struct rename *re;
-       int i;
-
-       if (rename == NULL)
-               return;
-
-       for (i = 0; i < rename->nr; i++) {
-               re = rename->items[i].util;
-               diff_free_filepair(re->pair);
-       }
-       string_list_clear(rename, 1);
-       free(rename);
-}
-
-static void final_cleanup_renames(struct rename_info *re_info)
-{
-       final_cleanup_rename(re_info->head_renames);
-       final_cleanup_rename(re_info->merge_renames);
-}
-
 static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
 {
        return (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;
@@ -2210,7 +1656,7 @@ static int read_oid_strbuf(struct merge_options *o,
        void *buf;
        enum object_type type;
        unsigned long size;
-       buf = read_sha1_file(oid->hash, &type, &size);
+       buf = read_object_file(oid, &type, &size);
        if (!buf)
                return err(o, _("cannot read object %s"), oid_to_hex(oid));
        if (type != OBJ_BLOB) {
@@ -2558,8 +2004,7 @@ int merge_trees(struct merge_options *o,
        }
 
        if (unmerged_cache()) {
-               struct string_list *entries;
-               struct rename_info re_info;
+               struct string_list *entries, *re_head, *re_merge;
                int i;
                /*
                 * Only need the hashmap while processing entries, so
@@ -2573,8 +2018,9 @@ int merge_trees(struct merge_options *o,
                get_files_dirs(o, merge);
 
                entries = get_unmerged();
-               clean = handle_renames(o, common, head, merge, entries,
-                                      &re_info);
+               re_head  = get_renames(o, head, common, head, merge, entries);
+               re_merge = get_renames(o, merge, common, head, merge, entries);
+               clean = process_renames(o, re_head, re_merge);
                record_df_conflict_files(o, entries);
                if (clean < 0)
                        goto cleanup;
@@ -2599,13 +2045,16 @@ int merge_trees(struct merge_options *o,
                }
 
 cleanup:
-               final_cleanup_renames(&re_info);
-
+               string_list_clear(re_merge, 0);
+               string_list_clear(re_head, 0);
                string_list_clear(entries, 1);
-               free(entries);
 
                hashmap_free(&o->current_file_dir_set, 1);
 
+               free(re_merge);
+               free(re_head);
+               free(entries);
+
                if (clean < 0)
                        return clean;
        }
@@ -2641,7 +2090,7 @@ int merge_recursive(struct merge_options *o,
 {
        struct commit_list *iter;
        struct commit *merged_common_ancestors;
-       struct tree *mrtree = mrtree;
+       struct tree *mrtree;
        int clean;
 
        if (show(o, 4)) {
@@ -2769,11 +2218,13 @@ int merge_recursive_generic(struct merge_options *o,
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
-       if (clean < 0)
+       if (clean < 0) {
+               rollback_lock_file(&lock);
                return clean;
+       }
 
-       if (active_cache_changed &&
-           write_locked_index(&the_index, &lock, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock,
+                              COMMIT_LOCK | SKIP_IF_UNCHANGED))
                return err(o, _("Unable to write index."));
 
        return clean ? 0 : 1;