diff options
-rw-r--r-- | merge-recursive.c | 104 |
1 files changed, 62 insertions, 42 deletions
diff --git a/merge-recursive.c b/merge-recursive.c index 8020d4b51..ccb934356 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -67,7 +67,8 @@ enum rename_type { RENAME_NORMAL = 0, RENAME_DELETE, RENAME_ONE_FILE_TO_ONE, - RENAME_ONE_FILE_TO_TWO + RENAME_ONE_FILE_TO_TWO, + RENAME_TWO_FILES_TO_ONE }; struct rename_conflict_info { @@ -1019,32 +1020,40 @@ static void conflict_rename_rename_1to2(struct merge_options *o, } static void conflict_rename_rename_2to1(struct merge_options *o, - struct rename *ren1, - const char *branch1, - struct rename *ren2, - const char *branch2) + struct rename_conflict_info *ci) { - char *path = ren1->pair->two->path; /* same as ren2->pair->two->path */ - /* Two files were renamed to the same thing. */ + /* Two files, a & b, were renamed to the same thing, c. */ + struct diff_filespec *a = ci->pair1->one; + struct diff_filespec *b = ci->pair2->one; + struct diff_filespec *c1 = ci->pair1->two; + struct diff_filespec *c2 = ci->pair2->two; + char *path = c1->path; /* == c2->path */ + + output(o, 1, "CONFLICT (rename/rename): " + "Rename %s->%s in %s. " + "Rename %s->%s in %s", + a->path, c1->path, ci->branch1, + b->path, c2->path, ci->branch2); + + remove_file(o, 1, a->path, would_lose_untracked(a->path)); + remove_file(o, 1, b->path, would_lose_untracked(b->path)); + if (o->call_depth) { struct merge_file_info mfi; mfi = merge_file(o, path, null_sha1, 0, - ren1->pair->two->sha1, ren1->pair->two->mode, - ren2->pair->two->sha1, ren2->pair->two->mode, - branch1, branch2); + c1->sha1, c1->mode, + c2->sha1, c2->mode, + ci->branch1, ci->branch2); output(o, 1, "Adding merged %s", path); update_file(o, 0, mfi.sha, mfi.mode, path); } else { - char *new_path1 = unique_path(o, path, branch1); - char *new_path2 = unique_path(o, path, branch2); + char *new_path1 = unique_path(o, path, ci->branch1); + char *new_path2 = unique_path(o, path, ci->branch2); output(o, 1, "Renaming %s to %s and %s to %s instead", - ren1->pair->one->path, new_path1, - ren2->pair->one->path, new_path2); + a->path, new_path1, b->path, new_path2); remove_file(o, 0, path, 0); - update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, - new_path1); - update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, - new_path2); + update_file(o, 0, c1->sha1, c1->mode, new_path1); + update_file(o, 0, c2->sha1, c2->mode, new_path2); free(new_path2); free(new_path1); } @@ -1075,6 +1084,7 @@ static int process_renames(struct merge_options *o, struct rename *ren1 = NULL, *ren2 = NULL; const char *branch1, *branch2; const char *ren1_src, *ren1_dst; + struct string_list_item *lookup; if (i >= a_renames->nr) { ren2 = b_renames->items[j++].util; @@ -1106,30 +1116,30 @@ static int process_renames(struct merge_options *o, ren1 = tmp; } + if (ren1->processed) + continue; + ren1->processed = 1; ren1->dst_entry->processed = 1; /* BUG: We should only mark src_entry as processed if we * are not dealing with a rename + add-source case. */ ren1->src_entry->processed = 1; - if (ren1->processed) - continue; - ren1->processed = 1; - ren1_src = ren1->pair->one->path; ren1_dst = ren1->pair->two->path; if (ren2) { + /* One file renamed on both sides */ const char *ren2_src = ren2->pair->one->path; const char *ren2_dst = ren2->pair->two->path; enum rename_type rename_type; - /* Renamed in 1 and renamed in 2 */ if (strcmp(ren1_src, ren2_src) != 0) - die("ren1.src != ren2.src"); + die("ren1_src != ren2_src"); ren2->dst_entry->processed = 1; ren2->processed = 1; if (strcmp(ren1_dst, ren2_dst) != 0) { rename_type = RENAME_ONE_FILE_TO_TWO; + clean_merge = 0; } else { rename_type = RENAME_ONE_FILE_TO_ONE; /* BUG: We should only remove ren1_src in @@ -1149,9 +1159,32 @@ static int process_renames(struct merge_options *o, branch2, ren1->dst_entry, ren2->dst_entry); + } else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) { + /* Two different files renamed to the same thing */ + char *ren2_dst; + ren2 = lookup->util; + ren2_dst = ren2->pair->two->path; + if (strcmp(ren1_dst, ren2_dst) != 0) + die("ren1_dst != ren2_dst"); + + clean_merge = 0; + ren2->processed = 1; + /* + * BUG: We should only mark src_entry as processed + * if we are not dealing with a rename + add-source + * case. + */ + ren2->src_entry->processed = 1; + + setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE, + ren1->pair, + ren2->pair, + branch1, + branch2, + ren1->dst_entry, + ren2->dst_entry); } else { /* Renamed in 1, maybe changed in 2 */ - struct string_list_item *item; /* we only use sha1 and mode of these */ struct diff_filespec src_other, dst_other; int try_merge; @@ -1186,23 +1219,6 @@ static int process_renames(struct merge_options *o, branch2, ren1->dst_entry, NULL); - } else if ((item = string_list_lookup(renames2Dst, ren1_dst))) { - char *ren2_src, *ren2_dst; - ren2 = item->util; - ren2_src = ren2->pair->one->path; - ren2_dst = ren2->pair->two->path; - - clean_merge = 0; - ren2->processed = 1; - remove_file(o, 1, ren2_src, - renamed_stage == 3 || would_lose_untracked(ren1_src)); - - output(o, 1, "CONFLICT (rename/rename): " - "Rename %s->%s in %s. " - "Rename %s->%s in %s", - ren1_src, ren1_dst, branch1, - ren2_src, ren2_dst, branch2); - conflict_rename_rename_2to1(o, ren1, branch1, ren2, branch2); } else if ((dst_other.mode == ren1->pair->two->mode) && sha_eq(dst_other.sha1, ren1->pair->two->sha1)) { /* Added file on the other side @@ -1502,6 +1518,10 @@ static int process_entry(struct merge_options *o, clean_merge = 0; conflict_rename_rename_1to2(o, conflict_info); break; + case RENAME_TWO_FILES_TO_ONE: + clean_merge = 0; + conflict_rename_rename_2to1(o, conflict_info); + break; default: entry->processed = 0; break; |