summaryrefslogtreecommitdiff
path: root/merge-recursive.c
diff options
context:
space:
mode:
authorElijah Newren <newren@gmail.com>2011-08-12 05:20:11 (GMT)
committerJunio C Hamano <gitster@pobox.com>2011-08-14 21:19:38 (GMT)
commitec61d14963470b02db0d6e3825e2b5bbb1815cb6 (patch)
treeeb2925c7839584acf9575b31625b37526e362804 /merge-recursive.c
parent5b448b8530308b1f5a7a721cb1bf0ba557b5c78d (diff)
downloadgit-ec61d14963470b02db0d6e3825e2b5bbb1815cb6.zip
git-ec61d14963470b02db0d6e3825e2b5bbb1815cb6.tar.gz
git-ec61d14963470b02db0d6e3825e2b5bbb1815cb6.tar.bz2
merge-recursive: Fix modify/delete resolution in the recursive case
When o->call_depth>0 and we have conflicts, we try to find "middle ground" when creating the virtual merge base. In the case of content conflicts, this can be done by doing a three-way content merge and using the result. In all parts where the three-way content merge is clean, it is the correct middle ground, and in parts where it conflicts there is no middle ground but the conflict markers provide a good compromise since they are unlikely to accidentally match any further changes. In the case of a modify/delete conflict, we cannot do the same thing. Accepting either endpoint as the resolution for the virtual merge base runs the risk that when handling the non-recursive case we will silently accept one person's resolution over another without flagging a conflict. In this case, the closest "middle ground" we have is actually the merge base of the candidate merge bases. (We could alternatively attempt a three way content merge using an empty file in place of the deleted file, but that seems to be more work than necessary.) Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'merge-recursive.c')
-rw-r--r--merge-recursive.c38
1 files changed, 24 insertions, 14 deletions
diff --git a/merge-recursive.c b/merge-recursive.c
index 8b88d62..897cada 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1322,27 +1322,42 @@ error_return:
static void handle_delete_modify(struct merge_options *o,
const char *path,
- const char *new_path,
+ unsigned char *o_sha, int o_mode,
unsigned char *a_sha, int a_mode,
unsigned char *b_sha, int b_mode)
{
- if (!a_sha) {
+ char *renamed = NULL;
+ if (dir_in_way(path, !o->call_depth)) {
+ renamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2);
+ }
+
+ if (o->call_depth) {
+ /*
+ * We cannot arbitrarily accept either a_sha or b_sha as
+ * correct; since there is no true "middle point" between
+ * them, simply reuse the base version for virtual merge base.
+ */
+ remove_file_from_cache(path);
+ update_file(o, 0, o_sha, o_mode, renamed ? renamed : path);
+ } else if (!a_sha) {
output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
"and modified in %s. Version %s of %s left in tree%s%s.",
path, o->branch1,
o->branch2, o->branch2, path,
- NULL == new_path ? "" : " at ",
- NULL == new_path ? "" : new_path);
- update_file(o, 0, b_sha, b_mode, new_path ? new_path : path);
+ NULL == renamed ? "" : " at ",
+ NULL == renamed ? "" : renamed);
+ update_file(o, 0, b_sha, b_mode, renamed ? renamed : path);
} else {
output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
"and modified in %s. Version %s of %s left in tree%s%s.",
path, o->branch2,
o->branch1, o->branch1, path,
- NULL == new_path ? "" : " at ",
- NULL == new_path ? "" : new_path);
- update_file(o, 0, a_sha, a_mode, new_path ? new_path : path);
+ NULL == renamed ? "" : " at ",
+ NULL == renamed ? "" : renamed);
+ update_file(o, 0, a_sha, a_mode, renamed ? renamed : path);
}
+ free(renamed);
+
}
static int merge_content(struct merge_options *o,
@@ -1512,14 +1527,9 @@ static int process_entry(struct merge_options *o,
remove_file(o, 1, path, !a_sha);
} else {
/* Modify/delete; deleted side may have put a directory in the way */
- char *renamed = NULL;
clean_merge = 0;
- if (dir_in_way(path, !o->call_depth)) {
- renamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2);
- }
- handle_delete_modify(o, path, renamed,
+ handle_delete_modify(o, path, o_sha, o_mode,
a_sha, a_mode, b_sha, b_mode);
- free(renamed);
}
} else if ((!o_sha && a_sha && !b_sha) ||
(!o_sha && !a_sha && b_sha)) {