merge-recursive: fix numerous argument alignment issues
[gitweb.git] / merge-recursive.c
index ddb0fa73698122542e727befa7fbedf47b9226bf..09c249b93e45e197513f55d8a206535ecf89d750 100644 (file)
@@ -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;
@@ -162,7 +163,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;
 }
@@ -308,8 +309,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
 }
 
 static int add_cacheinfo(struct merge_options *o,
-               unsigned int mode, const struct object_id *oid,
-               const char *path, int stage, int refresh, int options)
+                        unsigned int mode, const struct object_id *oid,
+                        const char *path, int stage, int refresh, int options)
 {
        struct cache_entry *ce;
        int ret;
@@ -382,6 +383,7 @@ static int unpack_trees_start(struct merge_options *o,
 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)
@@ -397,7 +399,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)
@@ -415,8 +417,8 @@ struct tree *write_tree_from_memory(struct merge_options *o)
 }
 
 static int save_files_dirs(const struct object_id *oid,
-               struct strbuf *base, const char *path,
-               unsigned int mode, int stage, void *context)
+                          struct strbuf *base, const char *path,
+                          unsigned int mode, int stage, void *context)
 {
        struct path_hashmap_entry *entry;
        int baselen = base->len;
@@ -537,7 +539,7 @@ static void record_df_conflict_files(struct merge_options *o,
                                     struct string_list *entries)
 {
        /* If there is a D/F conflict and the file for such a conflict
-        * currently exist in the working tree, we want to allow it to be
+        * currently exists in the working tree, we want to allow it to be
         * removed to make room for the corresponding directory if needed.
         * The files underneath the directories of such D/F conflicts will
         * be processed before the corresponding file involved in the D/F
@@ -911,7 +913,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)
         */
        if (would_lose_untracked(path))
                return err(o, _("refusing to lose untracked file at '%s'"),
-                            path);
+                          path);
 
        /* Successful unlink is good.. */
        if (!unlink(path))
@@ -990,7 +992,7 @@ static int update_file_flags(struct merge_options *o,
                        unlink(path);
                        if (symlink(lnk, path))
                                ret = err(o, _("failed to symlink '%s': %s"),
-                                       path, strerror(errno));
+                                         path, strerror(errno));
                        free(lnk);
                } else
                        ret = err(o,
@@ -1087,6 +1089,185 @@ static int merge_3way(struct merge_options *o,
        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,
@@ -1150,12 +1331,11 @@ static int merge_file_1(struct merge_options *o,
                                return ret;
                        result->clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result->clean = merge_submodule(&result->oid,
-                                                      one->path,
-                                                      &one->oid,
-                                                      &a->oid,
-                                                      &b->oid,
-                                                      !o->call_depth);
+                       result->clean = merge_submodule(o, &result->oid,
+                                                       one->path,
+                                                       &one->oid,
+                                                       &a->oid,
+                                                       &b->oid);
                } else if (S_ISLNK(a->mode)) {
                        switch (o->recursive_variant) {
                        case MERGE_RECURSIVE_NORMAL:
@@ -1171,7 +1351,7 @@ 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)
@@ -1263,13 +1443,13 @@ static int conflict_rename_dir(struct merge_options *o,
 }
 
 static int handle_change_delete(struct merge_options *o,
-                                const char *path, const char *old_path,
-                                const struct object_id *o_oid, int o_mode,
-                                const struct object_id *changed_oid,
-                                int changed_mode,
-                                const char *change_branch,
-                                const char *delete_branch,
-                                const char *change, const char *change_past)
+                               const char *path, const char *old_path,
+                               const struct object_id *o_oid, int o_mode,
+                               const struct object_id *changed_oid,
+                               int changed_mode,
+                               const char *change_branch,
+                               const char *delete_branch,
+                               const char *change, const char *change_past)
 {
        char *alt_path = NULL;
        const char *update_path = path;
@@ -1330,9 +1510,9 @@ static int handle_change_delete(struct merge_options *o,
 }
 
 static int conflict_rename_delete(struct merge_options *o,
-                                  struct diff_filepair *pair,
-                                  const char *rename_branch,
-                                  const char *delete_branch)
+                                 struct diff_filepair *pair,
+                                 const char *rename_branch,
+                                 const char *delete_branch)
 {
        const struct diff_filespec *orig = pair->one;
        const struct diff_filespec *dest = pair->two;
@@ -1435,7 +1615,7 @@ static int handle_file(struct merge_options *o,
 }
 
 static int conflict_rename_rename_1to2(struct merge_options *o,
-                                       struct rename_conflict_info *ci)
+                                      struct rename_conflict_info *ci)
 {
        /* One file was renamed in both branches, but to different names. */
        struct diff_filespec *one = ci->pair1->one;
@@ -1497,7 +1677,7 @@ static int conflict_rename_rename_1to2(struct merge_options *o,
 }
 
 static int conflict_rename_rename_2to1(struct merge_options *o,
-                                       struct rename_conflict_info *ci)
+                                      struct rename_conflict_info *ci)
 {
        /* Two files, a & b, were renamed to the same thing, c. */
        struct diff_filespec *a = ci->pair1->one;
@@ -2424,7 +2604,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) {
@@ -2458,7 +2638,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;
@@ -2724,7 +2904,8 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
 }
 
 static int read_oid_strbuf(struct merge_options *o,
-       const struct object_id *oid, struct strbuf *dst)
+                          const struct object_id *oid,
+                          struct strbuf *dst)
 {
        void *buf;
        enum object_type type;
@@ -2777,10 +2958,10 @@ static int blob_unchanged(struct merge_options *opt,
 }
 
 static int handle_modify_delete(struct merge_options *o,
-                                const char *path,
-                                struct object_id *o_oid, int o_mode,
-                                struct object_id *a_oid, int a_mode,
-                                struct object_id *b_oid, int b_mode)
+                               const char *path,
+                               struct object_id *o_oid, int o_mode,
+                               struct object_id *a_oid, int a_mode,
+                               struct object_id *b_oid, int b_mode)
 {
        const char *modify_branch, *delete_branch;
        struct object_id *changed_oid;
@@ -3062,7 +3243,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;
 }
@@ -3141,7 +3322,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);
                }
 
@@ -3256,7 +3437,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);
@@ -3312,14 +3494,14 @@ int merge_recursive_generic(struct merge_options *o,
                        struct commit *base;
                        if (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))
                                return err(o, _("Could not parse object '%s'"),
-                                       oid_to_hex(base_list[i]));
+                                          oid_to_hex(base_list[i]));
                        commit_list_insert(base, &ca);
                }
        }
 
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
-                       result);
+                               result);
        if (clean < 0) {
                rollback_lock_file(&lock);
                return clean;