diff options
Diffstat (limited to 'merge-recursive.c')
-rw-r--r-- | merge-recursive.c | 192 |
1 files changed, 91 insertions, 101 deletions
diff --git a/merge-recursive.c b/merge-recursive.c index 1b5c255918..acc2f64a4e 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -27,6 +27,7 @@ #include "dir.h" #include "submodule.h" #include "revision.h" +#include "commit-reach.h" struct path_hashmap_entry { struct hashmap_entry e; @@ -156,7 +157,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two, shift_tree_by(&one->object.oid, &two->object.oid, &shifted, subtree_shift); } - if (!oidcmp(&two->object.oid, &shifted)) + if (oideq(&two->object.oid, &shifted)) return two; return lookup_tree(the_repository, &shifted); } @@ -179,7 +180,7 @@ static int oid_eq(const struct object_id *a, const struct object_id *b) { if (!a && !b) return 2; - return a && b && oidcmp(a, b) == 0; + return a && b && oideq(a, b); } enum rename_type { @@ -227,7 +228,26 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type, struct stage_data *src_entry1, struct stage_data *src_entry2) { - struct rename_conflict_info *ci = xcalloc(1, sizeof(struct rename_conflict_info)); + struct rename_conflict_info *ci; + + /* + * When we have two renames involved, it's easiest to get the + * correct things into stage 2 and 3, and to make sure that the + * content merge puts HEAD before the other branch if we just + * ensure that branch1 == o->branch1. So, simply flip arguments + * around if we don't have that. + */ + if (dst_entry2 && branch1 != o->branch1) { + setup_rename_conflict_info(rename_type, + pair2, pair1, + branch2, branch1, + dst_entry2, dst_entry1, + o, + src_entry2, src_entry1); + return; + } + + ci = xcalloc(1, sizeof(struct rename_conflict_info)); ci->rename_type = rename_type; ci->pair1 = pair1; ci->branch1 = branch1; @@ -1083,7 +1103,8 @@ static int merge_3way(struct merge_options *o, read_mmblob(&src2, &b->oid); merge_status = ll_merge(result_buf, a->path, &orig, base_name, - &src1, name1, &src2, name2, &ll_opts); + &src1, name1, &src2, name2, + &the_index, &ll_opts); free(base_name); free(name1); @@ -1114,7 +1135,7 @@ static int find_first_merges(struct object_array *result, const char *path, /* get all revisions that merge commit a */ xsnprintf(merged_revision, sizeof(merged_revision), "^%s", oid_to_hex(&a->object.oid)); - init_revisions(&revs, NULL); + repo_init_revisions(the_repository, &revs, NULL); rev_opts.submodule = path; /* FIXME: can't handle linked worktrees in submodules yet */ revs.single_worktree = path != NULL; @@ -1273,15 +1294,26 @@ static int merge_submodule(struct merge_options *o, return 0; } -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 *filename, - const char *branch1, - const char *branch2, - struct merge_file_info *result) +static int merge_mode_and_contents(struct merge_options *o, + 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) { + if (o->branch1 != branch1) { + /* + * It's weird getting a reverse merge with HEAD on the bottom + * side of the conflict markers and the other branch on the + * top. Fix that. + */ + return merge_mode_and_contents(o, one, b, a, + filename, + branch2, branch1, result); + } + result->merge = 0; result->clean = 1; @@ -1365,56 +1397,6 @@ static int merge_file_1(struct merge_options *o, 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 *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; - int ret; - - if (filename1) - side1 = xstrfmt("%s:%s", branch1, filename1); - if (filename2) - side2 = xstrfmt("%s:%s", branch2, filename2); - - 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) -{ - struct diff_filespec one, a, b; - - one.path = a.path = b.path = (char *)path; - oidcpy(&one.oid, o_oid); - one.mode = o_mode; - oidcpy(&a.oid, a_oid); - a.mode = a_mode; - oidcpy(&b.oid, b_oid); - b.mode = b_mode; - return merge_file_1(o, &one, &a, &b, path, branch1, branch2, mfi); -} - static int handle_rename_via_dir(struct merge_options *o, struct diff_filepair *pair, const char *rename_branch, @@ -1658,11 +1640,8 @@ static int handle_rename_rename_1to2(struct merge_options *o, struct merge_file_info mfi; struct diff_filespec other; struct diff_filespec *add; - if (merge_file_one(o, one->path, - &one->oid, one->mode, - &a->oid, a->mode, - &b->oid, b->mode, - ci->branch1, ci->branch2, &mfi)) + if (merge_mode_and_contents(o, one, a, b, one->path, + ci->branch1, ci->branch2, &mfi)) return -1; /* @@ -1726,16 +1705,12 @@ static int handle_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)) + path_side_1_desc = xstrfmt("version of %s from %s", path, a->path); + path_side_2_desc = xstrfmt("version of %s from %s", path, b->path); + if (merge_mode_and_contents(o, a, c1, &ci->ren1_other, path_side_1_desc, + o->branch1, o->branch2, &mfi_c1) || + merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc, + o->branch1, o->branch2, &mfi_c2)) return -1; free(path_side_1_desc); free(path_side_2_desc); @@ -1812,7 +1787,7 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o, struct diff_queue_struct *ret; struct diff_options opts; - diff_setup(&opts); + repo_diff_setup(the_repository, &opts); opts.flags.recursive = 1; opts.flags.rename_empty = 0; opts.detect_rename = merge_detect_rename(o); @@ -2765,12 +2740,23 @@ static int process_renames(struct merge_options *o, ren1_dst, branch2); if (o->call_depth) { struct merge_file_info mfi; - if (merge_file_one(o, ren1_dst, &null_oid, 0, - &ren1->pair->two->oid, - ren1->pair->two->mode, - &dst_other.oid, - dst_other.mode, - branch1, branch2, &mfi)) { + struct diff_filespec one, a, b; + + oidcpy(&one.oid, &null_oid); + one.mode = 0; + one.path = ren1->pair->two->path; + + oidcpy(&a.oid, &ren1->pair->two->oid); + a.mode = ren1->pair->two->mode; + a.path = one.path; + + oidcpy(&b.oid, &dst_other.oid); + b.mode = dst_other.mode; + b.path = one.path; + + if (merge_mode_and_contents(o, &one, &a, &b, ren1_dst, + branch1, branch2, + &mfi)) { clean_merge = -1; goto cleanup_and_return; } @@ -3020,13 +3006,13 @@ static int handle_modify_delete(struct merge_options *o, _("modify"), _("modified")); } -static int merge_content(struct merge_options *o, - const char *path, - int is_dirty, - struct object_id *o_oid, int o_mode, - struct object_id *a_oid, int a_mode, - struct object_id *b_oid, int b_mode, - struct rename_conflict_info *rename_conflict_info) +static int handle_content_merge(struct merge_options *o, + const char *path, + int is_dirty, + struct object_id *o_oid, int o_mode, + struct object_id *a_oid, int a_mode, + struct object_id *b_oid, int b_mode, + struct rename_conflict_info *rename_conflict_info) { const char *reason = _("content"); const char *path1 = NULL, *path2 = NULL; @@ -3058,14 +3044,16 @@ static int merge_content(struct merge_options *o, path2 = (rename_conflict_info->pair2 || o->branch2 == rename_conflict_info->branch1) ? pair1->two->path : pair1->one->path; + one.path = pair1->one->path; + a.path = (char *)path1; + b.path = (char *)path2; if (dir_in_way(path, !o->call_depth, S_ISGITLINK(pair1->two->mode))) df_conflict_remains = 1; } - if (merge_file_special_markers(o, &one, &a, &b, path, - o->branch1, path1, - o->branch2, path2, &mfi)) + if (merge_mode_and_contents(o, &one, &a, &b, path, + o->branch1, o->branch2, &mfi)) return -1; /* @@ -3156,9 +3144,9 @@ static int handle_rename_normal(struct merge_options *o, struct rename_conflict_info *ci) { /* Merge the content and write it out */ - return merge_content(o, path, was_dirty(o, path), - o_oid, o_mode, a_oid, a_mode, b_oid, b_mode, - ci); + return handle_content_merge(o, path, was_dirty(o, path), + o_oid, o_mode, a_oid, a_mode, b_oid, b_mode, + ci); } /* Per entry merge function */ @@ -3282,9 +3270,11 @@ static int process_entry(struct merge_options *o, /* Case C: Added in both (check for same permissions) and */ /* case D: Modified in both, but differently. */ int is_dirty = 0; /* unpack_trees would have bailed if dirty */ - clean_merge = merge_content(o, path, is_dirty, - o_oid, o_mode, a_oid, a_mode, b_oid, b_mode, - NULL); + clean_merge = handle_content_merge(o, path, is_dirty, + o_oid, o_mode, + a_oid, a_mode, + b_oid, b_mode, + NULL); } else if (!o_oid && !a_oid && !b_oid) { /* * this entry was deleted altogether. a_mode == 0 means |