Merge branch 'bw/ref-prefix-for-configured-refspec'
[gitweb.git] / merge-recursive.c
index 9015be55be9ac67bde9aad7419b5c9d837ecf5e3..e64b4b9d031afff282bf37c7c373d29e11157835 100644 (file)
@@ -162,7 +162,7 @@ static struct commit *make_virtual_commit(struct tree *tree, const char *comment
        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;
 }
@@ -356,6 +356,7 @@ static int git_merge_trees(struct merge_options *o,
        o->unpack_opts.fn = threeway_merge;
        o->unpack_opts.src_index = &the_index;
        o->unpack_opts.dst_index = &tmp_index;
+       o->unpack_opts.aggressive = !merge_detect_rename(o);
        setup_unpack_trees_porcelain(&o->unpack_opts, "merge");
 
        init_tree_desc_from_tree(t+0, common);
@@ -391,7 +392,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
                                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)
@@ -779,6 +780,25 @@ static int dir_in_way(const char *path, int check_working_copy, int empty_ok)
                !(empty_ok && is_empty_dir(path));
 }
 
+/*
+ * Returns whether path was tracked in the index before the merge started,
+ * and its oid and mode match the specified values
+ */
+static int was_tracked_and_matches(struct merge_options *o, const char *path,
+                                  const struct object_id *oid, unsigned mode)
+{
+       int pos = index_name_pos(&o->orig_index, path, strlen(path));
+       struct cache_entry *ce;
+
+       if (0 > pos)
+               /* we were not tracking this path before the merge */
+               return 0;
+
+       /* See if the file we were tracking before matches */
+       ce = o->orig_index.cache[pos];
+       return (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);
+}
+
 /*
  * Returns whether path was tracked in the index before the merge started
  */
@@ -1063,12 +1083,13 @@ static int merge_3way(struct merge_options *o,
 }
 
 static int merge_file_1(struct merge_options *o,
-                                          const struct diff_filespec *one,
-                                          const struct diff_filespec *a,
-                                          const struct diff_filespec *b,
-                                          const char *branch1,
-                                          const char *branch2,
-                                          struct merge_file_info *result)
+                       const struct diff_filespec *one,
+                       const struct diff_filespec *a,
+                       const struct diff_filespec *b,
+                       const char *filename,
+                       const char *branch1,
+                       const char *branch2,
+                       struct merge_file_info *result)
 {
        result->merge = 0;
        result->clean = 1;
@@ -1145,21 +1166,25 @@ static int merge_file_1(struct merge_options *o,
                                break;
                        }
                } else
-                       die("BUG: unsupported object type in the tree");
+                       BUG("unsupported object type in the tree");
        }
 
+       if (result->merge)
+               output(o, 2, _("Auto-merging %s"), filename);
+
        return 0;
 }
 
 static int merge_file_special_markers(struct merge_options *o,
-                          const struct diff_filespec *one,
-                          const struct diff_filespec *a,
-                          const struct diff_filespec *b,
-                          const char *branch1,
-                          const char *filename1,
-                          const char *branch2,
-                          const char *filename2,
-                          struct merge_file_info *mfi)
+                                     const struct diff_filespec *one,
+                                     const struct diff_filespec *a,
+                                     const struct diff_filespec *b,
+                                     const char *target_filename,
+                                     const char *branch1,
+                                     const char *filename1,
+                                     const char *branch2,
+                                     const char *filename2,
+                                     struct merge_file_info *mfi)
 {
        char *side1 = NULL;
        char *side2 = NULL;
@@ -1170,22 +1195,23 @@ static int merge_file_special_markers(struct merge_options *o,
        if (filename2)
                side2 = xstrfmt("%s:%s", branch2, filename2);
 
-       ret = merge_file_1(o, one, a, b,
+       ret = merge_file_1(o, one, a, b, target_filename,
                           side1 ? side1 : branch1,
                           side2 ? side2 : branch2, mfi);
+
        free(side1);
        free(side2);
        return ret;
 }
 
 static int merge_file_one(struct merge_options *o,
-                                        const char *path,
-                                        const struct object_id *o_oid, int o_mode,
-                                        const struct object_id *a_oid, int a_mode,
-                                        const struct object_id *b_oid, int b_mode,
-                                        const char *branch1,
-                                        const char *branch2,
-                                        struct merge_file_info *mfi)
+                         const char *path,
+                         const struct object_id *o_oid, int o_mode,
+                         const struct object_id *a_oid, int a_mode,
+                         const struct object_id *b_oid, int b_mode,
+                         const char *branch1,
+                         const char *branch2,
+                         struct merge_file_info *mfi)
 {
        struct diff_filespec one, a, b;
 
@@ -1196,7 +1222,7 @@ static int merge_file_one(struct merge_options *o,
        a.mode = a_mode;
        oidcpy(&b.oid, b_oid);
        b.mode = b_mode;
-       return merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);
+       return merge_file_1(o, &one, &a, &b, path, branch1, branch2, mfi);
 }
 
 static int conflict_rename_dir(struct merge_options *o,
@@ -1474,6 +1500,8 @@ static int conflict_rename_rename_2to1(struct merge_options *o,
        struct diff_filespec *c1 = ci->pair1->two;
        struct diff_filespec *c2 = ci->pair2->two;
        char *path = c1->path; /* == c2->path */
+       char *path_side_1_desc;
+       char *path_side_2_desc;
        struct merge_file_info mfi_c1;
        struct merge_file_info mfi_c2;
        int ret;
@@ -1487,13 +1515,19 @@ static int conflict_rename_rename_2to1(struct merge_options *o,
        remove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));
        remove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));
 
+       path_side_1_desc = xstrfmt("%s (was %s)", path, a->path);
+       path_side_2_desc = xstrfmt("%s (was %s)", path, b->path);
        if (merge_file_special_markers(o, a, c1, &ci->ren1_other,
+                                      path_side_1_desc,
                                       o->branch1, c1->path,
                                       o->branch2, ci->ren1_other.path, &mfi_c1) ||
            merge_file_special_markers(o, b, &ci->ren2_other, c2,
+                                      path_side_2_desc,
                                       o->branch1, ci->ren2_other.path,
                                       o->branch2, c2->path, &mfi_c2))
                return -1;
+       free(path_side_1_desc);
+       free(path_side_2_desc);
 
        if (o->call_depth) {
                /*
@@ -1570,7 +1604,15 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
        diff_setup(&opts);
        opts.flags.recursive = 1;
        opts.flags.rename_empty = 0;
-       opts.detect_rename = DIFF_DETECT_RENAME;
+       opts.detect_rename = merge_detect_rename(o);
+       /*
+        * We do not have logic to handle the detection of copies.  In
+        * fact, it may not even make sense to add such logic: would we
+        * really want a change to a base file to be propagated through
+        * multiple other files by a merge?
+        */
+       if (opts.detect_rename > DIFF_DETECT_RENAME)
+               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;
@@ -2377,7 +2419,7 @@ static int process_renames(struct merge_options *o,
                        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) {
@@ -2411,7 +2453,7 @@ static int process_renames(struct merge_options *o,
                        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;
@@ -2610,7 +2652,7 @@ static int handle_renames(struct merge_options *o,
        ri->head_renames = NULL;
        ri->merge_renames = NULL;
 
-       if (!o->detect_rename)
+       if (!merge_detect_rename(o))
                return 1;
 
        head_pairs = get_diffpairs(o, common, head);
@@ -2802,30 +2844,26 @@ static int merge_content(struct merge_options *o,
                               S_ISGITLINK(pair1->two->mode)))
                        df_conflict_remains = 1;
        }
-       if (merge_file_special_markers(o, &one, &a, &b,
+       if (merge_file_special_markers(o, &one, &a, &b, path,
                                       o->branch1, path1,
                                       o->branch2, path2, &mfi))
                return -1;
 
-       if (mfi.clean && !df_conflict_remains &&
-           oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {
-               int path_renamed_outside_HEAD;
+       /*
+        * We can skip updating the working tree file iff:
+        *   a) The merge is clean
+        *   b) The merge matches what was in HEAD (content, mode, pathname)
+        *   c) The target path is usable (i.e. not involved in D/F conflict)
+        */
+       if (mfi.clean &&
+           was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) &&
+           !df_conflict_remains) {
                output(o, 3, _("Skipped %s (merged same as existing)"), path);
-               /*
-                * The content merge resulted in the same file contents we
-                * already had.  We can return early if those file contents
-                * are recorded at the correct path (which may not be true
-                * if the merge involves a rename).
-                */
-               path_renamed_outside_HEAD = !path2 || !strcmp(path, path2);
-               if (!path_renamed_outside_HEAD) {
-                       if (add_cacheinfo(o, mfi.mode, &mfi.oid, path,
-                                         0, (!o->call_depth && !is_dirty), 0))
-                               return -1;
-                       return mfi.clean;
-               }
-       } else
-               output(o, 2, _("Auto-merging %s"), path);
+               if (add_cacheinfo(o, mfi.mode, &mfi.oid, path,
+                                 0, (!o->call_depth && !is_dirty), 0))
+                       return -1;
+               return mfi.clean;
+       }
 
        if (!mfi.clean) {
                if (S_ISGITLINK(mfi.mode))
@@ -3019,7 +3057,7 @@ static int process_entry(struct merge_options *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;
 }
@@ -3097,7 +3135,7 @@ int merge_trees(struct merge_options *o,
                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);
                }
 
@@ -3217,7 +3255,8 @@ int merge_recursive(struct merge_options *o,
                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);
@@ -3295,9 +3334,18 @@ int merge_recursive_generic(struct merge_options *o,
 
 static void merge_recursive_config(struct merge_options *o)
 {
+       char *value = NULL;
        git_config_get_int("merge.verbosity", &o->verbosity);
        git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
        git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
+       if (!git_config_get_string("diff.renames", &value)) {
+               o->diff_detect_rename = git_config_rename("diff.renames", value);
+               free(value);
+       }
+       if (!git_config_get_string("merge.renames", &value)) {
+               o->merge_detect_rename = git_config_rename("merge.renames", value);
+               free(value);
+       }
        git_config(git_xmerge_config, NULL);
 }
 
@@ -3310,7 +3358,8 @@ void init_merge_options(struct merge_options *o)
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
        o->renormalize = 0;
-       o->detect_rename = 1;
+       o->diff_detect_rename = -1;
+       o->merge_detect_rename = -1;
        merge_recursive_config(o);
        merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
        if (merge_verbosity)
@@ -3361,16 +3410,16 @@ int parse_merge_opt(struct merge_options *o, const char *s)
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
        else if (!strcmp(s, "no-renames"))
-               o->detect_rename = 0;
+               o->merge_detect_rename = 0;
        else if (!strcmp(s, "find-renames")) {
-               o->detect_rename = 1;
+               o->merge_detect_rename = 1;
                o->rename_score = 0;
        }
        else if (skip_prefix(s, "find-renames=", &arg) ||
                 skip_prefix(s, "rename-threshold=", &arg)) {
                if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
                        return -1;
-               o->detect_rename = 1;
+               o->merge_detect_rename = 1;
        }
        else
                return -1;