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