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 -c merge.directoryRenames=true merge -s recursive B^0 >out && 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 -c merge.directoryRenames=true 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# (Related to testcase 12b -- joint-transitivity?) 163# Commit O: z/{b,c}, x/d 164# Commit A: y/{b,c}, x/d 165# Commit B: z/{b,c,d} 166# Expected: y/{b,c,d} (because x/d -> z/d -> y/d) 167 168test_expect_success '1c-setup: Transitive renaming'' 169 test_create_repo 1c && 170 ( 171 cd 1c && 172 173 mkdir z && 174 echo b >z/b && 175 echo c >z/c && 176 mkdir x && 177 echo d >x/d && 178 git add z x && 179 test_tick && 180 git commit -m "O" && 181 182 git branch O && 183 git branch A && 184 git branch B && 185 186 git checkout A && 187 git mv z y && 188 test_tick && 189 git commit -m "A" && 190 191 git checkout B && 192 git mv x/d z/d && 193 test_tick && 194 git commit -m "B" 195 ) 196' 197 198test_expect_success '1c-check: Transitive renaming'' 199 ( 200 cd 1c && 201 202 git checkout A^0 && 203 204 git -c merge.directoryRenames=true merge -s recursive B^0 >out && 205 206 git ls-files -s >out && 207 test_line_count = 3 out && 208 209 git rev-parse >actual \ 210 HEAD:y/b HEAD:y/c HEAD:y/d && 211 git rev-parse >expect \ 212 O:z/b O:z/c O:x/d && 213 test_cmp expect actual && 214 test_must_fail git rev-parse HEAD:x/d && 215 test_must_fail git rev-parse HEAD:z/d && 216 test_path_is_missing z/d 217 ) 218' 219 220# Testcase 1d, Directory renames (merging two directories into one new one) 221# cause a rename/rename(2to1) conflict 222# (Related to testcases 1c and 7b) 223# Commit O. z/{b,c}, y/{d,e} 224# Commit A. x/{b,c}, y/{d,e,m,wham_1} 225# Commit B. z/{b,c,n,wham_2}, x/{d,e} 226# Expected: x/{b,c,d,e,m,n}, CONFLICT:(y/wham_1 & z/wham_2 -> x/wham) 227# Note: y/m & z/n should definitely move into x. By the same token, both 228# y/wham_1 & z/wham_2 should too...giving us a conflict. 229 230test_expect_success '1d-setup: Directory renames cause a rename/rename(2to1) conflict'' 231 test_create_repo 1d && 232 ( 233 cd 1d && 234 235 mkdir z && 236 echo b >z/b && 237 echo c >z/c && 238 mkdir y && 239 echo d >y/d && 240 echo e >y/e && 241 git add z y && 242 test_tick && 243 git commit -m "O" && 244 245 git branch O && 246 git branch A && 247 git branch B && 248 249 git checkout A && 250 git mv z x && 251 echo m >y/m && 252 echo wham1 >y/wham && 253 git add y && 254 test_tick && 255 git commit -m "A" && 256 257 git checkout B && 258 git mv y x && 259 echo n >z/n && 260 echo wham2 >z/wham && 261 git add z && 262 test_tick && 263 git commit -m "B" 264 ) 265' 266 267test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) conflict'' 268 ( 269 cd 1d && 270 271 git checkout A^0 && 272 273 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 274 test_i18ngrep "CONFLICT (rename/rename)" out && 275 276 git ls-files -s >out && 277 test_line_count = 8 out && 278 git ls-files -u >out && 279 test_line_count = 2 out && 280 git ls-files -o >out && 281 test_line_count = 1 out && 282 283 git rev-parse >actual \ 284 :0:x/b :0:x/c :0:x/d :0:x/e :0:x/m :0:x/n && 285 git rev-parse >expect \ 286 O:z/b O:z/c O:y/d O:y/e A:y/m B:z/n && 287 test_cmp expect actual && 288 289 test_must_fail git rev-parse :0:x/wham && 290 git rev-parse >actual \ 291 :2:x/wham :3:x/wham && 292 git rev-parse >expect \ 293 A:y/wham B:z/wham && 294 test_cmp expect actual && 295 296 # Test that the two-way merge in x/wham is as expected 297 git cat-file -p :2:x/wham >expect && 298 git cat-file -p :3:x/wham >other && 299 >empty && 300 test_must_fail git merge-file \ 301 -L "HEAD" \ 302 -L "" \ 303 -L "B^0" \ 304 expect empty other && 305 test_cmp expect x/wham 306 ) 307' 308 309# Testcase 1e, Renamed directory, with all filenames being renamed too 310# (Related to testcases 9f & 9g) 311# Commit O: z/{oldb,oldc} 312# Commit A: y/{newb,newc} 313# Commit B: z/{oldb,oldc,d} 314# Expected: y/{newb,newc,d} 315 316test_expect_success '1e-setup: Renamed directory, with all files being renamed too'' 317 test_create_repo 1e && 318 ( 319 cd 1e && 320 321 mkdir z && 322 echo b >z/oldb && 323 echo c >z/oldc && 324 git add z && 325 test_tick && 326 git commit -m "O" && 327 328 git branch O && 329 git branch A && 330 git branch B && 331 332 git checkout A && 333 mkdir y && 334 git mv z/oldb y/newb && 335 git mv z/oldc y/newc && 336 test_tick && 337 git commit -m "A" && 338 339 git checkout B && 340 echo d >z/d && 341 git add z/d && 342 test_tick && 343 git commit -m "B" 344 ) 345' 346 347test_expect_success '1e-check: Renamed directory, with all files being renamed too'' 348 ( 349 cd 1e && 350 351 git checkout A^0 && 352 353 git -c merge.directoryRenames=true merge -s recursive B^0 && 354 355 git ls-files -s >out && 356 test_line_count = 3 out && 357 358 git rev-parse >actual \ 359 HEAD:y/newb HEAD:y/newc HEAD:y/d && 360 git rev-parse >expect \ 361 O:z/oldb O:z/oldc B:z/d && 362 test_cmp expect actual && 363 test_must_fail git rev-parse HEAD:z/d 364 ) 365' 366 367# Testcase 1f, Split a directory into two other directories 368# (Related to testcases 3a, all of section 2, and all of section 4) 369# Commit O: z/{b,c,d,e,f} 370# Commit A: z/{b,c,d,e,f,g} 371# Commit B: y/{b,c}, x/{d,e,f} 372# Expected: y/{b,c}, x/{d,e,f,g} 373 374test_expect_success '1f-setup: Split a directory into two other directories'' 375 test_create_repo 1f && 376 ( 377 cd 1f && 378 379 mkdir z && 380 echo b >z/b && 381 echo c >z/c && 382 echo d >z/d && 383 echo e >z/e && 384 echo f >z/f && 385 git add z && 386 test_tick && 387 git commit -m "O" && 388 389 git branch O && 390 git branch A && 391 git branch B && 392 393 git checkout A && 394 echo g >z/g && 395 git add z/g && 396 test_tick && 397 git commit -m "A" && 398 399 git checkout B && 400 mkdir y && 401 mkdir x && 402 git mv z/b y/ && 403 git mv z/c y/ && 404 git mv z/d x/ && 405 git mv z/e x/ && 406 git mv z/f x/ && 407 rmdir z && 408 test_tick && 409 git commit -m "B" 410 ) 411' 412 413test_expect_success '1f-check: Split a directory into two other directories'' 414 ( 415 cd 1f && 416 417 git checkout A^0 && 418 419 git -c merge.directoryRenames=true merge -s recursive B^0 && 420 421 git ls-files -s >out && 422 test_line_count = 6 out && 423 424 git rev-parse >actual \ 425 HEAD:y/b HEAD:y/c HEAD:x/d HEAD:x/e HEAD:x/f HEAD:x/g && 426 git rev-parse >expect \ 427 O:z/b O:z/c O:z/d O:z/e O:z/f A:z/g && 428 test_cmp expect actual && 429 test_path_is_missing z/g && 430 test_must_fail git rev-parse HEAD:z/g 431 ) 432' 433 434########################################################################### 435# Rules suggested by testcases in section 1: 436# 437# We should still detect the directory rename even if it wasn't just 438# the directory renamed, but the files within it. (see 1b) 439# 440# If renames split a directory into two or more others, the directory 441# with the most renames, "wins" (see 1c). However, see the testcases 442# in section 2, plus testcases 3a and 4a. 443########################################################################### 444 445 446########################################################################### 447# SECTION 2: Split into multiple directories, with equal number of paths 448# 449# Explore the splitting-a-directory rules a bit; what happens in the 450# edge cases? 451# 452# Note that there is a closely related case of a directory not being 453# split on either side of history, but being renamed differently on 454# each side. See testcase 8e for that. 455########################################################################### 456 457# Testcase 2a, Directory split into two on one side, with equal numbers of paths 458# Commit O: z/{b,c} 459# Commit A: y/b, w/c 460# Commit B: z/{b,c,d} 461# Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict 462test_expect_success '2a-setup: Directory split into two on one side, with equal numbers of paths'' 463 test_create_repo 2a && 464 ( 465 cd 2a && 466 467 mkdir z && 468 echo b >z/b && 469 echo c >z/c && 470 git add z && 471 test_tick && 472 git commit -m "O" && 473 474 git branch O && 475 git branch A && 476 git branch B && 477 478 git checkout A && 479 mkdir y && 480 mkdir w && 481 git mv z/b y/ && 482 git mv z/c w/ && 483 test_tick && 484 git commit -m "A" && 485 486 git checkout B && 487 echo d >z/d && 488 git add z/d && 489 test_tick && 490 git commit -m "B" 491 ) 492' 493 494test_expect_success '2a-check: Directory split into two on one side, with equal numbers of paths'' 495 ( 496 cd 2a && 497 498 git checkout A^0 && 499 500 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 501 test_i18ngrep "CONFLICT.*directory rename split" out && 502 503 git ls-files -s >out && 504 test_line_count = 3 out && 505 git ls-files -u >out && 506 test_line_count = 0 out && 507 git ls-files -o >out && 508 test_line_count = 1 out && 509 510 git rev-parse >actual \ 511 :0:y/b :0:w/c :0:z/d && 512 git rev-parse >expect \ 513 O:z/b O:z/c B:z/d && 514 test_cmp expect actual 515 ) 516' 517 518# Testcase 2b, Directory split into two on one side, with equal numbers of paths 519# Commit O: z/{b,c} 520# Commit A: y/b, w/c 521# Commit B: z/{b,c}, x/d 522# Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict 523test_expect_success '2b-setup: Directory split into two on one side, with equal numbers of paths'' 524 test_create_repo 2b && 525 ( 526 cd 2b && 527 528 mkdir z && 529 echo b >z/b && 530 echo c >z/c && 531 git add z && 532 test_tick && 533 git commit -m "O" && 534 535 git branch O && 536 git branch A && 537 git branch B && 538 539 git checkout A && 540 mkdir y && 541 mkdir w && 542 git mv z/b y/ && 543 git mv z/c w/ && 544 test_tick && 545 git commit -m "A" && 546 547 git checkout B && 548 mkdir x && 549 echo d >x/d && 550 git add x/d && 551 test_tick && 552 git commit -m "B" 553 ) 554' 555 556test_expect_success '2b-check: Directory split into two on one side, with equal numbers of paths'' 557 ( 558 cd 2b && 559 560 git checkout A^0 && 561 562 git -c merge.directoryRenames=true merge -s recursive B^0 >out && 563 564 git ls-files -s >out && 565 test_line_count = 3 out && 566 git ls-files -u >out && 567 test_line_count = 0 out && 568 git ls-files -o >out && 569 test_line_count = 1 out && 570 571 git rev-parse >actual \ 572 :0:y/b :0:w/c :0:x/d && 573 git rev-parse >expect \ 574 O:z/b O:z/c B:x/d && 575 test_cmp expect actual && 576 test_i18ngrep ! "CONFLICT.*directory rename split" out 577 ) 578' 579 580########################################################################### 581# Rules suggested by section 2: 582# 583# None; the rule was already covered in section 1. These testcases are 584# here just to make sure the conflict resolution and necessary warning 585# messages are handled correctly. 586########################################################################### 587 588 589########################################################################### 590# SECTION 3: Path in question is the source path for some rename already 591# 592# Combining cases from Section 1 and trying to handle them could lead to 593# directory renaming detection being over-applied. So, this section 594# provides some good testcases to check that the implementation doesn't go 595# too far. 596########################################################################### 597 598# Testcase 3a, Avoid implicit rename if involved as source on other side 599# (Related to testcases 1c, 1f, and 9h) 600# Commit O: z/{b,c,d} 601# Commit A: z/{b,c,d} (no change) 602# Commit B: y/{b,c}, x/d 603# Expected: y/{b,c}, x/d 604test_expect_success '3a-setup: Avoid implicit rename if involved as source on other side'' 605 test_create_repo 3a && 606 ( 607 cd 3a && 608 609 mkdir z && 610 echo b >z/b && 611 echo c >z/c && 612 echo d >z/d && 613 git add z && 614 test_tick && 615 git commit -m "O" && 616 617 git branch O && 618 git branch A && 619 git branch B && 620 621 git checkout A && 622 test_tick && 623 git commit --allow-empty -m "A" && 624 625 git checkout B && 626 mkdir y && 627 mkdir x && 628 git mv z/b y/ && 629 git mv z/c y/ && 630 git mv z/d x/ && 631 rmdir z && 632 test_tick && 633 git commit -m "B" 634 ) 635' 636 637test_expect_success '3a-check: Avoid implicit rename if involved as source on other side'' 638 ( 639 cd 3a && 640 641 git checkout A^0 && 642 643 git -c merge.directoryRenames=true merge -s recursive B^0 && 644 645 git ls-files -s >out && 646 test_line_count = 3 out && 647 648 git rev-parse >actual \ 649 HEAD:y/b HEAD:y/c HEAD:x/d && 650 git rev-parse >expect \ 651 O:z/b O:z/c O:z/d && 652 test_cmp expect actual 653 ) 654' 655 656# Testcase 3b, Avoid implicit rename if involved as source on other side 657# (Related to testcases 5c and 7c, also kind of 1e and 1f) 658# Commit O: z/{b,c,d} 659# Commit A: y/{b,c}, x/d 660# Commit B: z/{b,c}, w/d 661# Expected: y/{b,c}, CONFLICT:(z/d -> x/d vs. w/d) 662# NOTE: We're particularly checking that since z/d is already involved as 663# a source in a file rename on the same side of history, that we don't 664# get it involved in directory rename detection. If it were, we might 665# end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a 666# rename/rename/rename(1to3) conflict, which is just weird. 667test_expect_success '3b-setup: Avoid implicit rename if involved as source on current side'' 668 test_create_repo 3b && 669 ( 670 cd 3b && 671 672 mkdir z && 673 echo b >z/b && 674 echo c >z/c && 675 echo d >z/d && 676 git add z && 677 test_tick && 678 git commit -m "O" && 679 680 git branch O && 681 git branch A && 682 git branch B && 683 684 git checkout A && 685 mkdir y && 686 mkdir x && 687 git mv z/b y/ && 688 git mv z/c y/ && 689 git mv z/d x/ && 690 rmdir z && 691 test_tick && 692 git commit -m "A" && 693 694 git checkout B && 695 mkdir w && 696 git mv z/d w/ && 697 test_tick && 698 git commit -m "B" 699 ) 700' 701 702test_expect_success '3b-check: Avoid implicit rename if involved as source on current side'' 703 ( 704 cd 3b && 705 706 git checkout A^0 && 707 708 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 709 test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out && 710 test_i18ngrep ! CONFLICT.*rename/rename.*y/d out && 711 712 git ls-files -s >out && 713 test_line_count = 5 out && 714 git ls-files -u >out && 715 test_line_count = 3 out && 716 git ls-files -o >out && 717 test_line_count = 1 out && 718 719 git rev-parse >actual \ 720 :0:y/b :0:y/c :1:z/d :2:x/d :3:w/d && 721 git rev-parse >expect \ 722 O:z/b O:z/c O:z/d O:z/d O:z/d && 723 test_cmp expect actual && 724 725 test_path_is_missing z/d && 726 git hash-object >actual \ 727 x/d w/d && 728 git rev-parse >expect \ 729 O:z/d O:z/d && 730 test_cmp expect actual 731 ) 732' 733 734########################################################################### 735# Rules suggested by section 3: 736# 737# Avoid directory-rename-detection for a path, if that path is the source 738# of a rename on either side of a merge. 739########################################################################### 740 741 742########################################################################### 743# SECTION 4: Partially renamed directory; still exists on both sides of merge 744# 745# What if we were to attempt to do directory rename detection when someone 746# "mostly" moved a directory but still left some files around, or, 747# equivalently, fully renamed a directory in one commmit and then recreated 748# that directory in a later commit adding some new files and then tried to 749# merge? 750# 751# It's hard to divine user intent in these cases, because you can make an 752# argument that, depending on the intermediate history of the side being 753# merged, that some users will want files in that directory to 754# automatically be detected and renamed, while users with a different 755# intermediate history wouldn't want that rename to happen. 756# 757# I think that it is best to simply not have directory rename detection 758# apply to such cases. My reasoning for this is four-fold: (1) it's 759# easiest for users in general to figure out what happened if we don't 760# apply directory rename detection in any such case, (2) it's an easy rule 761# to explain ["We don't do directory rename detection if the directory 762# still exists on both sides of the merge"], (3) we can get some hairy 763# edge/corner cases that would be really confusing and possibly not even 764# representable in the index if we were to even try, and [related to 3] (4) 765# attempting to resolve this issue of divining user intent by examining 766# intermediate history goes against the spirit of three-way merges and is a 767# path towards crazy corner cases that are far more complex than what we're 768# already dealing with. 769# 770# Note that the wording of the rule ("We don't do directory rename 771# detection if the directory still exists on both sides of the merge.") 772# also excludes "renaming" of a directory into a subdirectory of itself 773# (e.g. /some/dir/* -> /some/dir/subdir/*). It may be possible to carve 774# out an exception for "renaming"-beneath-itself cases without opening 775# weird edge/corner cases for other partial directory renames, but for now 776# we are keeping the rule simple. 777# 778# This section contains a test for a partially-renamed-directory case. 779########################################################################### 780 781# Testcase 4a, Directory split, with original directory still present 782# (Related to testcase 1f) 783# Commit O: z/{b,c,d,e} 784# Commit A: y/{b,c,d}, z/e 785# Commit B: z/{b,c,d,e,f} 786# Expected: y/{b,c,d}, z/{e,f} 787# NOTE: Even though most files from z moved to y, we don't want f to follow. 788 789test_expect_success '4a-setup: Directory split, with original directory still present'' 790 test_create_repo 4a && 791 ( 792 cd 4a && 793 794 mkdir z && 795 echo b >z/b && 796 echo c >z/c && 797 echo d >z/d && 798 echo e >z/e && 799 git add z && 800 test_tick && 801 git commit -m "O" && 802 803 git branch O && 804 git branch A && 805 git branch B && 806 807 git checkout A && 808 mkdir y && 809 git mv z/b y/ && 810 git mv z/c y/ && 811 git mv z/d y/ && 812 test_tick && 813 git commit -m "A" && 814 815 git checkout B && 816 echo f >z/f && 817 git add z/f && 818 test_tick && 819 git commit -m "B" 820 ) 821' 822 823test_expect_success '4a-check: Directory split, with original directory still present'' 824 ( 825 cd 4a && 826 827 git checkout A^0 && 828 829 git -c merge.directoryRenames=true merge -s recursive B^0 && 830 831 git ls-files -s >out && 832 test_line_count = 5 out && 833 git ls-files -u >out && 834 test_line_count = 0 out && 835 git ls-files -o >out && 836 test_line_count = 1 out && 837 838 git rev-parse >actual \ 839 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/e HEAD:z/f && 840 git rev-parse >expect \ 841 O:z/b O:z/c O:z/d O:z/e B:z/f && 842 test_cmp expect actual 843 ) 844' 845 846########################################################################### 847# Rules suggested by section 4: 848# 849# Directory-rename-detection should be turned off for any directories (as 850# a source for renames) that exist on both sides of the merge. (The "as 851# a source for renames" clarification is due to cases like 1c where 852# the target directory exists on both sides and we do want the rename 853# detection.) But, sadly, see testcase 8b. 854########################################################################### 855 856 857########################################################################### 858# SECTION 5: Files/directories in the way of subset of to-be-renamed paths 859# 860# Implicitly renaming files due to a detected directory rename could run 861# into problems if there are files or directories in the way of the paths 862# we want to rename. Explore such cases in this section. 863########################################################################### 864 865# Testcase 5a, Merge directories, other side adds files to original and target 866# Commit O: z/{b,c}, y/d 867# Commit A: z/{b,c,e_1,f}, y/{d,e_2} 868# Commit B: y/{b,c,d} 869# Expected: z/e_1, y/{b,c,d,e_2,f} + CONFLICT warning 870# NOTE: While directory rename detection is active here causing z/f to 871# become y/f, we did not apply this for z/e_1 because that would 872# give us an add/add conflict for y/e_1 vs y/e_2. This problem with 873# this add/add, is that both versions of y/e are from the same side 874# of history, giving us no way to represent this conflict in the 875# index. 876 877test_expect_success '5a-setup: Merge directories, other side adds files to original and target'' 878 test_create_repo 5a && 879 ( 880 cd 5a && 881 882 mkdir z && 883 echo b >z/b && 884 echo c >z/c && 885 mkdir y && 886 echo d >y/d && 887 git add z y && 888 test_tick && 889 git commit -m "O" && 890 891 git branch O && 892 git branch A && 893 git branch B && 894 895 git checkout A && 896 echo e1 >z/e && 897 echo f >z/f && 898 echo e2 >y/e && 899 git add z/e z/f y/e && 900 test_tick && 901 git commit -m "A" && 902 903 git checkout B && 904 git mv z/b y/ && 905 git mv z/c y/ && 906 rmdir z && 907 test_tick && 908 git commit -m "B" 909 ) 910' 911 912test_expect_success '5a-check: Merge directories, other side adds files to original and target'' 913 ( 914 cd 5a && 915 916 git checkout A^0 && 917 918 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 919 test_i18ngrep "CONFLICT.*implicit dir rename" out && 920 921 git ls-files -s >out && 922 test_line_count = 6 out && 923 git ls-files -u >out && 924 test_line_count = 0 out && 925 git ls-files -o >out && 926 test_line_count = 1 out && 927 928 git rev-parse >actual \ 929 :0:y/b :0:y/c :0:y/d :0:y/e :0:z/e :0:y/f && 930 git rev-parse >expect \ 931 O:z/b O:z/c O:y/d A:y/e A:z/e A:z/f && 932 test_cmp expect actual 933 ) 934' 935 936# Testcase 5b, Rename/delete in order to get add/add/add conflict 937# (Related to testcase 8d; these may appear slightly inconsistent to users; 938# Also related to testcases 7d and 7e) 939# Commit O: z/{b,c,d_1} 940# Commit A: y/{b,c,d_2} 941# Commit B: z/{b,c,d_1,e}, y/d_3 942# Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3) 943# NOTE: If z/d_1 in commit B were to be involved in dir rename detection, as 944# we normaly would since z/ is being renamed to y/, then this would be 945# a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add 946# conflict of y/d_1 vs. y/d_2 vs. y/d_3. Add/add/add is not 947# representable in the index, so the existence of y/d_3 needs to 948# cause us to bail on directory rename detection for that path, falling 949# back to git behavior without the directory rename detection. 950 951test_expect_success '5b-setup: Rename/delete in order to get add/add/add conflict'' 952 test_create_repo 5b && 953 ( 954 cd 5b && 955 956 mkdir z && 957 echo b >z/b && 958 echo c >z/c && 959 echo d1 >z/d && 960 git add z && 961 test_tick && 962 git commit -m "O" && 963 964 git branch O && 965 git branch A && 966 git branch B && 967 968 git checkout A && 969 git rm z/d && 970 git mv z y && 971 echo d2 >y/d && 972 git add y/d && 973 test_tick && 974 git commit -m "A" && 975 976 git checkout B && 977 mkdir y && 978 echo d3 >y/d && 979 echo e >z/e && 980 git add y/d z/e && 981 test_tick && 982 git commit -m "B" 983 ) 984' 985 986test_expect_success '5b-check: Rename/delete in order to get add/add/add conflict'' 987 ( 988 cd 5b && 989 990 git checkout A^0 && 991 992 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 993 test_i18ngrep "CONFLICT (add/add).* y/d" out && 994 995 git ls-files -s >out && 996 test_line_count = 5 out && 997 git ls-files -u >out && 998 test_line_count = 2 out && 999 git ls-files -o >out &&1000 test_line_count = 1 out &&10011002 git rev-parse >actual \1003 :0:y/b :0:y/c :0:y/e :2:y/d :3:y/d &&1004 git rev-parse >expect \1005 O:z/b O:z/c B:z/e A:y/d B:y/d &&1006 test_cmp expect actual &&10071008 test_must_fail git rev-parse :1:y/d &&1009 test_path_is_file y/d1010 )1011'10121013# Testcase 5c, Transitive rename would cause rename/rename/rename/add/add/add1014# (Directory rename detection would result in transitive rename vs.1015# rename/rename(1to2) and turn it into a rename/rename(1to3). Further,1016# rename paths conflict with separate adds on the other side)1017# (Related to testcases 3b and 7c)1018# Commit O: z/{b,c}, x/d_11019# Commit A: y/{b,c,d_2}, w/d_11020# Commit B: z/{b,c,d_1,e}, w/d_3, y/d_41021# Expected: A mess, but only a rename/rename(1to2)/add/add mess. Use the1022# presence of y/d_4 in B to avoid doing transitive rename of1023# x/d_1 -> z/d_1 -> y/d_1, so that the only paths we have at1024# y/d are y/d_2 and y/d_4. We still do the move from z/e to y/e,1025# though, because it doesn't have anything in the way.10261027test_expect_success '5c-setup: Transitive rename would cause rename/rename/rename/add/add/add''1028 test_create_repo 5c &&1029 (1030 cd 5c &&10311032 mkdir z &&1033 echo b >z/b &&1034 echo c >z/c &&1035 mkdir x &&1036 echo d1 >x/d &&1037 git add z x &&1038 test_tick &&1039 git commit -m "O" &&10401041 git branch O &&1042 git branch A &&1043 git branch B &&10441045 git checkout A &&1046 git mv z y &&1047 echo d2 >y/d &&1048 git add y/d &&1049 git mv x w &&1050 test_tick &&1051 git commit -m "A" &&10521053 git checkout B &&1054 git mv x/d z/ &&1055 mkdir w &&1056 mkdir y &&1057 echo d3 >w/d &&1058 echo d4 >y/d &&1059 echo e >z/e &&1060 git add w/ y/ z/e &&1061 test_tick &&1062 git commit -m "B"1063 )1064'10651066test_expect_success '5c-check: Transitive rename would cause rename/rename/rename/add/add/add''1067 (1068 cd 5c &&10691070 git checkout A^0 &&10711072 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1073 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&1074 test_i18ngrep "CONFLICT (add/add).* y/d" out &&10751076 git ls-files -s >out &&1077 test_line_count = 9 out &&1078 git ls-files -u >out &&1079 test_line_count = 6 out &&1080 git ls-files -o >out &&1081 test_line_count = 1 out &&10821083 git rev-parse >actual \1084 :0:y/b :0:y/c :0:y/e &&1085 git rev-parse >expect \1086 O:z/b O:z/c B:z/e &&1087 test_cmp expect actual &&10881089 test_must_fail git rev-parse :1:y/d &&1090 git rev-parse >actual \1091 :2:w/d :3:w/d :1:x/d :2:y/d :3:y/d :3:z/d &&1092 git rev-parse >expect \1093 O:x/d B:w/d O:x/d A:y/d B:y/d O:x/d &&1094 test_cmp expect actual &&10951096 git hash-object >actual \1097 z/d &&1098 git rev-parse >expect \1099 O:x/d &&1100 test_cmp expect actual &&1101 test_path_is_missing x/d &&1102 test_path_is_file y/d &&1103 grep -q "<<<<" y/d # conflict markers should be present1104 )1105'11061107# Testcase 5d, Directory/file/file conflict due to directory rename1108# Commit O: z/{b,c}1109# Commit A: y/{b,c,d_1}1110# Commit B: z/{b,c,d_2,f}, y/d/e1111# Expected: y/{b,c,d/e,f}, z/d_2, CONFLICT(file/directory), y/d_1~HEAD1112# Note: The fact that y/d/ exists in B makes us bail on directory rename1113# detection for z/d_2, but that doesn't prevent us from applying the1114# directory rename detection for z/f -> y/f.11151116test_expect_success '5d-setup: Directory/file/file conflict due to directory rename''1117 test_create_repo 5d &&1118 (1119 cd 5d &&11201121 mkdir z &&1122 echo b >z/b &&1123 echo c >z/c &&1124 git add z &&1125 test_tick &&1126 git commit -m "O" &&11271128 git branch O &&1129 git branch A &&1130 git branch B &&11311132 git checkout A &&1133 git mv z y &&1134 echo d1 >y/d &&1135 git add y/d &&1136 test_tick &&1137 git commit -m "A" &&11381139 git checkout B &&1140 mkdir -p y/d &&1141 echo e >y/d/e &&1142 echo d2 >z/d &&1143 echo f >z/f &&1144 git add y/d/e z/d z/f &&1145 test_tick &&1146 git commit -m "B"1147 )1148'11491150test_expect_success '5d-check: Directory/file/file conflict due to directory rename''1151 (1152 cd 5d &&11531154 git checkout A^0 &&11551156 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1157 test_i18ngrep "CONFLICT (file/directory).*y/d" out &&11581159 git ls-files -s >out &&1160 test_line_count = 6 out &&1161 git ls-files -u >out &&1162 test_line_count = 1 out &&1163 git ls-files -o >out &&1164 test_line_count = 2 out &&11651166 git rev-parse >actual \1167 :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e &&1168 git rev-parse >expect \1169 O:z/b O:z/c B:z/d B:z/f A:y/d B:y/d/e &&1170 test_cmp expect actual &&11711172 git hash-object y/d~HEAD >actual &&1173 git rev-parse A:y/d >expect &&1174 test_cmp expect actual1175 )1176'11771178###########################################################################1179# Rules suggested by section 5:1180#1181# If a subset of to-be-renamed files have a file or directory in the way,1182# "turn off" the directory rename for those specific sub-paths, falling1183# back to old handling. But, sadly, see testcases 8a and 8b.1184###########################################################################118511861187###########################################################################1188# SECTION 6: Same side of the merge was the one that did the rename1189#1190# It may sound obvious that you only want to apply implicit directory1191# renames to directories if the _other_ side of history did the renaming.1192# If you did make an implementation that didn't explicitly enforce this1193# rule, the majority of cases that would fall under this section would1194# also be solved by following the rules from the above sections. But1195# there are still a few that stick out, so this section covers them just1196# to make sure we also get them right.1197###########################################################################11981199# Testcase 6a, Tricky rename/delete1200# Commit O: z/{b,c,d}1201# Commit A: z/b1202# Commit B: y/{b,c}, z/d1203# Expected: y/b, CONFLICT(rename/delete, z/c -> y/c vs. NULL)1204# Note: We're just checking here that the rename of z/b and z/c to put1205# them under y/ doesn't accidentally catch z/d and make it look like1206# it is also involved in a rename/delete conflict.12071208test_expect_success '6a-setup: Tricky rename/delete''1209 test_create_repo 6a &&1210 (1211 cd 6a &&12121213 mkdir z &&1214 echo b >z/b &&1215 echo c >z/c &&1216 echo d >z/d &&1217 git add z &&1218 test_tick &&1219 git commit -m "O" &&12201221 git branch O &&1222 git branch A &&1223 git branch B &&12241225 git checkout A &&1226 git rm z/c &&1227 git rm z/d &&1228 test_tick &&1229 git commit -m "A" &&12301231 git checkout B &&1232 mkdir y &&1233 git mv z/b y/ &&1234 git mv z/c y/ &&1235 test_tick &&1236 git commit -m "B"1237 )1238'12391240test_expect_success '6a-check: Tricky rename/delete''1241 (1242 cd 6a &&12431244 git checkout A^0 &&12451246 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1247 test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&12481249 git ls-files -s >out &&1250 test_line_count = 2 out &&1251 git ls-files -u >out &&1252 test_line_count = 1 out &&1253 git ls-files -o >out &&1254 test_line_count = 1 out &&12551256 git rev-parse >actual \1257 :0:y/b :3:y/c &&1258 git rev-parse >expect \1259 O:z/b O:z/c &&1260 test_cmp expect actual1261 )1262'12631264# Testcase 6b, Same rename done on both sides1265# (Related to testcases 6c and 8e)1266# Commit O: z/{b,c}1267# Commit A: y/{b,c}1268# Commit B: y/{b,c}, z/d1269# Expected: y/{b,c}, z/d1270# Note: If we did directory rename detection here, we'd move z/d into y/,1271# but B did that rename and still decided to put the file into z/,1272# so we probably shouldn't apply directory rename detection for it.12731274test_expect_success '6b-setup: Same rename done on both sides''1275 test_create_repo 6b &&1276 (1277 cd 6b &&12781279 mkdir z &&1280 echo b >z/b &&1281 echo c >z/c &&1282 git add z &&1283 test_tick &&1284 git commit -m "O" &&12851286 git branch O &&1287 git branch A &&1288 git branch B &&12891290 git checkout A &&1291 git mv z y &&1292 test_tick &&1293 git commit -m "A" &&12941295 git checkout B &&1296 git mv z y &&1297 mkdir z &&1298 echo d >z/d &&1299 git add z/d &&1300 test_tick &&1301 git commit -m "B"1302 )1303'13041305test_expect_success '6b-check: Same rename done on both sides''1306 (1307 cd 6b &&13081309 git checkout A^0 &&13101311 git -c merge.directoryRenames=true merge -s recursive B^0 &&13121313 git ls-files -s >out &&1314 test_line_count = 3 out &&1315 git ls-files -u >out &&1316 test_line_count = 0 out &&1317 git ls-files -o >out &&1318 test_line_count = 1 out &&13191320 git rev-parse >actual \1321 HEAD:y/b HEAD:y/c HEAD:z/d &&1322 git rev-parse >expect \1323 O:z/b O:z/c B:z/d &&1324 test_cmp expect actual1325 )1326'13271328# Testcase 6c, Rename only done on same side1329# (Related to testcases 6b and 8e)1330# Commit O: z/{b,c}1331# Commit A: z/{b,c} (no change)1332# Commit B: y/{b,c}, z/d1333# Expected: y/{b,c}, z/d1334# NOTE: Seems obvious, but just checking that the implementation doesn't1335# "accidentally detect a rename" and give us y/{b,c,d}.13361337test_expect_success '6c-setup: Rename only done on same side''1338 test_create_repo 6c &&1339 (1340 cd 6c &&13411342 mkdir z &&1343 echo b >z/b &&1344 echo c >z/c &&1345 git add z &&1346 test_tick &&1347 git commit -m "O" &&13481349 git branch O &&1350 git branch A &&1351 git branch B &&13521353 git checkout A &&1354 test_tick &&1355 git commit --allow-empty -m "A" &&13561357 git checkout B &&1358 git mv z y &&1359 mkdir z &&1360 echo d >z/d &&1361 git add z/d &&1362 test_tick &&1363 git commit -m "B"1364 )1365'13661367test_expect_success '6c-check: Rename only done on same side''1368 (1369 cd 6c &&13701371 git checkout A^0 &&13721373 git -c merge.directoryRenames=true merge -s recursive B^0 &&13741375 git ls-files -s >out &&1376 test_line_count = 3 out &&1377 git ls-files -u >out &&1378 test_line_count = 0 out &&1379 git ls-files -o >out &&1380 test_line_count = 1 out &&13811382 git rev-parse >actual \1383 HEAD:y/b HEAD:y/c HEAD:z/d &&1384 git rev-parse >expect \1385 O:z/b O:z/c B:z/d &&1386 test_cmp expect actual1387 )1388'13891390# Testcase 6d, We don't always want transitive renaming1391# (Related to testcase 1c)1392# Commit O: z/{b,c}, x/d1393# Commit A: z/{b,c}, x/d (no change)1394# Commit B: y/{b,c}, z/d1395# Expected: y/{b,c}, z/d1396# NOTE: Again, this seems obvious but just checking that the implementation1397# doesn't "accidentally detect a rename" and give us y/{b,c,d}.13981399test_expect_success '6d-setup: We do not always want transitive renaming''1400 test_create_repo 6d &&1401 (1402 cd 6d &&14031404 mkdir z &&1405 echo b >z/b &&1406 echo c >z/c &&1407 mkdir x &&1408 echo d >x/d &&1409 git add z x &&1410 test_tick &&1411 git commit -m "O" &&14121413 git branch O &&1414 git branch A &&1415 git branch B &&14161417 git checkout A &&1418 test_tick &&1419 git commit --allow-empty -m "A" &&14201421 git checkout B &&1422 git mv z y &&1423 git mv x z &&1424 test_tick &&1425 git commit -m "B"1426 )1427'14281429test_expect_success '6d-check: We do not always want transitive renaming''1430 (1431 cd 6d &&14321433 git checkout A^0 &&14341435 git -c merge.directoryRenames=true merge -s recursive B^0 &&14361437 git ls-files -s >out &&1438 test_line_count = 3 out &&1439 git ls-files -u >out &&1440 test_line_count = 0 out &&1441 git ls-files -o >out &&1442 test_line_count = 1 out &&14431444 git rev-parse >actual \1445 HEAD:y/b HEAD:y/c HEAD:z/d &&1446 git rev-parse >expect \1447 O:z/b O:z/c O:x/d &&1448 test_cmp expect actual1449 )1450'14511452# Testcase 6e, Add/add from one-side1453# Commit O: z/{b,c}1454# Commit A: z/{b,c} (no change)1455# Commit B: y/{b,c,d_1}, z/d_21456# Expected: y/{b,c,d_1}, z/d_21457# NOTE: Again, this seems obvious but just checking that the implementation1458# doesn't "accidentally detect a rename" and give us y/{b,c} +1459# add/add conflict on y/d_1 vs y/d_2.14601461test_expect_success '6e-setup: Add/add from one side''1462 test_create_repo 6e &&1463 (1464 cd 6e &&14651466 mkdir z &&1467 echo b >z/b &&1468 echo c >z/c &&1469 git add z &&1470 test_tick &&1471 git commit -m "O" &&14721473 git branch O &&1474 git branch A &&1475 git branch B &&14761477 git checkout A &&1478 test_tick &&1479 git commit --allow-empty -m "A" &&14801481 git checkout B &&1482 git mv z y &&1483 echo d1 > y/d &&1484 mkdir z &&1485 echo d2 > z/d &&1486 git add y/d z/d &&1487 test_tick &&1488 git commit -m "B"1489 )1490'14911492test_expect_success '6e-check: Add/add from one side''1493 (1494 cd 6e &&14951496 git checkout A^0 &&14971498 git -c merge.directoryRenames=true merge -s recursive B^0 &&14991500 git ls-files -s >out &&1501 test_line_count = 4 out &&1502 git ls-files -u >out &&1503 test_line_count = 0 out &&1504 git ls-files -o >out &&1505 test_line_count = 1 out &&15061507 git rev-parse >actual \1508 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/d &&1509 git rev-parse >expect \1510 O:z/b O:z/c B:y/d B:z/d &&1511 test_cmp expect actual1512 )1513'15141515###########################################################################1516# Rules suggested by section 6:1517#1518# Only apply implicit directory renames to directories if the other1519# side of history is the one doing the renaming.1520###########################################################################152115221523###########################################################################1524# SECTION 7: More involved Edge/Corner cases1525#1526# The ruleset we have generated in the above sections seems to provide1527# well-defined merges. But can we find edge/corner cases that either (a)1528# are harder for users to understand, or (b) have a resolution that is1529# non-intuitive or suboptimal?1530#1531# The testcases in this section dive into cases that I've tried to craft in1532# a way to find some that might be surprising to users or difficult for1533# them to understand (the next section will look at non-intuitive or1534# suboptimal merge results). Some of the testcases are similar to ones1535# from past sections, but have been simplified to try to highlight error1536# messages using a "modified" path (due to the directory rename). Are1537# users okay with these?1538#1539# In my opinion, testcases that are difficult to understand from this1540# section is due to difficulty in the testcase rather than the directory1541# renaming (similar to how t6042 and t6036 have difficult resolutions due1542# to the problem setup itself being complex). And I don't think the1543# error messages are a problem.1544#1545# On the other hand, the testcases in section 8 worry me slightly more...1546###########################################################################15471548# Testcase 7a, rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file1549# Commit O: z/{b,c}1550# Commit A: y/{b,c}1551# Commit B: w/b, x/c, z/d1552# Expected: y/d, CONFLICT(rename/rename for both z/b and z/c)1553# NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d.15541555test_expect_success '7a-setup: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file''1556 test_create_repo 7a &&1557 (1558 cd 7a &&15591560 mkdir z &&1561 echo b >z/b &&1562 echo c >z/c &&1563 git add z &&1564 test_tick &&1565 git commit -m "O" &&15661567 git branch O &&1568 git branch A &&1569 git branch B &&15701571 git checkout A &&1572 git mv z y &&1573 test_tick &&1574 git commit -m "A" &&15751576 git checkout B &&1577 mkdir w &&1578 mkdir x &&1579 git mv z/b w/ &&1580 git mv z/c x/ &&1581 echo d > z/d &&1582 git add z/d &&1583 test_tick &&1584 git commit -m "B"1585 )1586'15871588test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file''1589 (1590 cd 7a &&15911592 git checkout A^0 &&15931594 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1595 test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&1596 test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&15971598 git ls-files -s >out &&1599 test_line_count = 7 out &&1600 git ls-files -u >out &&1601 test_line_count = 6 out &&1602 git ls-files -o >out &&1603 test_line_count = 1 out &&16041605 git rev-parse >actual \1606 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:x/c :0:y/d &&1607 git rev-parse >expect \1608 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d &&1609 test_cmp expect actual &&16101611 git hash-object >actual \1612 y/b w/b y/c x/c &&1613 git rev-parse >expect \1614 O:z/b O:z/b O:z/c O:z/c &&1615 test_cmp expect actual1616 )1617'16181619# Testcase 7b, rename/rename(2to1), but only due to transitive rename1620# (Related to testcase 1d)1621# Commit O: z/{b,c}, x/d_1, w/d_21622# Commit A: y/{b,c,d_2}, x/d_11623# Commit B: z/{b,c,d_1}, w/d_21624# Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d)16251626test_expect_success '7b-setup: rename/rename(2to1), but only due to transitive rename''1627 test_create_repo 7b &&1628 (1629 cd 7b &&16301631 mkdir z &&1632 mkdir x &&1633 mkdir w &&1634 echo b >z/b &&1635 echo c >z/c &&1636 echo d1 > x/d &&1637 echo d2 > w/d &&1638 git add z x w &&1639 test_tick &&1640 git commit -m "O" &&16411642 git branch O &&1643 git branch A &&1644 git branch B &&16451646 git checkout A &&1647 git mv z y &&1648 git mv w/d y/ &&1649 test_tick &&1650 git commit -m "A" &&16511652 git checkout B &&1653 git mv x/d z/ &&1654 rmdir x &&1655 test_tick &&1656 git commit -m "B"1657 )1658'16591660test_expect_success '7b-check: rename/rename(2to1), but only due to transitive rename''1661 (1662 cd 7b &&16631664 git checkout A^0 &&16651666 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1667 test_i18ngrep "CONFLICT (rename/rename)" out &&16681669 git ls-files -s >out &&1670 test_line_count = 4 out &&1671 git ls-files -u >out &&1672 test_line_count = 2 out &&1673 git ls-files -o >out &&1674 test_line_count = 1 out &&16751676 git rev-parse >actual \1677 :0:y/b :0:y/c :2:y/d :3:y/d &&1678 git rev-parse >expect \1679 O:z/b O:z/c O:w/d O:x/d &&1680 test_cmp expect actual &&16811682 # Test that the two-way merge in y/d is as expected1683 git cat-file -p :2:y/d >expect &&1684 git cat-file -p :3:y/d >other &&1685 >empty &&1686 test_must_fail git merge-file \1687 -L "HEAD" \1688 -L "" \1689 -L "B^0" \1690 expect empty other &&1691 test_cmp expect y/d1692 )1693'16941695# Testcase 7c, rename/rename(1to...2or3); transitive rename may add complexity1696# (Related to testcases 3b and 5c)1697# Commit O: z/{b,c}, x/d1698# Commit A: y/{b,c}, w/d1699# Commit B: z/{b,c,d}1700# Expected: y/{b,c}, CONFLICT(x/d -> w/d vs. y/d)1701# NOTE: z/ was renamed to y/ so we do want to report1702# neither CONFLICT(x/d -> w/d vs. z/d)1703# nor CONFLiCT x/d -> w/d vs. y/d vs. z/d)17041705test_expect_success '7c-setup: rename/rename(1to...2or3); transitive rename may add complexity''1706 test_create_repo 7c &&1707 (1708 cd 7c &&17091710 mkdir z &&1711 echo b >z/b &&1712 echo c >z/c &&1713 mkdir x &&1714 echo d >x/d &&1715 git add z x &&1716 test_tick &&1717 git commit -m "O" &&17181719 git branch O &&1720 git branch A &&1721 git branch B &&17221723 git checkout A &&1724 git mv z y &&1725 git mv x w &&1726 test_tick &&1727 git commit -m "A" &&17281729 git checkout B &&1730 git mv x/d z/ &&1731 rmdir x &&1732 test_tick &&1733 git commit -m "B"1734 )1735'17361737test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may add complexity''1738 (1739 cd 7c &&17401741 git checkout A^0 &&17421743 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1744 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&17451746 git ls-files -s >out &&1747 test_line_count = 5 out &&1748 git ls-files -u >out &&1749 test_line_count = 3 out &&1750 git ls-files -o >out &&1751 test_line_count = 1 out &&17521753 git rev-parse >actual \1754 :0:y/b :0:y/c :1:x/d :2:w/d :3:y/d &&1755 git rev-parse >expect \1756 O:z/b O:z/c O:x/d O:x/d O:x/d &&1757 test_cmp expect actual1758 )1759'17601761# Testcase 7d, transitive rename involved in rename/delete; how is it reported?1762# (Related somewhat to testcases 5b and 8d)1763# Commit O: z/{b,c}, x/d1764# Commit A: y/{b,c}1765# Commit B: z/{b,c,d}1766# Expected: y/{b,c}, CONFLICT(delete x/d vs rename to y/d)1767# NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d)17681769test_expect_success '7d-setup: transitive rename involved in rename/delete; how is it reported?''1770 test_create_repo 7d &&1771 (1772 cd 7d &&17731774 mkdir z &&1775 echo b >z/b &&1776 echo c >z/c &&1777 mkdir x &&1778 echo d >x/d &&1779 git add z x &&1780 test_tick &&1781 git commit -m "O" &&17821783 git branch O &&1784 git branch A &&1785 git branch B &&17861787 git checkout A &&1788 git mv z y &&1789 git rm -rf x &&1790 test_tick &&1791 git commit -m "A" &&17921793 git checkout B &&1794 git mv x/d z/ &&1795 rmdir x &&1796 test_tick &&1797 git commit -m "B"1798 )1799'18001801test_expect_success '7d-check: transitive rename involved in rename/delete; how is it reported?''1802 (1803 cd 7d &&18041805 git checkout A^0 &&18061807 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1808 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&18091810 git ls-files -s >out &&1811 test_line_count = 3 out &&1812 git ls-files -u >out &&1813 test_line_count = 1 out &&1814 git ls-files -o >out &&1815 test_line_count = 1 out &&18161817 git rev-parse >actual \1818 :0:y/b :0:y/c :3:y/d &&1819 git rev-parse >expect \1820 O:z/b O:z/c O:x/d &&1821 test_cmp expect actual1822 )1823'18241825# Testcase 7e, transitive rename in rename/delete AND dirs in the way1826# (Very similar to 'both rename source and destination involved in D/F conflict' from t6022-merge-rename.sh)1827# (Also related to testcases 9c and 9d)1828# Commit O: z/{b,c}, x/d_11829# Commit A: y/{b,c,d/g}, x/d/f1830# Commit B: z/{b,c,d_1}1831# Expected: rename/delete(x/d_1->y/d_1 vs. None) + D/F conflict on y/d1832# y/{b,c,d/g}, y/d_1~B^0, x/d/f18331834# NOTE: The main path of interest here is d_1 and where it ends up, but1835# this is actually a case that has two potential directory renames1836# involved and D/F conflict(s), so it makes sense to walk through1837# each step.1838#1839# Commit A renames z/ -> y/. Thus everything that B adds to z/1840# should be instead moved to y/. This gives us the D/F conflict on1841# y/d because x/d_1 -> z/d_1 -> y/d_1 conflicts with y/d/g.1842#1843# Further, commit B renames x/ -> z/, thus everything A adds to x/1844# should instead be moved to z/...BUT we removed z/ and renamed it1845# to y/, so maybe everything should move not from x/ to z/, but1846# from x/ to z/ to y/. Doing so might make sense from the logic so1847# far, but note that commit A had both an x/ and a y/; it did the1848# renaming of z/ to y/ and created x/d/f and it clearly made these1849# things separate, so it doesn't make much sense to push these1850# together. Doing so is what I'd call a doubly transitive rename;1851# see testcases 9c and 9d for further discussion of this issue and1852# how it's resolved.18531854test_expect_success '7e-setup: transitive rename in rename/delete AND dirs in the way''1855 test_create_repo 7e &&1856 (1857 cd 7e &&18581859 mkdir z &&1860 echo b >z/b &&1861 echo c >z/c &&1862 mkdir x &&1863 echo d1 >x/d &&1864 git add z x &&1865 test_tick &&1866 git commit -m "O" &&18671868 git branch O &&1869 git branch A &&1870 git branch B &&18711872 git checkout A &&1873 git mv z y &&1874 git rm x/d &&1875 mkdir -p x/d &&1876 mkdir -p y/d &&1877 echo f >x/d/f &&1878 echo g >y/d/g &&1879 git add x/d/f y/d/g &&1880 test_tick &&1881 git commit -m "A" &&18821883 git checkout B &&1884 git mv x/d z/ &&1885 rmdir x &&1886 test_tick &&1887 git commit -m "B"1888 )1889'18901891test_expect_success '7e-check: transitive rename in rename/delete AND dirs in the way''1892 (1893 cd 7e &&18941895 git checkout A^0 &&18961897 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&1898 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&18991900 git ls-files -s >out &&1901 test_line_count = 5 out &&1902 git ls-files -u >out &&1903 test_line_count = 1 out &&1904 git ls-files -o >out &&1905 test_line_count = 2 out &&19061907 git rev-parse >actual \1908 :0:x/d/f :0:y/d/g :0:y/b :0:y/c :3:y/d &&1909 git rev-parse >expect \1910 A:x/d/f A:y/d/g O:z/b O:z/c O:x/d &&1911 test_cmp expect actual &&19121913 git hash-object y/d~B^0 >actual &&1914 git rev-parse O:x/d >expect &&1915 test_cmp expect actual1916 )1917'19181919###########################################################################1920# SECTION 8: Suboptimal merges1921#1922# As alluded to in the last section, the ruleset we have built up for1923# detecting directory renames unfortunately has some special cases where it1924# results in slightly suboptimal or non-intuitive behavior. This section1925# explores these cases.1926#1927# To be fair, we already had non-intuitive or suboptimal behavior for most1928# of these cases in git before introducing implicit directory rename1929# detection, but it'd be nice if there was a modified ruleset out there1930# that handled these cases a bit better.1931###########################################################################19321933# Testcase 8a, Dual-directory rename, one into the others' way1934# Commit O. x/{a,b}, y/{c,d}1935# Commit A. x/{a,b,e}, y/{c,d,f}1936# Commit B. y/{a,b}, z/{c,d}1937#1938# Possible Resolutions:1939# w/o dir-rename detection: y/{a,b,f}, z/{c,d}, x/e1940# Currently expected: y/{a,b,e,f}, z/{c,d}1941# Optimal: y/{a,b,e}, z/{c,d,f}1942#1943# Note: Both x and y got renamed and it'd be nice to detect both, and we do1944# better with directory rename detection than git did without, but the1945# simple rule from section 5 prevents me from handling this as optimally as1946# we potentially could.19471948test_expect_success '8a-setup: Dual-directory rename, one into the others way''1949 test_create_repo 8a &&1950 (1951 cd 8a &&19521953 mkdir x &&1954 mkdir y &&1955 echo a >x/a &&1956 echo b >x/b &&1957 echo c >y/c &&1958 echo d >y/d &&1959 git add x y &&1960 test_tick &&1961 git commit -m "O" &&19621963 git branch O &&1964 git branch A &&1965 git branch B &&19661967 git checkout A &&1968 echo e >x/e &&1969 echo f >y/f &&1970 git add x/e y/f &&1971 test_tick &&1972 git commit -m "A" &&19731974 git checkout B &&1975 git mv y z &&1976 git mv x y &&1977 test_tick &&1978 git commit -m "B"1979 )1980'19811982test_expect_success '8a-check: Dual-directory rename, one into the others way''1983 (1984 cd 8a &&19851986 git checkout A^0 &&19871988 git -c merge.directoryRenames=true merge -s recursive B^0 &&19891990 git ls-files -s >out &&1991 test_line_count = 6 out &&1992 git ls-files -u >out &&1993 test_line_count = 0 out &&1994 git ls-files -o >out &&1995 test_line_count = 1 out &&19961997 git rev-parse >actual \1998 HEAD:y/a HEAD:y/b HEAD:y/e HEAD:y/f HEAD:z/c HEAD:z/d &&1999 git rev-parse >expect \2000 O:x/a O:x/b A:x/e A:y/f O:y/c O:y/d &&2001 test_cmp expect actual2002 )2003'20042005# Testcase 8b, Dual-directory rename, one into the others' way, with conflicting filenames2006# Commit O. x/{a_1,b_1}, y/{a_2,b_2}2007# Commit A. x/{a_1,b_1,e_1}, y/{a_2,b_2,e_2}2008# Commit B. y/{a_1,b_1}, z/{a_2,b_2}2009#2010# w/o dir-rename detection: y/{a_1,b_1,e_2}, z/{a_2,b_2}, x/e_12011# Currently expected: <same>2012# Scary: y/{a_1,b_1}, z/{a_2,b_2}, CONFLICT(add/add, e_1 vs. e_2)2013# Optimal: y/{a_1,b_1,e_1}, z/{a_2,b_2,e_2}2014#2015# Note: Very similar to 8a, except instead of 'e' and 'f' in directories x and2016# y, both are named 'e'. Without directory rename detection, neither file2017# moves directories. Implement directory rename detection suboptimally, and2018# you get an add/add conflict, but both files were added in commit A, so this2019# is an add/add conflict where one side of history added both files --2020# something we can't represent in the index. Obviously, we'd prefer the last2021# resolution, but our previous rules are too coarse to allow it. Using both2022# the rules from section 4 and section 5 save us from the Scary resolution,2023# making us fall back to pre-directory-rename-detection behavior for both2024# e_1 and e_2.20252026test_expect_success '8b-setup: Dual-directory rename, one into the others way, with conflicting filenames''2027 test_create_repo 8b &&2028 (2029 cd 8b &&20302031 mkdir x &&2032 mkdir y &&2033 echo a1 >x/a &&2034 echo b1 >x/b &&2035 echo a2 >y/a &&2036 echo b2 >y/b &&2037 git add x y &&2038 test_tick &&2039 git commit -m "O" &&20402041 git branch O &&2042 git branch A &&2043 git branch B &&20442045 git checkout A &&2046 echo e1 >x/e &&2047 echo e2 >y/e &&2048 git add x/e y/e &&2049 test_tick &&2050 git commit -m "A" &&20512052 git checkout B &&2053 git mv y z &&2054 git mv x y &&2055 test_tick &&2056 git commit -m "B"2057 )2058'20592060test_expect_success '8b-check: Dual-directory rename, one into the others way, with conflicting filenames''2061 (2062 cd 8b &&20632064 git checkout A^0 &&20652066 git -c merge.directoryRenames=true merge -s recursive B^0 &&20672068 git ls-files -s >out &&2069 test_line_count = 6 out &&2070 git ls-files -u >out &&2071 test_line_count = 0 out &&2072 git ls-files -o >out &&2073 test_line_count = 1 out &&20742075 git rev-parse >actual \2076 HEAD:y/a HEAD:y/b HEAD:z/a HEAD:z/b HEAD:x/e HEAD:y/e &&2077 git rev-parse >expect \2078 O:x/a O:x/b O:y/a O:y/b A:x/e A:y/e &&2079 test_cmp expect actual2080 )2081'20822083# Testcase 8c, modify/delete or rename+modify/delete?2084# (Related to testcases 5b, 8d, and 9h)2085# Commit O: z/{b,c,d}2086# Commit A: y/{b,c}2087# Commit B: z/{b,c,d_modified,e}2088# Expected: y/{b,c,e}, CONFLICT(modify/delete: on z/d)2089#2090# Note: It could easily be argued that the correct resolution here is2091# y/{b,c,e}, CONFLICT(rename/delete: z/d -> y/d vs deleted)2092# and that the modifed version of d should be present in y/ after2093# the merge, just marked as conflicted. Indeed, I previously did2094# argue that. But applying directory renames to the side of2095# history where a file is merely modified results in spurious2096# rename/rename(1to2) conflicts -- see testcase 9h. See also2097# notes in 8d.20982099test_expect_success '8c-setup: modify/delete or rename+modify/delete?''2100 test_create_repo 8c &&2101 (2102 cd 8c &&21032104 mkdir z &&2105 echo b >z/b &&2106 echo c >z/c &&2107 test_seq 1 10 >z/d &&2108 git add z &&2109 test_tick &&2110 git commit -m "O" &&21112112 git branch O &&2113 git branch A &&2114 git branch B &&21152116 git checkout A &&2117 git rm z/d &&2118 git mv z y &&2119 test_tick &&2120 git commit -m "A" &&21212122 git checkout B &&2123 echo 11 >z/d &&2124 test_chmod +x z/d &&2125 echo e >z/e &&2126 git add z/d z/e &&2127 test_tick &&2128 git commit -m "B"2129 )2130'21312132test_expect_success '8c-check: modify/delete or rename+modify/delete''2133 (2134 cd 8c &&21352136 git checkout A^0 &&21372138 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&2139 test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&21402141 git ls-files -s >out &&2142 test_line_count = 5 out &&2143 git ls-files -u >out &&2144 test_line_count = 2 out &&2145 git ls-files -o >out &&2146 test_line_count = 1 out &&21472148 git rev-parse >actual \2149 :0:y/b :0:y/c :0:y/e :1:z/d :3:z/d &&2150 git rev-parse >expect \2151 O:z/b O:z/c B:z/e O:z/d B:z/d &&2152 test_cmp expect actual &&21532154 test_must_fail git rev-parse :2:z/d &&2155 git ls-files -s z/d | grep ^100755 &&2156 test_path_is_file z/d &&2157 test_path_is_missing y/d2158 )2159'21602161# Testcase 8d, rename/delete...or not?2162# (Related to testcase 5b; these may appear slightly inconsistent to users;2163# Also related to testcases 7d and 7e)2164# Commit O: z/{b,c,d}2165# Commit A: y/{b,c}2166# Commit B: z/{b,c,d,e}2167# Expected: y/{b,c,e}2168#2169# Note: It would also be somewhat reasonable to resolve this as2170# y/{b,c,e}, CONFLICT(rename/delete: x/d -> y/d or deleted)2171#2172# In this case, I'm leaning towards: commit A was the one that deleted z/d2173# and it did the rename of z to y, so the two "conflicts" (rename vs.2174# delete) are both coming from commit A, which is illogical. Conflicts2175# during merging are supposed to be about opposite sides doing things2176# differently.21772178test_expect_success '8d-setup: rename/delete...or not?''2179 test_create_repo 8d &&2180 (2181 cd 8d &&21822183 mkdir z &&2184 echo b >z/b &&2185 echo c >z/c &&2186 test_seq 1 10 >z/d &&2187 git add z &&2188 test_tick &&2189 git commit -m "O" &&21902191 git branch O &&2192 git branch A &&2193 git branch B &&21942195 git checkout A &&2196 git rm z/d &&2197 git mv z y &&2198 test_tick &&2199 git commit -m "A" &&22002201 git checkout B &&2202 echo e >z/e &&2203 git add z/e &&2204 test_tick &&2205 git commit -m "B"2206 )2207'22082209test_expect_success '8d-check: rename/delete...or not?''2210 (2211 cd 8d &&22122213 git checkout A^0 &&22142215 git -c merge.directoryRenames=true merge -s recursive B^0 &&22162217 git ls-files -s >out &&2218 test_line_count = 3 out &&22192220 git rev-parse >actual \2221 HEAD:y/b HEAD:y/c HEAD:y/e &&2222 git rev-parse >expect \2223 O:z/b O:z/c B:z/e &&2224 test_cmp expect actual2225 )2226'22272228# Testcase 8e, Both sides rename, one side adds to original directory2229# Commit O: z/{b,c}2230# Commit A: y/{b,c}2231# Commit B: w/{b,c}, z/d2232#2233# Possible Resolutions:2234# w/o dir-rename detection: z/d, CONFLICT(z/b -> y/b vs. w/b),2235# CONFLICT(z/c -> y/c vs. w/c)2236# Currently expected: y/d, CONFLICT(z/b -> y/b vs. w/b),2237# CONFLICT(z/c -> y/c vs. w/c)2238# Optimal: ??2239#2240# Notes: In commit A, directory z got renamed to y. In commit B, directory z2241# did NOT get renamed; the directory is still present; instead it is2242# considered to have just renamed a subset of paths in directory z2243# elsewhere. Therefore, the directory rename done in commit A to z/2244# applies to z/d and maps it to y/d.2245#2246# It's possible that users would get confused about this, but what2247# should we do instead? Silently leaving at z/d seems just as bad or2248# maybe even worse. Perhaps we could print a big warning about z/d2249# and how we're moving to y/d in this case, but when I started thinking2250# about the ramifications of doing that, I didn't know how to rule out2251# that opening other weird edge and corner cases so I just punted.22522253test_expect_success '8e-setup: Both sides rename, one side adds to original directory''2254 test_create_repo 8e &&2255 (2256 cd 8e &&22572258 mkdir z &&2259 echo b >z/b &&2260 echo c >z/c &&2261 git add z &&2262 test_tick &&2263 git commit -m "O" &&22642265 git branch O &&2266 git branch A &&2267 git branch B &&22682269 git checkout A &&2270 git mv z y &&2271 test_tick &&2272 git commit -m "A" &&22732274 git checkout B &&2275 git mv z w &&2276 mkdir z &&2277 echo d >z/d &&2278 git add z/d &&2279 test_tick &&2280 git commit -m "B"2281 )2282'22832284test_expect_success '8e-check: Both sides rename, one side adds to original directory''2285 (2286 cd 8e &&22872288 git checkout A^0 &&22892290 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&2291 test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&2292 test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&22932294 git ls-files -s >out &&2295 test_line_count = 7 out &&2296 git ls-files -u >out &&2297 test_line_count = 6 out &&2298 git ls-files -o >out &&2299 test_line_count = 2 out &&23002301 git rev-parse >actual \2302 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:w/c :0:y/d &&2303 git rev-parse >expect \2304 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d &&2305 test_cmp expect actual &&23062307 git hash-object >actual \2308 y/b w/b y/c w/c &&2309 git rev-parse >expect \2310 O:z/b O:z/b O:z/c O:z/c &&2311 test_cmp expect actual &&23122313 test_path_is_missing z/b &&2314 test_path_is_missing z/c2315 )2316'23172318###########################################################################2319# SECTION 9: Other testcases2320#2321# This section consists of miscellaneous testcases I thought of during2322# the implementation which round out the testing.2323###########################################################################23242325# Testcase 9a, Inner renamed directory within outer renamed directory2326# (Related to testcase 1f)2327# Commit O: z/{b,c,d/{e,f,g}}2328# Commit A: y/{b,c}, x/w/{e,f,g}2329# Commit B: z/{b,c,d/{e,f,g,h},i}2330# Expected: y/{b,c,i}, x/w/{e,f,g,h}2331# NOTE: The only reason this one is interesting is because when a directory2332# is split into multiple other directories, we determine by the weight2333# of which one had the most paths going to it. A naive implementation2334# of that could take the new file in commit B at z/i to x/w/i or x/i.23352336test_expect_success '9a-setup: Inner renamed directory within outer renamed directory''2337 test_create_repo 9a &&2338 (2339 cd 9a &&23402341 mkdir -p z/d &&2342 echo b >z/b &&2343 echo c >z/c &&2344 echo e >z/d/e &&2345 echo f >z/d/f &&2346 echo g >z/d/g &&2347 git add z &&2348 test_tick &&2349 git commit -m "O" &&23502351 git branch O &&2352 git branch A &&2353 git branch B &&23542355 git checkout A &&2356 mkdir x &&2357 git mv z/d x/w &&2358 git mv z y &&2359 test_tick &&2360 git commit -m "A" &&23612362 git checkout B &&2363 echo h >z/d/h &&2364 echo i >z/i &&2365 git add z &&2366 test_tick &&2367 git commit -m "B"2368 )2369'23702371test_expect_success '9a-check: Inner renamed directory within outer renamed directory''2372 (2373 cd 9a &&23742375 git checkout A^0 &&23762377 git -c merge.directoryRenames=true merge -s recursive B^0 &&23782379 git ls-files -s >out &&2380 test_line_count = 7 out &&2381 git ls-files -u >out &&2382 test_line_count = 0 out &&2383 git ls-files -o >out &&2384 test_line_count = 1 out &&23852386 git rev-parse >actual \2387 HEAD:y/b HEAD:y/c HEAD:y/i &&2388 git rev-parse >expect \2389 O:z/b O:z/c B:z/i &&2390 test_cmp expect actual &&23912392 git rev-parse >actual \2393 HEAD:x/w/e HEAD:x/w/f HEAD:x/w/g HEAD:x/w/h &&2394 git rev-parse >expect \2395 O:z/d/e O:z/d/f O:z/d/g B:z/d/h &&2396 test_cmp expect actual2397 )2398'23992400# Testcase 9b, Transitive rename with content merge2401# (Related to testcase 1c)2402# Commit O: z/{b,c}, x/d_12403# Commit A: y/{b,c}, x/d_22404# Commit B: z/{b,c,d_3}2405# Expected: y/{b,c,d_merged}24062407test_expect_success '9b-setup: Transitive rename with content merge''2408 test_create_repo 9b &&2409 (2410 cd 9b &&24112412 mkdir z &&2413 echo b >z/b &&2414 echo c >z/c &&2415 mkdir x &&2416 test_seq 1 10 >x/d &&2417 git add z x &&2418 test_tick &&2419 git commit -m "O" &&24202421 git branch O &&2422 git branch A &&2423 git branch B &&24242425 git checkout A &&2426 git mv z y &&2427 test_seq 1 11 >x/d &&2428 git add x/d &&2429 test_tick &&2430 git commit -m "A" &&24312432 git checkout B &&2433 test_seq 0 10 >x/d &&2434 git mv x/d z/d &&2435 git add z/d &&2436 test_tick &&2437 git commit -m "B"2438 )2439'24402441test_expect_success '9b-check: Transitive rename with content merge''2442 (2443 cd 9b &&24442445 git checkout A^0 &&24462447 git -c merge.directoryRenames=true merge -s recursive B^0 &&24482449 git ls-files -s >out &&2450 test_line_count = 3 out &&24512452 test_seq 0 11 >expected &&2453 test_cmp expected y/d &&2454 git add expected &&2455 git rev-parse >actual \2456 HEAD:y/b HEAD:y/c HEAD:y/d &&2457 git rev-parse >expect \2458 O:z/b O:z/c :0:expected &&2459 test_cmp expect actual &&2460 test_must_fail git rev-parse HEAD:x/d &&2461 test_must_fail git rev-parse HEAD:z/d &&2462 test_path_is_missing z/d &&24632464 test$(git rev-parse HEAD:y/d)!=$(git rev-parse O:x/d)&&2465 test$(git rev-parse HEAD:y/d)!=$(git rev-parse A:x/d)&&2466 test$(git rev-parse HEAD:y/d)!=$(git rev-parse B:z/d)2467 )2468'24692470# Testcase 9c, Doubly transitive rename?2471# (Related to testcase 1c, 7e, and 9d)2472# Commit O: z/{b,c}, x/{d,e}, w/f2473# Commit A: y/{b,c}, x/{d,e,f,g}2474# Commit B: z/{b,c,d,e}, w/f2475# Expected: y/{b,c,d,e}, x/{f,g}2476#2477# NOTE: x/f and x/g may be slightly confusing here. The rename from w/f to2478# x/f is clear. Let's look beyond that. Here's the logic:2479# Commit B renamed x/ -> z/2480# Commit A renamed z/ -> y/2481# So, we could possibly further rename x/f to z/f to y/f, a doubly2482# transient rename. However, where does it end? We can chain these2483# indefinitely (see testcase 9d). What if there is a D/F conflict2484# at z/f/ or y/f/? Or just another file conflict at one of those2485# paths? In the case of an N-long chain of transient renamings,2486# where do we "abort" the rename at? Can the user make sense of2487# the resulting conflict and resolve it?2488#2489# To avoid this confusion I use the simple rule that if the other side2490# of history did a directory rename to a path that your side renamed2491# away, then ignore that particular rename from the other side of2492# history for any implicit directory renames.24932494test_expect_success '9c-setup: Doubly transitive rename?''2495 test_create_repo 9c &&2496 (2497 cd 9c &&24982499 mkdir z &&2500 echo b >z/b &&2501 echo c >z/c &&2502 mkdir x &&2503 echo d >x/d &&2504 echo e >x/e &&2505 mkdir w &&2506 echo f >w/f &&2507 git add z x w &&2508 test_tick &&2509 git commit -m "O" &&25102511 git branch O &&2512 git branch A &&2513 git branch B &&25142515 git checkout A &&2516 git mv z y &&2517 git mv w/f x/ &&2518 echo g >x/g &&2519 git add x/g &&2520 test_tick &&2521 git commit -m "A" &&25222523 git checkout B &&2524 git mv x/d z/d &&2525 git mv x/e z/e &&2526 test_tick &&2527 git commit -m "B"2528 )2529'25302531test_expect_success '9c-check: Doubly transitive rename?''2532 (2533 cd 9c &&25342535 git checkout A^0 &&25362537 git -c merge.directoryRenames=true merge -s recursive B^0 >out &&2538 test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&25392540 git ls-files -s >out &&2541 test_line_count = 6 out &&2542 git ls-files -o >out &&2543 test_line_count = 1 out &&25442545 git rev-parse >actual \2546 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e HEAD:x/f HEAD:x/g &&2547 git rev-parse >expect \2548 O:z/b O:z/c O:x/d O:x/e O:w/f A:x/g &&2549 test_cmp expect actual2550 )2551'25522553# Testcase 9d, N-fold transitive rename?2554# (Related to testcase 9c...and 1c and 7e)2555# Commit O: z/a, y/b, x/c, w/d, v/e, u/f2556# Commit A: y/{a,b}, w/{c,d}, u/{e,f}2557# Commit B: z/{a,t}, x/{b,c}, v/{d,e}, u/f2558# Expected: <see NOTE first>2559#2560# NOTE: z/ -> y/ (in commit A)2561# y/ -> x/ (in commit B)2562# x/ -> w/ (in commit A)2563# w/ -> v/ (in commit B)2564# v/ -> u/ (in commit A)2565# So, if we add a file to z, say z/t, where should it end up? In u?2566# What if there's another file or directory named 't' in one of the2567# intervening directories and/or in u itself? Also, shouldn't the2568# same logic that places 't' in u/ also move ALL other files to u/?2569# What if there are file or directory conflicts in any of them? If2570# we attempted to do N-way (N-fold? N-ary? N-uple?) transitive renames2571# like this, would the user have any hope of understanding any2572# conflicts or how their working tree ended up? I think not, so I'm2573# ruling out N-ary transitive renames for N>1.2574#2575# Therefore our expected result is:2576# z/t, y/a, x/b, w/c, u/d, u/e, u/f2577# The reason that v/d DOES get transitively renamed to u/d is that u/ isn't2578# renamed somewhere. A slightly sub-optimal result, but it uses fairly2579# simple rules that are consistent with what we need for all the other2580# testcases and simplifies things for the user.25812582test_expect_success '9d-setup: N-way transitive rename?''2583 test_create_repo 9d &&2584 (2585 cd 9d &&25862587 mkdir z y x w v u &&2588 echo a >z/a &&2589 echo b >y/b &&2590 echo c >x/c &&2591 echo d >w/d &&2592 echo e >v/e &&2593 echo f >u/f &&2594 git add z y x w v u &&2595 test_tick &&2596 git commit -m "O" &&25972598 git branch O &&2599 git branch A &&2600 git branch B &&26012602 git checkout A &&2603 git mv z/a y/ &&2604 git mv x/c w/ &&2605 git mv v/e u/ &&2606 test_tick &&2607 git commit -m "A" &&26082609 git checkout B &&2610 echo t >z/t &&2611 git mv y/b x/ &&2612 git mv w/d v/ &&2613 git add z/t &&2614 test_tick &&2615 git commit -m "B"2616 )2617'26182619test_expect_success '9d-check: N-way transitive rename?''2620 (2621 cd 9d &&26222623 git checkout A^0 &&26242625 git -c merge.directoryRenames=true merge -s recursive B^0 >out &&2626 test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&2627 test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&2628 test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&2629 test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out &&26302631 git ls-files -s >out &&2632 test_line_count = 7 out &&2633 git ls-files -o >out &&2634 test_line_count = 1 out &&26352636 git rev-parse >actual \2637 HEAD:z/t \2638 HEAD:y/a HEAD:x/b HEAD:w/c \2639 HEAD:u/d HEAD:u/e HEAD:u/f &&2640 git rev-parse >expect \2641 B:z/t \2642 O:z/a O:y/b O:x/c \2643 O:w/d O:v/e A:u/f &&2644 test_cmp expect actual2645 )2646'26472648# Testcase 9e, N-to-1 whammo2649# (Related to testcase 9c...and 1c and 7e)2650# Commit O: dir1/{a,b}, dir2/{d,e}, dir3/{g,h}, dirN/{j,k}2651# Commit A: dir1/{a,b,c,yo}, dir2/{d,e,f,yo}, dir3/{g,h,i,yo}, dirN/{j,k,l,yo}2652# Commit B: combined/{a,b,d,e,g,h,j,k}2653# Expected: combined/{a,b,c,d,e,f,g,h,i,j,k,l}, CONFLICT(Nto1) warnings,2654# dir1/yo, dir2/yo, dir3/yo, dirN/yo26552656test_expect_success '9e-setup: N-to-1 whammo''2657 test_create_repo 9e &&2658 (2659 cd 9e &&26602661 mkdir dir1 dir2 dir3 dirN &&2662 echo a >dir1/a &&2663 echo b >dir1/b &&2664 echo d >dir2/d &&2665 echo e >dir2/e &&2666 echo g >dir3/g &&2667 echo h >dir3/h &&2668 echo j >dirN/j &&2669 echo k >dirN/k &&2670 git add dir* &&2671 test_tick &&2672 git commit -m "O" &&26732674 git branch O &&2675 git branch A &&2676 git branch B &&26772678 git checkout A &&2679 echo c >dir1/c &&2680 echo yo >dir1/yo &&2681 echo f >dir2/f &&2682 echo yo >dir2/yo &&2683 echo i >dir3/i &&2684 echo yo >dir3/yo &&2685 echo l >dirN/l &&2686 echo yo >dirN/yo &&2687 git add dir* &&2688 test_tick &&2689 git commit -m "A" &&26902691 git checkout B &&2692 git mv dir1 combined &&2693 git mv dir2/* combined/ &&2694 git mv dir3/* combined/ &&2695 git mv dirN/* combined/ &&2696 test_tick &&2697 git commit -m "B"2698 )2699'27002701test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo''2702 (2703 cd 9e &&27042705 git checkout A^0 &&27062707 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&2708 grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line &&2709 grep -q dir1/yo error_line &&2710 grep -q dir2/yo error_line &&2711 grep -q dir3/yo error_line &&2712 grep -q dirN/yo error_line &&27132714 git ls-files -s >out &&2715 test_line_count = 16 out &&2716 git ls-files -u >out &&2717 test_line_count = 0 out &&2718 git ls-files -o >out &&2719 test_line_count = 2 out &&27202721 git rev-parse >actual \2722 :0:combined/a :0:combined/b :0:combined/c \2723 :0:combined/d :0:combined/e :0:combined/f \2724 :0:combined/g :0:combined/h :0:combined/i \2725 :0:combined/j :0:combined/k :0:combined/l &&2726 git rev-parse >expect \2727 O:dir1/a O:dir1/b A:dir1/c \2728 O:dir2/d O:dir2/e A:dir2/f \2729 O:dir3/g O:dir3/h A:dir3/i \2730 O:dirN/j O:dirN/k A:dirN/l &&2731 test_cmp expect actual &&27322733 git rev-parse >actual \2734 :0:dir1/yo :0:dir2/yo :0:dir3/yo :0:dirN/yo &&2735 git rev-parse >expect \2736 A:dir1/yo A:dir2/yo A:dir3/yo A:dirN/yo &&2737 test_cmp expect actual2738 )2739'27402741# Testcase 9f, Renamed directory that only contained immediate subdirs2742# (Related to testcases 1e & 9g)2743# Commit O: goal/{a,b}/$more_files2744# Commit A: priority/{a,b}/$more_files2745# Commit B: goal/{a,b}/$more_files, goal/c2746# Expected: priority/{a,b}/$more_files, priority/c27472748test_expect_success '9f-setup: Renamed directory that only contained immediate subdirs''2749 test_create_repo 9f &&2750 (2751 cd 9f &&27522753 mkdir -p goal/a &&2754 mkdir -p goal/b &&2755 echo foo >goal/a/foo &&2756 echo bar >goal/b/bar &&2757 echo baz >goal/b/baz &&2758 git add goal &&2759 test_tick &&2760 git commit -m "O" &&27612762 git branch O &&2763 git branch A &&2764 git branch B &&27652766 git checkout A &&2767 git mv goal/ priority &&2768 test_tick &&2769 git commit -m "A" &&27702771 git checkout B &&2772 echo c >goal/c &&2773 git add goal/c &&2774 test_tick &&2775 git commit -m "B"2776 )2777'27782779test_expect_success '9f-check: Renamed directory that only contained immediate subdirs''2780 (2781 cd 9f &&27822783 git checkout A^0 &&27842785 git -c merge.directoryRenames=true merge -s recursive B^0 &&27862787 git ls-files -s >out &&2788 test_line_count = 4 out &&27892790 git rev-parse >actual \2791 HEAD:priority/a/foo \2792 HEAD:priority/b/bar \2793 HEAD:priority/b/baz \2794 HEAD:priority/c &&2795 git rev-parse >expect \2796 O:goal/a/foo \2797 O:goal/b/bar \2798 O:goal/b/baz \2799 B:goal/c &&2800 test_cmp expect actual &&2801 test_must_fail git rev-parse HEAD:goal/c2802 )2803'28042805# Testcase 9g, Renamed directory that only contained immediate subdirs, immediate subdirs renamed2806# (Related to testcases 1e & 9f)2807# Commit O: goal/{a,b}/$more_files2808# Commit A: priority/{alpha,bravo}/$more_files2809# Commit B: goal/{a,b}/$more_files, goal/c2810# Expected: priority/{alpha,bravo}/$more_files, priority/c28112812test_expect_success '9g-setup: Renamed directory that only contained immediate subdirs, immediate subdirs renamed''2813 test_create_repo 9g &&2814 (2815 cd 9g &&28162817 mkdir -p goal/a &&2818 mkdir -p goal/b &&2819 echo foo >goal/a/foo &&2820 echo bar >goal/b/bar &&2821 echo baz >goal/b/baz &&2822 git add goal &&2823 test_tick &&2824 git commit -m "O" &&28252826 git branch O &&2827 git branch A &&2828 git branch B &&28292830 git checkout A &&2831 mkdir priority &&2832 git mv goal/a/ priority/alpha &&2833 git mv goal/b/ priority/beta &&2834 rmdir goal/ &&2835 test_tick &&2836 git commit -m "A" &&28372838 git checkout B &&2839 echo c >goal/c &&2840 git add goal/c &&2841 test_tick &&2842 git commit -m "B"2843 )2844'28452846test_expect_failure '9g-check: Renamed directory that only contained immediate subdirs, immediate subdirs renamed''2847 (2848 cd 9g &&28492850 git checkout A^0 &&28512852 git -c merge.directoryRenames=true merge -s recursive B^0 &&28532854 git ls-files -s >out &&2855 test_line_count = 4 out &&28562857 git rev-parse >actual \2858 HEAD:priority/alpha/foo \2859 HEAD:priority/beta/bar \2860 HEAD:priority/beta/baz \2861 HEAD:priority/c &&2862 git rev-parse >expect \2863 O:goal/a/foo \2864 O:goal/b/bar \2865 O:goal/b/baz \2866 B:goal/c &&2867 test_cmp expect actual &&2868 test_must_fail git rev-parse HEAD:goal/c2869 )2870'28712872# Testcase 9h, Avoid implicit rename if involved as source on other side2873# (Extremely closely related to testcase 3a)2874# Commit O: z/{b,c,d_1}2875# Commit A: z/{b,c,d_2}2876# Commit B: y/{b,c}, x/d_12877# Expected: y/{b,c}, x/d_22878# NOTE: If we applied the z/ -> y/ rename to z/d, then we'd end up with2879# a rename/rename(1to2) conflict (z/d -> y/d vs. x/d)2880test_expect_success '9h-setup: Avoid dir rename on merely modified path''2881 test_create_repo 9h &&2882 (2883 cd 9h &&28842885 mkdir z &&2886 echo b >z/b &&2887 echo c >z/c &&2888 printf "1\n2\n3\n4\n5\n6\n7\n8\nd\n" >z/d &&2889 git add z &&2890 test_tick &&2891 git commit -m "O" &&28922893 git branch O &&2894 git branch A &&2895 git branch B &&28962897 git checkout A &&2898 test_tick &&2899 echo more >>z/d &&2900 git add z/d &&2901 git commit -m "A" &&29022903 git checkout B &&2904 mkdir y &&2905 mkdir x &&2906 git mv z/b y/ &&2907 git mv z/c y/ &&2908 git mv z/d x/ &&2909 rmdir z &&2910 test_tick &&2911 git commit -m "B"2912 )2913'29142915test_expect_success '9h-check: Avoid dir rename on merely modified path''2916 (2917 cd 9h &&29182919 git checkout A^0 &&29202921 git -c merge.directoryRenames=true merge -s recursive B^0 &&29222923 git ls-files -s >out &&2924 test_line_count = 3 out &&29252926 git rev-parse >actual \2927 HEAD:y/b HEAD:y/c HEAD:x/d &&2928 git rev-parse >expect \2929 O:z/b O:z/c A:z/d &&2930 test_cmp expect actual2931 )2932'29332934###########################################################################2935# Rules suggested by section 9:2936#2937# If the other side of history did a directory rename to a path that your2938# side renamed away, then ignore that particular rename from the other2939# side of history for any implicit directory renames.2940###########################################################################29412942###########################################################################2943# SECTION 10: Handling untracked files2944#2945# unpack_trees(), upon which the recursive merge algorithm is based, aborts2946# the operation if untracked or dirty files would be deleted or overwritten2947# by the merge. Unfortunately, unpack_trees() does not understand renames,2948# and if it doesn't abort, then it muddies up the working directory before2949# we even get to the point of detecting renames, so we need some special2950# handling, at least in the case of directory renames.2951###########################################################################29522953# Testcase 10a, Overwrite untracked: normal rename/delete2954# Commit O: z/{b,c_1}2955# Commit A: z/b + untracked z/c + untracked z/d2956# Commit B: z/{b,d_1}2957# Expected: Aborted Merge +2958# ERROR_MSG(untracked working tree files would be overwritten by merge)29592960test_expect_success '10a-setup: Overwrite untracked with normal rename/delete''2961 test_create_repo 10a &&2962 (2963 cd 10a &&29642965 mkdir z &&2966 echo b >z/b &&2967 echo c >z/c &&2968 git add z &&2969 test_tick &&2970 git commit -m "O" &&29712972 git branch O &&2973 git branch A &&2974 git branch B &&29752976 git checkout A &&2977 git rm z/c &&2978 test_tick &&2979 git commit -m "A" &&29802981 git checkout B &&2982 git mv z/c z/d &&2983 test_tick &&2984 git commit -m "B"2985 )2986'29872988test_expect_success '10a-check: Overwrite untracked with normal rename/delete''2989 (2990 cd 10a &&29912992 git checkout A^0 &&2993 echo very >z/c &&2994 echo important >z/d &&29952996 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&2997 test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&29982999 git ls-files -s >out &&3000 test_line_count = 1 out &&3001 git ls-files -o >out &&3002 test_line_count = 4 out &&30033004 echo very >expect &&3005 test_cmp expect z/c &&30063007 echo important >expect &&3008 test_cmp expect z/d &&30093010 git rev-parse HEAD:z/b >actual &&3011 git rev-parse O:z/b >expect &&3012 test_cmp expect actual3013 )3014'30153016# Testcase 10b, Overwrite untracked: dir rename + delete3017# Commit O: z/{b,c_1}3018# Commit A: y/b + untracked y/{c,d,e}3019# Commit B: z/{b,d_1,e}3020# Expected: Failed Merge; y/b + untracked y/c + untracked y/d on disk +3021# z/c_1 -> z/d_1 rename recorded at stage 3 for y/d +3022# ERROR_MSG(refusing to lose untracked file at 'y/d')30233024test_expect_success '10b-setup: Overwrite untracked with dir rename + delete''3025 test_create_repo 10b &&3026 (3027 cd 10b &&30283029 mkdir z &&3030 echo b >z/b &&3031 echo c >z/c &&3032 git add z &&3033 test_tick &&3034 git commit -m "O" &&30353036 git branch O &&3037 git branch A &&3038 git branch B &&30393040 git checkout A &&3041 git rm z/c &&3042 git mv z/ y/ &&3043 test_tick &&3044 git commit -m "A" &&30453046 git checkout B &&3047 git mv z/c z/d &&3048 echo e >z/e &&3049 git add z/e &&3050 test_tick &&3051 git commit -m "B"3052 )3053'30543055test_expect_success '10b-check: Overwrite untracked with dir rename + delete''3056 (3057 cd 10b &&30583059 git checkout A^0 &&3060 echo very >y/c &&3061 echo important >y/d &&3062 echo contents >y/e &&30633064 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3065 test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&3066 test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&30673068 git ls-files -s >out &&3069 test_line_count = 3 out &&3070 git ls-files -u >out &&3071 test_line_count = 2 out &&3072 git ls-files -o >out &&3073 test_line_count = 5 out &&30743075 git rev-parse >actual \3076 :0:y/b :3:y/d :3:y/e &&3077 git rev-parse >expect \3078 O:z/b O:z/c B:z/e &&3079 test_cmp expect actual &&30803081 echo very >expect &&3082 test_cmp expect y/c &&30833084 echo important >expect &&3085 test_cmp expect y/d &&30863087 echo contents >expect &&3088 test_cmp expect y/e3089 )3090'30913092# Testcase 10c, Overwrite untracked: dir rename/rename(1to2)3093# Commit O: z/{a,b}, x/{c,d}3094# Commit A: y/{a,b}, w/c, x/d + different untracked y/c3095# Commit B: z/{a,b,c}, x/d3096# Expected: Failed Merge; y/{a,b} + x/d + untracked y/c +3097# CONFLICT(rename/rename) x/c -> w/c vs y/c +3098# y/c~B^0 +3099# ERROR_MSG(Refusing to lose untracked file at y/c)31003101test_expect_success '10c-setup: Overwrite untracked with dir rename/rename(1to2)''3102 test_create_repo 10c &&3103 (3104 cd 10c &&31053106 mkdir z x &&3107 echo a >z/a &&3108 echo b >z/b &&3109 echo c >x/c &&3110 echo d >x/d &&3111 git add z x &&3112 test_tick &&3113 git commit -m "O" &&31143115 git branch O &&3116 git branch A &&3117 git branch B &&31183119 git checkout A &&3120 mkdir w &&3121 git mv x/c w/c &&3122 git mv z/ y/ &&3123 test_tick &&3124 git commit -m "A" &&31253126 git checkout B &&3127 git mv x/c z/ &&3128 test_tick &&3129 git commit -m "B"3130 )3131'31323133test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)''3134 (3135 cd 10c &&31363137 git checkout A^0 &&3138 echo important >y/c &&31393140 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3141 test_i18ngrep "CONFLICT (rename/rename)" out &&3142 test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&31433144 git ls-files -s >out &&3145 test_line_count = 6 out &&3146 git ls-files -u >out &&3147 test_line_count = 3 out &&3148 git ls-files -o >out &&3149 test_line_count = 3 out &&31503151 git rev-parse >actual \3152 :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :3:y/c &&3153 git rev-parse >expect \3154 O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c &&3155 test_cmp expect actual &&31563157 git hash-object y/c~B^0 >actual &&3158 git rev-parse O:x/c >expect &&3159 test_cmp expect actual &&31603161 echo important >expect &&3162 test_cmp expect y/c3163 )3164'31653166test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2), other direction''3167 (3168 cd 10c &&31693170 git reset --hard &&3171 git clean -fdqx &&31723173 git checkout B^0 &&3174 mkdir y &&3175 echo important >y/c &&31763177 test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&3178 test_i18ngrep "CONFLICT (rename/rename)" out &&3179 test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&31803181 git ls-files -s >out &&3182 test_line_count = 6 out &&3183 git ls-files -u >out &&3184 test_line_count = 3 out &&3185 git ls-files -o >out &&3186 test_line_count = 3 out &&31873188 git rev-parse >actual \3189 :0:y/a :0:y/b :0:x/d :1:x/c :3:w/c :2:y/c &&3190 git rev-parse >expect \3191 O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c &&3192 test_cmp expect actual &&31933194 git hash-object y/c~HEAD >actual &&3195 git rev-parse O:x/c >expect &&3196 test_cmp expect actual &&31973198 echo important >expect &&3199 test_cmp expect y/c3200 )3201'32023203# Testcase 10d, Delete untracked w/ dir rename/rename(2to1)3204# Commit O: z/{a,b,c_1}, x/{d,e,f_2}3205# Commit A: y/{a,b}, x/{d,e,f_2,wham_1} + untracked y/wham3206# Commit B: z/{a,b,c_1,wham_2}, y/{d,e}3207# Expected: Failed Merge; y/{a,b,d,e} + untracked y/{wham,wham~merged}+3208# CONFLICT(rename/rename) z/c_1 vs x/f_2 -> y/wham3209# ERROR_MSG(Refusing to lose untracked file at y/wham)32103211test_expect_success '10d-setup: Delete untracked with dir rename/rename(2to1)''3212 test_create_repo 10d &&3213 (3214 cd 10d &&32153216 mkdir z x &&3217 echo a >z/a &&3218 echo b >z/b &&3219 echo c >z/c &&3220 echo d >x/d &&3221 echo e >x/e &&3222 echo f >x/f &&3223 git add z x &&3224 test_tick &&3225 git commit -m "O" &&32263227 git branch O &&3228 git branch A &&3229 git branch B &&32303231 git checkout A &&3232 git mv z/c x/wham &&3233 git mv z/ y/ &&3234 test_tick &&3235 git commit -m "A" &&32363237 git checkout B &&3238 git mv x/f z/wham &&3239 git mv x/ y/ &&3240 test_tick &&3241 git commit -m "B"3242 )3243'32443245test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)''3246 (3247 cd 10d &&32483249 git checkout A^0 &&3250 echo important >y/wham &&32513252 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3253 test_i18ngrep "CONFLICT (rename/rename)" out &&3254 test_i18ngrep "Refusing to lose untracked file at y/wham" out &&32553256 git ls-files -s >out &&3257 test_line_count = 6 out &&3258 git ls-files -u >out &&3259 test_line_count = 2 out &&3260 git ls-files -o >out &&3261 test_line_count = 3 out &&32623263 git rev-parse >actual \3264 :0:y/a :0:y/b :0:y/d :0:y/e :2:y/wham :3:y/wham &&3265 git rev-parse >expect \3266 O:z/a O:z/b O:x/d O:x/e O:z/c O:x/f &&3267 test_cmp expect actual &&32683269 test_must_fail git rev-parse :1:y/wham &&32703271 echo important >expect &&3272 test_cmp expect y/wham &&32733274 # Test that the two-way merge in y/wham~merged is as expected3275 git cat-file -p :2:y/wham >expect &&3276 git cat-file -p :3:y/wham >other &&3277 >empty &&3278 test_must_fail git merge-file \3279 -L "HEAD" \3280 -L "" \3281 -L "B^0" \3282 expect empty other &&3283 test_cmp expect y/wham~merged3284 )3285'32863287# Testcase 10e, Does git complain about untracked file that's not in the way?3288# Commit O: z/{a,b}3289# Commit A: y/{a,b} + untracked z/c3290# Commit B: z/{a,b,c}3291# Expected: y/{a,b,c} + untracked z/c32923293test_expect_success '10e-setup: Does git complain about untracked file that is not really in the way?''3294 test_create_repo 10e &&3295 (3296 cd 10e &&32973298 mkdir z &&3299 echo a >z/a &&3300 echo b >z/b &&3301 git add z &&3302 test_tick &&3303 git commit -m "O" &&33043305 git branch O &&3306 git branch A &&3307 git branch B &&33083309 git checkout A &&3310 git mv z/ y/ &&3311 test_tick &&3312 git commit -m "A" &&33133314 git checkout B &&3315 echo c >z/c &&3316 git add z/c &&3317 test_tick &&3318 git commit -m "B"3319 )3320'33213322test_expect_failure '10e-check: Does git complain about untracked file that is not really in the way?''3323 (3324 cd 10e &&33253326 git checkout A^0 &&3327 mkdir z &&3328 echo random >z/c &&33293330 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3331 test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&33323333 git ls-files -s >out &&3334 test_line_count = 3 out &&3335 git ls-files -u >out &&3336 test_line_count = 0 out &&3337 git ls-files -o >out &&3338 test_line_count = 3 out &&33393340 git rev-parse >actual \3341 :0:y/a :0:y/b :0:y/c &&3342 git rev-parse >expect \3343 O:z/a O:z/b B:z/c &&3344 test_cmp expect actual &&33453346 echo random >expect &&3347 test_cmp expect z/c3348 )3349'33503351###########################################################################3352# SECTION 11: Handling dirty (not up-to-date) files3353#3354# unpack_trees(), upon which the recursive merge algorithm is based, aborts3355# the operation if untracked or dirty files would be deleted or overwritten3356# by the merge. Unfortunately, unpack_trees() does not understand renames,3357# and if it doesn't abort, then it muddies up the working directory before3358# we even get to the point of detecting renames, so we need some special3359# handling. This was true even of normal renames, but there are additional3360# codepaths that need special handling with directory renames. Add3361# testcases for both renamed-by-directory-rename-detection and standard3362# rename cases.3363###########################################################################33643365# Testcase 11a, Avoid losing dirty contents with simple rename3366# Commit O: z/{a,b_v1},3367# Commit A: z/{a,c_v1}, and z/c_v1 has uncommitted mods3368# Commit B: z/{a,b_v2}3369# Expected: ERROR_MSG(Refusing to lose dirty file at z/c) +3370# z/a, staged version of z/c has sha1sum matching B:z/b_v2,3371# z/c~HEAD with contents of B:z/b_v2,3372# z/c with uncommitted mods on top of A:z/c_v133733374test_expect_success '11a-setup: Avoid losing dirty contents with simple rename''3375 test_create_repo 11a &&3376 (3377 cd 11a &&33783379 mkdir z &&3380 echo a >z/a &&3381 test_seq 1 10 >z/b &&3382 git add z &&3383 test_tick &&3384 git commit -m "O" &&33853386 git branch O &&3387 git branch A &&3388 git branch B &&33893390 git checkout A &&3391 git mv z/b z/c &&3392 test_tick &&3393 git commit -m "A" &&33943395 git checkout B &&3396 echo 11 >>z/b &&3397 git add z/b &&3398 test_tick &&3399 git commit -m "B"3400 )3401'34023403test_expect_success '11a-check: Avoid losing dirty contents with simple rename''3404 (3405 cd 11a &&34063407 git checkout A^0 &&3408 echo stuff >>z/c &&34093410 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3411 test_i18ngrep "Refusing to lose dirty file at z/c" out &&34123413 test_seq 1 10 >expected &&3414 echo stuff >>expected &&3415 test_cmp expected z/c &&34163417 git ls-files -s >out &&3418 test_line_count = 2 out &&3419 git ls-files -u >out &&3420 test_line_count = 1 out &&3421 git ls-files -o >out &&3422 test_line_count = 4 out &&34233424 git rev-parse >actual \3425 :0:z/a :2:z/c &&3426 git rev-parse >expect \3427 O:z/a B:z/b &&3428 test_cmp expect actual &&34293430 git hash-object z/c~HEAD >actual &&3431 git rev-parse B:z/b >expect &&3432 test_cmp expect actual3433 )3434'34353436# Testcase 11b, Avoid losing dirty file involved in directory rename3437# Commit O: z/a, x/{b,c_v1}3438# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods3439# Commit B: y/a, x/{b,c_v2}3440# Expected: y/{a,c_v2}, x/b, z/c_v1 with uncommitted mods untracked,3441# ERROR_MSG(Refusing to lose dirty file at z/c)344234433444test_expect_success '11b-setup: Avoid losing dirty file involved in directory rename''3445 test_create_repo 11b &&3446 (3447 cd 11b &&34483449 mkdir z x &&3450 echo a >z/a &&3451 echo b >x/b &&3452 test_seq 1 10 >x/c &&3453 git add z x &&3454 test_tick &&3455 git commit -m "O" &&34563457 git branch O &&3458 git branch A &&3459 git branch B &&34603461 git checkout A &&3462 git mv x/c z/c &&3463 test_tick &&3464 git commit -m "A" &&34653466 git checkout B &&3467 git mv z y &&3468 echo 11 >>x/c &&3469 git add x/c &&3470 test_tick &&3471 git commit -m "B"3472 )3473'34743475test_expect_success '11b-check: Avoid losing dirty file involved in directory rename''3476 (3477 cd 11b &&34783479 git checkout A^0 &&3480 echo stuff >>z/c &&34813482 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3483 test_i18ngrep "Refusing to lose dirty file at z/c" out &&34843485 grep -q stuff z/c &&3486 test_seq 1 10 >expected &&3487 echo stuff >>expected &&3488 test_cmp expected z/c &&34893490 git ls-files -s >out &&3491 test_line_count = 3 out &&3492 git ls-files -u >out &&3493 test_line_count = 0 out &&3494 git ls-files -m >out &&3495 test_line_count = 0 out &&3496 git ls-files -o >out &&3497 test_line_count = 4 out &&34983499 git rev-parse >actual \3500 :0:x/b :0:y/a :0:y/c &&3501 git rev-parse >expect \3502 O:x/b O:z/a B:x/c &&3503 test_cmp expect actual &&35043505 git hash-object y/c >actual &&3506 git rev-parse B:x/c >expect &&3507 test_cmp expect actual3508 )3509'35103511# Testcase 11c, Avoid losing not-up-to-date with rename + D/F conflict3512# Commit O: y/a, x/{b,c_v1}3513# Commit A: y/{a,c_v1}, x/b, and y/c_v1 has uncommitted mods3514# Commit B: y/{a,c/d}, x/{b,c_v2}3515# Expected: Abort_msg("following files would be overwritten by merge") +3516# y/c left untouched (still has uncommitted mods)35173518test_expect_success '11c-setup: Avoid losing not-uptodate with rename + D/F conflict''3519 test_create_repo 11c &&3520 (3521 cd 11c &&35223523 mkdir y x &&3524 echo a >y/a &&3525 echo b >x/b &&3526 test_seq 1 10 >x/c &&3527 git add y x &&3528 test_tick &&3529 git commit -m "O" &&35303531 git branch O &&3532 git branch A &&3533 git branch B &&35343535 git checkout A &&3536 git mv x/c y/c &&3537 test_tick &&3538 git commit -m "A" &&35393540 git checkout B &&3541 mkdir y/c &&3542 echo d >y/c/d &&3543 echo 11 >>x/c &&3544 git add x/c y/c/d &&3545 test_tick &&3546 git commit -m "B"3547 )3548'35493550test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conflict''3551 (3552 cd 11c &&35533554 git checkout A^0 &&3555 echo stuff >>y/c &&35563557 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3558 test_i18ngrep "following files would be overwritten by merge" err &&35593560 grep -q stuff y/c &&3561 test_seq 1 10 >expected &&3562 echo stuff >>expected &&3563 test_cmp expected y/c &&35643565 git ls-files -s >out &&3566 test_line_count = 3 out &&3567 git ls-files -u >out &&3568 test_line_count = 0 out &&3569 git ls-files -m >out &&3570 test_line_count = 1 out &&3571 git ls-files -o >out &&3572 test_line_count = 3 out3573 )3574'35753576# Testcase 11d, Avoid losing not-up-to-date with rename + D/F conflict3577# Commit O: z/a, x/{b,c_v1}3578# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods3579# Commit B: y/{a,c/d}, x/{b,c_v2}3580# Expected: D/F: y/c_v2 vs y/c/d) +3581# Warning_Msg("Refusing to lose dirty file at z/c) +3582# y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods35833584test_expect_success '11d-setup: Avoid losing not-uptodate with rename + D/F conflict''3585 test_create_repo 11d &&3586 (3587 cd 11d &&35883589 mkdir z x &&3590 echo a >z/a &&3591 echo b >x/b &&3592 test_seq 1 10 >x/c &&3593 git add z x &&3594 test_tick &&3595 git commit -m "O" &&35963597 git branch O &&3598 git branch A &&3599 git branch B &&36003601 git checkout A &&3602 git mv x/c z/c &&3603 test_tick &&3604 git commit -m "A" &&36053606 git checkout B &&3607 git mv z y &&3608 mkdir y/c &&3609 echo d >y/c/d &&3610 echo 11 >>x/c &&3611 git add x/c y/c/d &&3612 test_tick &&3613 git commit -m "B"3614 )3615'36163617test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conflict''3618 (3619 cd 11d &&36203621 git checkout A^0 &&3622 echo stuff >>z/c &&36233624 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3625 test_i18ngrep "Refusing to lose dirty file at z/c" out &&36263627 grep -q stuff z/c &&3628 test_seq 1 10 >expected &&3629 echo stuff >>expected &&3630 test_cmp expected z/c &&36313632 git ls-files -s >out &&3633 test_line_count = 4 out &&3634 git ls-files -u >out &&3635 test_line_count = 1 out &&3636 git ls-files -o >out &&3637 test_line_count = 5 out &&36383639 git rev-parse >actual \3640 :0:x/b :0:y/a :0:y/c/d :3:y/c &&3641 git rev-parse >expect \3642 O:x/b O:z/a B:y/c/d B:x/c &&3643 test_cmp expect actual &&36443645 git hash-object y/c~HEAD >actual &&3646 git rev-parse B:x/c >expect &&3647 test_cmp expect actual3648 )3649'36503651# Testcase 11e, Avoid deleting not-up-to-date with dir rename/rename(1to2)/add3652# Commit O: z/{a,b}, x/{c_1,d}3653# Commit A: y/{a,b,c_2}, x/d, w/c_1, and y/c_2 has uncommitted mods3654# Commit B: z/{a,b,c_1}, x/d3655# Expected: Failed Merge; y/{a,b} + x/d +3656# CONFLICT(rename/rename) x/c_1 -> w/c_1 vs y/c_1 +3657# ERROR_MSG(Refusing to lose dirty file at y/c)3658# y/c~B^0 has O:x/c_1 contents3659# y/c~HEAD has A:y/c_2 contents3660# y/c has dirty file from before merge36613662test_expect_success '11e-setup: Avoid deleting not-uptodate with dir rename/rename(1to2)/add''3663 test_create_repo 11e &&3664 (3665 cd 11e &&36663667 mkdir z x &&3668 echo a >z/a &&3669 echo b >z/b &&3670 echo c >x/c &&3671 echo d >x/d &&3672 git add z x &&3673 test_tick &&3674 git commit -m "O" &&36753676 git branch O &&3677 git branch A &&3678 git branch B &&36793680 git checkout A &&3681 git mv z/ y/ &&3682 echo different >y/c &&3683 mkdir w &&3684 git mv x/c w/ &&3685 git add y/c &&3686 test_tick &&3687 git commit -m "A" &&36883689 git checkout B &&3690 git mv x/c z/ &&3691 test_tick &&3692 git commit -m "B"3693 )3694'36953696test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rename(1to2)/add''3697 (3698 cd 11e &&36993700 git checkout A^0 &&3701 echo mods >>y/c &&37023703 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3704 test_i18ngrep "CONFLICT (rename/rename)" out &&3705 test_i18ngrep "Refusing to lose dirty file at y/c" out &&37063707 git ls-files -s >out &&3708 test_line_count = 7 out &&3709 git ls-files -u >out &&3710 test_line_count = 4 out &&3711 git ls-files -o >out &&3712 test_line_count = 3 out &&37133714 echo different >expected &&3715 echo mods >>expected &&3716 test_cmp expected y/c &&37173718 git rev-parse >actual \3719 :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :2:y/c :3:y/c &&3720 git rev-parse >expect \3721 O:z/a O:z/b O:x/d O:x/c O:x/c A:y/c O:x/c &&3722 test_cmp expect actual &&37233724 # See if y/c~merged has expected contents; requires manually3725 # doing the expected file merge3726 git cat-file -p A:y/c >c1 &&3727 git cat-file -p B:z/c >c2 &&3728 >empty &&3729 test_must_fail git merge-file \3730 -L "HEAD" \3731 -L "" \3732 -L "B^0" \3733 c1 empty c2 &&3734 test_cmp c1 y/c~merged3735 )3736'37373738# Testcase 11f, Avoid deleting not-up-to-date w/ dir rename/rename(2to1)3739# Commit O: z/{a,b}, x/{c_1,d_2}3740# Commit A: y/{a,b,wham_1}, x/d_2, except y/wham has uncommitted mods3741# Commit B: z/{a,b,wham_2}, x/c_13742# Expected: Failed Merge; y/{a,b} + untracked y/{wham~merged} +3743# y/wham with dirty changes from before merge +3744# CONFLICT(rename/rename) x/c vs x/d -> y/wham3745# ERROR_MSG(Refusing to lose dirty file at y/wham)37463747test_expect_success '11f-setup: Avoid deleting not-uptodate with dir rename/rename(2to1)''3748 test_create_repo 11f &&3749 (3750 cd 11f &&37513752 mkdir z x &&3753 echo a >z/a &&3754 echo b >z/b &&3755 test_seq 1 10 >x/c &&3756 echo d >x/d &&3757 git add z x &&3758 test_tick &&3759 git commit -m "O" &&37603761 git branch O &&3762 git branch A &&3763 git branch B &&37643765 git checkout A &&3766 git mv z/ y/ &&3767 git mv x/c y/wham &&3768 test_tick &&3769 git commit -m "A" &&37703771 git checkout B &&3772 git mv x/d z/wham &&3773 test_tick &&3774 git commit -m "B"3775 )3776'37773778test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rename(2to1)''3779 (3780 cd 11f &&37813782 git checkout A^0 &&3783 echo important >>y/wham &&37843785 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&3786 test_i18ngrep "CONFLICT (rename/rename)" out &&3787 test_i18ngrep "Refusing to lose dirty file at y/wham" out &&37883789 git ls-files -s >out &&3790 test_line_count = 4 out &&3791 git ls-files -u >out &&3792 test_line_count = 2 out &&3793 git ls-files -o >out &&3794 test_line_count = 3 out &&37953796 test_seq 1 10 >expected &&3797 echo important >>expected &&3798 test_cmp expected y/wham &&37993800 test_must_fail git rev-parse :1:y/wham &&38013802 git rev-parse >actual \3803 :0:y/a :0:y/b :2:y/wham :3:y/wham &&3804 git rev-parse >expect \3805 O:z/a O:z/b O:x/c O:x/d &&3806 test_cmp expect actual &&38073808 # Test that the two-way merge in y/wham~merged is as expected3809 git cat-file -p :2:y/wham >expect &&3810 git cat-file -p :3:y/wham >other &&3811 >empty &&3812 test_must_fail git merge-file \3813 -L "HEAD" \3814 -L "" \3815 -L "B^0" \3816 expect empty other &&3817 test_cmp expect y/wham~merged3818 )3819'38203821###########################################################################3822# SECTION 12: Everything else3823#3824# Tests suggested by others. Tests added after implementation completed3825# and submitted. Grab bag.3826###########################################################################38273828# Testcase 12a, Moving one directory hierarchy into another3829# (Related to testcase 9a)3830# Commit O: node1/{leaf1,leaf2}, node2/{leaf3,leaf4}3831# Commit A: node1/{leaf1,leaf2,node2/{leaf3,leaf4}}3832# Commit B: node1/{leaf1,leaf2,leaf5}, node2/{leaf3,leaf4,leaf6}3833# Expected: node1/{leaf1,leaf2,leaf5,node2/{leaf3,leaf4,leaf6}}38343835test_expect_success '12a-setup: Moving one directory hierarchy into another''3836 test_create_repo 12a &&3837 (3838 cd 12a &&38393840 mkdir -p node1 node2 &&3841 echo leaf1 >node1/leaf1 &&3842 echo leaf2 >node1/leaf2 &&3843 echo leaf3 >node2/leaf3 &&3844 echo leaf4 >node2/leaf4 &&3845 git add node1 node2 &&3846 test_tick &&3847 git commit -m "O" &&38483849 git branch O &&3850 git branch A &&3851 git branch B &&38523853 git checkout A &&3854 git mv node2/ node1/ &&3855 test_tick &&3856 git commit -m "A" &&38573858 git checkout B &&3859 echo leaf5 >node1/leaf5 &&3860 echo leaf6 >node2/leaf6 &&3861 git add node1 node2 &&3862 test_tick &&3863 git commit -m "B"3864 )3865'38663867test_expect_success '12a-check: Moving one directory hierarchy into another''3868 (3869 cd 12a &&38703871 git checkout A^0 &&38723873 git -c merge.directoryRenames=true merge -s recursive B^0 &&38743875 git ls-files -s >out &&3876 test_line_count = 6 out &&38773878 git rev-parse >actual \3879 HEAD:node1/leaf1 HEAD:node1/leaf2 HEAD:node1/leaf5 \3880 HEAD:node1/node2/leaf3 \3881 HEAD:node1/node2/leaf4 \3882 HEAD:node1/node2/leaf6 &&3883 git rev-parse >expect \3884 O:node1/leaf1 O:node1/leaf2 B:node1/leaf5 \3885 O:node2/leaf3 \3886 O:node2/leaf4 \3887 B:node2/leaf6 &&3888 test_cmp expect actual3889 )3890'38913892# Testcase 12b, Moving two directory hierarchies into each other3893# (Related to testcases 1c and 12c)3894# Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4}3895# Commit A: node1/{leaf1, leaf2, node2/{leaf3, leaf4}}3896# Commit B: node2/{leaf3, leaf4, node1/{leaf1, leaf2}}3897# Expected: node1/node2/node1/{leaf1, leaf2},3898# node2/node1/node2/{leaf3, leaf4}3899# NOTE: Without directory renames, we would expect3900# node2/node1/{leaf1, leaf2},3901# node1/node2/{leaf3, leaf4}3902# with directory rename detection, we note that3903# commit A renames node2/ -> node1/node2/3904# commit B renames node1/ -> node2/node1/3905# therefore, applying those directory renames to the initial result3906# (making all four paths experience a transitive renaming), yields3907# the expected result.3908#3909# You may ask, is it weird to have two directories rename each other?3910# To which, I can do no more than shrug my shoulders and say that3911# even simple rules give weird results when given weird inputs.39123913test_expect_success '12b-setup: Moving two directory hierarchies into each other''3914 test_create_repo 12b &&3915 (3916 cd 12b &&39173918 mkdir -p node1 node2 &&3919 echo leaf1 >node1/leaf1 &&3920 echo leaf2 >node1/leaf2 &&3921 echo leaf3 >node2/leaf3 &&3922 echo leaf4 >node2/leaf4 &&3923 git add node1 node2 &&3924 test_tick &&3925 git commit -m "O" &&39263927 git branch O &&3928 git branch A &&3929 git branch B &&39303931 git checkout A &&3932 git mv node2/ node1/ &&3933 test_tick &&3934 git commit -m "A" &&39353936 git checkout B &&3937 git mv node1/ node2/ &&3938 test_tick &&3939 git commit -m "B"3940 )3941'39423943test_expect_success '12b-check: Moving two directory hierarchies into each other''3944 (3945 cd 12b &&39463947 git checkout A^0 &&39483949 git -c merge.directoryRenames=true merge -s recursive B^0 &&39503951 git ls-files -s >out &&3952 test_line_count = 4 out &&39533954 git rev-parse >actual \3955 HEAD:node1/node2/node1/leaf1 \3956 HEAD:node1/node2/node1/leaf2 \3957 HEAD:node2/node1/node2/leaf3 \3958 HEAD:node2/node1/node2/leaf4 &&3959 git rev-parse >expect \3960 O:node1/leaf1 \3961 O:node1/leaf2 \3962 O:node2/leaf3 \3963 O:node2/leaf4 &&3964 test_cmp expect actual3965 )3966'39673968# Testcase 12c, Moving two directory hierarchies into each other w/ content merge3969# (Related to testcase 12b)3970# Commit O: node1/{ leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1}3971# Commit A: node1/{ leaf1_2, leaf2_2, node2/{leaf3_2, leaf4_2}}3972# Commit B: node2/{node1/{leaf1_3, leaf2_3}, leaf3_3, leaf4_3}3973# Expected: Content merge conflicts for each of:3974# node1/node2/node1/{leaf1, leaf2},3975# node2/node1/node2/{leaf3, leaf4}3976# NOTE: This is *exactly* like 12c, except that every path is modified on3977# each side of the merge.39783979test_expect_success '12c-setup: Moving one directory hierarchy into another w/ content merge''3980 test_create_repo 12c &&3981 (3982 cd 12c &&39833984 mkdir -p node1 node2 &&3985 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 &&3986 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf2\n" >node1/leaf2 &&3987 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf3\n" >node2/leaf3 &&3988 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf4\n" >node2/leaf4 &&3989 git add node1 node2 &&3990 test_tick &&3991 git commit -m "O" &&39923993 git branch O &&3994 git branch A &&3995 git branch B &&39963997 git checkout A &&3998 git mv node2/ node1/ &&3999 for i in `git ls-files`; do echo side A >>$i; done &&4000 git add -u &&4001 test_tick &&4002 git commit -m "A" &&40034004 git checkout B &&4005 git mv node1/ node2/ &&4006 for i in `git ls-files`; do echo side B >>$i; done &&4007 git add -u &&4008 test_tick &&4009 git commit -m "B"4010 )4011'40124013test_expect_success '12c-check: Moving one directory hierarchy into another w/ content merge''4014 (4015 cd 12c &&40164017 git checkout A^0 &&40184019 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&40204021 git ls-files -u >out &&4022 test_line_count = 12 out &&40234024 git rev-parse >actual \4025 :1:node1/node2/node1/leaf1 \4026 :1:node1/node2/node1/leaf2 \4027 :1:node2/node1/node2/leaf3 \4028 :1:node2/node1/node2/leaf4 \4029 :2:node1/node2/node1/leaf1 \4030 :2:node1/node2/node1/leaf2 \4031 :2:node2/node1/node2/leaf3 \4032 :2:node2/node1/node2/leaf4 \4033 :3:node1/node2/node1/leaf1 \4034 :3:node1/node2/node1/leaf2 \4035 :3:node2/node1/node2/leaf3 \4036 :3:node2/node1/node2/leaf4 &&4037 git rev-parse >expect \4038 O:node1/leaf1 \4039 O:node1/leaf2 \4040 O:node2/leaf3 \4041 O:node2/leaf4 \4042 A:node1/leaf1 \4043 A:node1/leaf2 \4044 A:node1/node2/leaf3 \4045 A:node1/node2/leaf4 \4046 B:node2/node1/leaf1 \4047 B:node2/node1/leaf2 \4048 B:node2/leaf3 \4049 B:node2/leaf4 &&4050 test_cmp expect actual4051 )4052'40534054###########################################################################4055# SECTION 13: Checking informational and conflict messages4056#4057# A year after directory rename detection became the default, it was4058# instead decided to report conflicts on the pathname on the basis that4059# some users may expect the new files added or moved into a directory to4060# be unrelated to all the other files in that directory, and thus that4061# directory rename detection is unexpected. Test that the messages printed4062# match our expectation.4063###########################################################################40644065# Testcase 13a, Basic directory rename with newly added files4066# Commit O: z/{b,c}4067# Commit A: y/{b,c}4068# Commit B: z/{b,c,d,e/f}4069# Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f40704071test_expect_success '13a-setup: messages for newly added files''4072 test_create_repo 13a &&4073 (4074 cd 13a &&40754076 mkdir z &&4077 echo b >z/b &&4078 echo c >z/c &&4079 git add z &&4080 test_tick &&4081 git commit -m "O" &&40824083 git branch O &&4084 git branch A &&4085 git branch B &&40864087 git checkout A &&4088 git mv z y &&4089 test_tick &&4090 git commit -m "A" &&40914092 git checkout B &&4093 echo d >z/d &&4094 mkdir z/e &&4095 echo f >z/e/f &&4096 git add z/d z/e/f &&4097 test_tick &&4098 git commit -m "B"4099 )4100'41014102test_expect_success '13a-check(conflict): messages for newly added files''4103 (4104 cd 13a &&41054106 git checkout A^0 &&41074108 test_must_fail git merge -s recursive B^0 >out 2>err &&41094110 test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&4111 test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&41124113 git ls-files >paths &&4114 ! grep z/ paths &&4115 grep "y/[de]" paths &&41164117 test_path_is_missing z/d &&4118 test_path_is_file y/d &&4119 test_path_is_missing z/e/f &&4120 test_path_is_file y/e/f4121 )4122'41234124test_expect_success '13a-check(info): messages for newly added files''4125 (4126 cd 13a &&41274128 git reset --hard &&4129 git checkout A^0 &&41304131 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&41324133 test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&4134 test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&41354136 git ls-files >paths &&4137 ! grep z/ paths &&4138 grep "y/[de]" paths &&41394140 test_path_is_missing z/d &&4141 test_path_is_file y/d &&4142 test_path_is_missing z/e/f &&4143 test_path_is_file y/e/f4144 )4145'41464147# Testcase 13b, Transitive rename with conflicted content merge and default4148# "conflict" setting4149# (Related to testcase 1c, 9b)4150# Commit O: z/{b,c}, x/d_14151# Commit A: y/{b,c}, x/d_24152# Commit B: z/{b,c,d_3}4153# Expected: y/{b,c,d_merged}, with two conflict messages for y/d,4154# one about content, and one about file location41554156test_expect_success '13b-setup: messages for transitive rename with conflicted content''4157 test_create_repo 13b &&4158 (4159 cd 13b &&41604161 mkdir x &&4162 mkdir z &&4163 test_seq 1 10 >x/d &&4164 echo b >z/b &&4165 echo c >z/c &&4166 git add x z &&4167 test_tick &&4168 git commit -m "O" &&41694170 git branch O &&4171 git branch A &&4172 git branch B &&41734174 git checkout A &&4175 git mv z y &&4176 echo 11 >>x/d &&4177 git add x/d &&4178 test_tick &&4179 git commit -m "A" &&41804181 git checkout B &&4182 echo eleven >>x/d &&4183 git mv x/d z/d &&4184 git add z/d &&4185 test_tick &&4186 git commit -m "B"4187 )4188'41894190test_expect_success '13b-check(conflict): messages for transitive rename with conflicted content''4191 (4192 cd 13b &&41934194 git checkout A^0 &&41954196 test_must_fail git merge -s recursive B^0 >out 2>err &&41974198 test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&4199 test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&42004201 git ls-files >paths &&4202 ! grep z/ paths &&4203 grep "y/d" paths &&42044205 test_path_is_missing z/d &&4206 test_path_is_file y/d4207 )4208'42094210test_expect_success '13b-check(info): messages for transitive rename with conflicted content''4211 (4212 cd 13b &&42134214 git reset --hard &&4215 git checkout A^0 &&42164217 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&42184219 test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&4220 test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&42214222 git ls-files >paths &&4223 ! grep z/ paths &&4224 grep "y/d" paths &&42254226 test_path_is_missing z/d &&4227 test_path_is_file y/d4228 )4229'42304231# Testcase 13c, Rename/rename(1to1) due to directory rename4232# Commit O: z/{b,c}, x/{d,e}4233# Commit A: y/{b,c,d}, x/e4234# Commit B: z/{b,c,d}, x/e4235# Expected: y/{b,c,d}, with info or conflict messages for d (4236# A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d4237# One could argue A had partial knowledge of what was done with4238# d and B had full knowledge, but that's a slippery slope as4239# shown in testcase 13d.42404241test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive rename''4242 test_create_repo 13c &&4243 (4244 cd 13c &&42454246 mkdir x &&4247 mkdir z &&4248 test_seq 1 10 >x/d &&4249 echo e >x/e &&4250 echo b >z/b &&4251 echo c >z/c &&4252 git add x z &&4253 test_tick &&4254 git commit -m "O" &&42554256 git branch O &&4257 git branch A &&4258 git branch B &&42594260 git checkout A &&4261 git mv z y &&4262 git mv x/d y/ &&4263 test_tick &&4264 git commit -m "A" &&42654266 git checkout B &&4267 git mv x/d z/d &&4268 git add z/d &&4269 test_tick &&4270 git commit -m "B"4271 )4272'42734274test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via transitive rename''4275 (4276 cd 13c &&42774278 git checkout A^0 &&42794280 test_must_fail git merge -s recursive B^0 >out 2>err &&42814282 test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&42834284 git ls-files >paths &&4285 ! grep z/ paths &&4286 grep "y/d" paths &&42874288 test_path_is_missing z/d &&4289 test_path_is_file y/d4290 )4291'42924293test_expect_success '13c-check(info): messages for rename/rename(1to1) via transitive rename''4294 (4295 cd 13c &&42964297 git reset --hard &&4298 git checkout A^0 &&42994300 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&43014302 test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&43034304 git ls-files >paths &&4305 ! grep z/ paths &&4306 grep "y/d" paths &&43074308 test_path_is_missing z/d &&4309 test_path_is_file y/d4310 )4311'43124313# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides4314# Commit O: a/{z,y}, b/x, c/w4315# Commit A: a/z, b/{y,x}, d/w4316# Commit B: a/z, d/x, c/{y,w}4317# Expected: a/z, d/{y,x,w} with no file location conflict for x4318# Easy cases:4319# * z is always in a; so it stays in a.4320# * x starts in b, only modified on one side to move into d/4321# * w starts in c, only modified on one side to move into d/4322# Hard case:4323# * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y4324# * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y4325# No conflict in where a/y ends up, so put it in d/y.43264327test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transitive rename''4328 test_create_repo 13d &&4329 (4330 cd 13d &&43314332 mkdir a &&4333 mkdir b &&4334 mkdir c &&4335 echo z >a/z &&4336 echo y >a/y &&4337 echo x >b/x &&4338 echo w >c/w &&4339 git add a b c &&4340 test_tick &&4341 git commit -m "O" &&43424343 git branch O &&4344 git branch A &&4345 git branch B &&43464347 git checkout A &&4348 git mv a/y b/ &&4349 git mv c/ d/ &&4350 test_tick &&4351 git commit -m "A" &&43524353 git checkout B &&4354 git mv a/y c/ &&4355 git mv b/ d/ &&4356 test_tick &&4357 git commit -m "B"4358 )4359'43604361test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via dual transitive rename''4362 (4363 cd 13d &&43644365 git checkout A^0 &&43664367 test_must_fail git merge -s recursive B^0 >out 2>err &&43684369 test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&4370 test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&43714372 git ls-files >paths &&4373 ! grep b/ paths &&4374 ! grep c/ paths &&4375 grep "d/y" paths &&43764377 test_path_is_missing b/y &&4378 test_path_is_missing c/y &&4379 test_path_is_file d/y4380 )4381'43824383test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual transitive rename''4384 (4385 cd 13d &&43864387 git reset --hard &&4388 git checkout A^0 &&43894390 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&43914392 test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&4393 test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&43944395 git ls-files >paths &&4396 ! grep b/ paths &&4397 ! grep c/ paths &&4398 grep "d/y" paths &&43994400 test_path_is_missing b/y &&4401 test_path_is_missing c/y &&4402 test_path_is_file d/y4403 )4404'44054406# Testcase 13e, directory rename in virtual merge base4407#4408# This testcase has a slightly different setup than all the above cases, in4409# order to include a recursive case:4410#4411# A C4412# o - o4413# / \ / \4414# O o X ?4415# \ / \ /4416# o o4417# B D4418#4419# Commit O: a/{z,y}4420# Commit A: b/{z,y}4421# Commit B: a/{z,y,x}4422# Commit C: b/{z,y,x}4423# Commit D: b/{z,y}, a/x4424# Expected: b/{z,y,x} (sort of; see below for why this might not be expected)4425#4426# NOTES: 'X' represents a virtual merge base. With the default of4427# directory rename detection yielding conflicts, merging A and B4428# results in a conflict complaining about whether 'x' should be4429# under 'a/' or 'b/'. However, when creating the virtual merge4430# base 'X', since virtual merge bases need to be written out as a4431# tree, we cannot have a conflict, so some resolution has to be4432# picked.4433#4434# In choosing the right resolution, it's worth noting here that4435# commits C & D are merges of A & B that choose different4436# locations for 'x' (i.e. they resolve the conflict differently),4437# and so it would be nice when merging C & D if git could detect4438# this difference of opinion and report a conflict. But the only4439# way to do so that I can think of would be to have the virtual4440# merge base place 'x' in some directory other than either 'a/' or4441# 'b/', which seems a little weird -- especially since it'd result4442# in a rename/rename(1to2) conflict with a source path that never4443# existed in any version.4444#4445# So, for now, when directory rename detection is set to4446# 'conflict' just avoid doing directory rename detection at all in4447# the recursive case. This will not allow us to detect a conflict4448# in the outer merge for this special kind of setup, but it at4449# least avoids hitting a BUG().4450#4451test_expect_success '13e-setup: directory rename detection in recursive case''4452 test_create_repo 13e &&4453 (4454 cd 13e &&44554456 mkdir a &&4457 echo z >a/z &&4458 echo y >a/y &&4459 git add a &&4460 test_tick &&4461 git commit -m "O" &&44624463 git branch O &&4464 git branch A &&4465 git branch B &&44664467 git checkout A &&4468 git mv a/ b/ &&4469 test_tick &&4470 git commit -m "A" &&44714472 git checkout B &&4473 echo x >a/x &&4474 git add a &&4475 test_tick &&4476 git commit -m "B" &&44774478 git branch C A &&4479 git branch D B &&44804481 git checkout C &&4482 test_must_fail git -c merge.directoryRenames=conflict merge B &&4483 git add b/x &&4484 test_tick &&4485 git commit -m "C" &&448644874488 git checkout D &&4489 test_must_fail git -c merge.directoryRenames=conflict merge A &&4490 git add b/x &&4491 mkdir a &&4492 git mv b/x a/x &&4493 test_tick &&4494 git commit -m "D"4495 )4496'44974498test_expect_success '13e-check: directory rename detection in recursive case''4499 (4500 cd 13e &&45014502 git checkout --quiet D^0 &&45034504 git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&45054506 test_i18ngrep ! CONFLICT out &&4507 test_i18ngrep ! BUG: err &&4508 test_i18ngrep ! core.dumped err &&4509 test_must_be_empty err &&45104511 git ls-files >paths &&4512 ! grep a/x paths &&4513 grep b/x paths4514 )4515'45164517test_done