Deprecate support for .git/info/grafts
[gitweb.git] / merge-recursive.c
index 7d3c4f4151cf2ce1e31ffbb8d4f7d1620556c573..0c0d48624da1d6162f0804e4a105841d965bb2c2 100644 (file)
@@ -49,44 +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 void flush_output(struct merge_options *o)
 {
        if (o->buffer_output < 2 && o->obuf.len) {
@@ -266,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)
@@ -373,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)
 {
@@ -408,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;
@@ -575,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,
@@ -811,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) {
@@ -997,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);
 
@@ -1014,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");
        }
@@ -1349,256 +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 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 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;
-}
-
-/*
- * 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 tree *tree,
-                                      struct tree *o_tree,
-                                      struct tree *a_tree,
-                                      struct tree *b_tree,
-                                      struct string_list *entries)
-{
-       int i;
-       struct string_list *renames;
-
-       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;
-       }
-       return renames;
-}
-
 static int process_renames(struct merge_options *o,
                           struct string_list *a_renames,
                           struct string_list *b_renames)
@@ -1854,92 +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);
-
-       ri->head_renames  = get_renames(o, head_pairs, head,
-                                        common, head, merge, entries);
-       ri->merge_renames = get_renames(o, merge_pairs, 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;
@@ -1951,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) {
@@ -2299,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
@@ -2314,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;
@@ -2340,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;
        }
@@ -2382,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)) {
@@ -2510,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;