зеркало из https://github.com/microsoft/git.git
merge-recursive: improve rename/rename(1to2)/add[/add] handling
When we have a rename/rename(1to2) conflict, each of the renames can collide with a file addition. Each of these rename/add conflicts suffered from the same kinds of problems that normal rename/add suffered from. Make the code use handle_file_conflicts() as well so that we get all the same fixes and consistent behavior between the different conflict types. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Родитель
dcf2815098
Коммит
48c9cb9d6d
|
@ -1709,80 +1709,17 @@ static int handle_rename_add(struct merge_options *o,
|
|||
ci->dst_entry1->stages[other_stage].mode);
|
||||
}
|
||||
|
||||
static int handle_file(struct merge_options *o,
|
||||
struct diff_filespec *rename,
|
||||
int stage,
|
||||
struct rename_conflict_info *ci)
|
||||
{
|
||||
char *dst_name = rename->path;
|
||||
struct stage_data *dst_entry;
|
||||
const char *cur_branch, *other_branch;
|
||||
struct diff_filespec other;
|
||||
struct diff_filespec *add;
|
||||
int ret;
|
||||
|
||||
if (stage == 2) {
|
||||
dst_entry = ci->dst_entry1;
|
||||
cur_branch = ci->branch1;
|
||||
other_branch = ci->branch2;
|
||||
} else {
|
||||
dst_entry = ci->dst_entry2;
|
||||
cur_branch = ci->branch2;
|
||||
other_branch = ci->branch1;
|
||||
}
|
||||
|
||||
add = filespec_from_entry(&other, dst_entry, stage ^ 1);
|
||||
if (add) {
|
||||
int ren_src_was_dirty = was_dirty(o, rename->path);
|
||||
char *add_name = unique_path(o, rename->path, other_branch);
|
||||
if (update_file(o, 0, &add->oid, add->mode, add_name))
|
||||
return -1;
|
||||
|
||||
if (ren_src_was_dirty) {
|
||||
output(o, 1, _("Refusing to lose dirty file at %s"),
|
||||
rename->path);
|
||||
}
|
||||
/*
|
||||
* Because the double negatives somehow keep confusing me...
|
||||
* 1) update_wd iff !ren_src_was_dirty.
|
||||
* 2) no_wd iff !update_wd
|
||||
* 3) so, no_wd == !!ren_src_was_dirty == ren_src_was_dirty
|
||||
*/
|
||||
remove_file(o, 0, rename->path, ren_src_was_dirty);
|
||||
dst_name = unique_path(o, rename->path, cur_branch);
|
||||
} else {
|
||||
if (dir_in_way(rename->path, !o->call_depth, 0)) {
|
||||
dst_name = unique_path(o, rename->path, cur_branch);
|
||||
output(o, 1, _("%s is a directory in %s adding as %s instead"),
|
||||
rename->path, other_branch, dst_name);
|
||||
} else if (!o->call_depth &&
|
||||
would_lose_untracked(rename->path)) {
|
||||
dst_name = unique_path(o, rename->path, cur_branch);
|
||||
output(o, 1, _("Refusing to lose untracked file at %s; "
|
||||
"adding as %s instead"),
|
||||
rename->path, dst_name);
|
||||
}
|
||||
}
|
||||
if ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))
|
||||
; /* fall through, do allow dst_name to be released */
|
||||
else if (stage == 2)
|
||||
ret = update_stages(o, rename->path, NULL, rename, add);
|
||||
else
|
||||
ret = update_stages(o, rename->path, NULL, add, rename);
|
||||
|
||||
if (dst_name != rename->path)
|
||||
free(dst_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_rename_rename_1to2(struct merge_options *o,
|
||||
struct rename_conflict_info *ci)
|
||||
{
|
||||
/* One file was renamed in both branches, but to different names. */
|
||||
struct merge_file_info mfi;
|
||||
struct diff_filespec other;
|
||||
struct diff_filespec *add;
|
||||
struct diff_filespec *one = ci->pair1->one;
|
||||
struct diff_filespec *a = ci->pair1->two;
|
||||
struct diff_filespec *b = ci->pair2->two;
|
||||
char *path_desc;
|
||||
|
||||
output(o, 1, _("CONFLICT (rename/rename): "
|
||||
"Rename \"%s\"->\"%s\" in branch \"%s\" "
|
||||
|
@ -1790,15 +1727,16 @@ static int handle_rename_rename_1to2(struct merge_options *o,
|
|||
one->path, a->path, ci->branch1,
|
||||
one->path, b->path, ci->branch2,
|
||||
o->call_depth ? _(" (left unresolved)") : "");
|
||||
if (o->call_depth) {
|
||||
struct merge_file_info mfi;
|
||||
struct diff_filespec other;
|
||||
struct diff_filespec *add;
|
||||
if (merge_mode_and_contents(o, one, a, b, one->path,
|
||||
ci->branch1, ci->branch2,
|
||||
o->call_depth * 2, &mfi))
|
||||
return -1;
|
||||
|
||||
path_desc = xstrfmt("%s and %s, both renamed from %s",
|
||||
a->path, b->path, one->path);
|
||||
if (merge_mode_and_contents(o, one, a, b, path_desc,
|
||||
ci->branch1, ci->branch2,
|
||||
o->call_depth * 2, &mfi))
|
||||
return -1;
|
||||
free(path_desc);
|
||||
|
||||
if (o->call_depth) {
|
||||
/*
|
||||
* FIXME: For rename/add-source conflicts (if we could detect
|
||||
* such), this is wrong. We should instead find a unique
|
||||
|
@ -1830,8 +1768,70 @@ static int handle_rename_rename_1to2(struct merge_options *o,
|
|||
}
|
||||
else
|
||||
remove_file_from_cache(b->path);
|
||||
} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))
|
||||
return -1;
|
||||
} else {
|
||||
/*
|
||||
* For each destination path, we need to see if there is a
|
||||
* rename/add collision. If not, we can write the file out
|
||||
* to the specified location.
|
||||
*/
|
||||
add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
|
||||
if (add) {
|
||||
if (handle_file_collision(o, a->path,
|
||||
NULL, NULL,
|
||||
ci->branch1, ci->branch2,
|
||||
&mfi.oid, mfi.mode,
|
||||
&add->oid, add->mode) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
char *new_path = NULL;
|
||||
if (dir_in_way(a->path, !o->call_depth, 0)) {
|
||||
new_path = unique_path(o, a->path, ci->branch1);
|
||||
output(o, 1, _("%s is a directory in %s adding "
|
||||
"as %s instead"),
|
||||
a->path, ci->branch2, new_path);
|
||||
} else if (would_lose_untracked(a->path)) {
|
||||
new_path = unique_path(o, a->path, ci->branch1);
|
||||
output(o, 1, _("Refusing to lose untracked file"
|
||||
" at %s; adding as %s instead"),
|
||||
a->path, new_path);
|
||||
}
|
||||
|
||||
if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))
|
||||
return -1;
|
||||
free(new_path);
|
||||
if (update_stages(o, a->path, NULL, a, NULL))
|
||||
return -1;
|
||||
}
|
||||
|
||||
add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
|
||||
if (add) {
|
||||
if (handle_file_collision(o, b->path,
|
||||
NULL, NULL,
|
||||
ci->branch1, ci->branch2,
|
||||
&add->oid, add->mode,
|
||||
&mfi.oid, mfi.mode) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
char *new_path = NULL;
|
||||
if (dir_in_way(b->path, !o->call_depth, 0)) {
|
||||
new_path = unique_path(o, b->path, ci->branch2);
|
||||
output(o, 1, _("%s is a directory in %s adding "
|
||||
"as %s instead"),
|
||||
b->path, ci->branch1, new_path);
|
||||
} else if (would_lose_untracked(b->path)) {
|
||||
new_path = unique_path(o, b->path, ci->branch2);
|
||||
output(o, 1, _("Refusing to lose untracked file"
|
||||
" at %s; adding as %s instead"),
|
||||
b->path, new_path);
|
||||
}
|
||||
|
||||
if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))
|
||||
return -1;
|
||||
free(new_path);
|
||||
if (update_stages(o, b->path, NULL, NULL, b))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -684,7 +684,7 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
|
|||
git ls-files -u c >out &&
|
||||
test_line_count = 2 out &&
|
||||
git ls-files -o >out &&
|
||||
test_line_count = 5 out &&
|
||||
test_line_count = 1 out &&
|
||||
|
||||
git rev-parse >expect \
|
||||
A:a C:b B:b C:c B:c &&
|
||||
|
@ -692,14 +692,27 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
|
|||
:1:a :2:b :3:b :2:c :3:c &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
git rev-parse >expect \
|
||||
C:c B:c C:b B:b &&
|
||||
git hash-object >actual \
|
||||
c~HEAD c~B\^0 b~HEAD b~B\^0 &&
|
||||
test_cmp expect actual &&
|
||||
# Record some contents for re-doing merges
|
||||
git cat-file -p A:a >stuff &&
|
||||
git cat-file -p C:b >important_info &&
|
||||
git cat-file -p B:c >precious_data &&
|
||||
>empty &&
|
||||
|
||||
test_path_is_missing b &&
|
||||
test_path_is_missing c
|
||||
# Test the merge in b
|
||||
test_must_fail git merge-file \
|
||||
-L "HEAD" \
|
||||
-L "" \
|
||||
-L "B^0" \
|
||||
important_info empty stuff &&
|
||||
test_cmp important_info b &&
|
||||
|
||||
# Test the merge in c
|
||||
test_must_fail git merge-file \
|
||||
-L "HEAD" \
|
||||
-L "" \
|
||||
-L "B^0" \
|
||||
stuff empty precious_data &&
|
||||
test_cmp stuff c
|
||||
)
|
||||
'
|
||||
|
||||
|
|
|
@ -1078,7 +1078,7 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam
|
|||
git ls-files -u >out &&
|
||||
test_line_count = 6 out &&
|
||||
git ls-files -o >out &&
|
||||
test_line_count = 3 out &&
|
||||
test_line_count = 1 out &&
|
||||
|
||||
git rev-parse >actual \
|
||||
:0:y/b :0:y/c :0:y/e &&
|
||||
|
@ -1094,9 +1094,9 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam
|
|||
test_cmp expect actual &&
|
||||
|
||||
git hash-object >actual \
|
||||
w/d~HEAD w/d~B^0 z/d &&
|
||||
z/d &&
|
||||
git rev-parse >expect \
|
||||
O:x/d B:w/d O:x/d &&
|
||||
O:x/d &&
|
||||
test_cmp expect actual &&
|
||||
test_path_is_missing x/d &&
|
||||
test_path_is_file y/d &&
|
||||
|
@ -3672,7 +3672,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena
|
|||
git ls-files -u >out &&
|
||||
test_line_count = 4 out &&
|
||||
git ls-files -o >out &&
|
||||
test_line_count = 4 out &&
|
||||
test_line_count = 3 out &&
|
||||
|
||||
echo different >expected &&
|
||||
echo mods >>expected &&
|
||||
|
@ -3684,11 +3684,17 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena
|
|||
O:z/a O:z/b O:x/d O:x/c O:x/c A:y/c O:x/c &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
git hash-object >actual \
|
||||
y/c~B^0 y/c~HEAD &&
|
||||
git rev-parse >expect \
|
||||
O:x/c A:y/c &&
|
||||
test_cmp expect actual
|
||||
# See if y/c~merged has expected contents; requires manually
|
||||
# doing the expected file merge
|
||||
git cat-file -p A:y/c >c1 &&
|
||||
git cat-file -p B:z/c >c2 &&
|
||||
>empty &&
|
||||
test_must_fail git merge-file \
|
||||
-L "HEAD" \
|
||||
-L "" \
|
||||
-L "B^0" \
|
||||
c1 empty c2 &&
|
||||
test_cmp c1 y/c~merged
|
||||
)
|
||||
'
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче