summaryrefslogtreecommitdiff
path: root/merge-recursive.c
diff options
context:
space:
mode:
Diffstat (limited to 'merge-recursive.c')
-rw-r--r--merge-recursive.c192
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