merge-recursive: avoid clobbering untracked files with directory renames
Reviewed-by: Stefan Beller <sbeller@google.com> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Elijah Newren committed
Feb 14, 2018 at 10:52 UTC
7b3d3b06811c3d603d47d50eb3c8775571845a80
2 files changed
+43
-5
merge-recursive.c
+40
-2
@@ -1141,6 +1141,26 @@ static int conflict_rename_dir(struct merge_options *o,
1141
{
1142
const struct diff_filespec *dest = pair->two;
1143
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 */
1164
if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
1165
return -1;
1166
return 0;
@@ -1159,7 +1179,8 @@ static int handle_change_delete(struct merge_options *o,
1179
const char *update_path = path;
1180
int ret = 0;
1181
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))) {
1184
update_path = alt_path = unique_path(o, path, change_branch);
1185
}
1186
@@ -1285,6 +1306,12 @@ static int handle_file(struct merge_options *o,
1306
dst_name = unique_path(o, rename->path, cur_branch);
1307
output(o, 1, _("%s is a directory in %s adding as %s instead"),
1308
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);
1315
}
1316
}
1317
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,
1437
char *new_path2 = unique_path(o, path, ci->branch2);
1438
output(o, 1, _("Renaming %s to %s and %s to %s instead"),
1439
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);
1452
ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);
1453
if (!ret)
1454
ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
t/t6043-merge-rename-directories.sh
+3
-3
@@ -2992,7 +2992,7 @@ test_expect_success '10b-setup: Overwrite untracked with dir rename + delete' '
2992
)
2993
'
2994
2995
-test_expect_failure '10b-check: Overwrite untracked with dir rename + delete' '
2995
+test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '
2996
(
2997
cd 10b &&
2998
@@ -3070,7 +3070,7 @@ test_expect_success '10c-setup: Overwrite untracked with dir rename/rename(1to2)
3070
)
3071
'
3072
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)' '
3074
(
3075
cd 10c &&
3076
@@ -3145,7 +3145,7 @@ test_expect_success '10d-setup: Delete untracked with dir rename/rename(2to1)' '
3145
)
3146
'
3147
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)' '
3149
(
3150
cd 10d &&
3151