1#!/bin/sh 2 3test_description="recursive merge with directory renames" 4# includes checking of many corner cases, with a similar methodology to: 5# t6042: corner cases with renames but not criss-cross merges 6# t6036: corner cases with both renames and criss-cross merges 7# 8# The setup for all of them, pictorially, is: 9# 10# A 11# o 12# / \ 13# O o ? 14# \ / 15# o 16# B 17# 18# To help make it easier to follow the flow of tests, they have been 19# divided into sections and each test will start with a quick explanation 20# of what commits O, A, and B contain. 21# 22# Notation: 23# z/{b,c} means files z/b and z/c both exist 24# x/d_1 means file x/d exists with content d1. (Purpose of the 25# underscore notation is to differentiate different 26# files that might be renamed into each other's paths.) 27 28. ./test-lib.sh 29 30 31########################################################################### 32# SECTION 1: Basic cases we should be able to handle 33########################################################################### 34 35# Testcase 1a, Basic directory rename. 36# Commit O: z/{b,c} 37# Commit A: y/{b,c} 38# Commit B: z/{b,c,d,e/f} 39# Expected: y/{b,c,d,e/f} 40 41test_expect_success '1a-setup: Simple directory rename detection'' 42 test_create_repo 1a && 43 ( 44 cd 1a && 45 46 mkdir z && 47 echo b >z/b && 48 echo c >z/c && 49 git add z && 50 test_tick && 51 git commit -m "O" && 52 53 git branch O && 54 git branch A && 55 git branch B && 56 57 git checkout A && 58 git mv z y && 59 test_tick && 60 git commit -m "A" && 61 62 git checkout B && 63 echo d >z/d && 64 mkdir z/e && 65 echo f >z/e/f && 66 git add z/d z/e/f && 67 test_tick && 68 git commit -m "B" 69 ) 70' 71 72test_expect_success '1a-check: Simple directory rename detection'' 73 ( 74 cd 1a && 75 76 git checkout A^0 && 77 78 git merge -s recursive B^0 && 79 80 git ls-files -s >out && 81 test_line_count = 4 out && 82 83 git rev-parse >actual \ 84 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e/f && 85 git rev-parse >expect \ 86 O:z/b O:z/c B:z/d B:z/e/f && 87 test_cmp expect actual && 88 89 git hash-object y/d >actual && 90 git rev-parse B:z/d >expect && 91 test_cmp expect actual && 92 93 test_must_fail git rev-parse HEAD:z/d && 94 test_must_fail git rev-parse HEAD:z/e/f && 95 test_path_is_missing z/d && 96 test_path_is_missing z/e/f 97 ) 98' 99 100# Testcase 1b, Merge a directory with another 101# Commit O: z/{b,c}, y/d 102# Commit A: z/{b,c,e}, y/d 103# Commit B: y/{b,c,d} 104# Expected: y/{b,c,d,e} 105 106test_expect_success '1b-setup: Merge a directory with another'' 107 test_create_repo 1b && 108 ( 109 cd 1b && 110 111 mkdir z && 112 echo b >z/b && 113 echo c >z/c && 114 mkdir y && 115 echo d >y/d && 116 git add z y && 117 test_tick && 118 git commit -m "O" && 119 120 git branch O && 121 git branch A && 122 git branch B && 123 124 git checkout A && 125 echo e >z/e && 126 git add z/e && 127 test_tick && 128 git commit -m "A" && 129 130 git checkout B && 131 git mv z/b y && 132 git mv z/c y && 133 rmdir z && 134 test_tick && 135 git commit -m "B" 136 ) 137' 138 139test_expect_success '1b-check: Merge a directory with another'' 140 ( 141 cd 1b && 142 143 git checkout A^0 && 144 145 git merge -s recursive B^0 && 146 147 git ls-files -s >out && 148 test_line_count = 4 out && 149 150 git rev-parse >actual \ 151 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e && 152 git rev-parse >expect \ 153 O:z/b O:z/c O:y/d A:z/e && 154 test_cmp expect actual && 155 test_must_fail git rev-parse HEAD:z/e 156 ) 157' 158 159# Testcase 1c, Transitive renaming 160# (Related to testcases 3a and 6d -- when should a transitive rename apply?) 161# (Related to testcases 9c and 9d -- can transitivity repeat?) 162# Commit O: z/{b,c}, x/d 163# Commit A: y/{b,c}, x/d 164# Commit B: z/{b,c,d} 165# Expected: y/{b,c,d} (because x/d -> z/d -> y/d) 166 167test_expect_success '1c-setup: Transitive renaming'' 168 test_create_repo 1c && 169 ( 170 cd 1c && 171 172 mkdir z && 173 echo b >z/b && 174 echo c >z/c && 175 mkdir x && 176 echo d >x/d && 177 git add z x && 178 test_tick && 179 git commit -m "O" && 180 181 git branch O && 182 git branch A && 183 git branch B && 184 185 git checkout A && 186 git mv z y && 187 test_tick && 188 git commit -m "A" && 189 190 git checkout B && 191 git mv x/d z/d && 192 test_tick && 193 git commit -m "B" 194 ) 195' 196 197test_expect_success '1c-check: Transitive renaming'' 198 ( 199 cd 1c && 200 201 git checkout A^0 && 202 203 git merge -s recursive B^0 && 204 205 git ls-files -s >out && 206 test_line_count = 3 out && 207 208 git rev-parse >actual \ 209 HEAD:y/b HEAD:y/c HEAD:y/d && 210 git rev-parse >expect \ 211 O:z/b O:z/c O:x/d && 212 test_cmp expect actual && 213 test_must_fail git rev-parse HEAD:x/d && 214 test_must_fail git rev-parse HEAD:z/d && 215 test_path_is_missing z/d 216 ) 217' 218 219# Testcase 1d, Directory renames (merging two directories into one new one) 220# cause a rename/rename(2to1) conflict 221# (Related to testcases 1c and 7b) 222# Commit O. z/{b,c}, y/{d,e} 223# Commit A. x/{b,c}, y/{d,e,m,wham_1} 224# Commit B. z/{b,c,n,wham_2}, x/{d,e} 225# Expected: x/{b,c,d,e,m,n}, CONFLICT:(y/wham_1 & z/wham_2 -> x/wham) 226# Note: y/m & z/n should definitely move into x. By the same token, both 227# y/wham_1 & z/wham_2 should too...giving us a conflict. 228 229test_expect_success '1d-setup: Directory renames cause a rename/rename(2to1) conflict'' 230 test_create_repo 1d && 231 ( 232 cd 1d && 233 234 mkdir z && 235 echo b >z/b && 236 echo c >z/c && 237 mkdir y && 238 echo d >y/d && 239 echo e >y/e && 240 git add z y && 241 test_tick && 242 git commit -m "O" && 243 244 git branch O && 245 git branch A && 246 git branch B && 247 248 git checkout A && 249 git mv z x && 250 echo m >y/m && 251 echo wham1 >y/wham && 252 git add y && 253 test_tick && 254 git commit -m "A" && 255 256 git checkout B && 257 git mv y x && 258 echo n >z/n && 259 echo wham2 >z/wham && 260 git add z && 261 test_tick && 262 git commit -m "B" 263 ) 264' 265 266test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) conflict'' 267 ( 268 cd 1d && 269 270 git checkout A^0 && 271 272 test_must_fail git merge -s recursive B^0 >out && 273 test_i18ngrep "CONFLICT (rename/rename)" out && 274 275 git ls-files -s >out && 276 test_line_count = 8 out && 277 git ls-files -u >out && 278 test_line_count = 2 out && 279 git ls-files -o >out && 280 test_line_count = 3 out && 281 282 git rev-parse >actual \ 283 :0:x/b :0:x/c :0:x/d :0:x/e :0:x/m :0:x/n && 284 git rev-parse >expect \ 285 O:z/b O:z/c O:y/d O:y/e A:y/m B:z/n && 286 test_cmp expect actual && 287 288 test_must_fail git rev-parse :0:x/wham && 289 git rev-parse >actual \ 290 :2:x/wham :3:x/wham && 291 git rev-parse >expect \ 292 A:y/wham B:z/wham && 293 test_cmp expect actual && 294 295 test_path_is_missing x/wham && 296 test_path_is_file x/wham~HEAD && 297 test_path_is_file x/wham~B^0 && 298 299 git hash-object >actual \ 300 x/wham~HEAD x/wham~B^0 && 301 git rev-parse >expect \ 302 A:y/wham B:z/wham && 303 test_cmp expect actual 304 ) 305' 306 307# Testcase 1e, Renamed directory, with all filenames being renamed too 308# (Related to testcases 9f & 9g) 309# Commit O: z/{oldb,oldc} 310# Commit A: y/{newb,newc} 311# Commit B: z/{oldb,oldc,d} 312# Expected: y/{newb,newc,d} 313 314test_expect_success '1e-setup: Renamed directory, with all files being renamed too'' 315 test_create_repo 1e && 316 ( 317 cd 1e && 318 319 mkdir z && 320 echo b >z/oldb && 321 echo c >z/oldc && 322 git add z && 323 test_tick && 324 git commit -m "O" && 325 326 git branch O && 327 git branch A && 328 git branch B && 329 330 git checkout A && 331 mkdir y && 332 git mv z/oldb y/newb && 333 git mv z/oldc y/newc && 334 test_tick && 335 git commit -m "A" && 336 337 git checkout B && 338 echo d >z/d && 339 git add z/d && 340 test_tick && 341 git commit -m "B" 342 ) 343' 344 345test_expect_success '1e-check: Renamed directory, with all files being renamed too'' 346 ( 347 cd 1e && 348 349 git checkout A^0 && 350 351 git merge -s recursive B^0 && 352 353 git ls-files -s >out && 354 test_line_count = 3 out && 355 356 git rev-parse >actual \ 357 HEAD:y/newb HEAD:y/newc HEAD:y/d && 358 git rev-parse >expect \ 359 O:z/oldb O:z/oldc B:z/d && 360 test_cmp expect actual && 361 test_must_fail git rev-parse HEAD:z/d 362 ) 363' 364 365# Testcase 1f, Split a directory into two other directories 366# (Related to testcases 3a, all of section 2, and all of section 4) 367# Commit O: z/{b,c,d,e,f} 368# Commit A: z/{b,c,d,e,f,g} 369# Commit B: y/{b,c}, x/{d,e,f} 370# Expected: y/{b,c}, x/{d,e,f,g} 371 372test_expect_success '1f-setup: Split a directory into two other directories'' 373 test_create_repo 1f && 374 ( 375 cd 1f && 376 377 mkdir z && 378 echo b >z/b && 379 echo c >z/c && 380 echo d >z/d && 381 echo e >z/e && 382 echo f >z/f && 383 git add z && 384 test_tick && 385 git commit -m "O" && 386 387 git branch O && 388 git branch A && 389 git branch B && 390 391 git checkout A && 392 echo g >z/g && 393 git add z/g && 394 test_tick && 395 git commit -m "A" && 396 397 git checkout B && 398 mkdir y && 399 mkdir x && 400 git mv z/b y/ && 401 git mv z/c y/ && 402 git mv z/d x/ && 403 git mv z/e x/ && 404 git mv z/f x/ && 405 rmdir z && 406 test_tick && 407 git commit -m "B" 408 ) 409' 410 411test_expect_success '1f-check: Split a directory into two other directories'' 412 ( 413 cd 1f && 414 415 git checkout A^0 && 416 417 git merge -s recursive B^0 && 418 419 git ls-files -s >out && 420 test_line_count = 6 out && 421 422 git rev-parse >actual \ 423 HEAD:y/b HEAD:y/c HEAD:x/d HEAD:x/e HEAD:x/f HEAD:x/g && 424 git rev-parse >expect \ 425 O:z/b O:z/c O:z/d O:z/e O:z/f A:z/g && 426 test_cmp expect actual && 427 test_path_is_missing z/g && 428 test_must_fail git rev-parse HEAD:z/g 429 ) 430' 431 432########################################################################### 433# Rules suggested by testcases in section 1: 434# 435# We should still detect the directory rename even if it wasn't just 436# the directory renamed, but the files within it. (see 1b) 437# 438# If renames split a directory into two or more others, the directory 439# with the most renames, "wins" (see 1c). However, see the testcases 440# in section 2, plus testcases 3a and 4a. 441########################################################################### 442 443 444########################################################################### 445# SECTION 2: Split into multiple directories, with equal number of paths 446# 447# Explore the splitting-a-directory rules a bit; what happens in the 448# edge cases? 449# 450# Note that there is a closely related case of a directory not being 451# split on either side of history, but being renamed differently on 452# each side. See testcase 8e for that. 453########################################################################### 454 455# Testcase 2a, Directory split into two on one side, with equal numbers of paths 456# Commit O: z/{b,c} 457# Commit A: y/b, w/c 458# Commit B: z/{b,c,d} 459# Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict 460test_expect_success '2a-setup: Directory split into two on one side, with equal numbers of paths'' 461 test_create_repo 2a && 462 ( 463 cd 2a && 464 465 mkdir z && 466 echo b >z/b && 467 echo c >z/c && 468 git add z && 469 test_tick && 470 git commit -m "O" && 471 472 git branch O && 473 git branch A && 474 git branch B && 475 476 git checkout A && 477 mkdir y && 478 mkdir w && 479 git mv z/b y/ && 480 git mv z/c w/ && 481 test_tick && 482 git commit -m "A" && 483 484 git checkout B && 485 echo d >z/d && 486 git add z/d && 487 test_tick && 488 git commit -m "B" 489 ) 490' 491 492test_expect_success '2a-check: Directory split into two on one side, with equal numbers of paths'' 493 ( 494 cd 2a && 495 496 git checkout A^0 && 497 498 test_must_fail git merge -s recursive B^0 >out && 499 test_i18ngrep "CONFLICT.*directory rename split" out && 500 501 git ls-files -s >out && 502 test_line_count = 3 out && 503 git ls-files -u >out && 504 test_line_count = 0 out && 505 git ls-files -o >out && 506 test_line_count = 1 out && 507 508 git rev-parse >actual \ 509 :0:y/b :0:w/c :0:z/d && 510 git rev-parse >expect \ 511 O:z/b O:z/c B:z/d && 512 test_cmp expect actual 513 ) 514' 515 516# Testcase 2b, Directory split into two on one side, with equal numbers of paths 517# Commit O: z/{b,c} 518# Commit A: y/b, w/c 519# Commit B: z/{b,c}, x/d 520# Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict 521test_expect_success '2b-setup: Directory split into two on one side, with equal numbers of paths'' 522 test_create_repo 2b && 523 ( 524 cd 2b && 525 526 mkdir z && 527 echo b >z/b && 528 echo c >z/c && 529 git add z && 530 test_tick && 531 git commit -m "O" && 532 533 git branch O && 534 git branch A && 535 git branch B && 536 537 git checkout A && 538 mkdir y && 539 mkdir w && 540 git mv z/b y/ && 541 git mv z/c w/ && 542 test_tick && 543 git commit -m "A" && 544 545 git checkout B && 546 mkdir x && 547 echo d >x/d && 548 git add x/d && 549 test_tick && 550 git commit -m "B" 551 ) 552' 553 554test_expect_success '2b-check: Directory split into two on one side, with equal numbers of paths'' 555 ( 556 cd 2b && 557 558 git checkout A^0 && 559 560 git merge -s recursive B^0 >out && 561 562 git ls-files -s >out && 563 test_line_count = 3 out && 564 git ls-files -u >out && 565 test_line_count = 0 out && 566 git ls-files -o >out && 567 test_line_count = 1 out && 568 569 git rev-parse >actual \ 570 :0:y/b :0:w/c :0:x/d && 571 git rev-parse >expect \ 572 O:z/b O:z/c B:x/d && 573 test_cmp expect actual && 574 test_i18ngrep ! "CONFLICT.*directory rename split" out 575 ) 576' 577 578########################################################################### 579# Rules suggested by section 2: 580# 581# None; the rule was already covered in section 1. These testcases are 582# here just to make sure the conflict resolution and necessary warning 583# messages are handled correctly. 584########################################################################### 585 586 587########################################################################### 588# SECTION 3: Path in question is the source path for some rename already 589# 590# Combining cases from Section 1 and trying to handle them could lead to 591# directory renaming detection being over-applied. So, this section 592# provides some good testcases to check that the implementation doesn't go 593# too far. 594########################################################################### 595 596# Testcase 3a, Avoid implicit rename if involved as source on other side 597# (Related to testcases 1c, 1f, and 9h) 598# Commit O: z/{b,c,d} 599# Commit A: z/{b,c,d} (no change) 600# Commit B: y/{b,c}, x/d 601# Expected: y/{b,c}, x/d 602test_expect_success '3a-setup: Avoid implicit rename if involved as source on other side'' 603 test_create_repo 3a && 604 ( 605 cd 3a && 606 607 mkdir z && 608 echo b >z/b && 609 echo c >z/c && 610 echo d >z/d && 611 git add z && 612 test_tick && 613 git commit -m "O" && 614 615 git branch O && 616 git branch A && 617 git branch B && 618 619 git checkout A && 620 test_tick && 621 git commit --allow-empty -m "A" && 622 623 git checkout B && 624 mkdir y && 625 mkdir x && 626 git mv z/b y/ && 627 git mv z/c y/ && 628 git mv z/d x/ && 629 rmdir z && 630 test_tick && 631 git commit -m "B" 632 ) 633' 634 635test_expect_success '3a-check: Avoid implicit rename if involved as source on other side'' 636 ( 637 cd 3a && 638 639 git checkout A^0 && 640 641 git merge -s recursive B^0 && 642 643 git ls-files -s >out && 644 test_line_count = 3 out && 645 646 git rev-parse >actual \ 647 HEAD:y/b HEAD:y/c HEAD:x/d && 648 git rev-parse >expect \ 649 O:z/b O:z/c O:z/d && 650 test_cmp expect actual 651 ) 652' 653 654# Testcase 3b, Avoid implicit rename if involved as source on other side 655# (Related to testcases 5c and 7c, also kind of 1e and 1f) 656# Commit O: z/{b,c,d} 657# Commit A: y/{b,c}, x/d 658# Commit B: z/{b,c}, w/d 659# Expected: y/{b,c}, CONFLICT:(z/d -> x/d vs. w/d) 660# NOTE: We're particularly checking that since z/d is already involved as 661# a source in a file rename on the same side of history, that we don't 662# get it involved in directory rename detection. If it were, we might 663# end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a 664# rename/rename/rename(1to3) conflict, which is just weird. 665test_expect_success '3b-setup: Avoid implicit rename if involved as source on current side'' 666 test_create_repo 3b && 667 ( 668 cd 3b && 669 670 mkdir z && 671 echo b >z/b && 672 echo c >z/c && 673 echo d >z/d && 674 git add z && 675 test_tick && 676 git commit -m "O" && 677 678 git branch O && 679 git branch A && 680 git branch B && 681 682 git checkout A && 683 mkdir y && 684 mkdir x && 685 git mv z/b y/ && 686 git mv z/c y/ && 687 git mv z/d x/ && 688 rmdir z && 689 test_tick && 690 git commit -m "A" && 691 692 git checkout B && 693 mkdir w && 694 git mv z/d w/ && 695 test_tick && 696 git commit -m "B" 697 ) 698' 699 700test_expect_success '3b-check: Avoid implicit rename if involved as source on current side'' 701 ( 702 cd 3b && 703 704 git checkout A^0 && 705 706 test_must_fail git merge -s recursive B^0 >out && 707 test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out && 708 test_i18ngrep ! CONFLICT.*rename/rename.*y/d out && 709 710 git ls-files -s >out && 711 test_line_count = 5 out && 712 git ls-files -u >out && 713 test_line_count = 3 out && 714 git ls-files -o >out && 715 test_line_count = 1 out && 716 717 git rev-parse >actual \ 718 :0:y/b :0:y/c :1:z/d :2:x/d :3:w/d && 719 git rev-parse >expect \ 720 O:z/b O:z/c O:z/d O:z/d O:z/d && 721 test_cmp expect actual && 722 723 test_path_is_missing z/d && 724 git hash-object >actual \ 725 x/d w/d && 726 git rev-parse >expect \ 727 O:z/d O:z/d && 728 test_cmp expect actual 729 ) 730' 731 732########################################################################### 733# Rules suggested by section 3: 734# 735# Avoid directory-rename-detection for a path, if that path is the source 736# of a rename on either side of a merge. 737########################################################################### 738 739 740########################################################################### 741# SECTION 4: Partially renamed directory; still exists on both sides of merge 742# 743# What if we were to attempt to do directory rename detection when someone 744# "mostly" moved a directory but still left some files around, or, 745# equivalently, fully renamed a directory in one commmit and then recreated 746# that directory in a later commit adding some new files and then tried to 747# merge? 748# 749# It's hard to divine user intent in these cases, because you can make an 750# argument that, depending on the intermediate history of the side being 751# merged, that some users will want files in that directory to 752# automatically be detected and renamed, while users with a different 753# intermediate history wouldn't want that rename to happen. 754# 755# I think that it is best to simply not have directory rename detection 756# apply to such cases. My reasoning for this is four-fold: (1) it's 757# easiest for users in general to figure out what happened if we don't 758# apply directory rename detection in any such case, (2) it's an easy rule 759# to explain ["We don't do directory rename detection if the directory 760# still exists on both sides of the merge"], (3) we can get some hairy 761# edge/corner cases that would be really confusing and possibly not even 762# representable in the index if we were to even try, and [related to 3] (4) 763# attempting to resolve this issue of divining user intent by examining 764# intermediate history goes against the spirit of three-way merges and is a 765# path towards crazy corner cases that are far more complex than what we're 766# already dealing with. 767# 768# Note that the wording of the rule ("We don't do directory rename 769# detection if the directory still exists on both sides of the merge.") 770# also excludes "renaming" of a directory into a subdirectory of itself 771# (e.g. /some/dir/* -> /some/dir/subdir/*). It may be possible to carve 772# out an exception for "renaming"-beneath-itself cases without opening 773# weird edge/corner cases for other partial directory renames, but for now 774# we are keeping the rule simple. 775# 776# This section contains a test for a partially-renamed-directory case. 777########################################################################### 778 779# Testcase 4a, Directory split, with original directory still present 780# (Related to testcase 1f) 781# Commit O: z/{b,c,d,e} 782# Commit A: y/{b,c,d}, z/e 783# Commit B: z/{b,c,d,e,f} 784# Expected: y/{b,c,d}, z/{e,f} 785# NOTE: Even though most files from z moved to y, we don't want f to follow. 786 787test_expect_success '4a-setup: Directory split, with original directory still present'' 788 test_create_repo 4a && 789 ( 790 cd 4a && 791 792 mkdir z && 793 echo b >z/b && 794 echo c >z/c && 795 echo d >z/d && 796 echo e >z/e && 797 git add z && 798 test_tick && 799 git commit -m "O" && 800 801 git branch O && 802 git branch A && 803 git branch B && 804 805 git checkout A && 806 mkdir y && 807 git mv z/b y/ && 808 git mv z/c y/ && 809 git mv z/d y/ && 810 test_tick && 811 git commit -m "A" && 812 813 git checkout B && 814 echo f >z/f && 815 git add z/f && 816 test_tick && 817 git commit -m "B" 818 ) 819' 820 821test_expect_success '4a-check: Directory split, with original directory still present'' 822 ( 823 cd 4a && 824 825 git checkout A^0 && 826 827 git merge -s recursive B^0 && 828 829 git ls-files -s >out && 830 test_line_count = 5 out && 831 git ls-files -u >out && 832 test_line_count = 0 out && 833 git ls-files -o >out && 834 test_line_count = 1 out && 835 836 git rev-parse >actual \ 837 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/e HEAD:z/f && 838 git rev-parse >expect \ 839 O:z/b O:z/c O:z/d O:z/e B:z/f && 840 test_cmp expect actual 841 ) 842' 843 844########################################################################### 845# Rules suggested by section 4: 846# 847# Directory-rename-detection should be turned off for any directories (as 848# a source for renames) that exist on both sides of the merge. (The "as 849# a source for renames" clarification is due to cases like 1c where 850# the target directory exists on both sides and we do want the rename 851# detection.) But, sadly, see testcase 8b. 852########################################################################### 853 854 855########################################################################### 856# SECTION 5: Files/directories in the way of subset of to-be-renamed paths 857# 858# Implicitly renaming files due to a detected directory rename could run 859# into problems if there are files or directories in the way of the paths 860# we want to rename. Explore such cases in this section. 861########################################################################### 862 863# Testcase 5a, Merge directories, other side adds files to original and target 864# Commit O: z/{b,c}, y/d 865# Commit A: z/{b,c,e_1,f}, y/{d,e_2} 866# Commit B: y/{b,c,d} 867# Expected: z/e_1, y/{b,c,d,e_2,f} + CONFLICT warning 868# NOTE: While directory rename detection is active here causing z/f to 869# become y/f, we did not apply this for z/e_1 because that would 870# give us an add/add conflict for y/e_1 vs y/e_2. This problem with 871# this add/add, is that both versions of y/e are from the same side 872# of history, giving us no way to represent this conflict in the 873# index. 874 875test_expect_success '5a-setup: Merge directories, other side adds files to original and target'' 876 test_create_repo 5a && 877 ( 878 cd 5a && 879 880 mkdir z && 881 echo b >z/b && 882 echo c >z/c && 883 mkdir y && 884 echo d >y/d && 885 git add z y && 886 test_tick && 887 git commit -m "O" && 888 889 git branch O && 890 git branch A && 891 git branch B && 892 893 git checkout A && 894 echo e1 >z/e && 895 echo f >z/f && 896 echo e2 >y/e && 897 git add z/e z/f y/e && 898 test_tick && 899 git commit -m "A" && 900 901 git checkout B && 902 git mv z/b y/ && 903 git mv z/c y/ && 904 rmdir z && 905 test_tick && 906 git commit -m "B" 907 ) 908' 909 910test_expect_success '5a-check: Merge directories, other side adds files to original and target'' 911 ( 912 cd 5a && 913 914 git checkout A^0 && 915 916 test_must_fail git merge -s recursive B^0 >out && 917 test_i18ngrep "CONFLICT.*implicit dir rename" out && 918 919 git ls-files -s >out && 920 test_line_count = 6 out && 921 git ls-files -u >out && 922 test_line_count = 0 out && 923 git ls-files -o >out && 924 test_line_count = 1 out && 925 926 git rev-parse >actual \ 927 :0:y/b :0:y/c :0:y/d :0:y/e :0:z/e :0:y/f && 928 git rev-parse >expect \ 929 O:z/b O:z/c O:y/d A:y/e A:z/e A:z/f && 930 test_cmp expect actual 931 ) 932' 933 934# Testcase 5b, Rename/delete in order to get add/add/add conflict 935# (Related to testcase 8d; these may appear slightly inconsistent to users; 936# Also related to testcases 7d and 7e) 937# Commit O: z/{b,c,d_1} 938# Commit A: y/{b,c,d_2} 939# Commit B: z/{b,c,d_1,e}, y/d_3 940# Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3) 941# NOTE: If z/d_1 in commit B were to be involved in dir rename detection, as 942# we normaly would since z/ is being renamed to y/, then this would be 943# a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add 944# conflict of y/d_1 vs. y/d_2 vs. y/d_3. Add/add/add is not 945# representable in the index, so the existence of y/d_3 needs to 946# cause us to bail on directory rename detection for that path, falling 947# back to git behavior without the directory rename detection. 948 949test_expect_success '5b-setup: Rename/delete in order to get add/add/add conflict'' 950 test_create_repo 5b && 951 ( 952 cd 5b && 953 954 mkdir z && 955 echo b >z/b && 956 echo c >z/c && 957 echo d1 >z/d && 958 git add z && 959 test_tick && 960 git commit -m "O" && 961 962 git branch O && 963 git branch A && 964 git branch B && 965 966 git checkout A && 967 git rm z/d && 968 git mv z y && 969 echo d2 >y/d && 970 git add y/d && 971 test_tick && 972 git commit -m "A" && 973 974 git checkout B && 975 mkdir y && 976 echo d3 >y/d && 977 echo e >z/e && 978 git add y/d z/e && 979 test_tick && 980 git commit -m "B" 981 ) 982' 983 984test_expect_success '5b-check: Rename/delete in order to get add/add/add conflict'' 985 ( 986 cd 5b && 987 988 git checkout A^0 && 989 990 test_must_fail git merge -s recursive B^0 >out && 991 test_i18ngrep "CONFLICT (add/add).* y/d" out && 992 993 git ls-files -s >out && 994 test_line_count = 5 out && 995 git ls-files -u >out && 996 test_line_count = 2 out && 997 git ls-files -o >out && 998 test_line_count = 1 out && 9991000 git rev-parse >actual \1001 :0:y/b :0:y/c :0:y/e :2:y/d :3:y/d &&1002 git rev-parse >expect \1003 O:z/b O:z/c B:z/e A:y/d B:y/d &&1004 test_cmp expect actual &&10051006 test_must_fail git rev-parse :1:y/d &&1007 test_path_is_file y/d1008 )1009'10101011# Testcase 5c, Transitive rename would cause rename/rename/rename/add/add/add1012# (Directory rename detection would result in transitive rename vs.1013# rename/rename(1to2) and turn it into a rename/rename(1to3). Further,1014# rename paths conflict with separate adds on the other side)1015# (Related to testcases 3b and 7c)1016# Commit O: z/{b,c}, x/d_11017# Commit A: y/{b,c,d_2}, w/d_11018# Commit B: z/{b,c,d_1,e}, w/d_3, y/d_41019# Expected: A mess, but only a rename/rename(1to2)/add/add mess. Use the1020# presence of y/d_4 in B to avoid doing transitive rename of1021# x/d_1 -> z/d_1 -> y/d_1, so that the only paths we have at1022# y/d are y/d_2 and y/d_4. We still do the move from z/e to y/e,1023# though, because it doesn't have anything in the way.10241025test_expect_success '5c-setup: Transitive rename would cause rename/rename/rename/add/add/add''1026 test_create_repo 5c &&1027 (1028 cd 5c &&10291030 mkdir z &&1031 echo b >z/b &&1032 echo c >z/c &&1033 mkdir x &&1034 echo d1 >x/d &&1035 git add z x &&1036 test_tick &&1037 git commit -m "O" &&10381039 git branch O &&1040 git branch A &&1041 git branch B &&10421043 git checkout A &&1044 git mv z y &&1045 echo d2 >y/d &&1046 git add y/d &&1047 git mv x w &&1048 test_tick &&1049 git commit -m "A" &&10501051 git checkout B &&1052 git mv x/d z/ &&1053 mkdir w &&1054 mkdir y &&1055 echo d3 >w/d &&1056 echo d4 >y/d &&1057 echo e >z/e &&1058 git add w/ y/ z/e &&1059 test_tick &&1060 git commit -m "B"1061 )1062'10631064test_expect_success '5c-check: Transitive rename would cause rename/rename/rename/add/add/add''1065 (1066 cd 5c &&10671068 git checkout A^0 &&10691070 test_must_fail git merge -s recursive B^0 >out &&1071 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&1072 test_i18ngrep "CONFLICT (add/add).* y/d" out &&10731074 git ls-files -s >out &&1075 test_line_count = 9 out &&1076 git ls-files -u >out &&1077 test_line_count = 6 out &&1078 git ls-files -o >out &&1079 test_line_count = 3 out &&10801081 git rev-parse >actual \1082 :0:y/b :0:y/c :0:y/e &&1083 git rev-parse >expect \1084 O:z/b O:z/c B:z/e &&1085 test_cmp expect actual &&10861087 test_must_fail git rev-parse :1:y/d &&1088 git rev-parse >actual \1089 :2:w/d :3:w/d :1:x/d :2:y/d :3:y/d :3:z/d &&1090 git rev-parse >expect \1091 O:x/d B:w/d O:x/d A:y/d B:y/d O:x/d &&1092 test_cmp expect actual &&10931094 git hash-object >actual \1095 w/d~HEAD w/d~B^0 z/d &&1096 git rev-parse >expect \1097 O:x/d B:w/d O:x/d &&1098 test_cmp expect actual &&1099 test_path_is_missing x/d &&1100 test_path_is_file y/d &&1101 grep -q "<<<<" y/d # conflict markers should be present1102 )1103'11041105# Testcase 5d, Directory/file/file conflict due to directory rename1106# Commit O: z/{b,c}1107# Commit A: y/{b,c,d_1}1108# Commit B: z/{b,c,d_2,f}, y/d/e1109# Expected: y/{b,c,d/e,f}, z/d_2, CONFLICT(file/directory), y/d_1~HEAD1110# Note: The fact that y/d/ exists in B makes us bail on directory rename1111# detection for z/d_2, but that doesn't prevent us from applying the1112# directory rename detection for z/f -> y/f.11131114test_expect_success '5d-setup: Directory/file/file conflict due to directory rename''1115 test_create_repo 5d &&1116 (1117 cd 5d &&11181119 mkdir z &&1120 echo b >z/b &&1121 echo c >z/c &&1122 git add z &&1123 test_tick &&1124 git commit -m "O" &&11251126 git branch O &&1127 git branch A &&1128 git branch B &&11291130 git checkout A &&1131 git mv z y &&1132 echo d1 >y/d &&1133 git add y/d &&1134 test_tick &&1135 git commit -m "A" &&11361137 git checkout B &&1138 mkdir -p y/d &&1139 echo e >y/d/e &&1140 echo d2 >z/d &&1141 echo f >z/f &&1142 git add y/d/e z/d z/f &&1143 test_tick &&1144 git commit -m "B"1145 )1146'11471148test_expect_success '5d-check: Directory/file/file conflict due to directory rename''1149 (1150 cd 5d &&11511152 git checkout A^0 &&11531154 test_must_fail git merge -s recursive B^0 >out &&1155 test_i18ngrep "CONFLICT (file/directory).*y/d" out &&11561157 git ls-files -s >out &&1158 test_line_count = 6 out &&1159 git ls-files -u >out &&1160 test_line_count = 1 out &&1161 git ls-files -o >out &&1162 test_line_count = 2 out &&11631164 git rev-parse >actual \1165 :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e &&1166 git rev-parse >expect \1167 O:z/b O:z/c B:z/d B:z/f A:y/d B:y/d/e &&1168 test_cmp expect actual &&11691170 git hash-object y/d~HEAD >actual &&1171 git rev-parse A:y/d >expect &&1172 test_cmp expect actual1173 )1174'11751176###########################################################################1177# Rules suggested by section 5:1178#1179# If a subset of to-be-renamed files have a file or directory in the way,1180# "turn off" the directory rename for those specific sub-paths, falling1181# back to old handling. But, sadly, see testcases 8a and 8b.1182###########################################################################118311841185###########################################################################1186# SECTION 6: Same side of the merge was the one that did the rename1187#1188# It may sound obvious that you only want to apply implicit directory1189# renames to directories if the _other_ side of history did the renaming.1190# If you did make an implementation that didn't explicitly enforce this1191# rule, the majority of cases that would fall under this section would1192# also be solved by following the rules from the above sections. But1193# there are still a few that stick out, so this section covers them just1194# to make sure we also get them right.1195###########################################################################11961197# Testcase 6a, Tricky rename/delete1198# Commit O: z/{b,c,d}1199# Commit A: z/b1200# Commit B: y/{b,c}, z/d1201# Expected: y/b, CONFLICT(rename/delete, z/c -> y/c vs. NULL)1202# Note: We're just checking here that the rename of z/b and z/c to put1203# them under y/ doesn't accidentally catch z/d and make it look like1204# it is also involved in a rename/delete conflict.12051206test_expect_success '6a-setup: Tricky rename/delete''1207 test_create_repo 6a &&1208 (1209 cd 6a &&12101211 mkdir z &&1212 echo b >z/b &&1213 echo c >z/c &&1214 echo d >z/d &&1215 git add z &&1216 test_tick &&1217 git commit -m "O" &&12181219 git branch O &&1220 git branch A &&1221 git branch B &&12221223 git checkout A &&1224 git rm z/c &&1225 git rm z/d &&1226 test_tick &&1227 git commit -m "A" &&12281229 git checkout B &&1230 mkdir y &&1231 git mv z/b y/ &&1232 git mv z/c y/ &&1233 test_tick &&1234 git commit -m "B"1235 )1236'12371238test_expect_success '6a-check: Tricky rename/delete''1239 (1240 cd 6a &&12411242 git checkout A^0 &&12431244 test_must_fail git merge -s recursive B^0 >out &&1245 test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&12461247 git ls-files -s >out &&1248 test_line_count = 2 out &&1249 git ls-files -u >out &&1250 test_line_count = 1 out &&1251 git ls-files -o >out &&1252 test_line_count = 1 out &&12531254 git rev-parse >actual \1255 :0:y/b :3:y/c &&1256 git rev-parse >expect \1257 O:z/b O:z/c &&1258 test_cmp expect actual1259 )1260'12611262# Testcase 6b, Same rename done on both sides1263# (Related to testcases 6c and 8e)1264# Commit O: z/{b,c}1265# Commit A: y/{b,c}1266# Commit B: y/{b,c}, z/d1267# Expected: y/{b,c}, z/d1268# Note: If we did directory rename detection here, we'd move z/d into y/,1269# but B did that rename and still decided to put the file into z/,1270# so we probably shouldn't apply directory rename detection for it.12711272test_expect_success '6b-setup: Same rename done on both sides''1273 test_create_repo 6b &&1274 (1275 cd 6b &&12761277 mkdir z &&1278 echo b >z/b &&1279 echo c >z/c &&1280 git add z &&1281 test_tick &&1282 git commit -m "O" &&12831284 git branch O &&1285 git branch A &&1286 git branch B &&12871288 git checkout A &&1289 git mv z y &&1290 test_tick &&1291 git commit -m "A" &&12921293 git checkout B &&1294 git mv z y &&1295 mkdir z &&1296 echo d >z/d &&1297 git add z/d &&1298 test_tick &&1299 git commit -m "B"1300 )1301'13021303test_expect_success '6b-check: Same rename done on both sides''1304 (1305 cd 6b &&13061307 git checkout A^0 &&13081309 git merge -s recursive B^0 &&13101311 git ls-files -s >out &&1312 test_line_count = 3 out &&1313 git ls-files -u >out &&1314 test_line_count = 0 out &&1315 git ls-files -o >out &&1316 test_line_count = 1 out &&13171318 git rev-parse >actual \1319 HEAD:y/b HEAD:y/c HEAD:z/d &&1320 git rev-parse >expect \1321 O:z/b O:z/c B:z/d &&1322 test_cmp expect actual1323 )1324'13251326# Testcase 6c, Rename only done on same side1327# (Related to testcases 6b and 8e)1328# Commit O: z/{b,c}1329# Commit A: z/{b,c} (no change)1330# Commit B: y/{b,c}, z/d1331# Expected: y/{b,c}, z/d1332# NOTE: Seems obvious, but just checking that the implementation doesn't1333# "accidentally detect a rename" and give us y/{b,c,d}.13341335test_expect_success '6c-setup: Rename only done on same side''1336 test_create_repo 6c &&1337 (1338 cd 6c &&13391340 mkdir z &&1341 echo b >z/b &&1342 echo c >z/c &&1343 git add z &&1344 test_tick &&1345 git commit -m "O" &&13461347 git branch O &&1348 git branch A &&1349 git branch B &&13501351 git checkout A &&1352 test_tick &&1353 git commit --allow-empty -m "A" &&13541355 git checkout B &&1356 git mv z y &&1357 mkdir z &&1358 echo d >z/d &&1359 git add z/d &&1360 test_tick &&1361 git commit -m "B"1362 )1363'13641365test_expect_success '6c-check: Rename only done on same side''1366 (1367 cd 6c &&13681369 git checkout A^0 &&13701371 git merge -s recursive B^0 &&13721373 git ls-files -s >out &&1374 test_line_count = 3 out &&1375 git ls-files -u >out &&1376 test_line_count = 0 out &&1377 git ls-files -o >out &&1378 test_line_count = 1 out &&13791380 git rev-parse >actual \1381 HEAD:y/b HEAD:y/c HEAD:z/d &&1382 git rev-parse >expect \1383 O:z/b O:z/c B:z/d &&1384 test_cmp expect actual1385 )1386'13871388# Testcase 6d, We don't always want transitive renaming1389# (Related to testcase 1c)1390# Commit O: z/{b,c}, x/d1391# Commit A: z/{b,c}, x/d (no change)1392# Commit B: y/{b,c}, z/d1393# Expected: y/{b,c}, z/d1394# NOTE: Again, this seems obvious but just checking that the implementation1395# doesn't "accidentally detect a rename" and give us y/{b,c,d}.13961397test_expect_success '6d-setup: We do not always want transitive renaming''1398 test_create_repo 6d &&1399 (1400 cd 6d &&14011402 mkdir z &&1403 echo b >z/b &&1404 echo c >z/c &&1405 mkdir x &&1406 echo d >x/d &&1407 git add z x &&1408 test_tick &&1409 git commit -m "O" &&14101411 git branch O &&1412 git branch A &&1413 git branch B &&14141415 git checkout A &&1416 test_tick &&1417 git commit --allow-empty -m "A" &&14181419 git checkout B &&1420 git mv z y &&1421 git mv x z &&1422 test_tick &&1423 git commit -m "B"1424 )1425'14261427test_expect_success '6d-check: We do not always want transitive renaming''1428 (1429 cd 6d &&14301431 git checkout A^0 &&14321433 git merge -s recursive B^0 &&14341435 git ls-files -s >out &&1436 test_line_count = 3 out &&1437 git ls-files -u >out &&1438 test_line_count = 0 out &&1439 git ls-files -o >out &&1440 test_line_count = 1 out &&14411442 git rev-parse >actual \1443 HEAD:y/b HEAD:y/c HEAD:z/d &&1444 git rev-parse >expect \1445 O:z/b O:z/c O:x/d &&1446 test_cmp expect actual1447 )1448'14491450# Testcase 6e, Add/add from one-side1451# Commit O: z/{b,c}1452# Commit A: z/{b,c} (no change)1453# Commit B: y/{b,c,d_1}, z/d_21454# Expected: y/{b,c,d_1}, z/d_21455# NOTE: Again, this seems obvious but just checking that the implementation1456# doesn't "accidentally detect a rename" and give us y/{b,c} +1457# add/add conflict on y/d_1 vs y/d_2.14581459test_expect_success '6e-setup: Add/add from one side''1460 test_create_repo 6e &&1461 (1462 cd 6e &&14631464 mkdir z &&1465 echo b >z/b &&1466 echo c >z/c &&1467 git add z &&1468 test_tick &&1469 git commit -m "O" &&14701471 git branch O &&1472 git branch A &&1473 git branch B &&14741475 git checkout A &&1476 test_tick &&1477 git commit --allow-empty -m "A" &&14781479 git checkout B &&1480 git mv z y &&1481 echo d1 > y/d &&1482 mkdir z &&1483 echo d2 > z/d &&1484 git add y/d z/d &&1485 test_tick &&1486 git commit -m "B"1487 )1488'14891490test_expect_success '6e-check: Add/add from one side''1491 (1492 cd 6e &&14931494 git checkout A^0 &&14951496 git merge -s recursive B^0 &&14971498 git ls-files -s >out &&1499 test_line_count = 4 out &&1500 git ls-files -u >out &&1501 test_line_count = 0 out &&1502 git ls-files -o >out &&1503 test_line_count = 1 out &&15041505 git rev-parse >actual \1506 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/d &&1507 git rev-parse >expect \1508 O:z/b O:z/c B:y/d B:z/d &&1509 test_cmp expect actual1510 )1511'15121513###########################################################################1514# Rules suggested by section 6:1515#1516# Only apply implicit directory renames to directories if the other1517# side of history is the one doing the renaming.1518###########################################################################151915201521###########################################################################1522# SECTION 7: More involved Edge/Corner cases1523#1524# The ruleset we have generated in the above sections seems to provide1525# well-defined merges. But can we find edge/corner cases that either (a)1526# are harder for users to understand, or (b) have a resolution that is1527# non-intuitive or suboptimal?1528#1529# The testcases in this section dive into cases that I've tried to craft in1530# a way to find some that might be surprising to users or difficult for1531# them to understand (the next section will look at non-intuitive or1532# suboptimal merge results). Some of the testcases are similar to ones1533# from past sections, but have been simplified to try to highlight error1534# messages using a "modified" path (due to the directory rename). Are1535# users okay with these?1536#1537# In my opinion, testcases that are difficult to understand from this1538# section is due to difficulty in the testcase rather than the directory1539# renaming (similar to how t6042 and t6036 have difficult resolutions due1540# to the problem setup itself being complex). And I don't think the1541# error messages are a problem.1542#1543# On the other hand, the testcases in section 8 worry me slightly more...1544###########################################################################15451546# Testcase 7a, rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file1547# Commit O: z/{b,c}1548# Commit A: y/{b,c}1549# Commit B: w/b, x/c, z/d1550# Expected: y/d, CONFLICT(rename/rename for both z/b and z/c)1551# NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d.15521553test_expect_success '7a-setup: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file''1554 test_create_repo 7a &&1555 (1556 cd 7a &&15571558 mkdir z &&1559 echo b >z/b &&1560 echo c >z/c &&1561 git add z &&1562 test_tick &&1563 git commit -m "O" &&15641565 git branch O &&1566 git branch A &&1567 git branch B &&15681569 git checkout A &&1570 git mv z y &&1571 test_tick &&1572 git commit -m "A" &&15731574 git checkout B &&1575 mkdir w &&1576 mkdir x &&1577 git mv z/b w/ &&1578 git mv z/c x/ &&1579 echo d > z/d &&1580 git add z/d &&1581 test_tick &&1582 git commit -m "B"1583 )1584'15851586test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file''1587 (1588 cd 7a &&15891590 git checkout A^0 &&15911592 test_must_fail git merge -s recursive B^0 >out &&1593 test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&1594 test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&15951596 git ls-files -s >out &&1597 test_line_count = 7 out &&1598 git ls-files -u >out &&1599 test_line_count = 6 out &&1600 git ls-files -o >out &&1601 test_line_count = 1 out &&16021603 git rev-parse >actual \1604 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:x/c :0:y/d &&1605 git rev-parse >expect \1606 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d &&1607 test_cmp expect actual &&16081609 git hash-object >actual \1610 y/b w/b y/c x/c &&1611 git rev-parse >expect \1612 O:z/b O:z/b O:z/c O:z/c &&1613 test_cmp expect actual1614 )1615'16161617# Testcase 7b, rename/rename(2to1), but only due to transitive rename1618# (Related to testcase 1d)1619# Commit O: z/{b,c}, x/d_1, w/d_21620# Commit A: y/{b,c,d_2}, x/d_11621# Commit B: z/{b,c,d_1}, w/d_21622# Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d)16231624test_expect_success '7b-setup: rename/rename(2to1), but only due to transitive rename''1625 test_create_repo 7b &&1626 (1627 cd 7b &&16281629 mkdir z &&1630 mkdir x &&1631 mkdir w &&1632 echo b >z/b &&1633 echo c >z/c &&1634 echo d1 > x/d &&1635 echo d2 > w/d &&1636 git add z x w &&1637 test_tick &&1638 git commit -m "O" &&16391640 git branch O &&1641 git branch A &&1642 git branch B &&16431644 git checkout A &&1645 git mv z y &&1646 git mv w/d y/ &&1647 test_tick &&1648 git commit -m "A" &&16491650 git checkout B &&1651 git mv x/d z/ &&1652 rmdir x &&1653 test_tick &&1654 git commit -m "B"1655 )1656'16571658test_expect_success '7b-check: rename/rename(2to1), but only due to transitive rename''1659 (1660 cd 7b &&16611662 git checkout A^0 &&16631664 test_must_fail git merge -s recursive B^0 >out &&1665 test_i18ngrep "CONFLICT (rename/rename)" out &&16661667 git ls-files -s >out &&1668 test_line_count = 4 out &&1669 git ls-files -u >out &&1670 test_line_count = 2 out &&1671 git ls-files -o >out &&1672 test_line_count = 3 out &&16731674 git rev-parse >actual \1675 :0:y/b :0:y/c :2:y/d :3:y/d &&1676 git rev-parse >expect \1677 O:z/b O:z/c O:w/d O:x/d &&1678 test_cmp expect actual &&16791680 test_path_is_missing y/d &&1681 test_path_is_file y/d~HEAD &&1682 test_path_is_file y/d~B^0 &&16831684 git hash-object >actual \1685 y/d~HEAD y/d~B^0 &&1686 git rev-parse >expect \1687 O:w/d O:x/d &&1688 test_cmp expect actual1689 )1690'16911692# Testcase 7c, rename/rename(1to...2or3); transitive rename may add complexity1693# (Related to testcases 3b and 5c)1694# Commit O: z/{b,c}, x/d1695# Commit A: y/{b,c}, w/d1696# Commit B: z/{b,c,d}1697# Expected: y/{b,c}, CONFLICT(x/d -> w/d vs. y/d)1698# NOTE: z/ was renamed to y/ so we do want to report1699# neither CONFLICT(x/d -> w/d vs. z/d)1700# nor CONFLiCT x/d -> w/d vs. y/d vs. z/d)17011702test_expect_success '7c-setup: rename/rename(1to...2or3); transitive rename may add complexity''1703 test_create_repo 7c &&1704 (1705 cd 7c &&17061707 mkdir z &&1708 echo b >z/b &&1709 echo c >z/c &&1710 mkdir x &&1711 echo d >x/d &&1712 git add z x &&1713 test_tick &&1714 git commit -m "O" &&17151716 git branch O &&1717 git branch A &&1718 git branch B &&17191720 git checkout A &&1721 git mv z y &&1722 git mv x w &&1723 test_tick &&1724 git commit -m "A" &&17251726 git checkout B &&1727 git mv x/d z/ &&1728 rmdir x &&1729 test_tick &&1730 git commit -m "B"1731 )1732'17331734test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may add complexity''1735 (1736 cd 7c &&17371738 git checkout A^0 &&17391740 test_must_fail git merge -s recursive B^0 >out &&1741 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&17421743 git ls-files -s >out &&1744 test_line_count = 5 out &&1745 git ls-files -u >out &&1746 test_line_count = 3 out &&1747 git ls-files -o >out &&1748 test_line_count = 1 out &&17491750 git rev-parse >actual \1751 :0:y/b :0:y/c :1:x/d :2:w/d :3:y/d &&1752 git rev-parse >expect \1753 O:z/b O:z/c O:x/d O:x/d O:x/d &&1754 test_cmp expect actual1755 )1756'17571758# Testcase 7d, transitive rename involved in rename/delete; how is it reported?1759# (Related somewhat to testcases 5b and 8d)1760# Commit O: z/{b,c}, x/d1761# Commit A: y/{b,c}1762# Commit B: z/{b,c,d}1763# Expected: y/{b,c}, CONFLICT(delete x/d vs rename to y/d)1764# NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d)17651766test_expect_success '7d-setup: transitive rename involved in rename/delete; how is it reported?''1767 test_create_repo 7d &&1768 (1769 cd 7d &&17701771 mkdir z &&1772 echo b >z/b &&1773 echo c >z/c &&1774 mkdir x &&1775 echo d >x/d &&1776 git add z x &&1777 test_tick &&1778 git commit -m "O" &&17791780 git branch O &&1781 git branch A &&1782 git branch B &&17831784 git checkout A &&1785 git mv z y &&1786 git rm -rf x &&1787 test_tick &&1788 git commit -m "A" &&17891790 git checkout B &&1791 git mv x/d z/ &&1792 rmdir x &&1793 test_tick &&1794 git commit -m "B"1795 )1796'17971798test_expect_success '7d-check: transitive rename involved in rename/delete; how is it reported?''1799 (1800 cd 7d &&18011802 git checkout A^0 &&18031804 test_must_fail git merge -s recursive B^0 >out &&1805 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&18061807 git ls-files -s >out &&1808 test_line_count = 3 out &&1809 git ls-files -u >out &&1810 test_line_count = 1 out &&1811 git ls-files -o >out &&1812 test_line_count = 1 out &&18131814 git rev-parse >actual \1815 :0:y/b :0:y/c :3:y/d &&1816 git rev-parse >expect \1817 O:z/b O:z/c O:x/d &&1818 test_cmp expect actual1819 )1820'18211822# Testcase 7e, transitive rename in rename/delete AND dirs in the way1823# (Very similar to 'both rename source and destination involved in D/F conflict' from t6022-merge-rename.sh)1824# (Also related to testcases 9c and 9d)1825# Commit O: z/{b,c}, x/d_11826# Commit A: y/{b,c,d/g}, x/d/f1827# Commit B: z/{b,c,d_1}1828# Expected: rename/delete(x/d_1->y/d_1 vs. None) + D/F conflict on y/d1829# y/{b,c,d/g}, y/d_1~B^0, x/d/f18301831# NOTE: The main path of interest here is d_1 and where it ends up, but1832# this is actually a case that has two potential directory renames1833# involved and D/F conflict(s), so it makes sense to walk through1834# each step.1835#1836# Commit A renames z/ -> y/. Thus everything that B adds to z/1837# should be instead moved to y/. This gives us the D/F conflict on1838# y/d because x/d_1 -> z/d_1 -> y/d_1 conflicts with y/d/g.1839#1840# Further, commit B renames x/ -> z/, thus everything A adds to x/1841# should instead be moved to z/...BUT we removed z/ and renamed it1842# to y/, so maybe everything should move not from x/ to z/, but1843# from x/ to z/ to y/. Doing so might make sense from the logic so1844# far, but note that commit A had both an x/ and a y/; it did the1845# renaming of z/ to y/ and created x/d/f and it clearly made these1846# things separate, so it doesn't make much sense to push these1847# together. Doing so is what I'd call a doubly transitive rename;1848# see testcases 9c and 9d for further discussion of this issue and1849# how it's resolved.18501851test_expect_success '7e-setup: transitive rename in rename/delete AND dirs in the way''1852 test_create_repo 7e &&1853 (1854 cd 7e &&18551856 mkdir z &&1857 echo b >z/b &&1858 echo c >z/c &&1859 mkdir x &&1860 echo d1 >x/d &&1861 git add z x &&1862 test_tick &&1863 git commit -m "O" &&18641865 git branch O &&1866 git branch A &&1867 git branch B &&18681869 git checkout A &&1870 git mv z y &&1871 git rm x/d &&1872 mkdir -p x/d &&1873 mkdir -p y/d &&1874 echo f >x/d/f &&1875 echo g >y/d/g &&1876 git add x/d/f y/d/g &&1877 test_tick &&1878 git commit -m "A" &&18791880 git checkout B &&1881 git mv x/d z/ &&1882 rmdir x &&1883 test_tick &&1884 git commit -m "B"1885 )1886'18871888test_expect_success '7e-check: transitive rename in rename/delete AND dirs in the way''1889 (1890 cd 7e &&18911892 git checkout A^0 &&18931894 test_must_fail git merge -s recursive B^0 >out &&1895 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&18961897 git ls-files -s >out &&1898 test_line_count = 5 out &&1899 git ls-files -u >out &&1900 test_line_count = 1 out &&1901 git ls-files -o >out &&1902 test_line_count = 2 out &&19031904 git rev-parse >actual \1905 :0:x/d/f :0:y/d/g :0:y/b :0:y/c :3:y/d &&1906 git rev-parse >expect \1907 A:x/d/f A:y/d/g O:z/b O:z/c O:x/d &&1908 test_cmp expect actual &&19091910 git hash-object y/d~B^0 >actual &&1911 git rev-parse O:x/d >expect &&1912 test_cmp expect actual1913 )1914'19151916###########################################################################1917# SECTION 8: Suboptimal merges1918#1919# As alluded to in the last section, the ruleset we have built up for1920# detecting directory renames unfortunately has some special cases where it1921# results in slightly suboptimal or non-intuitive behavior. This section1922# explores these cases.1923#1924# To be fair, we already had non-intuitive or suboptimal behavior for most1925# of these cases in git before introducing implicit directory rename1926# detection, but it'd be nice if there was a modified ruleset out there1927# that handled these cases a bit better.1928###########################################################################19291930# Testcase 8a, Dual-directory rename, one into the others' way1931# Commit O. x/{a,b}, y/{c,d}1932# Commit A. x/{a,b,e}, y/{c,d,f}1933# Commit B. y/{a,b}, z/{c,d}1934#1935# Possible Resolutions:1936# w/o dir-rename detection: y/{a,b,f}, z/{c,d}, x/e1937# Currently expected: y/{a,b,e,f}, z/{c,d}1938# Optimal: y/{a,b,e}, z/{c,d,f}1939#1940# Note: Both x and y got renamed and it'd be nice to detect both, and we do1941# better with directory rename detection than git did without, but the1942# simple rule from section 5 prevents me from handling this as optimally as1943# we potentially could.19441945test_expect_success '8a-setup: Dual-directory rename, one into the others way''1946 test_create_repo 8a &&1947 (1948 cd 8a &&19491950 mkdir x &&1951 mkdir y &&1952 echo a >x/a &&1953 echo b >x/b &&1954 echo c >y/c &&1955 echo d >y/d &&1956 git add x y &&1957 test_tick &&1958 git commit -m "O" &&19591960 git branch O &&1961 git branch A &&1962 git branch B &&19631964 git checkout A &&1965 echo e >x/e &&1966 echo f >y/f &&1967 git add x/e y/f &&1968 test_tick &&1969 git commit -m "A" &&19701971 git checkout B &&1972 git mv y z &&1973 git mv x y &&1974 test_tick &&1975 git commit -m "B"1976 )1977'19781979test_expect_success '8a-check: Dual-directory rename, one into the others way''1980 (1981 cd 8a &&19821983 git checkout A^0 &&19841985 git merge -s recursive B^0 &&19861987 git ls-files -s >out &&1988 test_line_count = 6 out &&1989 git ls-files -u >out &&1990 test_line_count = 0 out &&1991 git ls-files -o >out &&1992 test_line_count = 1 out &&19931994 git rev-parse >actual \1995 HEAD:y/a HEAD:y/b HEAD:y/e HEAD:y/f HEAD:z/c HEAD:z/d &&1996 git rev-parse >expect \1997 O:x/a O:x/b A:x/e A:y/f O:y/c O:y/d &&1998 test_cmp expect actual1999 )2000'20012002# Testcase 8b, Dual-directory rename, one into the others' way, with conflicting filenames2003# Commit O. x/{a_1,b_1}, y/{a_2,b_2}2004# Commit A. x/{a_1,b_1,e_1}, y/{a_2,b_2,e_2}2005# Commit B. y/{a_1,b_1}, z/{a_2,b_2}2006#2007# w/o dir-rename detection: y/{a_1,b_1,e_2}, z/{a_2,b_2}, x/e_12008# Currently expected: <same>2009# Scary: y/{a_1,b_1}, z/{a_2,b_2}, CONFLICT(add/add, e_1 vs. e_2)2010# Optimal: y/{a_1,b_1,e_1}, z/{a_2,b_2,e_2}2011#2012# Note: Very similar to 8a, except instead of 'e' and 'f' in directories x and2013# y, both are named 'e'. Without directory rename detection, neither file2014# moves directories. Implement directory rename detection suboptimally, and2015# you get an add/add conflict, but both files were added in commit A, so this2016# is an add/add conflict where one side of history added both files --2017# something we can't represent in the index. Obviously, we'd prefer the last2018# resolution, but our previous rules are too coarse to allow it. Using both2019# the rules from section 4 and section 5 save us from the Scary resolution,2020# making us fall back to pre-directory-rename-detection behavior for both2021# e_1 and e_2.20222023test_expect_success '8b-setup: Dual-directory rename, one into the others way, with conflicting filenames''2024 test_create_repo 8b &&2025 (2026 cd 8b &&20272028 mkdir x &&2029 mkdir y &&2030 echo a1 >x/a &&2031 echo b1 >x/b &&2032 echo a2 >y/a &&2033 echo b2 >y/b &&2034 git add x y &&2035 test_tick &&2036 git commit -m "O" &&20372038 git branch O &&2039 git branch A &&2040 git branch B &&20412042 git checkout A &&2043 echo e1 >x/e &&2044 echo e2 >y/e &&2045 git add x/e y/e &&2046 test_tick &&2047 git commit -m "A" &&20482049 git checkout B &&2050 git mv y z &&2051 git mv x y &&2052 test_tick &&2053 git commit -m "B"2054 )2055'20562057test_expect_success '8b-check: Dual-directory rename, one into the others way, with conflicting filenames''2058 (2059 cd 8b &&20602061 git checkout A^0 &&20622063 git merge -s recursive B^0 &&20642065 git ls-files -s >out &&2066 test_line_count = 6 out &&2067 git ls-files -u >out &&2068 test_line_count = 0 out &&2069 git ls-files -o >out &&2070 test_line_count = 1 out &&20712072 git rev-parse >actual \2073 HEAD:y/a HEAD:y/b HEAD:z/a HEAD:z/b HEAD:x/e HEAD:y/e &&2074 git rev-parse >expect \2075 O:x/a O:x/b O:y/a O:y/b A:x/e A:y/e &&2076 test_cmp expect actual2077 )2078'20792080# Testcase 8c, rename+modify/delete2081# (Related to testcases 5b and 8d)2082# Commit O: z/{b,c,d}2083# Commit A: y/{b,c}2084# Commit B: z/{b,c,d_modified,e}2085# Expected: y/{b,c,e}, CONFLICT(rename+modify/delete: x/d -> y/d or deleted)2086#2087# Note: This testcase doesn't present any concerns for me...until you2088# compare it with testcases 5b and 8d. See notes in 8d for more2089# details.20902091test_expect_success '8c-setup: rename+modify/delete''2092 test_create_repo 8c &&2093 (2094 cd 8c &&20952096 mkdir z &&2097 echo b >z/b &&2098 echo c >z/c &&2099 test_seq 1 10 >z/d &&2100 git add z &&2101 test_tick &&2102 git commit -m "O" &&21032104 git branch O &&2105 git branch A &&2106 git branch B &&21072108 git checkout A &&2109 git rm z/d &&2110 git mv z y &&2111 test_tick &&2112 git commit -m "A" &&21132114 git checkout B &&2115 echo 11 >z/d &&2116 test_chmod +x z/d &&2117 echo e >z/e &&2118 git add z/d z/e &&2119 test_tick &&2120 git commit -m "B"2121 )2122'21232124test_expect_success '8c-check: rename+modify/delete''2125 (2126 cd 8c &&21272128 git checkout A^0 &&21292130 test_must_fail git merge -s recursive B^0 >out &&2131 test_i18ngrep "CONFLICT (rename/delete).* z/d.*y/d" out &&21322133 git ls-files -s >out &&2134 test_line_count = 4 out &&2135 git ls-files -u >out &&2136 test_line_count = 1 out &&2137 git ls-files -o >out &&2138 test_line_count = 1 out &&21392140 git rev-parse >actual \2141 :0:y/b :0:y/c :0:y/e :3:y/d &&2142 git rev-parse >expect \2143 O:z/b O:z/c B:z/e B:z/d &&2144 test_cmp expect actual &&21452146 test_must_fail git rev-parse :1:y/d &&2147 test_must_fail git rev-parse :2:y/d &&2148 git ls-files -s y/d | grep ^100755 &&2149 test_path_is_file y/d2150 )2151'21522153# Testcase 8d, rename/delete...or not?2154# (Related to testcase 5b; these may appear slightly inconsistent to users;2155# Also related to testcases 7d and 7e)2156# Commit O: z/{b,c,d}2157# Commit A: y/{b,c}2158# Commit B: z/{b,c,d,e}2159# Expected: y/{b,c,e}2160#2161# Note: It would also be somewhat reasonable to resolve this as2162# y/{b,c,e}, CONFLICT(rename/delete: x/d -> y/d or deleted)2163# The logic being that the only difference between this testcase and 8c2164# is that there is no modification to d. That suggests that instead of a2165# rename/modify vs. delete conflict, we should just have a rename/delete2166# conflict, otherwise we are being inconsistent.2167#2168# However...as far as consistency goes, we didn't report a conflict for2169# path d_1 in testcase 5b due to a different file being in the way. So,2170# we seem to be forced to have cases where users can change things2171# slightly and get what they may perceive as inconsistent results. It2172# would be nice to avoid that, but I'm not sure I see how.2173#2174# In this case, I'm leaning towards: commit A was the one that deleted z/d2175# and it did the rename of z to y, so the two "conflicts" (rename vs.2176# delete) are both coming from commit A, which is illogical. Conflicts2177# during merging are supposed to be about opposite sides doing things2178# differently.21792180test_expect_success '8d-setup: rename/delete...or not?''2181 test_create_repo 8d &&2182 (2183 cd 8d &&21842185 mkdir z &&2186 echo b >z/b &&2187 echo c >z/c &&2188 test_seq 1 10 >z/d &&2189 git add z &&2190 test_tick &&2191 git commit -m "O" &&21922193 git branch O &&2194 git branch A &&2195 git branch B &&21962197 git checkout A &&2198 git rm z/d &&2199 git mv z y &&2200 test_tick &&2201 git commit -m "A" &&22022203 git checkout B &&2204 echo e >z/e &&2205 git add z/e &&2206 test_tick &&2207 git commit -m "B"2208 )2209'22102211test_expect_success '8d-check: rename/delete...or not?''2212 (2213 cd 8d &&22142215 git checkout A^0 &&22162217 git merge -s recursive B^0 &&22182219 git ls-files -s >out &&2220 test_line_count = 3 out &&22212222 git rev-parse >actual \2223 HEAD:y/b HEAD:y/c HEAD:y/e &&2224 git rev-parse >expect \2225 O:z/b O:z/c B:z/e &&2226 test_cmp expect actual2227 )2228'22292230# Testcase 8e, Both sides rename, one side adds to original directory2231# Commit O: z/{b,c}2232# Commit A: y/{b,c}2233# Commit B: w/{b,c}, z/d2234#2235# Possible Resolutions:2236# w/o dir-rename detection: z/d, CONFLICT(z/b -> y/b vs. w/b),2237# CONFLICT(z/c -> y/c vs. w/c)2238# Currently expected: y/d, CONFLICT(z/b -> y/b vs. w/b),2239# CONFLICT(z/c -> y/c vs. w/c)2240# Optimal: ??2241#2242# Notes: In commit A, directory z got renamed to y. In commit B, directory z2243# did NOT get renamed; the directory is still present; instead it is2244# considered to have just renamed a subset of paths in directory z2245# elsewhere. Therefore, the directory rename done in commit A to z/2246# applies to z/d and maps it to y/d.2247#2248# It's possible that users would get confused about this, but what2249# should we do instead? Silently leaving at z/d seems just as bad or2250# maybe even worse. Perhaps we could print a big warning about z/d2251# and how we're moving to y/d in this case, but when I started thinking2252# about the ramifications of doing that, I didn't know how to rule out2253# that opening other weird edge and corner cases so I just punted.22542255test_expect_success '8e-setup: Both sides rename, one side adds to original directory''2256 test_create_repo 8e &&2257 (2258 cd 8e &&22592260 mkdir z &&2261 echo b >z/b &&2262 echo c >z/c &&2263 git add z &&2264 test_tick &&2265 git commit -m "O" &&22662267 git branch O &&2268 git branch A &&2269 git branch B &&22702271 git checkout A &&2272 git mv z y &&2273 test_tick &&2274 git commit -m "A" &&22752276 git checkout B &&2277 git mv z w &&2278 mkdir z &&2279 echo d >z/d &&2280 git add z/d &&2281 test_tick &&2282 git commit -m "B"2283 )2284'22852286test_expect_success '8e-check: Both sides rename, one side adds to original directory''2287 (2288 cd 8e &&22892290 git checkout A^0 &&22912292 test_must_fail git merge -s recursive B^0 >out 2>err &&2293 test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&2294 test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&22952296 git ls-files -s >out &&2297 test_line_count = 7 out &&2298 git ls-files -u >out &&2299 test_line_count = 6 out &&2300 git ls-files -o >out &&2301 test_line_count = 2 out &&23022303 git rev-parse >actual \2304 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:w/c :0:y/d &&2305 git rev-parse >expect \2306 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d &&2307 test_cmp expect actual &&23082309 git hash-object >actual \2310 y/b w/b y/c w/c &&2311 git rev-parse >expect \2312 O:z/b O:z/b O:z/c O:z/c &&2313 test_cmp expect actual &&23142315 test_path_is_missing z/b &&2316 test_path_is_missing z/c2317 )2318'23192320###########################################################################2321# SECTION 9: Other testcases2322#2323# This section consists of miscellaneous testcases I thought of during2324# the implementation which round out the testing.2325###########################################################################23262327# Testcase 9a, Inner renamed directory within outer renamed directory2328# (Related to testcase 1f)2329# Commit O: z/{b,c,d/{e,f,g}}2330# Commit A: y/{b,c}, x/w/{e,f,g}2331# Commit B: z/{b,c,d/{e,f,g,h},i}2332# Expected: y/{b,c,i}, x/w/{e,f,g,h}2333# NOTE: The only reason this one is interesting is because when a directory2334# is split into multiple other directories, we determine by the weight2335# of which one had the most paths going to it. A naive implementation2336# of that could take the new file in commit B at z/i to x/w/i or x/i.23372338test_expect_success '9a-setup: Inner renamed directory within outer renamed directory''2339 test_create_repo 9a &&2340 (2341 cd 9a &&23422343 mkdir -p z/d &&2344 echo b >z/b &&2345 echo c >z/c &&2346 echo e >z/d/e &&2347 echo f >z/d/f &&2348 echo g >z/d/g &&2349 git add z &&2350 test_tick &&2351 git commit -m "O" &&23522353 git branch O &&2354 git branch A &&2355 git branch B &&23562357 git checkout A &&2358 mkdir x &&2359 git mv z/d x/w &&2360 git mv z y &&2361 test_tick &&2362 git commit -m "A" &&23632364 git checkout B &&2365 echo h >z/d/h &&2366 echo i >z/i &&2367 git add z &&2368 test_tick &&2369 git commit -m "B"2370 )2371'23722373test_expect_success '9a-check: Inner renamed directory within outer renamed directory''2374 (2375 cd 9a &&23762377 git checkout A^0 &&23782379 git merge -s recursive B^0 &&23802381 git ls-files -s >out &&2382 test_line_count = 7 out &&2383 git ls-files -u >out &&2384 test_line_count = 0 out &&2385 git ls-files -o >out &&2386 test_line_count = 1 out &&23872388 git rev-parse >actual \2389 HEAD:y/b HEAD:y/c HEAD:y/i &&2390 git rev-parse >expect \2391 O:z/b O:z/c B:z/i &&2392 test_cmp expect actual &&23932394 git rev-parse >actual \2395 HEAD:x/w/e HEAD:x/w/f HEAD:x/w/g HEAD:x/w/h &&2396 git rev-parse >expect \2397 O:z/d/e O:z/d/f O:z/d/g B:z/d/h &&2398 test_cmp expect actual2399 )2400'24012402# Testcase 9b, Transitive rename with content merge2403# (Related to testcase 1c)2404# Commit O: z/{b,c}, x/d_12405# Commit A: y/{b,c}, x/d_22406# Commit B: z/{b,c,d_3}2407# Expected: y/{b,c,d_merged}24082409test_expect_success '9b-setup: Transitive rename with content merge''2410 test_create_repo 9b &&2411 (2412 cd 9b &&24132414 mkdir z &&2415 echo b >z/b &&2416 echo c >z/c &&2417 mkdir x &&2418 test_seq 1 10 >x/d &&2419 git add z x &&2420 test_tick &&2421 git commit -m "O" &&24222423 git branch O &&2424 git branch A &&2425 git branch B &&24262427 git checkout A &&2428 git mv z y &&2429 test_seq 1 11 >x/d &&2430 git add x/d &&2431 test_tick &&2432 git commit -m "A" &&24332434 git checkout B &&2435 test_seq 0 10 >x/d &&2436 git mv x/d z/d &&2437 git add z/d &&2438 test_tick &&2439 git commit -m "B"2440 )2441'24422443test_expect_success '9b-check: Transitive rename with content merge''2444 (2445 cd 9b &&24462447 git checkout A^0 &&24482449 git merge -s recursive B^0 &&24502451 git ls-files -s >out &&2452 test_line_count = 3 out &&24532454 test_seq 0 11 >expected &&2455 test_cmp expected y/d &&2456 git add expected &&2457 git rev-parse >actual \2458 HEAD:y/b HEAD:y/c HEAD:y/d &&2459 git rev-parse >expect \2460 O:z/b O:z/c :0:expected &&2461 test_cmp expect actual &&2462 test_must_fail git rev-parse HEAD:x/d &&2463 test_must_fail git rev-parse HEAD:z/d &&2464 test_path_is_missing z/d &&24652466 test$(git rev-parse HEAD:y/d)!=$(git rev-parse O:x/d)&&2467 test$(git rev-parse HEAD:y/d)!=$(git rev-parse A:x/d)&&2468 test$(git rev-parse HEAD:y/d)!=$(git rev-parse B:z/d)2469 )2470'24712472# Testcase 9c, Doubly transitive rename?2473# (Related to testcase 1c, 7e, and 9d)2474# Commit O: z/{b,c}, x/{d,e}, w/f2475# Commit A: y/{b,c}, x/{d,e,f,g}2476# Commit B: z/{b,c,d,e}, w/f2477# Expected: y/{b,c,d,e}, x/{f,g}2478#2479# NOTE: x/f and x/g may be slightly confusing here. The rename from w/f to2480# x/f is clear. Let's look beyond that. Here's the logic:2481# Commit B renamed x/ -> z/2482# Commit A renamed z/ -> y/2483# So, we could possibly further rename x/f to z/f to y/f, a doubly2484# transient rename. However, where does it end? We can chain these2485# indefinitely (see testcase 9d). What if there is a D/F conflict2486# at z/f/ or y/f/? Or just another file conflict at one of those2487# paths? In the case of an N-long chain of transient renamings,2488# where do we "abort" the rename at? Can the user make sense of2489# the resulting conflict and resolve it?2490#2491# To avoid this confusion I use the simple rule that if the other side2492# of history did a directory rename to a path that your side renamed2493# away, then ignore that particular rename from the other side of2494# history for any implicit directory renames.24952496test_expect_success '9c-setup: Doubly transitive rename?''2497 test_create_repo 9c &&2498 (2499 cd 9c &&25002501 mkdir z &&2502 echo b >z/b &&2503 echo c >z/c &&2504 mkdir x &&2505 echo d >x/d &&2506 echo e >x/e &&2507 mkdir w &&2508 echo f >w/f &&2509 git add z x w &&2510 test_tick &&2511 git commit -m "O" &&25122513 git branch O &&2514 git branch A &&2515 git branch B &&25162517 git checkout A &&2518 git mv z y &&2519 git mv w/f x/ &&2520 echo g >x/g &&2521 git add x/g &&2522 test_tick &&2523 git commit -m "A" &&25242525 git checkout B &&2526 git mv x/d z/d &&2527 git mv x/e z/e &&2528 test_tick &&2529 git commit -m "B"2530 )2531'25322533test_expect_success '9c-check: Doubly transitive rename?''2534 (2535 cd 9c &&25362537 git checkout A^0 &&25382539 git merge -s recursive B^0 >out &&2540 test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&25412542 git ls-files -s >out &&2543 test_line_count = 6 out &&2544 git ls-files -o >out &&2545 test_line_count = 1 out &&25462547 git rev-parse >actual \2548 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e HEAD:x/f HEAD:x/g &&2549 git rev-parse >expect \2550 O:z/b O:z/c O:x/d O:x/e O:w/f A:x/g &&2551 test_cmp expect actual2552 )2553'25542555# Testcase 9d, N-fold transitive rename?2556# (Related to testcase 9c...and 1c and 7e)2557# Commit O: z/a, y/b, x/c, w/d, v/e, u/f2558# Commit A: y/{a,b}, w/{c,d}, u/{e,f}2559# Commit B: z/{a,t}, x/{b,c}, v/{d,e}, u/f2560# Expected: <see NOTE first>2561#2562# NOTE: z/ -> y/ (in commit A)2563# y/ -> x/ (in commit B)2564# x/ -> w/ (in commit A)2565# w/ -> v/ (in commit B)2566# v/ -> u/ (in commit A)2567# So, if we add a file to z, say z/t, where should it end up? In u?2568# What if there's another file or directory named 't' in one of the2569# intervening directories and/or in u itself? Also, shouldn't the2570# same logic that places 't' in u/ also move ALL other files to u/?2571# What if there are file or directory conflicts in any of them? If2572# we attempted to do N-way (N-fold? N-ary? N-uple?) transitive renames2573# like this, would the user have any hope of understanding any2574# conflicts or how their working tree ended up? I think not, so I'm2575# ruling out N-ary transitive renames for N>1.2576#2577# Therefore our expected result is:2578# z/t, y/a, x/b, w/c, u/d, u/e, u/f2579# The reason that v/d DOES get transitively renamed to u/d is that u/ isn't2580# renamed somewhere. A slightly sub-optimal result, but it uses fairly2581# simple rules that are consistent with what we need for all the other2582# testcases and simplifies things for the user.25832584test_expect_success '9d-setup: N-way transitive rename?''2585 test_create_repo 9d &&2586 (2587 cd 9d &&25882589 mkdir z y x w v u &&2590 echo a >z/a &&2591 echo b >y/b &&2592 echo c >x/c &&2593 echo d >w/d &&2594 echo e >v/e &&2595 echo f >u/f &&2596 git add z y x w v u &&2597 test_tick &&2598 git commit -m "O" &&25992600 git branch O &&2601 git branch A &&2602 git branch B &&26032604 git checkout A &&2605 git mv z/a y/ &&2606 git mv x/c w/ &&2607 git mv v/e u/ &&2608 test_tick &&2609 git commit -m "A" &&26102611 git checkout B &&2612 echo t >z/t &&2613 git mv y/b x/ &&2614 git mv w/d v/ &&2615 git add z/t &&2616 test_tick &&2617 git commit -m "B"2618 )2619'26202621test_expect_success '9d-check: N-way transitive rename?''2622 (2623 cd 9d &&26242625 git checkout A^0 &&26262627 git merge -s recursive B^0 >out &&2628 test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&2629 test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&2630 test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&2631 test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out &&26322633 git ls-files -s >out &&2634 test_line_count = 7 out &&2635 git ls-files -o >out &&2636 test_line_count = 1 out &&26372638 git rev-parse >actual \2639 HEAD:z/t \2640 HEAD:y/a HEAD:x/b HEAD:w/c \2641 HEAD:u/d HEAD:u/e HEAD:u/f &&2642 git rev-parse >expect \2643 B:z/t \2644 O:z/a O:y/b O:x/c \2645 O:w/d O:v/e A:u/f &&2646 test_cmp expect actual2647 )2648'26492650# Testcase 9e, N-to-1 whammo2651# (Related to testcase 9c...and 1c and 7e)2652# Commit O: dir1/{a,b}, dir2/{d,e}, dir3/{g,h}, dirN/{j,k}2653# Commit A: dir1/{a,b,c,yo}, dir2/{d,e,f,yo}, dir3/{g,h,i,yo}, dirN/{j,k,l,yo}2654# Commit B: combined/{a,b,d,e,g,h,j,k}2655# Expected: combined/{a,b,c,d,e,f,g,h,i,j,k,l}, CONFLICT(Nto1) warnings,2656# dir1/yo, dir2/yo, dir3/yo, dirN/yo26572658test_expect_success '9e-setup: N-to-1 whammo''2659 test_create_repo 9e &&2660 (2661 cd 9e &&26622663 mkdir dir1 dir2 dir3 dirN &&2664 echo a >dir1/a &&2665 echo b >dir1/b &&2666 echo d >dir2/d &&2667 echo e >dir2/e &&2668 echo g >dir3/g &&2669 echo h >dir3/h &&2670 echo j >dirN/j &&2671 echo k >dirN/k &&2672 git add dir* &&2673 test_tick &&2674 git commit -m "O" &&26752676 git branch O &&2677 git branch A &&2678 git branch B &&26792680 git checkout A &&2681 echo c >dir1/c &&2682 echo yo >dir1/yo &&2683 echo f >dir2/f &&2684 echo yo >dir2/yo &&2685 echo i >dir3/i &&2686 echo yo >dir3/yo &&2687 echo l >dirN/l &&2688 echo yo >dirN/yo &&2689 git add dir* &&2690 test_tick &&2691 git commit -m "A" &&26922693 git checkout B &&2694 git mv dir1 combined &&2695 git mv dir2/* combined/ &&2696 git mv dir3/* combined/ &&2697 git mv dirN/* combined/ &&2698 test_tick &&2699 git commit -m "B"2700 )2701'27022703test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo''2704 (2705 cd 9e &&27062707 git checkout A^0 &&27082709 test_must_fail git merge -s recursive B^0 >out &&2710 grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line &&2711 grep -q dir1/yo error_line &&2712 grep -q dir2/yo error_line &&2713 grep -q dir3/yo error_line &&2714 grep -q dirN/yo error_line &&27152716 git ls-files -s >out &&2717 test_line_count = 16 out &&2718 git ls-files -u >out &&2719 test_line_count = 0 out &&2720 git ls-files -o >out &&2721 test_line_count = 2 out &&27222723 git rev-parse >actual \2724 :0:combined/a :0:combined/b :0:combined/c \2725 :0:combined/d :0:combined/e :0:combined/f \2726 :0:combined/g :0:combined/h :0:combined/i \2727 :0:combined/j :0:combined/k :0:combined/l &&2728 git rev-parse >expect \2729 O:dir1/a O:dir1/b A:dir1/c \2730 O:dir2/d O:dir2/e A:dir2/f \2731 O:dir3/g O:dir3/h A:dir3/i \2732 O:dirN/j O:dirN/k A:dirN/l &&2733 test_cmp expect actual &&27342735 git rev-parse >actual \2736 :0:dir1/yo :0:dir2/yo :0:dir3/yo :0:dirN/yo &&2737 git rev-parse >expect \2738 A:dir1/yo A:dir2/yo A:dir3/yo A:dirN/yo &&2739 test_cmp expect actual2740 )2741'27422743# Testcase 9f, Renamed directory that only contained immediate subdirs2744# (Related to testcases 1e & 9g)2745# Commit O: goal/{a,b}/$more_files2746# Commit A: priority/{a,b}/$more_files2747# Commit B: goal/{a,b}/$more_files, goal/c2748# Expected: priority/{a,b}/$more_files, priority/c27492750test_expect_success '9f-setup: Renamed directory that only contained immediate subdirs''2751 test_create_repo 9f &&2752 (2753 cd 9f &&27542755 mkdir -p goal/a &&2756 mkdir -p goal/b &&2757 echo foo >goal/a/foo &&2758 echo bar >goal/b/bar &&2759 echo baz >goal/b/baz &&2760 git add goal &&2761 test_tick &&2762 git commit -m "O" &&27632764 git branch O &&2765 git branch A &&2766 git branch B &&27672768 git checkout A &&2769 git mv goal/ priority &&2770 test_tick &&2771 git commit -m "A" &&27722773 git checkout B &&2774 echo c >goal/c &&2775 git add goal/c &&2776 test_tick &&2777 git commit -m "B"2778 )2779'27802781test_expect_success '9f-check: Renamed directory that only contained immediate subdirs''2782 (2783 cd 9f &&27842785 git checkout A^0 &&27862787 git merge -s recursive B^0 &&27882789 git ls-files -s >out &&2790 test_line_count = 4 out &&27912792 git rev-parse >actual \2793 HEAD:priority/a/foo \2794 HEAD:priority/b/bar \2795 HEAD:priority/b/baz \2796 HEAD:priority/c &&2797 git rev-parse >expect \2798 O:goal/a/foo \2799 O:goal/b/bar \2800 O:goal/b/baz \2801 B:goal/c &&2802 test_cmp expect actual &&2803 test_must_fail git rev-parse HEAD:goal/c2804 )2805'28062807# Testcase 9g, Renamed directory that only contained immediate subdirs, immediate subdirs renamed2808# (Related to testcases 1e & 9f)2809# Commit O: goal/{a,b}/$more_files2810# Commit A: priority/{alpha,bravo}/$more_files2811# Commit B: goal/{a,b}/$more_files, goal/c2812# Expected: priority/{alpha,bravo}/$more_files, priority/c28132814test_expect_success '9g-setup: Renamed directory that only contained immediate subdirs, immediate subdirs renamed''2815 test_create_repo 9g &&2816 (2817 cd 9g &&28182819 mkdir -p goal/a &&2820 mkdir -p goal/b &&2821 echo foo >goal/a/foo &&2822 echo bar >goal/b/bar &&2823 echo baz >goal/b/baz &&2824 git add goal &&2825 test_tick &&2826 git commit -m "O" &&28272828 git branch O &&2829 git branch A &&2830 git branch B &&28312832 git checkout A &&2833 mkdir priority &&2834 git mv goal/a/ priority/alpha &&2835 git mv goal/b/ priority/beta &&2836 rmdir goal/ &&2837 test_tick &&2838 git commit -m "A" &&28392840 git checkout B &&2841 echo c >goal/c &&2842 git add goal/c &&2843 test_tick &&2844 git commit -m "B"2845 )2846'28472848test_expect_failure '9g-check: Renamed directory that only contained immediate subdirs, immediate subdirs renamed''2849 (2850 cd 9g &&28512852 git checkout A^0 &&28532854 git merge -s recursive B^0 &&28552856 git ls-files -s >out &&2857 test_line_count = 4 out &&28582859 git rev-parse >actual \2860 HEAD:priority/alpha/foo \2861 HEAD:priority/beta/bar \2862 HEAD:priority/beta/baz \2863 HEAD:priority/c &&2864 git rev-parse >expect \2865 O:goal/a/foo \2866 O:goal/b/bar \2867 O:goal/b/baz \2868 B:goal/c &&2869 test_cmp expect actual &&2870 test_must_fail git rev-parse HEAD:goal/c2871 )2872'28732874###########################################################################2875# Rules suggested by section 9:2876#2877# If the other side of history did a directory rename to a path that your2878# side renamed away, then ignore that particular rename from the other2879# side of history for any implicit directory renames.2880###########################################################################28812882###########################################################################2883# SECTION 10: Handling untracked files2884#2885# unpack_trees(), upon which the recursive merge algorithm is based, aborts2886# the operation if untracked or dirty files would be deleted or overwritten2887# by the merge. Unfortunately, unpack_trees() does not understand renames,2888# and if it doesn't abort, then it muddies up the working directory before2889# we even get to the point of detecting renames, so we need some special2890# handling, at least in the case of directory renames.2891###########################################################################28922893# Testcase 10a, Overwrite untracked: normal rename/delete2894# Commit O: z/{b,c_1}2895# Commit A: z/b + untracked z/c + untracked z/d2896# Commit B: z/{b,d_1}2897# Expected: Aborted Merge +2898# ERROR_MSG(untracked working tree files would be overwritten by merge)28992900test_expect_success '10a-setup: Overwrite untracked with normal rename/delete''2901 test_create_repo 10a &&2902 (2903 cd 10a &&29042905 mkdir z &&2906 echo b >z/b &&2907 echo c >z/c &&2908 git add z &&2909 test_tick &&2910 git commit -m "O" &&29112912 git branch O &&2913 git branch A &&2914 git branch B &&29152916 git checkout A &&2917 git rm z/c &&2918 test_tick &&2919 git commit -m "A" &&29202921 git checkout B &&2922 git mv z/c z/d &&2923 test_tick &&2924 git commit -m "B"2925 )2926'29272928test_expect_success '10a-check: Overwrite untracked with normal rename/delete''2929 (2930 cd 10a &&29312932 git checkout A^0 &&2933 echo very >z/c &&2934 echo important >z/d &&29352936 test_must_fail git merge -s recursive B^0 >out 2>err &&2937 test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&29382939 git ls-files -s >out &&2940 test_line_count = 1 out &&2941 git ls-files -o >out &&2942 test_line_count = 4 out &&29432944 echo very >expect &&2945 test_cmp expect z/c &&29462947 echo important >expect &&2948 test_cmp expect z/d &&29492950 git rev-parse HEAD:z/b >actual &&2951 git rev-parse O:z/b >expect &&2952 test_cmp expect actual2953 )2954'29552956# Testcase 10b, Overwrite untracked: dir rename + delete2957# Commit O: z/{b,c_1}2958# Commit A: y/b + untracked y/{c,d,e}2959# Commit B: z/{b,d_1,e}2960# Expected: Failed Merge; y/b + untracked y/c + untracked y/d on disk +2961# z/c_1 -> z/d_1 rename recorded at stage 3 for y/d +2962# ERROR_MSG(refusing to lose untracked file at 'y/d')29632964test_expect_success '10b-setup: Overwrite untracked with dir rename + delete''2965 test_create_repo 10b &&2966 (2967 cd 10b &&29682969 mkdir z &&2970 echo b >z/b &&2971 echo c >z/c &&2972 git add z &&2973 test_tick &&2974 git commit -m "O" &&29752976 git branch O &&2977 git branch A &&2978 git branch B &&29792980 git checkout A &&2981 git rm z/c &&2982 git mv z/ y/ &&2983 test_tick &&2984 git commit -m "A" &&29852986 git checkout B &&2987 git mv z/c z/d &&2988 echo e >z/e &&2989 git add z/e &&2990 test_tick &&2991 git commit -m "B"2992 )2993'29942995test_expect_failure '10b-check: Overwrite untracked with dir rename + delete''2996 (2997 cd 10b &&29982999 git checkout A^0 &&3000 echo very >y/c &&3001 echo important >y/d &&3002 echo contents >y/e &&30033004 test_must_fail git merge -s recursive B^0 >out 2>err &&3005 test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&3006 test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&30073008 git ls-files -s >out &&3009 test_line_count = 3 out &&3010 git ls-files -u >out &&3011 test_line_count = 2 out &&3012 git ls-files -o >out &&3013 test_line_count = 5 out &&30143015 git rev-parse >actual \3016 :0:y/b :3:y/d :3:y/e &&3017 git rev-parse >expect \3018 O:z/b O:z/c B:z/e &&3019 test_cmp expect actual &&30203021 echo very >expect &&3022 test_cmp expect y/c &&30233024 echo important >expect &&3025 test_cmp expect y/d &&30263027 echo contents >expect &&3028 test_cmp expect y/e3029 )3030'30313032# Testcase 10c, Overwrite untracked: dir rename/rename(1to2)3033# Commit O: z/{a,b}, x/{c,d}3034# Commit A: y/{a,b}, w/c, x/d + different untracked y/c3035# Commit B: z/{a,b,c}, x/d3036# Expected: Failed Merge; y/{a,b} + x/d + untracked y/c +3037# CONFLICT(rename/rename) x/c -> w/c vs y/c +3038# y/c~B^0 +3039# ERROR_MSG(Refusing to lose untracked file at y/c)30403041test_expect_success '10c-setup: Overwrite untracked with dir rename/rename(1to2)''3042 test_create_repo 10c &&3043 (3044 cd 10c &&30453046 mkdir z x &&3047 echo a >z/a &&3048 echo b >z/b &&3049 echo c >x/c &&3050 echo d >x/d &&3051 git add z x &&3052 test_tick &&3053 git commit -m "O" &&30543055 git branch O &&3056 git branch A &&3057 git branch B &&30583059 git checkout A &&3060 mkdir w &&3061 git mv x/c w/c &&3062 git mv z/ y/ &&3063 test_tick &&3064 git commit -m "A" &&30653066 git checkout B &&3067 git mv x/c z/ &&3068 test_tick &&3069 git commit -m "B"3070 )3071'30723073test_expect_failure '10c-check: Overwrite untracked with dir rename/rename(1to2)''3074 (3075 cd 10c &&30763077 git checkout A^0 &&3078 echo important >y/c &&30793080 test_must_fail git merge -s recursive B^0 >out 2>err &&3081 test_i18ngrep "CONFLICT (rename/rename)" out &&3082 test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&30833084 git ls-files -s >out &&3085 test_line_count = 6 out &&3086 git ls-files -u >out &&3087 test_line_count = 3 out &&3088 git ls-files -o >out &&3089 test_line_count = 3 out &&30903091 git rev-parse >actual \3092 :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :3:y/c &&3093 git rev-parse >expect \3094 O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c &&3095 test_cmp expect actual &&30963097 git hash-object y/c~B^0 >actual &&3098 git rev-parse O:x/c >expect &&3099 test_cmp expect actual &&31003101 echo important >expect &&3102 test_cmp expect y/c3103 )3104'31053106# Testcase 10d, Delete untracked w/ dir rename/rename(2to1)3107# Commit O: z/{a,b,c_1}, x/{d,e,f_2}3108# Commit A: y/{a,b}, x/{d,e,f_2,wham_1} + untracked y/wham3109# Commit B: z/{a,b,c_1,wham_2}, y/{d,e}3110# Expected: Failed Merge; y/{a,b,d,e} + untracked y/{wham,wham~B^0,wham~HEAD}+3111# CONFLICT(rename/rename) z/c_1 vs x/f_2 -> y/wham3112# ERROR_MSG(Refusing to lose untracked file at y/wham)31133114test_expect_success '10d-setup: Delete untracked with dir rename/rename(2to1)''3115 test_create_repo 10d &&3116 (3117 cd 10d &&31183119 mkdir z x &&3120 echo a >z/a &&3121 echo b >z/b &&3122 echo c >z/c &&3123 echo d >x/d &&3124 echo e >x/e &&3125 echo f >x/f &&3126 git add z x &&3127 test_tick &&3128 git commit -m "O" &&31293130 git branch O &&3131 git branch A &&3132 git branch B &&31333134 git checkout A &&3135 git mv z/c x/wham &&3136 git mv z/ y/ &&3137 test_tick &&3138 git commit -m "A" &&31393140 git checkout B &&3141 git mv x/f z/wham &&3142 git mv x/ y/ &&3143 test_tick &&3144 git commit -m "B"3145 )3146'31473148test_expect_failure '10d-check: Delete untracked with dir rename/rename(2to1)''3149 (3150 cd 10d &&31513152 git checkout A^0 &&3153 echo important >y/wham &&31543155 test_must_fail git merge -s recursive B^0 >out 2>err &&3156 test_i18ngrep "CONFLICT (rename/rename)" out &&3157 test_i18ngrep "Refusing to lose untracked file at y/wham" out &&31583159 git ls-files -s >out &&3160 test_line_count = 6 out &&3161 git ls-files -u >out &&3162 test_line_count = 2 out &&3163 git ls-files -o >out &&3164 test_line_count = 4 out &&31653166 git rev-parse >actual \3167 :0:y/a :0:y/b :0:y/d :0:y/e :2:y/wham :3:y/wham &&3168 git rev-parse >expect \3169 O:z/a O:z/b O:x/d O:x/e O:z/c O:x/f &&3170 test_cmp expect actual &&31713172 test_must_fail git rev-parse :1:y/wham &&31733174 echo important >expect &&3175 test_cmp expect y/wham &&31763177 git hash-object >actual \3178 y/wham~B^0 y/wham~HEAD &&3179 git rev-parse >expect \3180 O:x/f O:z/c &&3181 test_cmp expect actual3182 )3183'31843185# Testcase 10e, Does git complain about untracked file that's not in the way?3186# Commit O: z/{a,b}3187# Commit A: y/{a,b} + untracked z/c3188# Commit B: z/{a,b,c}3189# Expected: y/{a,b,c} + untracked z/c31903191test_expect_success '10e-setup: Does git complain about untracked file that is not really in the way?''3192 test_create_repo 10e &&3193 (3194 cd 10e &&31953196 mkdir z &&3197 echo a >z/a &&3198 echo b >z/b &&3199 git add z &&3200 test_tick &&3201 git commit -m "O" &&32023203 git branch O &&3204 git branch A &&3205 git branch B &&32063207 git checkout A &&3208 git mv z/ y/ &&3209 test_tick &&3210 git commit -m "A" &&32113212 git checkout B &&3213 echo c >z/c &&3214 git add z/c &&3215 test_tick &&3216 git commit -m "B"3217 )3218'32193220test_expect_failure '10e-check: Does git complain about untracked file that is not really in the way?''3221 (3222 cd 10e &&32233224 git checkout A^0 &&3225 mkdir z &&3226 echo random >z/c &&32273228 git merge -s recursive B^0 >out 2>err &&3229 test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&32303231 git ls-files -s >out &&3232 test_line_count = 3 out &&3233 git ls-files -u >out &&3234 test_line_count = 0 out &&3235 git ls-files -o >out &&3236 test_line_count = 3 out &&32373238 git rev-parse >actual \3239 :0:y/a :0:y/b :0:y/c &&3240 git rev-parse >expect \3241 O:z/a O:z/b B:z/c &&3242 test_cmp expect actual &&32433244 echo random >expect &&3245 test_cmp expect z/c3246 )3247'32483249###########################################################################3250# SECTION 11: Handling dirty (not up-to-date) files3251#3252# unpack_trees(), upon which the recursive merge algorithm is based, aborts3253# the operation if untracked or dirty files would be deleted or overwritten3254# by the merge. Unfortunately, unpack_trees() does not understand renames,3255# and if it doesn't abort, then it muddies up the working directory before3256# we even get to the point of detecting renames, so we need some special3257# handling. This was true even of normal renames, but there are additional3258# codepaths that need special handling with directory renames. Add3259# testcases for both renamed-by-directory-rename-detection and standard3260# rename cases.3261###########################################################################32623263# Testcase 11a, Avoid losing dirty contents with simple rename3264# Commit O: z/{a,b_v1},3265# Commit A: z/{a,c_v1}, and z/c_v1 has uncommitted mods3266# 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_v132713272test_expect_success '11a-setup: Avoid losing dirty contents with simple rename''3273 test_create_repo 11a &&3274 (3275 cd 11a &&32763277 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" &&32833284 git branch O &&3285 git branch A &&3286 git branch B &&32873288 git checkout A &&3289 git mv z/b z/c &&3290 test_tick &&3291 git commit -m "A" &&32923293 git checkout B &&3294 echo 11 >>z/b &&3295 git add z/b &&3296 test_tick &&3297 git commit -m "B"3298 )3299'33003301test_expect_failure '11a-check: Avoid losing dirty contents with simple rename''3302 (3303 cd 11a &&33043305 git checkout A^0 &&3306 echo stuff >>z/c &&33073308 test_must_fail git merge -s recursive B^0 >out 2>err &&3309 test_i18ngrep "Refusing to lose dirty file at z/c" out &&33103311 test_seq 1 10 >expected &&3312 echo stuff >>expected &&3313 test_cmp expected z/c &&33143315 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 &&33213322 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 &&33273328 git hash-object z/c~HEAD >actual &&3329 git rev-parse B:z/b >expect &&3330 test_cmp expect actual3331 )3332'33333334# Testcase 11b, Avoid losing dirty file involved in directory rename3335# Commit O: z/a, x/{b,c_v1}3336# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods3337# 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)334033413342test_expect_success '11b-setup: Avoid losing dirty file involved in directory rename''3343 test_create_repo 11b &&3344 (3345 cd 11b &&33463347 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" &&33543355 git branch O &&3356 git branch A &&3357 git branch B &&33583359 git checkout A &&3360 git mv x/c z/c &&3361 test_tick &&3362 git commit -m "A" &&33633364 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'33723373test_expect_failure '11b-check: Avoid losing dirty file involved in directory rename''3374 (3375 cd 11b &&33763377 git checkout A^0 &&3378 echo stuff >>z/c &&33793380 git merge -s recursive B^0 >out 2>err &&3381 test_i18ngrep "Refusing to lose dirty file at z/c" out &&33823383 grep -q stuff z/c &&3384 test_seq 1 10 >expected &&3385 echo stuff >>expected &&3386 test_cmp expected z/c &&33873388 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 &&33963397 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 &&34023403 git hash-object y/c >actual &&3404 git rev-parse B:x/c >expect &&3405 test_cmp expect actual3406 )3407'34083409# Testcase 11c, Avoid losing not-up-to-date with rename + D/F conflict3410# Commit O: y/a, x/{b,c_v1}3411# Commit A: y/{a,c_v1}, x/b, and y/c_v1 has uncommitted mods3412# 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)34153416test_expect_success '11c-setup: Avoid losing not-uptodate with rename + D/F conflict''3417 test_create_repo 11c &&3418 (3419 cd 11c &&34203421 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" &&34283429 git branch O &&3430 git branch A &&3431 git branch B &&34323433 git checkout A &&3434 git mv x/c y/c &&3435 test_tick &&3436 git commit -m "A" &&34373438 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'34473448test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conflict''3449 (3450 cd 11c &&34513452 git checkout A^0 &&3453 echo stuff >>y/c &&34543455 test_must_fail git merge -s recursive B^0 >out 2>err &&3456 test_i18ngrep "following files would be overwritten by merge" err &&34573458 grep -q stuff y/c &&3459 test_seq 1 10 >expected &&3460 echo stuff >>expected &&3461 test_cmp expected y/c &&34623463 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 out3471 )3472'34733474# Testcase 11d, Avoid losing not-up-to-date with rename + D/F conflict3475# Commit O: z/a, x/{b,c_v1}3476# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods3477# 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 mods34813482test_expect_success '11d-setup: Avoid losing not-uptodate with rename + D/F conflict''3483 test_create_repo 11d &&3484 (3485 cd 11d &&34863487 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" &&34943495 git branch O &&3496 git branch A &&3497 git branch B &&34983499 git checkout A &&3500 git mv x/c z/c &&3501 test_tick &&3502 git commit -m "A" &&35033504 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'35143515test_expect_failure '11d-check: Avoid losing not-uptodate with rename + D/F conflict''3516 (3517 cd 11d &&35183519 git checkout A^0 &&3520 echo stuff >>z/c &&35213522 test_must_fail git merge -s recursive B^0 >out 2>err &&3523 test_i18ngrep "Refusing to lose dirty file at z/c" out &&35243525 grep -q stuff z/c &&3526 test_seq 1 10 >expected &&3527 echo stuff >>expected &&3528 test_cmp expected z/c35293530 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 &&35363537 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 &&35423543 git hash-object y/c~HEAD >actual &&3544 git rev-parse B:x/c >expect &&3545 test_cmp expect actual3546 )3547'35483549# Testcase 11e, Avoid deleting not-up-to-date with dir rename/rename(1to2)/add3550# 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 mods3552# Commit B: z/{a,b,c_1}, x/d3553# 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 contents3557# y/c~HEAD has A:y/c_2 contents3558# y/c has dirty file from before merge35593560test_expect_success '11e-setup: Avoid deleting not-uptodate with dir rename/rename(1to2)/add''3561 test_create_repo 11e &&3562 (3563 cd 11e &&35643565 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" &&35733574 git branch O &&3575 git branch A &&3576 git branch B &&35773578 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" &&35863587 git checkout B &&3588 git mv x/c z/ &&3589 test_tick &&3590 git commit -m "B"3591 )3592'35933594test_expect_failure '11e-check: Avoid deleting not-uptodate with dir rename/rename(1to2)/add''3595 (3596 cd 11e &&35973598 git checkout A^0 &&3599 echo mods >>y/c &&36003601 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 &&36043605 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 &&36113612 echo different >expected &&3613 echo mods >>expected &&3614 test_cmp expected y/c &&36153616 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 &&36213622 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 actual3627 )3628'36293630# 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 mods3633# Commit B: z/{a,b,wham_2}, x/c_13634# 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/wham3637# ERROR_MSG(Refusing to lose dirty file at y/wham)36383639test_expect_success '11f-setup: Avoid deleting not-uptodate with dir rename/rename(2to1)''3640 test_create_repo 11f &&3641 (3642 cd 11f &&36433644 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" &&36523653 git branch O &&3654 git branch A &&3655 git branch B &&36563657 git checkout A &&3658 git mv z/ y/ &&3659 git mv x/c y/wham &&3660 test_tick &&3661 git commit -m "A" &&36623663 git checkout B &&3664 git mv x/d z/wham &&3665 test_tick &&3666 git commit -m "B"3667 )3668'36693670test_expect_failure '11f-check: Avoid deleting not-uptodate with dir rename/rename(2to1)''3671 (3672 cd 11f &&36733674 git checkout A^0 &&3675 echo important >>y/wham &&36763677 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 &&36803681 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 &&36873688 test_seq 1 10 >expected &&3689 echo important >>expected &&3690 test_cmp expected y/wham &&36913692 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 &&36983699 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 actual3704 )3705'37063707test_done