Skip to content

Commit 7b3d3b0

Browse files
newrengitster
authored andcommitted
merge-recursive: avoid clobbering untracked files with directory renames
Reviewed-by: Stefan Beller <[email protected]> Signed-off-by: Elijah Newren <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3b9616f commit 7b3d3b0

File tree

2 files changed

+43
-5
lines changed

2 files changed

+43
-5
lines changed

merge-recursive.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,26 @@ static int conflict_rename_dir(struct merge_options *o,
11411141
{
11421142
const struct diff_filespec *dest = pair->two;
11431143

1144+
if (!o->call_depth && would_lose_untracked(dest->path)) {
1145+
char *alt_path = unique_path(o, dest->path, rename_branch);
1146+
1147+
output(o, 1, _("Error: Refusing to lose untracked file at %s; "
1148+
"writing to %s instead."),
1149+
dest->path, alt_path);
1150+
/*
1151+
* Write the file in worktree at alt_path, but not in the
1152+
* index. Instead, write to dest->path for the index but
1153+
* only at the higher appropriate stage.
1154+
*/
1155+
if (update_file(o, 0, &dest->oid, dest->mode, alt_path))
1156+
return -1;
1157+
free(alt_path);
1158+
return update_stages(o, dest->path, NULL,
1159+
rename_branch == o->branch1 ? dest : NULL,
1160+
rename_branch == o->branch1 ? NULL : dest);
1161+
}
1162+
1163+
/* Update dest->path both in index and in worktree */
11441164
if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
11451165
return -1;
11461166
return 0;
@@ -1159,7 +1179,8 @@ static int handle_change_delete(struct merge_options *o,
11591179
const char *update_path = path;
11601180
int ret = 0;
11611181

1162-
if (dir_in_way(path, !o->call_depth, 0)) {
1182+
if (dir_in_way(path, !o->call_depth, 0) ||
1183+
(!o->call_depth && would_lose_untracked(path))) {
11631184
update_path = alt_path = unique_path(o, path, change_branch);
11641185
}
11651186

@@ -1285,6 +1306,12 @@ static int handle_file(struct merge_options *o,
12851306
dst_name = unique_path(o, rename->path, cur_branch);
12861307
output(o, 1, _("%s is a directory in %s adding as %s instead"),
12871308
rename->path, other_branch, dst_name);
1309+
} else if (!o->call_depth &&
1310+
would_lose_untracked(rename->path)) {
1311+
dst_name = unique_path(o, rename->path, cur_branch);
1312+
output(o, 1, _("Refusing to lose untracked file at %s; "
1313+
"adding as %s instead"),
1314+
rename->path, dst_name);
12881315
}
12891316
}
12901317
if ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))
@@ -1410,7 +1437,18 @@ static int conflict_rename_rename_2to1(struct merge_options *o,
14101437
char *new_path2 = unique_path(o, path, ci->branch2);
14111438
output(o, 1, _("Renaming %s to %s and %s to %s instead"),
14121439
a->path, new_path1, b->path, new_path2);
1413-
remove_file(o, 0, path, 0);
1440+
if (would_lose_untracked(path))
1441+
/*
1442+
* Only way we get here is if both renames were from
1443+
* a directory rename AND user had an untracked file
1444+
* at the location where both files end up after the
1445+
* two directory renames. See testcase 10d of t6043.
1446+
*/
1447+
output(o, 1, _("Refusing to lose untracked file at "
1448+
"%s, even though it's in the way."),
1449+
path);
1450+
else
1451+
remove_file(o, 0, path, 0);
14141452
ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);
14151453
if (!ret)
14161454
ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,

t/t6043-merge-rename-directories.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2992,7 +2992,7 @@ test_expect_success '10b-setup: Overwrite untracked with dir rename + delete' '
29922992
)
29932993
'
29942994

2995-
test_expect_failure '10b-check: Overwrite untracked with dir rename + delete' '
2995+
test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '
29962996
(
29972997
cd 10b &&
29982998
@@ -3070,7 +3070,7 @@ test_expect_success '10c-setup: Overwrite untracked with dir rename/rename(1to2)
30703070
)
30713071
'
30723072

3073-
test_expect_failure '10c-check: Overwrite untracked with dir rename/rename(1to2)' '
3073+
test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)' '
30743074
(
30753075
cd 10c &&
30763076
@@ -3145,7 +3145,7 @@ test_expect_success '10d-setup: Delete untracked with dir rename/rename(2to1)' '
31453145
)
31463146
'
31473147

3148-
test_expect_failure '10d-check: Delete untracked with dir rename/rename(2to1)' '
3148+
test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '
31493149
(
31503150
cd 10d &&
31513151

0 commit comments

Comments
 (0)