completion: fix __gitcomp_builtin no longer consider extra options
[gitweb.git] / merge-recursive.c
index 9015be55be9ac67bde9aad7419b5c9d837ecf5e3..35df695fa420073d04997a3792f1c3a45d278b5d 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;
 }
@@ -779,6 +779,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 +1082,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;
@@ -1148,18 +1168,22 @@ static int merge_file_1(struct merge_options *o,
                        die("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 +1194,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 +1221,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 +1499,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 +1514,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) {
                /*
@@ -2802,30 +2835,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))
@@ -3217,7 +3246,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);