directory rename detection: tests for handling overwriting dirty files

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 Apr 19, 2018 at 10:57 UTC a7a436042a272cb48641a8aa658ca01329790a9f
1 file changed +458
t/t6043-merge-rename-directories.sh
+458
@@ -3246,4 +3246,462 @@ test_expect_failure '10e-check: Does git complain about untracked file that is n
3246 )
3247 '
3248
3249 +###########################################################################
3250 +# SECTION 11: Handling dirty (not up-to-date) files
3251 +#
3252 +# unpack_trees(), upon which the recursive merge algorithm is based, aborts
3253 +# the operation if untracked or dirty files would be deleted or overwritten
3254 +# by the merge. Unfortunately, unpack_trees() does not understand renames,
3255 +# and if it doesn't abort, then it muddies up the working directory before
3256 +# we even get to the point of detecting renames, so we need some special
3257 +# handling. This was true even of normal renames, but there are additional
3258 +# codepaths that need special handling with directory renames. Add
3259 +# testcases for both renamed-by-directory-rename-detection and standard
3260 +# rename cases.
3261 +###########################################################################
3262 +
3263 +# Testcase 11a, Avoid losing dirty contents with simple rename
3264 +# Commit O: z/{a,b_v1},
3265 +# Commit A: z/{a,c_v1}, and z/c_v1 has uncommitted mods
3266 +# Commit B: z/{a,b_v2}
3267 +# Expected: ERROR_MSG(Refusing to lose dirty file at z/c) +
3268 +# z/a, staged version of z/c has sha1sum matching B:z/b_v2,
3269 +# z/c~HEAD with contents of B:z/b_v2,
3270 +# z/c with uncommitted mods on top of A:z/c_v1
3271 +
3272 +test_expect_success '11a-setup: Avoid losing dirty contents with simple rename' '
3273 + test_create_repo 11a &&
3274 + (
3275 + cd 11a &&
3276 +
3277 + mkdir z &&
3278 + echo a >z/a &&
3279 + test_seq 1 10 >z/b &&
3280 + git add z &&
3281 + test_tick &&
3282 + git commit -m "O" &&
3283 +
3284 + git branch O &&
3285 + git branch A &&
3286 + git branch B &&
3287 +
3288 + git checkout A &&
3289 + git mv z/b z/c &&
3290 + test_tick &&
3291 + git commit -m "A" &&
3292 +
3293 + git checkout B &&
3294 + echo 11 >>z/b &&
3295 + git add z/b &&
3296 + test_tick &&
3297 + git commit -m "B"
3298 + )
3299 +'
3300 +
3301 +test_expect_failure '11a-check: Avoid losing dirty contents with simple rename' '
3302 + (
3303 + cd 11a &&
3304 +
3305 + git checkout A^0 &&
3306 + echo stuff >>z/c &&
3307 +
3308 + test_must_fail git merge -s recursive B^0 >out 2>err &&
3309 + test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3310 +
3311 + test_seq 1 10 >expected &&
3312 + echo stuff >>expected &&
3313 + test_cmp expected z/c &&
3314 +
3315 + git ls-files -s >out &&
3316 + test_line_count = 2 out &&
3317 + git ls-files -u >out &&
3318 + test_line_count = 1 out &&
3319 + git ls-files -o >out &&
3320 + test_line_count = 4 out &&
3321 +
3322 + git rev-parse >actual \
3323 + :0:z/a :2:z/c &&
3324 + git rev-parse >expect \
3325 + O:z/a B:z/b &&
3326 + test_cmp expect actual &&
3327 +
3328 + git hash-object z/c~HEAD >actual &&
3329 + git rev-parse B:z/b >expect &&
3330 + test_cmp expect actual
3331 + )
3332 +'
3333 +
3334 +# Testcase 11b, Avoid losing dirty file involved in directory rename
3335 +# Commit O: z/a, x/{b,c_v1}
3336 +# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods
3337 +# Commit B: y/a, x/{b,c_v2}
3338 +# Expected: y/{a,c_v2}, x/b, z/c_v1 with uncommitted mods untracked,
3339 +# ERROR_MSG(Refusing to lose dirty file at z/c)
3340 +
3341 +
3342 +test_expect_success '11b-setup: Avoid losing dirty file involved in directory rename' '
3343 + test_create_repo 11b &&
3344 + (
3345 + cd 11b &&
3346 +
3347 + mkdir z x &&
3348 + echo a >z/a &&
3349 + echo b >x/b &&
3350 + test_seq 1 10 >x/c &&
3351 + git add z x &&
3352 + test_tick &&
3353 + git commit -m "O" &&
3354 +
3355 + git branch O &&
3356 + git branch A &&
3357 + git branch B &&
3358 +
3359 + git checkout A &&
3360 + git mv x/c z/c &&
3361 + test_tick &&
3362 + git commit -m "A" &&
3363 +
3364 + git checkout B &&
3365 + git mv z y &&
3366 + echo 11 >>x/c &&
3367 + git add x/c &&
3368 + test_tick &&
3369 + git commit -m "B"
3370 + )
3371 +'
3372 +
3373 +test_expect_failure '11b-check: Avoid losing dirty file involved in directory rename' '
3374 + (
3375 + cd 11b &&
3376 +
3377 + git checkout A^0 &&
3378 + echo stuff >>z/c &&
3379 +
3380 + git merge -s recursive B^0 >out 2>err &&
3381 + test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3382 +
3383 + grep -q stuff z/c &&
3384 + test_seq 1 10 >expected &&
3385 + echo stuff >>expected &&
3386 + test_cmp expected z/c &&
3387 +
3388 + git ls-files -s >out &&
3389 + test_line_count = 3 out &&
3390 + git ls-files -u >out &&
3391 + test_line_count = 0 out &&
3392 + git ls-files -m >out &&
3393 + test_line_count = 0 out &&
3394 + git ls-files -o >out &&
3395 + test_line_count = 4 out &&
3396 +
3397 + git rev-parse >actual \
3398 + :0:x/b :0:y/a :0:y/c &&
3399 + git rev-parse >expect \
3400 + O:x/b O:z/a B:x/c &&
3401 + test_cmp expect actual &&
3402 +
3403 + git hash-object y/c >actual &&
3404 + git rev-parse B:x/c >expect &&
3405 + test_cmp expect actual
3406 + )
3407 +'
3408 +
3409 +# Testcase 11c, Avoid losing not-up-to-date with rename + D/F conflict
3410 +# Commit O: y/a, x/{b,c_v1}
3411 +# Commit A: y/{a,c_v1}, x/b, and y/c_v1 has uncommitted mods
3412 +# Commit B: y/{a,c/d}, x/{b,c_v2}
3413 +# Expected: Abort_msg("following files would be overwritten by merge") +
3414 +# y/c left untouched (still has uncommitted mods)
3415 +
3416 +test_expect_success '11c-setup: Avoid losing not-uptodate with rename + D/F conflict' '
3417 + test_create_repo 11c &&
3418 + (
3419 + cd 11c &&
3420 +
3421 + mkdir y x &&
3422 + echo a >y/a &&
3423 + echo b >x/b &&
3424 + test_seq 1 10 >x/c &&
3425 + git add y x &&
3426 + test_tick &&
3427 + git commit -m "O" &&
3428 +
3429 + git branch O &&
3430 + git branch A &&
3431 + git branch B &&
3432 +
3433 + git checkout A &&
3434 + git mv x/c y/c &&
3435 + test_tick &&
3436 + git commit -m "A" &&
3437 +
3438 + git checkout B &&
3439 + mkdir y/c &&
3440 + echo d >y/c/d &&
3441 + echo 11 >>x/c &&
3442 + git add x/c y/c/d &&
3443 + test_tick &&
3444 + git commit -m "B"
3445 + )
3446 +'
3447 +
3448 +test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conflict' '
3449 + (
3450 + cd 11c &&
3451 +
3452 + git checkout A^0 &&
3453 + echo stuff >>y/c &&
3454 +
3455 + test_must_fail git merge -s recursive B^0 >out 2>err &&
3456 + test_i18ngrep "following files would be overwritten by merge" err &&
3457 +
3458 + grep -q stuff y/c &&
3459 + test_seq 1 10 >expected &&
3460 + echo stuff >>expected &&
3461 + test_cmp expected y/c &&
3462 +
3463 + git ls-files -s >out &&
3464 + test_line_count = 3 out &&
3465 + git ls-files -u >out &&
3466 + test_line_count = 0 out &&
3467 + git ls-files -m >out &&
3468 + test_line_count = 1 out &&
3469 + git ls-files -o >out &&
3470 + test_line_count = 3 out
3471 + )
3472 +'
3473 +
3474 +# Testcase 11d, Avoid losing not-up-to-date with rename + D/F conflict
3475 +# Commit O: z/a, x/{b,c_v1}
3476 +# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods
3477 +# Commit B: y/{a,c/d}, x/{b,c_v2}
3478 +# Expected: D/F: y/c_v2 vs y/c/d) +
3479 +# Warning_Msg("Refusing to lose dirty file at z/c) +
3480 +# y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods
3481 +
3482 +test_expect_success '11d-setup: Avoid losing not-uptodate with rename + D/F conflict' '
3483 + test_create_repo 11d &&
3484 + (
3485 + cd 11d &&
3486 +
3487 + mkdir z x &&
3488 + echo a >z/a &&
3489 + echo b >x/b &&
3490 + test_seq 1 10 >x/c &&
3491 + git add z x &&
3492 + test_tick &&
3493 + git commit -m "O" &&
3494 +
3495 + git branch O &&
3496 + git branch A &&
3497 + git branch B &&
3498 +
3499 + git checkout A &&
3500 + git mv x/c z/c &&
3501 + test_tick &&
3502 + git commit -m "A" &&
3503 +
3504 + git checkout B &&
3505 + git mv z y &&
3506 + mkdir y/c &&
3507 + echo d >y/c/d &&
3508 + echo 11 >>x/c &&
3509 + git add x/c y/c/d &&
3510 + test_tick &&
3511 + git commit -m "B"
3512 + )
3513 +'
3514 +
3515 +test_expect_failure '11d-check: Avoid losing not-uptodate with rename + D/F conflict' '
3516 + (
3517 + cd 11d &&
3518 +
3519 + git checkout A^0 &&
3520 + echo stuff >>z/c &&
3521 +
3522 + test_must_fail git merge -s recursive B^0 >out 2>err &&
3523 + test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3524 +
3525 + grep -q stuff z/c &&
3526 + test_seq 1 10 >expected &&
3527 + echo stuff >>expected &&
3528 + test_cmp expected z/c
3529 +
3530 + git ls-files -s >out &&
3531 + test_line_count = 4 out &&
3532 + git ls-files -u >out &&
3533 + test_line_count = 1 out &&
3534 + git ls-files -o >out &&
3535 + test_line_count = 5 out &&
3536 +
3537 + git rev-parse >actual \
3538 + :0:x/b :0:y/a :0:y/c/d :3:y/c &&
3539 + git rev-parse >expect \
3540 + O:x/b O:z/a B:y/c/d B:x/c &&
3541 + test_cmp expect actual &&
3542 +
3543 + git hash-object y/c~HEAD >actual &&
3544 + git rev-parse B:x/c >expect &&
3545 + test_cmp expect actual
3546 + )
3547 +'
3548 +
3549 +# Testcase 11e, Avoid deleting not-up-to-date with dir rename/rename(1to2)/add
3550 +# Commit O: z/{a,b}, x/{c_1,d}
3551 +# Commit A: y/{a,b,c_2}, x/d, w/c_1, and y/c_2 has uncommitted mods
3552 +# Commit B: z/{a,b,c_1}, x/d
3553 +# Expected: Failed Merge; y/{a,b} + x/d +
3554 +# CONFLICT(rename/rename) x/c_1 -> w/c_1 vs y/c_1 +
3555 +# ERROR_MSG(Refusing to lose dirty file at y/c)
3556 +# y/c~B^0 has O:x/c_1 contents
3557 +# y/c~HEAD has A:y/c_2 contents
3558 +# y/c has dirty file from before merge
3559 +
3560 +test_expect_success '11e-setup: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
3561 + test_create_repo 11e &&
3562 + (
3563 + cd 11e &&
3564 +
3565 + mkdir z x &&
3566 + echo a >z/a &&
3567 + echo b >z/b &&
3568 + echo c >x/c &&
3569 + echo d >x/d &&
3570 + git add z x &&
3571 + test_tick &&
3572 + git commit -m "O" &&
3573 +
3574 + git branch O &&
3575 + git branch A &&
3576 + git branch B &&
3577 +
3578 + git checkout A &&
3579 + git mv z/ y/ &&
3580 + echo different >y/c &&
3581 + mkdir w &&
3582 + git mv x/c w/ &&
3583 + git add y/c &&
3584 + test_tick &&
3585 + git commit -m "A" &&
3586 +
3587 + git checkout B &&
3588 + git mv x/c z/ &&
3589 + test_tick &&
3590 + git commit -m "B"
3591 + )
3592 +'
3593 +
3594 +test_expect_failure '11e-check: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
3595 + (
3596 + cd 11e &&
3597 +
3598 + git checkout A^0 &&
3599 + echo mods >>y/c &&
3600 +
3601 + test_must_fail git merge -s recursive B^0 >out 2>err &&
3602 + test_i18ngrep "CONFLICT (rename/rename)" out &&
3603 + test_i18ngrep "Refusing to lose dirty file at y/c" out &&
3604 +
3605 + git ls-files -s >out &&
3606 + test_line_count = 7 out &&
3607 + git ls-files -u >out &&
3608 + test_line_count = 4 out &&
3609 + git ls-files -o >out &&
3610 + test_line_count = 4 out &&
3611 +
3612 + echo different >expected &&
3613 + echo mods >>expected &&
3614 + test_cmp expected y/c &&
3615 +
3616 + git rev-parse >actual \
3617 + :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :2:y/c :3:y/c &&
3618 + git rev-parse >expect \
3619 + O:z/a O:z/b O:x/d O:x/c O:x/c A:y/c O:x/c &&
3620 + test_cmp expect actual &&
3621 +
3622 + git hash-object >actual \
3623 + y/c~B^0 y/c~HEAD &&
3624 + git rev-parse >expect \
3625 + O:x/c A:y/c &&
3626 + test_cmp expect actual
3627 + )
3628 +'
3629 +
3630 +# Testcase 11f, Avoid deleting not-up-to-date w/ dir rename/rename(2to1)
3631 +# Commit O: z/{a,b}, x/{c_1,d_2}
3632 +# Commit A: y/{a,b,wham_1}, x/d_2, except y/wham has uncommitted mods
3633 +# Commit B: z/{a,b,wham_2}, x/c_1
3634 +# Expected: Failed Merge; y/{a,b} + untracked y/{wham~B^0,wham~B^HEAD} +
3635 +# y/wham with dirty changes from before merge +
3636 +# CONFLICT(rename/rename) x/c vs x/d -> y/wham
3637 +# ERROR_MSG(Refusing to lose dirty file at y/wham)
3638 +
3639 +test_expect_success '11f-setup: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
3640 + test_create_repo 11f &&
3641 + (
3642 + cd 11f &&
3643 +
3644 + mkdir z x &&
3645 + echo a >z/a &&
3646 + echo b >z/b &&
3647 + test_seq 1 10 >x/c &&
3648 + echo d >x/d &&
3649 + git add z x &&
3650 + test_tick &&
3651 + git commit -m "O" &&
3652 +
3653 + git branch O &&
3654 + git branch A &&
3655 + git branch B &&
3656 +
3657 + git checkout A &&
3658 + git mv z/ y/ &&
3659 + git mv x/c y/wham &&
3660 + test_tick &&
3661 + git commit -m "A" &&
3662 +
3663 + git checkout B &&
3664 + git mv x/d z/wham &&
3665 + test_tick &&
3666 + git commit -m "B"
3667 + )
3668 +'
3669 +
3670 +test_expect_failure '11f-check: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
3671 + (
3672 + cd 11f &&
3673 +
3674 + git checkout A^0 &&
3675 + echo important >>y/wham &&
3676 +
3677 + test_must_fail git merge -s recursive B^0 >out 2>err &&
3678 + test_i18ngrep "CONFLICT (rename/rename)" out &&
3679 + test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
3680 +
3681 + git ls-files -s >out &&
3682 + test_line_count = 4 out &&
3683 + git ls-files -u >out &&
3684 + test_line_count = 2 out &&
3685 + git ls-files -o >out &&
3686 + test_line_count = 4 out &&
3687 +
3688 + test_seq 1 10 >expected &&
3689 + echo important >>expected &&
3690 + test_cmp expected y/wham &&
3691 +
3692 + test_must_fail git rev-parse :1:y/wham &&
3693 + git hash-object >actual \
3694 + y/wham~B^0 y/wham~HEAD &&
3695 + git rev-parse >expect \
3696 + O:x/d O:x/c &&
3697 + test_cmp expect actual &&
3698 +
3699 + git rev-parse >actual \
3700 + :0:y/a :0:y/b :2:y/wham :3:y/wham &&
3701 + git rev-parse >expect \
3702 + O:z/a O:z/b O:x/c O:x/d &&
3703 + test_cmp expect actual
3704 + )
3705 +'
3706 +
3707 test_done