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. "$TEST_DIRECTORY"/lib-merge.sh 30 31 32########################################################################### 33# SECTION 1: Basic cases we should be able to handle 34########################################################################### 35 36# Testcase 1a, Basic directory rename. 37# Commit O: z/{b,c} 38# Commit A: y/{b,c} 39# Commit B: z/{b,c,d,e/f} 40# Expected: y/{b,c,d,e/f} 41 42test_setup_1a () { 43 test_create_repo 1a && 44 ( 45 cd 1a && 46 47 mkdir z && 48 echo b >z/b && 49 echo c >z/c && 50 git add z && 51 test_tick && 52 git commit -m "O" && 53 54 git branch O && 55 git branch A && 56 git branch B && 57 58 git checkout A && 59 git mv z y && 60 test_tick && 61 git commit -m "A" && 62 63 git checkout B && 64 echo d >z/d && 65 mkdir z/e && 66 echo f >z/e/f && 67 git add z/d z/e/f && 68 test_tick && 69 git commit -m "B" 70 ) 71} 72 73test_expect_success '1a: Simple directory rename detection' ' 74 test_setup_1a && 75 ( 76 cd 1a && 77 78 git checkout A^0 && 79 80 git -c merge.directoryRenames=true merge -s recursive B^0 >out && 81 82 git ls-files -s >out && 83 test_line_count = 4 out && 84 85 git rev-parse >actual \ 86 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e/f && 87 git rev-parse >expect \ 88 O:z/b O:z/c B:z/d B:z/e/f && 89 test_cmp expect actual && 90 91 git hash-object y/d >actual && 92 git rev-parse B:z/d >expect && 93 test_cmp expect actual && 94 95 test_must_fail git rev-parse HEAD:z/d && 96 test_must_fail git rev-parse HEAD:z/e/f && 97 test_path_is_missing z/d && 98 test_path_is_missing z/e/f 99 ) 100' 101 102# Testcase 1b, Merge a directory with another 103# Commit O: z/{b,c}, y/d 104# Commit A: z/{b,c,e}, y/d 105# Commit B: y/{b,c,d} 106# Expected: y/{b,c,d,e} 107 108test_setup_1b () { 109 test_create_repo 1b && 110 ( 111 cd 1b && 112 113 mkdir z && 114 echo b >z/b && 115 echo c >z/c && 116 mkdir y && 117 echo d >y/d && 118 git add z y && 119 test_tick && 120 git commit -m "O" && 121 122 git branch O && 123 git branch A && 124 git branch B && 125 126 git checkout A && 127 echo e >z/e && 128 git add z/e && 129 test_tick && 130 git commit -m "A" && 131 132 git checkout B && 133 git mv z/b y && 134 git mv z/c y && 135 rmdir z && 136 test_tick && 137 git commit -m "B" 138 ) 139} 140 141test_expect_success '1b: Merge a directory with another' ' 142 test_setup_1b && 143 ( 144 cd 1b && 145 146 git checkout A^0 && 147 148 git -c merge.directoryRenames=true merge -s recursive B^0 && 149 150 git ls-files -s >out && 151 test_line_count = 4 out && 152 153 git rev-parse >actual \ 154 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e && 155 git rev-parse >expect \ 156 O:z/b O:z/c O:y/d A:z/e && 157 test_cmp expect actual && 158 test_must_fail git rev-parse HEAD:z/e 159 ) 160' 161 162# Testcase 1c, Transitive renaming 163# (Related to testcases 3a and 6d -- when should a transitive rename apply?) 164# (Related to testcases 9c and 9d -- can transitivity repeat?) 165# (Related to testcase 12b -- joint-transitivity?) 166# Commit O: z/{b,c}, x/d 167# Commit A: y/{b,c}, x/d 168# Commit B: z/{b,c,d} 169# Expected: y/{b,c,d} (because x/d -> z/d -> y/d) 170 171test_setup_1c () { 172 test_create_repo 1c && 173 ( 174 cd 1c && 175 176 mkdir z && 177 echo b >z/b && 178 echo c >z/c && 179 mkdir x && 180 echo d >x/d && 181 git add z x && 182 test_tick && 183 git commit -m "O" && 184 185 git branch O && 186 git branch A && 187 git branch B && 188 189 git checkout A && 190 git mv z y && 191 test_tick && 192 git commit -m "A" && 193 194 git checkout B && 195 git mv x/d z/d && 196 test_tick && 197 git commit -m "B" 198 ) 199} 200 201test_expect_success '1c: Transitive renaming' ' 202 test_setup_1c && 203 ( 204 cd 1c && 205 206 git checkout A^0 && 207 208 git -c merge.directoryRenames=true merge -s recursive B^0 >out && 209 210 git ls-files -s >out && 211 test_line_count = 3 out && 212 213 git rev-parse >actual \ 214 HEAD:y/b HEAD:y/c HEAD:y/d && 215 git rev-parse >expect \ 216 O:z/b O:z/c O:x/d && 217 test_cmp expect actual && 218 test_must_fail git rev-parse HEAD:x/d && 219 test_must_fail git rev-parse HEAD:z/d && 220 test_path_is_missing z/d 221 ) 222' 223 224# Testcase 1d, Directory renames (merging two directories into one new one) 225# cause a rename/rename(2to1) conflict 226# (Related to testcases 1c and 7b) 227# Commit O. z/{b,c}, y/{d,e} 228# Commit A. x/{b,c}, y/{d,e,m,wham_1} 229# Commit B. z/{b,c,n,wham_2}, x/{d,e} 230# Expected: x/{b,c,d,e,m,n}, CONFLICT:(y/wham_1 & z/wham_2 -> x/wham) 231# Note: y/m & z/n should definitely move into x. By the same token, both 232# y/wham_1 & z/wham_2 should too...giving us a conflict. 233 234test_setup_1d () { 235 test_create_repo 1d && 236 ( 237 cd 1d && 238 239 mkdir z && 240 echo b >z/b && 241 echo c >z/c && 242 mkdir y && 243 echo d >y/d && 244 echo e >y/e && 245 git add z y && 246 test_tick && 247 git commit -m "O" && 248 249 git branch O && 250 git branch A && 251 git branch B && 252 253 git checkout A && 254 git mv z x && 255 echo m >y/m && 256 echo wham1 >y/wham && 257 git add y && 258 test_tick && 259 git commit -m "A" && 260 261 git checkout B && 262 git mv y x && 263 echo n >z/n && 264 echo wham2 >z/wham && 265 git add z && 266 test_tick && 267 git commit -m "B" 268 ) 269} 270 271test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict' ' 272 test_setup_1d && 273 ( 274 cd 1d && 275 276 git checkout A^0 && 277 278 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 279 test_i18ngrep "CONFLICT (\(.*\)/\1)" out && 280 281 git ls-files -s >out && 282 test_line_count = 8 out && 283 git ls-files -u >out && 284 test_line_count = 2 out && 285 git ls-files -o >out && 286 test_line_count = 1 out && 287 288 git rev-parse >actual \ 289 :0:x/b :0:x/c :0:x/d :0:x/e :0:x/m :0:x/n && 290 git rev-parse >expect \ 291 O:z/b O:z/c O:y/d O:y/e A:y/m B:z/n && 292 test_cmp expect actual && 293 294 test_must_fail git rev-parse :0:x/wham && 295 git rev-parse >actual \ 296 :2:x/wham :3:x/wham && 297 git rev-parse >expect \ 298 A:y/wham B:z/wham && 299 test_cmp expect actual && 300 301 # Test that the two-way merge in x/wham is as expected 302 git cat-file -p :2:x/wham >expect && 303 git cat-file -p :3:x/wham >other && 304 >empty && 305 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 306 then 307 test_must_fail git merge-file \ 308 -L "HEAD:y/wham" \ 309 -L "" \ 310 -L "B^0:z/wham" \ 311 expect empty other 312 else 313 test_must_fail git merge-file \ 314 -L "HEAD" \ 315 -L "" \ 316 -L "B^0" \ 317 expect empty other 318 fi && 319 test_cmp expect x/wham 320 ) 321' 322 323# Testcase 1e, Renamed directory, with all filenames being renamed too 324# (Related to testcases 9f & 9g) 325# Commit O: z/{oldb,oldc} 326# Commit A: y/{newb,newc} 327# Commit B: z/{oldb,oldc,d} 328# Expected: y/{newb,newc,d} 329 330test_setup_1e () { 331 test_create_repo 1e && 332 ( 333 cd 1e && 334 335 mkdir z && 336 echo b >z/oldb && 337 echo c >z/oldc && 338 git add z && 339 test_tick && 340 git commit -m "O" && 341 342 git branch O && 343 git branch A && 344 git branch B && 345 346 git checkout A && 347 mkdir y && 348 git mv z/oldb y/newb && 349 git mv z/oldc y/newc && 350 test_tick && 351 git commit -m "A" && 352 353 git checkout B && 354 echo d >z/d && 355 git add z/d && 356 test_tick && 357 git commit -m "B" 358 ) 359} 360 361test_expect_success '1e: Renamed directory, with all files being renamed too' ' 362 test_setup_1e && 363 ( 364 cd 1e && 365 366 git checkout A^0 && 367 368 git -c merge.directoryRenames=true merge -s recursive B^0 && 369 370 git ls-files -s >out && 371 test_line_count = 3 out && 372 373 git rev-parse >actual \ 374 HEAD:y/newb HEAD:y/newc HEAD:y/d && 375 git rev-parse >expect \ 376 O:z/oldb O:z/oldc B:z/d && 377 test_cmp expect actual && 378 test_must_fail git rev-parse HEAD:z/d 379 ) 380' 381 382# Testcase 1f, Split a directory into two other directories 383# (Related to testcases 3a, all of section 2, and all of section 4) 384# Commit O: z/{b,c,d,e,f} 385# Commit A: z/{b,c,d,e,f,g} 386# Commit B: y/{b,c}, x/{d,e,f} 387# Expected: y/{b,c}, x/{d,e,f,g} 388 389test_setup_1f () { 390 test_create_repo 1f && 391 ( 392 cd 1f && 393 394 mkdir z && 395 echo b >z/b && 396 echo c >z/c && 397 echo d >z/d && 398 echo e >z/e && 399 echo f >z/f && 400 git add z && 401 test_tick && 402 git commit -m "O" && 403 404 git branch O && 405 git branch A && 406 git branch B && 407 408 git checkout A && 409 echo g >z/g && 410 git add z/g && 411 test_tick && 412 git commit -m "A" && 413 414 git checkout B && 415 mkdir y && 416 mkdir x && 417 git mv z/b y/ && 418 git mv z/c y/ && 419 git mv z/d x/ && 420 git mv z/e x/ && 421 git mv z/f x/ && 422 rmdir z && 423 test_tick && 424 git commit -m "B" 425 ) 426} 427 428test_expect_success '1f: Split a directory into two other directories' ' 429 test_setup_1f && 430 ( 431 cd 1f && 432 433 git checkout A^0 && 434 435 git -c merge.directoryRenames=true merge -s recursive B^0 && 436 437 git ls-files -s >out && 438 test_line_count = 6 out && 439 440 git rev-parse >actual \ 441 HEAD:y/b HEAD:y/c HEAD:x/d HEAD:x/e HEAD:x/f HEAD:x/g && 442 git rev-parse >expect \ 443 O:z/b O:z/c O:z/d O:z/e O:z/f A:z/g && 444 test_cmp expect actual && 445 test_path_is_missing z/g && 446 test_must_fail git rev-parse HEAD:z/g 447 ) 448' 449 450########################################################################### 451# Rules suggested by testcases in section 1: 452# 453# We should still detect the directory rename even if it wasn't just 454# the directory renamed, but the files within it. (see 1b) 455# 456# If renames split a directory into two or more others, the directory 457# with the most renames, "wins" (see 1f). However, see the testcases 458# in section 2, plus testcases 3a and 4a. 459########################################################################### 460 461 462########################################################################### 463# SECTION 2: Split into multiple directories, with equal number of paths 464# 465# Explore the splitting-a-directory rules a bit; what happens in the 466# edge cases? 467# 468# Note that there is a closely related case of a directory not being 469# split on either side of history, but being renamed differently on 470# each side. See testcase 8e for that. 471########################################################################### 472 473# Testcase 2a, Directory split into two on one side, with equal numbers of paths 474# Commit O: z/{b,c} 475# Commit A: y/b, w/c 476# Commit B: z/{b,c,d} 477# Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict 478test_setup_2a () { 479 test_create_repo 2a && 480 ( 481 cd 2a && 482 483 mkdir z && 484 echo b >z/b && 485 echo c >z/c && 486 git add z && 487 test_tick && 488 git commit -m "O" && 489 490 git branch O && 491 git branch A && 492 git branch B && 493 494 git checkout A && 495 mkdir y && 496 mkdir w && 497 git mv z/b y/ && 498 git mv z/c w/ && 499 test_tick && 500 git commit -m "A" && 501 502 git checkout B && 503 echo d >z/d && 504 git add z/d && 505 test_tick && 506 git commit -m "B" 507 ) 508} 509 510test_expect_success '2a: Directory split into two on one side, with equal numbers of paths' ' 511 test_setup_2a && 512 ( 513 cd 2a && 514 515 git checkout A^0 && 516 517 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 518 test_i18ngrep "CONFLICT.*directory rename split" out && 519 520 git ls-files -s >out && 521 test_line_count = 3 out && 522 git ls-files -u >out && 523 test_line_count = 0 out && 524 git ls-files -o >out && 525 test_line_count = 1 out && 526 527 git rev-parse >actual \ 528 :0:y/b :0:w/c :0:z/d && 529 git rev-parse >expect \ 530 O:z/b O:z/c B:z/d && 531 test_cmp expect actual 532 ) 533' 534 535# Testcase 2b, Directory split into two on one side, with equal numbers of paths 536# Commit O: z/{b,c} 537# Commit A: y/b, w/c 538# Commit B: z/{b,c}, x/d 539# Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict 540test_setup_2b () { 541 test_create_repo 2b && 542 ( 543 cd 2b && 544 545 mkdir z && 546 echo b >z/b && 547 echo c >z/c && 548 git add z && 549 test_tick && 550 git commit -m "O" && 551 552 git branch O && 553 git branch A && 554 git branch B && 555 556 git checkout A && 557 mkdir y && 558 mkdir w && 559 git mv z/b y/ && 560 git mv z/c w/ && 561 test_tick && 562 git commit -m "A" && 563 564 git checkout B && 565 mkdir x && 566 echo d >x/d && 567 git add x/d && 568 test_tick && 569 git commit -m "B" 570 ) 571} 572 573test_expect_success '2b: Directory split into two on one side, with equal numbers of paths' ' 574 test_setup_2b && 575 ( 576 cd 2b && 577 578 git checkout A^0 && 579 580 git -c merge.directoryRenames=true merge -s recursive B^0 >out && 581 582 git ls-files -s >out && 583 test_line_count = 3 out && 584 git ls-files -u >out && 585 test_line_count = 0 out && 586 git ls-files -o >out && 587 test_line_count = 1 out && 588 589 git rev-parse >actual \ 590 :0:y/b :0:w/c :0:x/d && 591 git rev-parse >expect \ 592 O:z/b O:z/c B:x/d && 593 test_cmp expect actual && 594 test_i18ngrep ! "CONFLICT.*directory rename split" out 595 ) 596' 597 598########################################################################### 599# Rules suggested by section 2: 600# 601# None; the rule was already covered in section 1. These testcases are 602# here just to make sure the conflict resolution and necessary warning 603# messages are handled correctly. 604########################################################################### 605 606 607########################################################################### 608# SECTION 3: Path in question is the source path for some rename already 609# 610# Combining cases from Section 1 and trying to handle them could lead to 611# directory renaming detection being over-applied. So, this section 612# provides some good testcases to check that the implementation doesn't go 613# too far. 614########################################################################### 615 616# Testcase 3a, Avoid implicit rename if involved as source on other side 617# (Related to testcases 1c, 1f, and 9h) 618# Commit O: z/{b,c,d} 619# Commit A: z/{b,c,d} (no change) 620# Commit B: y/{b,c}, x/d 621# Expected: y/{b,c}, x/d 622test_setup_3a () { 623 test_create_repo 3a && 624 ( 625 cd 3a && 626 627 mkdir z && 628 echo b >z/b && 629 echo c >z/c && 630 echo d >z/d && 631 git add z && 632 test_tick && 633 git commit -m "O" && 634 635 git branch O && 636 git branch A && 637 git branch B && 638 639 git checkout A && 640 test_tick && 641 git commit --allow-empty -m "A" && 642 643 git checkout B && 644 mkdir y && 645 mkdir x && 646 git mv z/b y/ && 647 git mv z/c y/ && 648 git mv z/d x/ && 649 rmdir z && 650 test_tick && 651 git commit -m "B" 652 ) 653} 654 655test_expect_success '3a: Avoid implicit rename if involved as source on other side' ' 656 test_setup_3a && 657 ( 658 cd 3a && 659 660 git checkout A^0 && 661 662 git -c merge.directoryRenames=true merge -s recursive B^0 && 663 664 git ls-files -s >out && 665 test_line_count = 3 out && 666 667 git rev-parse >actual \ 668 HEAD:y/b HEAD:y/c HEAD:x/d && 669 git rev-parse >expect \ 670 O:z/b O:z/c O:z/d && 671 test_cmp expect actual 672 ) 673' 674 675# Testcase 3b, Avoid implicit rename if involved as source on other side 676# (Related to testcases 5c and 7c, also kind of 1e and 1f) 677# Commit O: z/{b,c,d} 678# Commit A: y/{b,c}, x/d 679# Commit B: z/{b,c}, w/d 680# Expected: y/{b,c}, CONFLICT:(z/d -> x/d vs. w/d) 681# NOTE: We're particularly checking that since z/d is already involved as 682# a source in a file rename on the same side of history, that we don't 683# get it involved in directory rename detection. If it were, we might 684# end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a 685# rename/rename/rename(1to3) conflict, which is just weird. 686test_setup_3b () { 687 test_create_repo 3b && 688 ( 689 cd 3b && 690 691 mkdir z && 692 echo b >z/b && 693 echo c >z/c && 694 echo d >z/d && 695 git add z && 696 test_tick && 697 git commit -m "O" && 698 699 git branch O && 700 git branch A && 701 git branch B && 702 703 git checkout A && 704 mkdir y && 705 mkdir x && 706 git mv z/b y/ && 707 git mv z/c y/ && 708 git mv z/d x/ && 709 rmdir z && 710 test_tick && 711 git commit -m "A" && 712 713 git checkout B && 714 mkdir w && 715 git mv z/d w/ && 716 test_tick && 717 git commit -m "B" 718 ) 719} 720 721test_expect_success '3b: Avoid implicit rename if involved as source on current side' ' 722 test_setup_3b && 723 ( 724 cd 3b && 725 726 git checkout A^0 && 727 728 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 729 test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out && 730 test_i18ngrep ! CONFLICT.*rename/rename.*y/d out && 731 732 git ls-files -s >out && 733 test_line_count = 5 out && 734 git ls-files -u >out && 735 test_line_count = 3 out && 736 git ls-files -o >out && 737 test_line_count = 1 out && 738 739 git rev-parse >actual \ 740 :0:y/b :0:y/c :1:z/d :2:x/d :3:w/d && 741 git rev-parse >expect \ 742 O:z/b O:z/c O:z/d O:z/d O:z/d && 743 test_cmp expect actual && 744 745 test_path_is_missing z/d && 746 git hash-object >actual \ 747 x/d w/d && 748 git rev-parse >expect \ 749 O:z/d O:z/d && 750 test_cmp expect actual 751 ) 752' 753 754########################################################################### 755# Rules suggested by section 3: 756# 757# Avoid directory-rename-detection for a path, if that path is the source 758# of a rename on either side of a merge. 759########################################################################### 760 761 762########################################################################### 763# SECTION 4: Partially renamed directory; still exists on both sides of merge 764# 765# What if we were to attempt to do directory rename detection when someone 766# "mostly" moved a directory but still left some files around, or, 767# equivalently, fully renamed a directory in one commit and then recreated 768# that directory in a later commit adding some new files and then tried to 769# merge? 770# 771# It's hard to divine user intent in these cases, because you can make an 772# argument that, depending on the intermediate history of the side being 773# merged, that some users will want files in that directory to 774# automatically be detected and renamed, while users with a different 775# intermediate history wouldn't want that rename to happen. 776# 777# I think that it is best to simply not have directory rename detection 778# apply to such cases. My reasoning for this is four-fold: (1) it's 779# easiest for users in general to figure out what happened if we don't 780# apply directory rename detection in any such case, (2) it's an easy rule 781# to explain ["We don't do directory rename detection if the directory 782# still exists on both sides of the merge"], (3) we can get some hairy 783# edge/corner cases that would be really confusing and possibly not even 784# representable in the index if we were to even try, and [related to 3] (4) 785# attempting to resolve this issue of divining user intent by examining 786# intermediate history goes against the spirit of three-way merges and is a 787# path towards crazy corner cases that are far more complex than what we're 788# already dealing with. 789# 790# Note that the wording of the rule ("We don't do directory rename 791# detection if the directory still exists on both sides of the merge.") 792# also excludes "renaming" of a directory into a subdirectory of itself 793# (e.g. /some/dir/* -> /some/dir/subdir/*). It may be possible to carve 794# out an exception for "renaming"-beneath-itself cases without opening 795# weird edge/corner cases for other partial directory renames, but for now 796# we are keeping the rule simple. 797# 798# This section contains a test for a partially-renamed-directory case. 799########################################################################### 800 801# Testcase 4a, Directory split, with original directory still present 802# (Related to testcase 1f) 803# Commit O: z/{b,c,d,e} 804# Commit A: y/{b,c,d}, z/e 805# Commit B: z/{b,c,d,e,f} 806# Expected: y/{b,c,d}, z/{e,f} 807# NOTE: Even though most files from z moved to y, we don't want f to follow. 808 809test_setup_4a () { 810 test_create_repo 4a && 811 ( 812 cd 4a && 813 814 mkdir z && 815 echo b >z/b && 816 echo c >z/c && 817 echo d >z/d && 818 echo e >z/e && 819 git add z && 820 test_tick && 821 git commit -m "O" && 822 823 git branch O && 824 git branch A && 825 git branch B && 826 827 git checkout A && 828 mkdir y && 829 git mv z/b y/ && 830 git mv z/c y/ && 831 git mv z/d y/ && 832 test_tick && 833 git commit -m "A" && 834 835 git checkout B && 836 echo f >z/f && 837 git add z/f && 838 test_tick && 839 git commit -m "B" 840 ) 841} 842 843test_expect_success '4a: Directory split, with original directory still present' ' 844 test_setup_4a && 845 ( 846 cd 4a && 847 848 git checkout A^0 && 849 850 git -c merge.directoryRenames=true merge -s recursive B^0 && 851 852 git ls-files -s >out && 853 test_line_count = 5 out && 854 git ls-files -u >out && 855 test_line_count = 0 out && 856 git ls-files -o >out && 857 test_line_count = 1 out && 858 859 git rev-parse >actual \ 860 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/e HEAD:z/f && 861 git rev-parse >expect \ 862 O:z/b O:z/c O:z/d O:z/e B:z/f && 863 test_cmp expect actual 864 ) 865' 866 867########################################################################### 868# Rules suggested by section 4: 869# 870# Directory-rename-detection should be turned off for any directories (as 871# a source for renames) that exist on both sides of the merge. (The "as 872# a source for renames" clarification is due to cases like 1c where 873# the target directory exists on both sides and we do want the rename 874# detection.) But, sadly, see testcase 8b. 875########################################################################### 876 877 878########################################################################### 879# SECTION 5: Files/directories in the way of subset of to-be-renamed paths 880# 881# Implicitly renaming files due to a detected directory rename could run 882# into problems if there are files or directories in the way of the paths 883# we want to rename. Explore such cases in this section. 884########################################################################### 885 886# Testcase 5a, Merge directories, other side adds files to original and target 887# Commit O: z/{b,c}, y/d 888# Commit A: z/{b,c,e_1,f}, y/{d,e_2} 889# Commit B: y/{b,c,d} 890# Expected: z/e_1, y/{b,c,d,e_2,f} + CONFLICT warning 891# NOTE: While directory rename detection is active here causing z/f to 892# become y/f, we did not apply this for z/e_1 because that would 893# give us an add/add conflict for y/e_1 vs y/e_2. This problem with 894# this add/add, is that both versions of y/e are from the same side 895# of history, giving us no way to represent this conflict in the 896# index. 897 898test_setup_5a () { 899 test_create_repo 5a && 900 ( 901 cd 5a && 902 903 mkdir z && 904 echo b >z/b && 905 echo c >z/c && 906 mkdir y && 907 echo d >y/d && 908 git add z y && 909 test_tick && 910 git commit -m "O" && 911 912 git branch O && 913 git branch A && 914 git branch B && 915 916 git checkout A && 917 echo e1 >z/e && 918 echo f >z/f && 919 echo e2 >y/e && 920 git add z/e z/f y/e && 921 test_tick && 922 git commit -m "A" && 923 924 git checkout B && 925 git mv z/b y/ && 926 git mv z/c y/ && 927 rmdir z && 928 test_tick && 929 git commit -m "B" 930 ) 931} 932 933test_expect_success '5a: Merge directories, other side adds files to original and target' ' 934 test_setup_5a && 935 ( 936 cd 5a && 937 938 git checkout A^0 && 939 940 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 941 test_i18ngrep "CONFLICT.*implicit dir rename" out && 942 943 git ls-files -s >out && 944 test_line_count = 6 out && 945 git ls-files -u >out && 946 test_line_count = 0 out && 947 git ls-files -o >out && 948 test_line_count = 1 out && 949 950 git rev-parse >actual \ 951 :0:y/b :0:y/c :0:y/d :0:y/e :0:z/e :0:y/f && 952 git rev-parse >expect \ 953 O:z/b O:z/c O:y/d A:y/e A:z/e A:z/f && 954 test_cmp expect actual 955 ) 956' 957 958# Testcase 5b, Rename/delete in order to get add/add/add conflict 959# (Related to testcase 8d; these may appear slightly inconsistent to users; 960# Also related to testcases 7d and 7e) 961# Commit O: z/{b,c,d_1} 962# Commit A: y/{b,c,d_2} 963# Commit B: z/{b,c,d_1,e}, y/d_3 964# Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3) 965# NOTE: If z/d_1 in commit B were to be involved in dir rename detection, as 966# we normally would since z/ is being renamed to y/, then this would be 967# a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add 968# conflict of y/d_1 vs. y/d_2 vs. y/d_3. Add/add/add is not 969# representable in the index, so the existence of y/d_3 needs to 970# cause us to bail on directory rename detection for that path, falling 971# back to git behavior without the directory rename detection. 972 973test_setup_5b () { 974 test_create_repo 5b && 975 ( 976 cd 5b && 977 978 mkdir z && 979 echo b >z/b && 980 echo c >z/c && 981 echo d1 >z/d && 982 git add z && 983 test_tick && 984 git commit -m "O" && 985 986 git branch O && 987 git branch A && 988 git branch B && 989 990 git checkout A && 991 git rm z/d && 992 git mv z y && 993 echo d2 >y/d && 994 git add y/d && 995 test_tick && 996 git commit -m "A" && 997 998 git checkout B && 999 mkdir y && 1000 echo d3 >y/d && 1001 echo e >z/e && 1002 git add y/d z/e && 1003 test_tick && 1004 git commit -m "B" 1005 ) 1006} 1007 1008test_expect_success '5b: Rename/delete in order to get add/add/add conflict' ' 1009 test_setup_5b && 1010 ( 1011 cd 5b && 1012 1013 git checkout A^0 && 1014 1015 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1016 test_i18ngrep "CONFLICT (add/add).* y/d" out && 1017 1018 git ls-files -s >out && 1019 test_line_count = 5 out && 1020 git ls-files -u >out && 1021 test_line_count = 2 out && 1022 git ls-files -o >out && 1023 test_line_count = 1 out && 1024 1025 git rev-parse >actual \ 1026 :0:y/b :0:y/c :0:y/e :2:y/d :3:y/d && 1027 git rev-parse >expect \ 1028 O:z/b O:z/c B:z/e A:y/d B:y/d && 1029 test_cmp expect actual && 1030 1031 test_must_fail git rev-parse :1:y/d && 1032 test_path_is_file y/d 1033 ) 1034' 1035 1036# Testcase 5c, Transitive rename would cause rename/rename/rename/add/add/add 1037# (Directory rename detection would result in transitive rename vs. 1038# rename/rename(1to2) and turn it into a rename/rename(1to3). Further, 1039# rename paths conflict with separate adds on the other side) 1040# (Related to testcases 3b and 7c) 1041# Commit O: z/{b,c}, x/d_1 1042# Commit A: y/{b,c,d_2}, w/d_1 1043# Commit B: z/{b,c,d_1,e}, w/d_3, y/d_4 1044# Expected: A mess, but only a rename/rename(1to2)/add/add mess. Use the 1045# presence of y/d_4 in B to avoid doing transitive rename of 1046# x/d_1 -> z/d_1 -> y/d_1, so that the only paths we have at 1047# y/d are y/d_2 and y/d_4. We still do the move from z/e to y/e, 1048# though, because it doesn't have anything in the way. 1049 1050test_setup_5c () { 1051 test_create_repo 5c && 1052 ( 1053 cd 5c && 1054 1055 mkdir z && 1056 echo b >z/b && 1057 echo c >z/c && 1058 mkdir x && 1059 echo d1 >x/d && 1060 git add z x && 1061 test_tick && 1062 git commit -m "O" && 1063 1064 git branch O && 1065 git branch A && 1066 git branch B && 1067 1068 git checkout A && 1069 git mv z y && 1070 echo d2 >y/d && 1071 git add y/d && 1072 git mv x w && 1073 test_tick && 1074 git commit -m "A" && 1075 1076 git checkout B && 1077 git mv x/d z/ && 1078 mkdir w && 1079 mkdir y && 1080 echo d3 >w/d && 1081 echo d4 >y/d && 1082 echo e >z/e && 1083 git add w/ y/ z/e && 1084 test_tick && 1085 git commit -m "B" 1086 ) 1087} 1088 1089test_expect_success '5c: Transitive rename would cause rename/rename/rename/add/add/add' ' 1090 test_setup_5c && 1091 ( 1092 cd 5c && 1093 1094 git checkout A^0 && 1095 1096 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1097 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out && 1098 test_i18ngrep "CONFLICT (add/add).* y/d" out && 1099 1100 git ls-files -s >out && 1101 test_line_count = 9 out && 1102 git ls-files -u >out && 1103 test_line_count = 6 out && 1104 git ls-files -o >out && 1105 test_line_count = 1 out && 1106 1107 git rev-parse >actual \ 1108 :0:y/b :0:y/c :0:y/e && 1109 git rev-parse >expect \ 1110 O:z/b O:z/c B:z/e && 1111 test_cmp expect actual && 1112 1113 test_must_fail git rev-parse :1:y/d && 1114 git rev-parse >actual \ 1115 :2:w/d :3:w/d :1:x/d :2:y/d :3:y/d :3:z/d && 1116 git rev-parse >expect \ 1117 O:x/d B:w/d O:x/d A:y/d B:y/d O:x/d && 1118 test_cmp expect actual && 1119 1120 git hash-object >actual \ 1121 z/d && 1122 git rev-parse >expect \ 1123 O:x/d && 1124 test_cmp expect actual && 1125 test_path_is_missing x/d && 1126 test_path_is_file y/d && 1127 grep -q "<<<<" y/d # conflict markers should be present 1128 ) 1129' 1130 1131# Testcase 5d, Directory/file/file conflict due to directory rename 1132# Commit O: z/{b,c} 1133# Commit A: y/{b,c,d_1} 1134# Commit B: z/{b,c,d_2,f}, y/d/e 1135# Expected: y/{b,c,d/e,f}, z/d_2, CONFLICT(file/directory), y/d_1~HEAD 1136# Note: The fact that y/d/ exists in B makes us bail on directory rename 1137# detection for z/d_2, but that doesn't prevent us from applying the 1138# directory rename detection for z/f -> y/f. 1139 1140test_setup_5d () { 1141 test_create_repo 5d && 1142 ( 1143 cd 5d && 1144 1145 mkdir z && 1146 echo b >z/b && 1147 echo c >z/c && 1148 git add z && 1149 test_tick && 1150 git commit -m "O" && 1151 1152 git branch O && 1153 git branch A && 1154 git branch B && 1155 1156 git checkout A && 1157 git mv z y && 1158 echo d1 >y/d && 1159 git add y/d && 1160 test_tick && 1161 git commit -m "A" && 1162 1163 git checkout B && 1164 mkdir -p y/d && 1165 echo e >y/d/e && 1166 echo d2 >z/d && 1167 echo f >z/f && 1168 git add y/d/e z/d z/f && 1169 test_tick && 1170 git commit -m "B" 1171 ) 1172} 1173 1174test_expect_success '5d: Directory/file/file conflict due to directory rename' ' 1175 test_setup_5d && 1176 ( 1177 cd 5d && 1178 1179 git checkout A^0 && 1180 1181 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1182 test_i18ngrep "CONFLICT (file/directory).*y/d" out && 1183 1184 git ls-files -s >out && 1185 test_line_count = 6 out && 1186 git ls-files -u >out && 1187 test_line_count = 1 out && 1188 git ls-files -o >out && 1189 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 1190 then 1191 test_line_count = 1 out && 1192 1193 git rev-parse >actual \ 1194 :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d~HEAD :0:y/d/e 1195 else 1196 test_line_count = 2 out && 1197 1198 git rev-parse >actual \ 1199 :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e 1200 fi && 1201 git rev-parse >expect \ 1202 O:z/b O:z/c B:z/d B:z/f A:y/d B:y/d/e && 1203 test_cmp expect actual && 1204 1205 git hash-object y/d~HEAD >actual && 1206 git rev-parse A:y/d >expect && 1207 test_cmp expect actual 1208 ) 1209' 1210 1211########################################################################### 1212# Rules suggested by section 5: 1213# 1214# If a subset of to-be-renamed files have a file or directory in the way, 1215# "turn off" the directory rename for those specific sub-paths, falling 1216# back to old handling. But, sadly, see testcases 8a and 8b. 1217########################################################################### 1218 1219 1220########################################################################### 1221# SECTION 6: Same side of the merge was the one that did the rename 1222# 1223# It may sound obvious that you only want to apply implicit directory 1224# renames to directories if the _other_ side of history did the renaming. 1225# If you did make an implementation that didn't explicitly enforce this 1226# rule, the majority of cases that would fall under this section would 1227# also be solved by following the rules from the above sections. But 1228# there are still a few that stick out, so this section covers them just 1229# to make sure we also get them right. 1230########################################################################### 1231 1232# Testcase 6a, Tricky rename/delete 1233# Commit O: z/{b,c,d} 1234# Commit A: z/b 1235# Commit B: y/{b,c}, z/d 1236# Expected: y/b, CONFLICT(rename/delete, z/c -> y/c vs. NULL) 1237# Note: We're just checking here that the rename of z/b and z/c to put 1238# them under y/ doesn't accidentally catch z/d and make it look like 1239# it is also involved in a rename/delete conflict. 1240 1241test_setup_6a () { 1242 test_create_repo 6a && 1243 ( 1244 cd 6a && 1245 1246 mkdir z && 1247 echo b >z/b && 1248 echo c >z/c && 1249 echo d >z/d && 1250 git add z && 1251 test_tick && 1252 git commit -m "O" && 1253 1254 git branch O && 1255 git branch A && 1256 git branch B && 1257 1258 git checkout A && 1259 git rm z/c && 1260 git rm z/d && 1261 test_tick && 1262 git commit -m "A" && 1263 1264 git checkout B && 1265 mkdir y && 1266 git mv z/b y/ && 1267 git mv z/c y/ && 1268 test_tick && 1269 git commit -m "B" 1270 ) 1271} 1272 1273test_expect_success '6a: Tricky rename/delete' ' 1274 test_setup_6a && 1275 ( 1276 cd 6a && 1277 1278 git checkout A^0 && 1279 1280 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1281 test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out && 1282 1283 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 1284 then 1285 git ls-files -s >out && 1286 test_line_count = 3 out && 1287 git ls-files -u >out && 1288 test_line_count = 2 out && 1289 git ls-files -o >out && 1290 test_line_count = 1 out && 1291 1292 git rev-parse >actual \ 1293 :0:y/b :1:y/c :3:y/c && 1294 git rev-parse >expect \ 1295 O:z/b O:z/c O:z/c 1296 else 1297 git ls-files -s >out && 1298 test_line_count = 2 out && 1299 git ls-files -u >out && 1300 test_line_count = 1 out && 1301 git ls-files -o >out && 1302 test_line_count = 1 out && 1303 1304 git rev-parse >actual \ 1305 :0:y/b :3:y/c && 1306 git rev-parse >expect \ 1307 O:z/b O:z/c 1308 fi && 1309 test_cmp expect actual 1310 ) 1311' 1312 1313# Testcase 6b1, Same rename done on both sides 1314# (Related to testcase 6b2 and 8e) 1315# Commit O: z/{b,c,d,e} 1316# Commit A: y/{b,c,d}, x/e 1317# Commit B: y/{b,c,d}, z/{e,f} 1318# Expected: y/{b,c,d,f}, x/e 1319# Note: Directory rename detection says A renamed z/ -> y/ (3 paths renamed 1320# to y/ and only 1 renamed to x/), therefore the new file 'z/f' in B 1321# should be moved to 'y/f'. 1322# 1323# This is a bit of an edge case where any behavior might surprise users, 1324# whether that is treating A as renaming z/ -> y/, treating A as renaming 1325# z/ -> x/, or treating A as not doing any directory rename. However, I 1326# think this answer is the least confusing and most consistent with the 1327# rules elsewhere. 1328# 1329# A note about z/ -> x/, since it may not be clear how that could come 1330# about: If we were to ignore files renamed by both sides 1331# (i.e. z/{b,c,d}), as directory rename detection did in git-2.18 thru 1332# at least git-2.28, then we would note there are no renames from z/ to 1333# y/ and one rename from z/ to x/ and thus come to the conclusion that 1334# A renamed z/ -> x/. This seems more confusing for end users than a 1335# rename of z/ to y/, it makes directory rename detection behavior 1336# harder for them to predict. As such, we modified the rule, changed 1337# the behavior on testcases 6b2 and 8e, and introduced this 6b1 testcase. 1338 1339test_setup_6b1 () { 1340 test_create_repo 6b1 && 1341 ( 1342 cd 6b1 && 1343 1344 mkdir z && 1345 echo b >z/b && 1346 echo c >z/c && 1347 echo d >z/d && 1348 echo e >z/e && 1349 git add z && 1350 test_tick && 1351 git commit -m "O" && 1352 1353 git branch O && 1354 git branch A && 1355 git branch B && 1356 1357 git checkout A && 1358 git mv z y && 1359 mkdir x && 1360 git mv y/e x/e && 1361 test_tick && 1362 git commit -m "A" && 1363 1364 git checkout B && 1365 git mv z y && 1366 mkdir z && 1367 git mv y/e z/e && 1368 echo f >z/f && 1369 git add z/f && 1370 test_tick && 1371 git commit -m "B" 1372 ) 1373} 1374 1375test_expect_merge_algorithm failure success '6b1: Same renames done on both sides, plus another rename' ' 1376 test_setup_6b1 && 1377 ( 1378 cd 6b1 && 1379 1380 git checkout A^0 && 1381 1382 git -c merge.directoryRenames=true merge -s recursive B^0 && 1383 1384 git ls-files -s >out && 1385 test_line_count = 5 out && 1386 git ls-files -u >out && 1387 test_line_count = 0 out && 1388 git ls-files -o >out && 1389 test_line_count = 1 out && 1390 1391 git rev-parse >actual \ 1392 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:x/e HEAD:y/f && 1393 git rev-parse >expect \ 1394 O:z/b O:z/c O:z/d O:z/e B:z/f && 1395 test_cmp expect actual 1396 ) 1397' 1398 1399# Testcase 6b2, Same rename done on both sides 1400# (Related to testcases 6c and 8e) 1401# Commit O: z/{b,c} 1402# Commit A: y/{b,c} 1403# Commit B: y/{b,c}, z/d 1404# Expected: y/{b,c,d} 1405# Alternate: y/{b,c}, z/d 1406# Note: Directory rename detection says A renamed z/ -> y/, therefore the new 1407# file 'z/d' in B should be moved to 'y/d'. 1408# 1409# We could potentially ignore the renames of z/{b,c} on side A since 1410# those were renamed on both sides. However, it's a bit of a corner 1411# case because what if there was also a z/e that side A moved to x/e 1412# and side B left alone? If we used the "ignore renames done on both 1413# sides" logic, then we'd compute that A renamed z/ -> x/, and move 1414# z/d to x/d. That seems more surprising and uglier than allowing 1415# the z/ -> y/ rename. 1416 1417test_setup_6b2 () { 1418 test_create_repo 6b2 && 1419 ( 1420 cd 6b2 && 1421 1422 mkdir z && 1423 echo b >z/b && 1424 echo c >z/c && 1425 git add z && 1426 test_tick && 1427 git commit -m "O" && 1428 1429 git branch O && 1430 git branch A && 1431 git branch B && 1432 1433 git checkout A && 1434 git mv z y && 1435 test_tick && 1436 git commit -m "A" && 1437 1438 git checkout B && 1439 git mv z y && 1440 mkdir z && 1441 echo d >z/d && 1442 git add z/d && 1443 test_tick && 1444 git commit -m "B" 1445 ) 1446} 1447 1448test_expect_merge_algorithm failure success '6b2: Same rename done on both sides' ' 1449 test_setup_6b2 && 1450 ( 1451 cd 6b2 && 1452 1453 git checkout A^0 && 1454 1455 git -c merge.directoryRenames=true merge -s recursive B^0 && 1456 1457 git ls-files -s >out && 1458 test_line_count = 3 out && 1459 git ls-files -u >out && 1460 test_line_count = 0 out && 1461 git ls-files -o >out && 1462 test_line_count = 1 out && 1463 1464 git rev-parse >actual \ 1465 HEAD:y/b HEAD:y/c HEAD:y/d && 1466 git rev-parse >expect \ 1467 O:z/b O:z/c B:z/d && 1468 test_cmp expect actual 1469 ) 1470' 1471 1472# Testcase 6c, Rename only done on same side 1473# (Related to testcases 6b1, 6b2, and 8e) 1474# Commit O: z/{b,c} 1475# Commit A: z/{b,c} (no change) 1476# Commit B: y/{b,c}, z/d 1477# Expected: y/{b,c}, z/d 1478# NOTE: Seems obvious, but just checking that the implementation doesn't 1479# "accidentally detect a rename" and give us y/{b,c,d}. 1480 1481test_setup_6c () { 1482 test_create_repo 6c && 1483 ( 1484 cd 6c && 1485 1486 mkdir z && 1487 echo b >z/b && 1488 echo c >z/c && 1489 git add z && 1490 test_tick && 1491 git commit -m "O" && 1492 1493 git branch O && 1494 git branch A && 1495 git branch B && 1496 1497 git checkout A && 1498 test_tick && 1499 git commit --allow-empty -m "A" && 1500 1501 git checkout B && 1502 git mv z y && 1503 mkdir z && 1504 echo d >z/d && 1505 git add z/d && 1506 test_tick && 1507 git commit -m "B" 1508 ) 1509} 1510 1511test_expect_success '6c: Rename only done on same side' ' 1512 test_setup_6c && 1513 ( 1514 cd 6c && 1515 1516 git checkout A^0 && 1517 1518 git -c merge.directoryRenames=true merge -s recursive B^0 && 1519 1520 git ls-files -s >out && 1521 test_line_count = 3 out && 1522 git ls-files -u >out && 1523 test_line_count = 0 out && 1524 git ls-files -o >out && 1525 test_line_count = 1 out && 1526 1527 git rev-parse >actual \ 1528 HEAD:y/b HEAD:y/c HEAD:z/d && 1529 git rev-parse >expect \ 1530 O:z/b O:z/c B:z/d && 1531 test_cmp expect actual 1532 ) 1533' 1534 1535# Testcase 6d, We don't always want transitive renaming 1536# (Related to testcase 1c) 1537# Commit O: z/{b,c}, x/d 1538# Commit A: z/{b,c}, x/d (no change) 1539# Commit B: y/{b,c}, z/d 1540# Expected: y/{b,c}, z/d 1541# NOTE: Again, this seems obvious but just checking that the implementation 1542# doesn't "accidentally detect a rename" and give us y/{b,c,d}. 1543 1544test_setup_6d () { 1545 test_create_repo 6d && 1546 ( 1547 cd 6d && 1548 1549 mkdir z && 1550 echo b >z/b && 1551 echo c >z/c && 1552 mkdir x && 1553 echo d >x/d && 1554 git add z x && 1555 test_tick && 1556 git commit -m "O" && 1557 1558 git branch O && 1559 git branch A && 1560 git branch B && 1561 1562 git checkout A && 1563 test_tick && 1564 git commit --allow-empty -m "A" && 1565 1566 git checkout B && 1567 git mv z y && 1568 git mv x z && 1569 test_tick && 1570 git commit -m "B" 1571 ) 1572} 1573 1574test_expect_success '6d: We do not always want transitive renaming' ' 1575 test_setup_6d && 1576 ( 1577 cd 6d && 1578 1579 git checkout A^0 && 1580 1581 git -c merge.directoryRenames=true merge -s recursive B^0 && 1582 1583 git ls-files -s >out && 1584 test_line_count = 3 out && 1585 git ls-files -u >out && 1586 test_line_count = 0 out && 1587 git ls-files -o >out && 1588 test_line_count = 1 out && 1589 1590 git rev-parse >actual \ 1591 HEAD:y/b HEAD:y/c HEAD:z/d && 1592 git rev-parse >expect \ 1593 O:z/b O:z/c O:x/d && 1594 test_cmp expect actual 1595 ) 1596' 1597 1598# Testcase 6e, Add/add from one-side 1599# Commit O: z/{b,c} 1600# Commit A: z/{b,c} (no change) 1601# Commit B: y/{b,c,d_1}, z/d_2 1602# Expected: y/{b,c,d_1}, z/d_2 1603# NOTE: Again, this seems obvious but just checking that the implementation 1604# doesn't "accidentally detect a rename" and give us y/{b,c} + 1605# add/add conflict on y/d_1 vs y/d_2. 1606 1607test_setup_6e () { 1608 test_create_repo 6e && 1609 ( 1610 cd 6e && 1611 1612 mkdir z && 1613 echo b >z/b && 1614 echo c >z/c && 1615 git add z && 1616 test_tick && 1617 git commit -m "O" && 1618 1619 git branch O && 1620 git branch A && 1621 git branch B && 1622 1623 git checkout A && 1624 test_tick && 1625 git commit --allow-empty -m "A" && 1626 1627 git checkout B && 1628 git mv z y && 1629 echo d1 > y/d && 1630 mkdir z && 1631 echo d2 > z/d && 1632 git add y/d z/d && 1633 test_tick && 1634 git commit -m "B" 1635 ) 1636} 1637 1638test_expect_success '6e: Add/add from one side' ' 1639 test_setup_6e && 1640 ( 1641 cd 6e && 1642 1643 git checkout A^0 && 1644 1645 git -c merge.directoryRenames=true merge -s recursive B^0 && 1646 1647 git ls-files -s >out && 1648 test_line_count = 4 out && 1649 git ls-files -u >out && 1650 test_line_count = 0 out && 1651 git ls-files -o >out && 1652 test_line_count = 1 out && 1653 1654 git rev-parse >actual \ 1655 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/d && 1656 git rev-parse >expect \ 1657 O:z/b O:z/c B:y/d B:z/d && 1658 test_cmp expect actual 1659 ) 1660' 1661 1662########################################################################### 1663# Rules suggested by section 6: 1664# 1665# Only apply implicit directory renames to directories if the other 1666# side of history is the one doing the renaming. 1667########################################################################### 1668 1669 1670########################################################################### 1671# SECTION 7: More involved Edge/Corner cases 1672# 1673# The ruleset we have generated in the above sections seems to provide 1674# well-defined merges. But can we find edge/corner cases that either (a) 1675# are harder for users to understand, or (b) have a resolution that is 1676# non-intuitive or suboptimal? 1677# 1678# The testcases in this section dive into cases that I've tried to craft in 1679# a way to find some that might be surprising to users or difficult for 1680# them to understand (the next section will look at non-intuitive or 1681# suboptimal merge results). Some of the testcases are similar to ones 1682# from past sections, but have been simplified to try to highlight error 1683# messages using a "modified" path (due to the directory rename). Are 1684# users okay with these? 1685# 1686# In my opinion, testcases that are difficult to understand from this 1687# section is due to difficulty in the testcase rather than the directory 1688# renaming (similar to how t6042 and t6036 have difficult resolutions due 1689# to the problem setup itself being complex). And I don't think the 1690# error messages are a problem. 1691# 1692# On the other hand, the testcases in section 8 worry me slightly more... 1693########################################################################### 1694 1695# Testcase 7a, rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file 1696# Commit O: z/{b,c} 1697# Commit A: y/{b,c} 1698# Commit B: w/b, x/c, z/d 1699# Expected: y/d, CONFLICT(rename/rename for both z/b and z/c) 1700# NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d. 1701 1702test_setup_7a () { 1703 test_create_repo 7a && 1704 ( 1705 cd 7a && 1706 1707 mkdir z && 1708 echo b >z/b && 1709 echo c >z/c && 1710 git add z && 1711 test_tick && 1712 git commit -m "O" && 1713 1714 git branch O && 1715 git branch A && 1716 git branch B && 1717 1718 git checkout A && 1719 git mv z y && 1720 test_tick && 1721 git commit -m "A" && 1722 1723 git checkout B && 1724 mkdir w && 1725 mkdir x && 1726 git mv z/b w/ && 1727 git mv z/c x/ && 1728 echo d > z/d && 1729 git add z/d && 1730 test_tick && 1731 git commit -m "B" 1732 ) 1733} 1734 1735test_expect_success '7a: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file' ' 1736 test_setup_7a && 1737 ( 1738 cd 7a && 1739 1740 git checkout A^0 && 1741 1742 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1743 test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out && 1744 test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out && 1745 1746 git ls-files -s >out && 1747 test_line_count = 7 out && 1748 git ls-files -u >out && 1749 test_line_count = 6 out && 1750 git ls-files -o >out && 1751 test_line_count = 1 out && 1752 1753 git rev-parse >actual \ 1754 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:x/c :0:y/d && 1755 git rev-parse >expect \ 1756 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d && 1757 test_cmp expect actual && 1758 1759 git hash-object >actual \ 1760 y/b w/b y/c x/c && 1761 git rev-parse >expect \ 1762 O:z/b O:z/b O:z/c O:z/c && 1763 test_cmp expect actual 1764 ) 1765' 1766 1767# Testcase 7b, rename/rename(2to1), but only due to transitive rename 1768# (Related to testcase 1d) 1769# Commit O: z/{b,c}, x/d_1, w/d_2 1770# Commit A: y/{b,c,d_2}, x/d_1 1771# Commit B: z/{b,c,d_1}, w/d_2 1772# Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d) 1773 1774test_setup_7b () { 1775 test_create_repo 7b && 1776 ( 1777 cd 7b && 1778 1779 mkdir z && 1780 mkdir x && 1781 mkdir w && 1782 echo b >z/b && 1783 echo c >z/c && 1784 echo d1 > x/d && 1785 echo d2 > w/d && 1786 git add z x w && 1787 test_tick && 1788 git commit -m "O" && 1789 1790 git branch O && 1791 git branch A && 1792 git branch B && 1793 1794 git checkout A && 1795 git mv z y && 1796 git mv w/d y/ && 1797 test_tick && 1798 git commit -m "A" && 1799 1800 git checkout B && 1801 git mv x/d z/ && 1802 rmdir x && 1803 test_tick && 1804 git commit -m "B" 1805 ) 1806} 1807 1808test_expect_success '7b: rename/rename(2to1), but only due to transitive rename' ' 1809 test_setup_7b && 1810 ( 1811 cd 7b && 1812 1813 git checkout A^0 && 1814 1815 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1816 test_i18ngrep "CONFLICT (\(.*\)/\1)" out && 1817 1818 git ls-files -s >out && 1819 test_line_count = 4 out && 1820 git ls-files -u >out && 1821 test_line_count = 2 out && 1822 git ls-files -o >out && 1823 test_line_count = 1 out && 1824 1825 git rev-parse >actual \ 1826 :0:y/b :0:y/c :2:y/d :3:y/d && 1827 git rev-parse >expect \ 1828 O:z/b O:z/c O:w/d O:x/d && 1829 test_cmp expect actual && 1830 1831 # Test that the two-way merge in y/d is as expected 1832 git cat-file -p :2:y/d >expect && 1833 git cat-file -p :3:y/d >other && 1834 >empty && 1835 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 1836 then 1837 test_must_fail git merge-file \ 1838 -L "HEAD:y/d" \ 1839 -L "" \ 1840 -L "B^0:z/d" \ 1841 expect empty other 1842 else 1843 test_must_fail git merge-file \ 1844 -L "HEAD" \ 1845 -L "" \ 1846 -L "B^0" \ 1847 expect empty other 1848 fi && 1849 test_cmp expect y/d 1850 ) 1851' 1852 1853# Testcase 7c, rename/rename(1to...2or3); transitive rename may add complexity 1854# (Related to testcases 3b and 5c) 1855# Commit O: z/{b,c}, x/d 1856# Commit A: y/{b,c}, w/d 1857# Commit B: z/{b,c,d} 1858# Expected: y/{b,c}, CONFLICT(x/d -> w/d vs. y/d) 1859# NOTE: z/ was renamed to y/ so we do want to report 1860# neither CONFLICT(x/d -> w/d vs. z/d) 1861# nor CONFLiCT x/d -> w/d vs. y/d vs. z/d) 1862 1863test_setup_7c () { 1864 test_create_repo 7c && 1865 ( 1866 cd 7c && 1867 1868 mkdir z && 1869 echo b >z/b && 1870 echo c >z/c && 1871 mkdir x && 1872 echo d >x/d && 1873 git add z x && 1874 test_tick && 1875 git commit -m "O" && 1876 1877 git branch O && 1878 git branch A && 1879 git branch B && 1880 1881 git checkout A && 1882 git mv z y && 1883 git mv x w && 1884 test_tick && 1885 git commit -m "A" && 1886 1887 git checkout B && 1888 git mv x/d z/ && 1889 rmdir x && 1890 test_tick && 1891 git commit -m "B" 1892 ) 1893} 1894 1895test_expect_success '7c: rename/rename(1to...2or3); transitive rename may add complexity' ' 1896 test_setup_7c && 1897 ( 1898 cd 7c && 1899 1900 git checkout A^0 && 1901 1902 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1903 test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out && 1904 1905 git ls-files -s >out && 1906 test_line_count = 5 out && 1907 git ls-files -u >out && 1908 test_line_count = 3 out && 1909 git ls-files -o >out && 1910 test_line_count = 1 out && 1911 1912 git rev-parse >actual \ 1913 :0:y/b :0:y/c :1:x/d :2:w/d :3:y/d && 1914 git rev-parse >expect \ 1915 O:z/b O:z/c O:x/d O:x/d O:x/d && 1916 test_cmp expect actual 1917 ) 1918' 1919 1920# Testcase 7d, transitive rename involved in rename/delete; how is it reported? 1921# (Related somewhat to testcases 5b and 8d) 1922# Commit O: z/{b,c}, x/d 1923# Commit A: y/{b,c} 1924# Commit B: z/{b,c,d} 1925# Expected: y/{b,c}, CONFLICT(delete x/d vs rename to y/d) 1926# NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d) 1927 1928test_setup_7d () { 1929 test_create_repo 7d && 1930 ( 1931 cd 7d && 1932 1933 mkdir z && 1934 echo b >z/b && 1935 echo c >z/c && 1936 mkdir x && 1937 echo d >x/d && 1938 git add z x && 1939 test_tick && 1940 git commit -m "O" && 1941 1942 git branch O && 1943 git branch A && 1944 git branch B && 1945 1946 git checkout A && 1947 git mv z y && 1948 git rm -rf x && 1949 test_tick && 1950 git commit -m "A" && 1951 1952 git checkout B && 1953 git mv x/d z/ && 1954 rmdir x && 1955 test_tick && 1956 git commit -m "B" 1957 ) 1958} 1959 1960test_expect_success '7d: transitive rename involved in rename/delete; how is it reported?' ' 1961 test_setup_7d && 1962 ( 1963 cd 7d && 1964 1965 git checkout A^0 && 1966 1967 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 1968 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out && 1969 1970 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 1971 then 1972 git ls-files -s >out && 1973 test_line_count = 4 out && 1974 git ls-files -u >out && 1975 test_line_count = 2 out && 1976 git ls-files -o >out && 1977 test_line_count = 1 out && 1978 1979 git rev-parse >actual \ 1980 :0:y/b :0:y/c :1:y/d :3:y/d && 1981 git rev-parse >expect \ 1982 O:z/b O:z/c O:x/d O:x/d 1983 else 1984 git ls-files -s >out && 1985 test_line_count = 3 out && 1986 git ls-files -u >out && 1987 test_line_count = 1 out && 1988 git ls-files -o >out && 1989 test_line_count = 1 out && 1990 1991 git rev-parse >actual \ 1992 :0:y/b :0:y/c :3:y/d && 1993 git rev-parse >expect \ 1994 O:z/b O:z/c O:x/d 1995 fi && 1996 test_cmp expect actual 1997 ) 1998' 1999 2000# Testcase 7e, transitive rename in rename/delete AND dirs in the way 2001# (Very similar to 'both rename source and destination involved in D/F conflict' from t6022-merge-rename.sh) 2002# (Also related to testcases 9c and 9d) 2003# Commit O: z/{b,c}, x/d_1 2004# Commit A: y/{b,c,d/g}, x/d/f 2005# Commit B: z/{b,c,d_1} 2006# Expected: rename/delete(x/d_1->y/d_1 vs. None) + D/F conflict on y/d 2007# y/{b,c,d/g}, y/d_1~B^0, x/d/f 2008 2009# NOTE: The main path of interest here is d_1 and where it ends up, but 2010# this is actually a case that has two potential directory renames 2011# involved and D/F conflict(s), so it makes sense to walk through 2012# each step. 2013# 2014# Commit A renames z/ -> y/. Thus everything that B adds to z/ 2015# should be instead moved to y/. This gives us the D/F conflict on 2016# y/d because x/d_1 -> z/d_1 -> y/d_1 conflicts with y/d/g. 2017# 2018# Further, commit B renames x/ -> z/, thus everything A adds to x/ 2019# should instead be moved to z/...BUT we removed z/ and renamed it 2020# to y/, so maybe everything should move not from x/ to z/, but 2021# from x/ to z/ to y/. Doing so might make sense from the logic so 2022# far, but note that commit A had both an x/ and a y/; it did the 2023# renaming of z/ to y/ and created x/d/f and it clearly made these 2024# things separate, so it doesn't make much sense to push these 2025# together. Doing so is what I'd call a doubly transitive rename; 2026# see testcases 9c and 9d for further discussion of this issue and 2027# how it's resolved. 2028 2029test_setup_7e () { 2030 test_create_repo 7e && 2031 ( 2032 cd 7e && 2033 2034 mkdir z && 2035 echo b >z/b && 2036 echo c >z/c && 2037 mkdir x && 2038 echo d1 >x/d && 2039 git add z x && 2040 test_tick && 2041 git commit -m "O" && 2042 2043 git branch O && 2044 git branch A && 2045 git branch B && 2046 2047 git checkout A && 2048 git mv z y && 2049 git rm x/d && 2050 mkdir -p x/d && 2051 mkdir -p y/d && 2052 echo f >x/d/f && 2053 echo g >y/d/g && 2054 git add x/d/f y/d/g && 2055 test_tick && 2056 git commit -m "A" && 2057 2058 git checkout B && 2059 git mv x/d z/ && 2060 rmdir x && 2061 test_tick && 2062 git commit -m "B" 2063 ) 2064} 2065 2066test_expect_success '7e: transitive rename in rename/delete AND dirs in the way' ' 2067 test_setup_7e && 2068 ( 2069 cd 7e && 2070 2071 git checkout A^0 && 2072 2073 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 2074 test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out && 2075 2076 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 2077 then 2078 git ls-files -s >out && 2079 test_line_count = 6 out && 2080 git ls-files -u >out && 2081 test_line_count = 2 out && 2082 git ls-files -o >out && 2083 test_line_count = 1 out && 2084 2085 git rev-parse >actual \ 2086 :0:x/d/f :0:y/d/g :0:y/b :0:y/c :1:y/d~B^0 :3:y/d~B^0 && 2087 git rev-parse >expect \ 2088 A:x/d/f A:y/d/g O:z/b O:z/c O:x/d O:x/d 2089 else 2090 git ls-files -s >out && 2091 test_line_count = 5 out && 2092 git ls-files -u >out && 2093 test_line_count = 1 out && 2094 git ls-files -o >out && 2095 test_line_count = 2 out && 2096 2097 git rev-parse >actual \ 2098 :0:x/d/f :0:y/d/g :0:y/b :0:y/c :3:y/d && 2099 git rev-parse >expect \ 2100 A:x/d/f A:y/d/g O:z/b O:z/c O:x/d 2101 fi && 2102 test_cmp expect actual && 2103 2104 git hash-object y/d~B^0 >actual && 2105 git rev-parse O:x/d >expect && 2106 test_cmp expect actual 2107 ) 2108' 2109 2110########################################################################### 2111# SECTION 8: Suboptimal merges 2112# 2113# As alluded to in the last section, the ruleset we have built up for 2114# detecting directory renames unfortunately has some special cases where it 2115# results in slightly suboptimal or non-intuitive behavior. This section 2116# explores these cases. 2117# 2118# To be fair, we already had non-intuitive or suboptimal behavior for most 2119# of these cases in git before introducing implicit directory rename 2120# detection, but it'd be nice if there was a modified ruleset out there 2121# that handled these cases a bit better. 2122########################################################################### 2123 2124# Testcase 8a, Dual-directory rename, one into the others' way 2125# Commit O. x/{a,b}, y/{c,d} 2126# Commit A. x/{a,b,e}, y/{c,d,f} 2127# Commit B. y/{a,b}, z/{c,d} 2128# 2129# Possible Resolutions: 2130# w/o dir-rename detection: y/{a,b,f}, z/{c,d}, x/e 2131# Currently expected: y/{a,b,e,f}, z/{c,d} 2132# Optimal: y/{a,b,e}, z/{c,d,f} 2133# 2134# Note: Both x and y got renamed and it'd be nice to detect both, and we do 2135# better with directory rename detection than git did without, but the 2136# simple rule from section 5 prevents me from handling this as optimally as 2137# we potentially could. 2138 2139test_setup_8a () { 2140 test_create_repo 8a && 2141 ( 2142 cd 8a && 2143 2144 mkdir x && 2145 mkdir y && 2146 echo a >x/a && 2147 echo b >x/b && 2148 echo c >y/c && 2149 echo d >y/d && 2150 git add x y && 2151 test_tick && 2152 git commit -m "O" && 2153 2154 git branch O && 2155 git branch A && 2156 git branch B && 2157 2158 git checkout A && 2159 echo e >x/e && 2160 echo f >y/f && 2161 git add x/e y/f && 2162 test_tick && 2163 git commit -m "A" && 2164 2165 git checkout B && 2166 git mv y z && 2167 git mv x y && 2168 test_tick && 2169 git commit -m "B" 2170 ) 2171} 2172 2173test_expect_success '8a: Dual-directory rename, one into the others way' ' 2174 test_setup_8a && 2175 ( 2176 cd 8a && 2177 2178 git checkout A^0 && 2179 2180 git -c merge.directoryRenames=true merge -s recursive B^0 && 2181 2182 git ls-files -s >out && 2183 test_line_count = 6 out && 2184 git ls-files -u >out && 2185 test_line_count = 0 out && 2186 git ls-files -o >out && 2187 test_line_count = 1 out && 2188 2189 git rev-parse >actual \ 2190 HEAD:y/a HEAD:y/b HEAD:y/e HEAD:y/f HEAD:z/c HEAD:z/d && 2191 git rev-parse >expect \ 2192 O:x/a O:x/b A:x/e A:y/f O:y/c O:y/d && 2193 test_cmp expect actual 2194 ) 2195' 2196 2197# Testcase 8b, Dual-directory rename, one into the others' way, with conflicting filenames 2198# Commit O. x/{a_1,b_1}, y/{a_2,b_2} 2199# Commit A. x/{a_1,b_1,e_1}, y/{a_2,b_2,e_2} 2200# Commit B. y/{a_1,b_1}, z/{a_2,b_2} 2201# 2202# w/o dir-rename detection: y/{a_1,b_1,e_2}, z/{a_2,b_2}, x/e_1 2203# Currently expected: <same> 2204# Scary: y/{a_1,b_1}, z/{a_2,b_2}, CONFLICT(add/add, e_1 vs. e_2) 2205# Optimal: y/{a_1,b_1,e_1}, z/{a_2,b_2,e_2} 2206# 2207# Note: Very similar to 8a, except instead of 'e' and 'f' in directories x and 2208# y, both are named 'e'. Without directory rename detection, neither file 2209# moves directories. Implement directory rename detection suboptimally, and 2210# you get an add/add conflict, but both files were added in commit A, so this 2211# is an add/add conflict where one side of history added both files -- 2212# something we can't represent in the index. Obviously, we'd prefer the last 2213# resolution, but our previous rules are too coarse to allow it. Using both 2214# the rules from section 4 and section 5 save us from the Scary resolution, 2215# making us fall back to pre-directory-rename-detection behavior for both 2216# e_1 and e_2. 2217 2218test_setup_8b () { 2219 test_create_repo 8b && 2220 ( 2221 cd 8b && 2222 2223 mkdir x && 2224 mkdir y && 2225 echo a1 >x/a && 2226 echo b1 >x/b && 2227 echo a2 >y/a && 2228 echo b2 >y/b && 2229 git add x y && 2230 test_tick && 2231 git commit -m "O" && 2232 2233 git branch O && 2234 git branch A && 2235 git branch B && 2236 2237 git checkout A && 2238 echo e1 >x/e && 2239 echo e2 >y/e && 2240 git add x/e y/e && 2241 test_tick && 2242 git commit -m "A" && 2243 2244 git checkout B && 2245 git mv y z && 2246 git mv x y && 2247 test_tick && 2248 git commit -m "B" 2249 ) 2250} 2251 2252test_expect_success '8b: Dual-directory rename, one into the others way, with conflicting filenames' ' 2253 test_setup_8b && 2254 ( 2255 cd 8b && 2256 2257 git checkout A^0 && 2258 2259 git -c merge.directoryRenames=true merge -s recursive B^0 && 2260 2261 git ls-files -s >out && 2262 test_line_count = 6 out && 2263 git ls-files -u >out && 2264 test_line_count = 0 out && 2265 git ls-files -o >out && 2266 test_line_count = 1 out && 2267 2268 git rev-parse >actual \ 2269 HEAD:y/a HEAD:y/b HEAD:z/a HEAD:z/b HEAD:x/e HEAD:y/e && 2270 git rev-parse >expect \ 2271 O:x/a O:x/b O:y/a O:y/b A:x/e A:y/e && 2272 test_cmp expect actual 2273 ) 2274' 2275 2276# Testcase 8c, modify/delete or rename+modify/delete? 2277# (Related to testcases 5b, 8d, and 9h) 2278# Commit O: z/{b,c,d} 2279# Commit A: y/{b,c} 2280# Commit B: z/{b,c,d_modified,e} 2281# Expected: y/{b,c,e}, CONFLICT(modify/delete: on z/d) 2282# 2283# Note: It could easily be argued that the correct resolution here is 2284# y/{b,c,e}, CONFLICT(rename/delete: z/d -> y/d vs deleted) 2285# and that the modified version of d should be present in y/ after 2286# the merge, just marked as conflicted. Indeed, I previously did 2287# argue that. But applying directory renames to the side of 2288# history where a file is merely modified results in spurious 2289# rename/rename(1to2) conflicts -- see testcase 9h. See also 2290# notes in 8d. 2291 2292test_setup_8c () { 2293 test_create_repo 8c && 2294 ( 2295 cd 8c && 2296 2297 mkdir z && 2298 echo b >z/b && 2299 echo c >z/c && 2300 test_seq 1 10 >z/d && 2301 git add z && 2302 test_tick && 2303 git commit -m "O" && 2304 2305 git branch O && 2306 git branch A && 2307 git branch B && 2308 2309 git checkout A && 2310 git rm z/d && 2311 git mv z y && 2312 test_tick && 2313 git commit -m "A" && 2314 2315 git checkout B && 2316 echo 11 >z/d && 2317 test_chmod +x z/d && 2318 echo e >z/e && 2319 git add z/d z/e && 2320 test_tick && 2321 git commit -m "B" 2322 ) 2323} 2324 2325test_expect_success '8c: modify/delete or rename+modify/delete' ' 2326 test_setup_8c && 2327 ( 2328 cd 8c && 2329 2330 git checkout A^0 && 2331 2332 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 2333 test_i18ngrep "CONFLICT (modify/delete).* z/d" out && 2334 2335 git ls-files -s >out && 2336 test_line_count = 5 out && 2337 git ls-files -u >out && 2338 test_line_count = 2 out && 2339 git ls-files -o >out && 2340 test_line_count = 1 out && 2341 2342 git rev-parse >actual \ 2343 :0:y/b :0:y/c :0:y/e :1:z/d :3:z/d && 2344 git rev-parse >expect \ 2345 O:z/b O:z/c B:z/e O:z/d B:z/d && 2346 test_cmp expect actual && 2347 2348 test_must_fail git rev-parse :2:z/d && 2349 git ls-files -s z/d | grep ^100755 && 2350 test_path_is_file z/d && 2351 test_path_is_missing y/d 2352 ) 2353' 2354 2355# Testcase 8d, rename/delete...or not? 2356# (Related to testcase 5b; these may appear slightly inconsistent to users; 2357# Also related to testcases 7d and 7e) 2358# Commit O: z/{b,c,d} 2359# Commit A: y/{b,c} 2360# Commit B: z/{b,c,d,e} 2361# Expected: y/{b,c,e} 2362# 2363# Note: It would also be somewhat reasonable to resolve this as 2364# y/{b,c,e}, CONFLICT(rename/delete: x/d -> y/d or deleted) 2365# 2366# In this case, I'm leaning towards: commit A was the one that deleted z/d 2367# and it did the rename of z to y, so the two "conflicts" (rename vs. 2368# delete) are both coming from commit A, which is illogical. Conflicts 2369# during merging are supposed to be about opposite sides doing things 2370# differently. 2371 2372test_setup_8d () { 2373 test_create_repo 8d && 2374 ( 2375 cd 8d && 2376 2377 mkdir z && 2378 echo b >z/b && 2379 echo c >z/c && 2380 test_seq 1 10 >z/d && 2381 git add z && 2382 test_tick && 2383 git commit -m "O" && 2384 2385 git branch O && 2386 git branch A && 2387 git branch B && 2388 2389 git checkout A && 2390 git rm z/d && 2391 git mv z y && 2392 test_tick && 2393 git commit -m "A" && 2394 2395 git checkout B && 2396 echo e >z/e && 2397 git add z/e && 2398 test_tick && 2399 git commit -m "B" 2400 ) 2401} 2402 2403test_expect_success '8d: rename/delete...or not?' ' 2404 test_setup_8d && 2405 ( 2406 cd 8d && 2407 2408 git checkout A^0 && 2409 2410 git -c merge.directoryRenames=true merge -s recursive B^0 && 2411 2412 git ls-files -s >out && 2413 test_line_count = 3 out && 2414 2415 git rev-parse >actual \ 2416 HEAD:y/b HEAD:y/c HEAD:y/e && 2417 git rev-parse >expect \ 2418 O:z/b O:z/c B:z/e && 2419 test_cmp expect actual 2420 ) 2421' 2422 2423# Testcase 8e, Both sides rename, one side adds to original directory 2424# Commit O: z/{b,c} 2425# Commit A: y/{b,c} 2426# Commit B: w/{b,c}, z/d 2427# 2428# Possible Resolutions: 2429# if z not considered renamed: z/d, CONFLICT(z/b -> y/b vs. w/b), 2430# CONFLICT(z/c -> y/c vs. w/c) 2431# if z->y rename considered: y/d, CONFLICT(z/b -> y/b vs. w/b), 2432# CONFLICT(z/c -> y/c vs. w/c) 2433# Optimal: ?? 2434# 2435# Notes: In commit A, directory z got renamed to y. In commit B, directory z 2436# did NOT get renamed; the directory is still present; instead it is 2437# considered to have just renamed a subset of paths in directory z 2438# elsewhere. This is much like testcase 6b2 (where commit B moves all 2439# the original paths out of z/ but opted to keep d within z/). 2440# 2441# It was not clear in the past what should be done with this testcase; 2442# in fact, I noted that I "just picked one" previously. However, 2443# following the new logic for testcase 6b2, we should take the rename 2444# and move z/d to y/d. 2445# 2446# 6b1, 6b2, and this case are definitely somewhat fuzzy in terms of 2447# whether they are optimal for end users, but (a) the default for 2448# directory rename detection is to mark these all as conflicts 2449# anyway, (b) it feels like this is less prone to higher order corner 2450# case confusion, and (c) the current algorithm requires less global 2451# knowledge (i.e. less coupling in the algorithm between renames done 2452# on both sides) which thus means users are better able to predict 2453# the behavior, and predict it without computing as many details. 2454 2455test_setup_8e () { 2456 test_create_repo 8e && 2457 ( 2458 cd 8e && 2459 2460 mkdir z && 2461 echo b >z/b && 2462 echo c >z/c && 2463 git add z && 2464 test_tick && 2465 git commit -m "O" && 2466 2467 git branch O && 2468 git branch A && 2469 git branch B && 2470 2471 git checkout A && 2472 git mv z y && 2473 test_tick && 2474 git commit -m "A" && 2475 2476 git checkout B && 2477 git mv z w && 2478 mkdir z && 2479 echo d >z/d && 2480 git add z/d && 2481 test_tick && 2482 git commit -m "B" 2483 ) 2484} 2485 2486test_expect_success '8e: Both sides rename, one side adds to original directory' ' 2487 test_setup_8e && 2488 ( 2489 cd 8e && 2490 2491 git checkout A^0 && 2492 2493 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 2494 test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out && 2495 test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out && 2496 2497 git ls-files -s >out && 2498 test_line_count = 7 out && 2499 git ls-files -u >out && 2500 test_line_count = 6 out && 2501 git ls-files -o >out && 2502 test_line_count = 2 out && 2503 2504 git rev-parse >actual \ 2505 :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:w/c :0:y/d && 2506 git rev-parse >expect \ 2507 O:z/b O:z/b O:z/b O:z/c O:z/c O:z/c B:z/d && 2508 test_cmp expect actual && 2509 2510 git hash-object >actual \ 2511 y/b w/b y/c w/c && 2512 git rev-parse >expect \ 2513 O:z/b O:z/b O:z/c O:z/c && 2514 test_cmp expect actual && 2515 2516 test_path_is_missing z/b && 2517 test_path_is_missing z/c 2518 ) 2519' 2520 2521########################################################################### 2522# SECTION 9: Other testcases 2523# 2524# This section consists of miscellaneous testcases I thought of during 2525# the implementation which round out the testing. 2526########################################################################### 2527 2528# Testcase 9a, Inner renamed directory within outer renamed directory 2529# (Related to testcase 1f) 2530# Commit O: z/{b,c,d/{e,f,g}} 2531# Commit A: y/{b,c}, x/w/{e,f,g} 2532# Commit B: z/{b,c,d/{e,f,g,h},i} 2533# Expected: y/{b,c,i}, x/w/{e,f,g,h} 2534# NOTE: The only reason this one is interesting is because when a directory 2535# is split into multiple other directories, we determine by the weight 2536# of which one had the most paths going to it. A naive implementation 2537# of that could take the new file in commit B at z/i to x/w/i or x/i. 2538 2539test_setup_9a () { 2540 test_create_repo 9a && 2541 ( 2542 cd 9a && 2543 2544 mkdir -p z/d && 2545 echo b >z/b && 2546 echo c >z/c && 2547 echo e >z/d/e && 2548 echo f >z/d/f && 2549 echo g >z/d/g && 2550 git add z && 2551 test_tick && 2552 git commit -m "O" && 2553 2554 git branch O && 2555 git branch A && 2556 git branch B && 2557 2558 git checkout A && 2559 mkdir x && 2560 git mv z/d x/w && 2561 git mv z y && 2562 test_tick && 2563 git commit -m "A" && 2564 2565 git checkout B && 2566 echo h >z/d/h && 2567 echo i >z/i && 2568 git add z && 2569 test_tick && 2570 git commit -m "B" 2571 ) 2572} 2573 2574test_expect_success '9a: Inner renamed directory within outer renamed directory' ' 2575 test_setup_9a && 2576 ( 2577 cd 9a && 2578 2579 git checkout A^0 && 2580 2581 git -c merge.directoryRenames=true merge -s recursive B^0 && 2582 2583 git ls-files -s >out && 2584 test_line_count = 7 out && 2585 git ls-files -u >out && 2586 test_line_count = 0 out && 2587 git ls-files -o >out && 2588 test_line_count = 1 out && 2589 2590 git rev-parse >actual \ 2591 HEAD:y/b HEAD:y/c HEAD:y/i && 2592 git rev-parse >expect \ 2593 O:z/b O:z/c B:z/i && 2594 test_cmp expect actual && 2595 2596 git rev-parse >actual \ 2597 HEAD:x/w/e HEAD:x/w/f HEAD:x/w/g HEAD:x/w/h && 2598 git rev-parse >expect \ 2599 O:z/d/e O:z/d/f O:z/d/g B:z/d/h && 2600 test_cmp expect actual 2601 ) 2602' 2603 2604# Testcase 9b, Transitive rename with content merge 2605# (Related to testcase 1c) 2606# Commit O: z/{b,c}, x/d_1 2607# Commit A: y/{b,c}, x/d_2 2608# Commit B: z/{b,c,d_3} 2609# Expected: y/{b,c,d_merged} 2610 2611test_setup_9b () { 2612 test_create_repo 9b && 2613 ( 2614 cd 9b && 2615 2616 mkdir z && 2617 echo b >z/b && 2618 echo c >z/c && 2619 mkdir x && 2620 test_seq 1 10 >x/d && 2621 git add z x && 2622 test_tick && 2623 git commit -m "O" && 2624 2625 git branch O && 2626 git branch A && 2627 git branch B && 2628 2629 git checkout A && 2630 git mv z y && 2631 test_seq 1 11 >x/d && 2632 git add x/d && 2633 test_tick && 2634 git commit -m "A" && 2635 2636 git checkout B && 2637 test_seq 0 10 >x/d && 2638 git mv x/d z/d && 2639 git add z/d && 2640 test_tick && 2641 git commit -m "B" 2642 ) 2643} 2644 2645test_expect_success '9b: Transitive rename with content merge' ' 2646 test_setup_9b && 2647 ( 2648 cd 9b && 2649 2650 git checkout A^0 && 2651 2652 git -c merge.directoryRenames=true merge -s recursive B^0 && 2653 2654 git ls-files -s >out && 2655 test_line_count = 3 out && 2656 2657 test_seq 0 11 >expected && 2658 test_cmp expected y/d && 2659 git add expected && 2660 git rev-parse >actual \ 2661 HEAD:y/b HEAD:y/c HEAD:y/d && 2662 git rev-parse >expect \ 2663 O:z/b O:z/c :0:expected && 2664 test_cmp expect actual && 2665 test_must_fail git rev-parse HEAD:x/d && 2666 test_must_fail git rev-parse HEAD:z/d && 2667 test_path_is_missing z/d && 2668 2669 test $(git rev-parse HEAD:y/d) != $(git rev-parse O:x/d) && 2670 test $(git rev-parse HEAD:y/d) != $(git rev-parse A:x/d) && 2671 test $(git rev-parse HEAD:y/d) != $(git rev-parse B:z/d) 2672 ) 2673' 2674 2675# Testcase 9c, Doubly transitive rename? 2676# (Related to testcase 1c, 7e, and 9d) 2677# Commit O: z/{b,c}, x/{d,e}, w/f 2678# Commit A: y/{b,c}, x/{d,e,f,g} 2679# Commit B: z/{b,c,d,e}, w/f 2680# Expected: y/{b,c,d,e}, x/{f,g} 2681# 2682# NOTE: x/f and x/g may be slightly confusing here. The rename from w/f to 2683# x/f is clear. Let's look beyond that. Here's the logic: 2684# Commit B renamed x/ -> z/ 2685# Commit A renamed z/ -> y/ 2686# So, we could possibly further rename x/f to z/f to y/f, a doubly 2687# transient rename. However, where does it end? We can chain these 2688# indefinitely (see testcase 9d). What if there is a D/F conflict 2689# at z/f/ or y/f/? Or just another file conflict at one of those 2690# paths? In the case of an N-long chain of transient renamings, 2691# where do we "abort" the rename at? Can the user make sense of 2692# the resulting conflict and resolve it? 2693# 2694# To avoid this confusion I use the simple rule that if the other side 2695# of history did a directory rename to a path that your side renamed 2696# away, then ignore that particular rename from the other side of 2697# history for any implicit directory renames. 2698 2699test_setup_9c () { 2700 test_create_repo 9c && 2701 ( 2702 cd 9c && 2703 2704 mkdir z && 2705 echo b >z/b && 2706 echo c >z/c && 2707 mkdir x && 2708 echo d >x/d && 2709 echo e >x/e && 2710 mkdir w && 2711 echo f >w/f && 2712 git add z x w && 2713 test_tick && 2714 git commit -m "O" && 2715 2716 git branch O && 2717 git branch A && 2718 git branch B && 2719 2720 git checkout A && 2721 git mv z y && 2722 git mv w/f x/ && 2723 echo g >x/g && 2724 git add x/g && 2725 test_tick && 2726 git commit -m "A" && 2727 2728 git checkout B && 2729 git mv x/d z/d && 2730 git mv x/e z/e && 2731 test_tick && 2732 git commit -m "B" 2733 ) 2734} 2735 2736test_expect_success '9c: Doubly transitive rename?' ' 2737 test_setup_9c && 2738 ( 2739 cd 9c && 2740 2741 git checkout A^0 && 2742 2743 git -c merge.directoryRenames=true merge -s recursive B^0 >out && 2744 test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out && 2745 2746 git ls-files -s >out && 2747 test_line_count = 6 out && 2748 git ls-files -o >out && 2749 test_line_count = 1 out && 2750 2751 git rev-parse >actual \ 2752 HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e HEAD:x/f HEAD:x/g && 2753 git rev-parse >expect \ 2754 O:z/b O:z/c O:x/d O:x/e O:w/f A:x/g && 2755 test_cmp expect actual 2756 ) 2757' 2758 2759# Testcase 9d, N-fold transitive rename? 2760# (Related to testcase 9c...and 1c and 7e) 2761# Commit O: z/a, y/b, x/c, w/d, v/e, u/f 2762# Commit A: y/{a,b}, w/{c,d}, u/{e,f} 2763# Commit B: z/{a,t}, x/{b,c}, v/{d,e}, u/f 2764# Expected: <see NOTE first> 2765# 2766# NOTE: z/ -> y/ (in commit A) 2767# y/ -> x/ (in commit B) 2768# x/ -> w/ (in commit A) 2769# w/ -> v/ (in commit B) 2770# v/ -> u/ (in commit A) 2771# So, if we add a file to z, say z/t, where should it end up? In u? 2772# What if there's another file or directory named 't' in one of the 2773# intervening directories and/or in u itself? Also, shouldn't the 2774# same logic that places 't' in u/ also move ALL other files to u/? 2775# What if there are file or directory conflicts in any of them? If 2776# we attempted to do N-way (N-fold? N-ary? N-uple?) transitive renames 2777# like this, would the user have any hope of understanding any 2778# conflicts or how their working tree ended up? I think not, so I'm 2779# ruling out N-ary transitive renames for N>1. 2780# 2781# Therefore our expected result is: 2782# z/t, y/a, x/b, w/c, u/d, u/e, u/f 2783# The reason that v/d DOES get transitively renamed to u/d is that u/ isn't 2784# renamed somewhere. A slightly sub-optimal result, but it uses fairly 2785# simple rules that are consistent with what we need for all the other 2786# testcases and simplifies things for the user. 2787 2788test_setup_9d () { 2789 test_create_repo 9d && 2790 ( 2791 cd 9d && 2792 2793 mkdir z y x w v u && 2794 echo a >z/a && 2795 echo b >y/b && 2796 echo c >x/c && 2797 echo d >w/d && 2798 echo e >v/e && 2799 echo f >u/f && 2800 git add z y x w v u && 2801 test_tick && 2802 git commit -m "O" && 2803 2804 git branch O && 2805 git branch A && 2806 git branch B && 2807 2808 git checkout A && 2809 git mv z/a y/ && 2810 git mv x/c w/ && 2811 git mv v/e u/ && 2812 test_tick && 2813 git commit -m "A" && 2814 2815 git checkout B && 2816 echo t >z/t && 2817 git mv y/b x/ && 2818 git mv w/d v/ && 2819 git add z/t && 2820 test_tick && 2821 git commit -m "B" 2822 ) 2823} 2824 2825test_expect_success '9d: N-way transitive rename?' ' 2826 test_setup_9d && 2827 ( 2828 cd 9d && 2829 2830 git checkout A^0 && 2831 2832 git -c merge.directoryRenames=true merge -s recursive B^0 >out && 2833 test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out && 2834 test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out && 2835 test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out && 2836 test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out && 2837 2838 git ls-files -s >out && 2839 test_line_count = 7 out && 2840 git ls-files -o >out && 2841 test_line_count = 1 out && 2842 2843 git rev-parse >actual \ 2844 HEAD:z/t \ 2845 HEAD:y/a HEAD:x/b HEAD:w/c \ 2846 HEAD:u/d HEAD:u/e HEAD:u/f && 2847 git rev-parse >expect \ 2848 B:z/t \ 2849 O:z/a O:y/b O:x/c \ 2850 O:w/d O:v/e A:u/f && 2851 test_cmp expect actual 2852 ) 2853' 2854 2855# Testcase 9e, N-to-1 whammo 2856# (Related to testcase 9c...and 1c and 7e) 2857# Commit O: dir1/{a,b}, dir2/{d,e}, dir3/{g,h}, dirN/{j,k} 2858# Commit A: dir1/{a,b,c,yo}, dir2/{d,e,f,yo}, dir3/{g,h,i,yo}, dirN/{j,k,l,yo} 2859# Commit B: combined/{a,b,d,e,g,h,j,k} 2860# Expected: combined/{a,b,c,d,e,f,g,h,i,j,k,l}, CONFLICT(Nto1) warnings, 2861# dir1/yo, dir2/yo, dir3/yo, dirN/yo 2862 2863test_setup_9e () { 2864 test_create_repo 9e && 2865 ( 2866 cd 9e && 2867 2868 mkdir dir1 dir2 dir3 dirN && 2869 echo a >dir1/a && 2870 echo b >dir1/b && 2871 echo d >dir2/d && 2872 echo e >dir2/e && 2873 echo g >dir3/g && 2874 echo h >dir3/h && 2875 echo j >dirN/j && 2876 echo k >dirN/k && 2877 git add dir* && 2878 test_tick && 2879 git commit -m "O" && 2880 2881 git branch O && 2882 git branch A && 2883 git branch B && 2884 2885 git checkout A && 2886 echo c >dir1/c && 2887 echo yo >dir1/yo && 2888 echo f >dir2/f && 2889 echo yo >dir2/yo && 2890 echo i >dir3/i && 2891 echo yo >dir3/yo && 2892 echo l >dirN/l && 2893 echo yo >dirN/yo && 2894 git add dir* && 2895 test_tick && 2896 git commit -m "A" && 2897 2898 git checkout B && 2899 git mv dir1 combined && 2900 git mv dir2/* combined/ && 2901 git mv dir3/* combined/ && 2902 git mv dirN/* combined/ && 2903 test_tick && 2904 git commit -m "B" 2905 ) 2906} 2907 2908test_expect_success '9e: N-to-1 whammo' ' 2909 test_setup_9e && 2910 ( 2911 cd 9e && 2912 2913 git checkout A^0 && 2914 2915 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && 2916 grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line && 2917 grep -q dir1/yo error_line && 2918 grep -q dir2/yo error_line && 2919 grep -q dir3/yo error_line && 2920 grep -q dirN/yo error_line && 2921 2922 git ls-files -s >out && 2923 test_line_count = 16 out && 2924 git ls-files -u >out && 2925 test_line_count = 0 out && 2926 git ls-files -o >out && 2927 test_line_count = 2 out && 2928 2929 git rev-parse >actual \ 2930 :0:combined/a :0:combined/b :0:combined/c \ 2931 :0:combined/d :0:combined/e :0:combined/f \ 2932 :0:combined/g :0:combined/h :0:combined/i \ 2933 :0:combined/j :0:combined/k :0:combined/l && 2934 git rev-parse >expect \ 2935 O:dir1/a O:dir1/b A:dir1/c \ 2936 O:dir2/d O:dir2/e A:dir2/f \ 2937 O:dir3/g O:dir3/h A:dir3/i \ 2938 O:dirN/j O:dirN/k A:dirN/l && 2939 test_cmp expect actual && 2940 2941 git rev-parse >actual \ 2942 :0:dir1/yo :0:dir2/yo :0:dir3/yo :0:dirN/yo && 2943 git rev-parse >expect \ 2944 A:dir1/yo A:dir2/yo A:dir3/yo A:dirN/yo && 2945 test_cmp expect actual 2946 ) 2947' 2948 2949# Testcase 9f, Renamed directory that only contained immediate subdirs 2950# (Related to testcases 1e & 9g) 2951# Commit O: goal/{a,b}/$more_files 2952# Commit A: priority/{a,b}/$more_files 2953# Commit B: goal/{a,b}/$more_files, goal/c 2954# Expected: priority/{a,b}/$more_files, priority/c 2955 2956test_setup_9f () { 2957 test_create_repo 9f && 2958 ( 2959 cd 9f && 2960 2961 mkdir -p goal/a && 2962 mkdir -p goal/b && 2963 echo foo >goal/a/foo && 2964 echo bar >goal/b/bar && 2965 echo baz >goal/b/baz && 2966 git add goal && 2967 test_tick && 2968 git commit -m "O" && 2969 2970 git branch O && 2971 git branch A && 2972 git branch B && 2973 2974 git checkout A && 2975 git mv goal/ priority && 2976 test_tick && 2977 git commit -m "A" && 2978 2979 git checkout B && 2980 echo c >goal/c && 2981 git add goal/c && 2982 test_tick && 2983 git commit -m "B" 2984 ) 2985} 2986 2987test_expect_success '9f: Renamed directory that only contained immediate subdirs' ' 2988 test_setup_9f && 2989 ( 2990 cd 9f && 2991 2992 git checkout A^0 && 2993 2994 git -c merge.directoryRenames=true merge -s recursive B^0 && 2995 2996 git ls-files -s >out && 2997 test_line_count = 4 out && 2998 2999 git rev-parse >actual \ 3000 HEAD:priority/a/foo \ 3001 HEAD:priority/b/bar \ 3002 HEAD:priority/b/baz \ 3003 HEAD:priority/c && 3004 git rev-parse >expect \ 3005 O:goal/a/foo \ 3006 O:goal/b/bar \ 3007 O:goal/b/baz \ 3008 B:goal/c && 3009 test_cmp expect actual && 3010 test_must_fail git rev-parse HEAD:goal/c 3011 ) 3012' 3013 3014# Testcase 9g, Renamed directory that only contained immediate subdirs, immediate subdirs renamed 3015# (Related to testcases 1e & 9f) 3016# Commit O: goal/{a,b}/$more_files 3017# Commit A: priority/{alpha,bravo}/$more_files 3018# Commit B: goal/{a,b}/$more_files, goal/c 3019# Expected: priority/{alpha,bravo}/$more_files, priority/c 3020# We currently fail this test because the directory renames we detect are 3021# goal/a/ -> priority/alpha/ 3022# goal/b/ -> priority/bravo/ 3023# We do not detect 3024# goal/ -> priority/ 3025# because of no files found within goal/, and the fact that "a" != "alpha" 3026# and "b" != "bravo". But I'm not sure it's really a failure given that 3027# viewpoint... 3028 3029test_setup_9g () { 3030 test_create_repo 9g && 3031 ( 3032 cd 9g && 3033 3034 mkdir -p goal/a && 3035 mkdir -p goal/b && 3036 echo foo >goal/a/foo && 3037 echo bar >goal/b/bar && 3038 echo baz >goal/b/baz && 3039 git add goal && 3040 test_tick && 3041 git commit -m "O" && 3042 3043 git branch O && 3044 git branch A && 3045 git branch B && 3046 3047 git checkout A && 3048 mkdir priority && 3049 git mv goal/a/ priority/alpha && 3050 git mv goal/b/ priority/beta && 3051 rmdir goal/ && 3052 test_tick && 3053 git commit -m "A" && 3054 3055 git checkout B && 3056 echo c >goal/c && 3057 git add goal/c && 3058 test_tick && 3059 git commit -m "B" 3060 ) 3061} 3062 3063test_expect_failure '9g: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' ' 3064 test_setup_9g && 3065 ( 3066 cd 9g && 3067 3068 git checkout A^0 && 3069 3070 git -c merge.directoryRenames=true merge -s recursive B^0 && 3071 3072 git ls-files -s >out && 3073 test_line_count = 4 out && 3074 3075 git rev-parse >actual \ 3076 HEAD:priority/alpha/foo \ 3077 HEAD:priority/beta/bar \ 3078 HEAD:priority/beta/baz \ 3079 HEAD:priority/c && 3080 git rev-parse >expect \ 3081 O:goal/a/foo \ 3082 O:goal/b/bar \ 3083 O:goal/b/baz \ 3084 B:goal/c && 3085 test_cmp expect actual && 3086 test_must_fail git rev-parse HEAD:goal/c 3087 ) 3088' 3089 3090# Testcase 9h, Avoid implicit rename if involved as source on other side 3091# (Extremely closely related to testcase 3a) 3092# Commit O: z/{b,c,d_1} 3093# Commit A: z/{b,c,d_2} 3094# Commit B: y/{b,c}, x/d_1 3095# Expected: y/{b,c}, x/d_2 3096# NOTE: If we applied the z/ -> y/ rename to z/d, then we'd end up with 3097# a rename/rename(1to2) conflict (z/d -> y/d vs. x/d) 3098test_setup_9h () { 3099 test_create_repo 9h && 3100 ( 3101 cd 9h && 3102 3103 mkdir z && 3104 echo b >z/b && 3105 echo c >z/c && 3106 printf "1\n2\n3\n4\n5\n6\n7\n8\nd\n" >z/d && 3107 git add z && 3108 test_tick && 3109 git commit -m "O" && 3110 3111 git branch O && 3112 git branch A && 3113 git branch B && 3114 3115 git checkout A && 3116 test_tick && 3117 echo more >>z/d && 3118 git add z/d && 3119 git commit -m "A" && 3120 3121 git checkout B && 3122 mkdir y && 3123 mkdir x && 3124 git mv z/b y/ && 3125 git mv z/c y/ && 3126 git mv z/d x/ && 3127 rmdir z && 3128 test_tick && 3129 git commit -m "B" 3130 ) 3131} 3132 3133test_expect_success '9h: Avoid dir rename on merely modified path' ' 3134 test_setup_9h && 3135 ( 3136 cd 9h && 3137 3138 git checkout A^0 && 3139 3140 git -c merge.directoryRenames=true merge -s recursive B^0 && 3141 3142 git ls-files -s >out && 3143 test_line_count = 3 out && 3144 3145 git rev-parse >actual \ 3146 HEAD:y/b HEAD:y/c HEAD:x/d && 3147 git rev-parse >expect \ 3148 O:z/b O:z/c A:z/d && 3149 test_cmp expect actual 3150 ) 3151' 3152 3153########################################################################### 3154# Rules suggested by section 9: 3155# 3156# If the other side of history did a directory rename to a path that your 3157# side renamed away, then ignore that particular rename from the other 3158# side of history for any implicit directory renames. 3159########################################################################### 3160 3161########################################################################### 3162# SECTION 10: Handling untracked files 3163# 3164# unpack_trees(), upon which the recursive merge algorithm is based, aborts 3165# the operation if untracked or dirty files would be deleted or overwritten 3166# by the merge. Unfortunately, unpack_trees() does not understand renames, 3167# and if it doesn't abort, then it muddies up the working directory before 3168# we even get to the point of detecting renames, so we need some special 3169# handling, at least in the case of directory renames. 3170########################################################################### 3171 3172# Testcase 10a, Overwrite untracked: normal rename/delete 3173# Commit O: z/{b,c_1} 3174# Commit A: z/b + untracked z/c + untracked z/d 3175# Commit B: z/{b,d_1} 3176# Expected: Aborted Merge + 3177# ERROR_MSG(untracked working tree files would be overwritten by merge) 3178 3179test_setup_10a () { 3180 test_create_repo 10a && 3181 ( 3182 cd 10a && 3183 3184 mkdir z && 3185 echo b >z/b && 3186 echo c >z/c && 3187 git add z && 3188 test_tick && 3189 git commit -m "O" && 3190 3191 git branch O && 3192 git branch A && 3193 git branch B && 3194 3195 git checkout A && 3196 git rm z/c && 3197 test_tick && 3198 git commit -m "A" && 3199 3200 git checkout B && 3201 git mv z/c z/d && 3202 test_tick && 3203 git commit -m "B" 3204 ) 3205} 3206 3207test_expect_success '10a: Overwrite untracked with normal rename/delete' ' 3208 test_setup_10a && 3209 ( 3210 cd 10a && 3211 3212 git checkout A^0 && 3213 echo very >z/c && 3214 echo important >z/d && 3215 3216 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3217 test_path_is_missing .git/MERGE_HEAD && 3218 test_i18ngrep "The following untracked working tree files would be overwritten by merge" err && 3219 3220 git ls-files -s >out && 3221 test_line_count = 1 out && 3222 git ls-files -o >out && 3223 test_line_count = 4 out && 3224 3225 echo very >expect && 3226 test_cmp expect z/c && 3227 3228 echo important >expect && 3229 test_cmp expect z/d && 3230 3231 git rev-parse HEAD:z/b >actual && 3232 git rev-parse O:z/b >expect && 3233 test_cmp expect actual 3234 ) 3235' 3236 3237# Testcase 10b, Overwrite untracked: dir rename + delete 3238# Commit O: z/{b,c_1} 3239# Commit A: y/b + untracked y/{c,d,e} 3240# Commit B: z/{b,d_1,e} 3241# Expected: Failed Merge; y/b + untracked y/c + untracked y/d on disk + 3242# z/c_1 -> z/d_1 rename recorded at stage 3 for y/d + 3243# ERROR_MSG(refusing to lose untracked file at 'y/d') 3244 3245test_setup_10b () { 3246 test_create_repo 10b && 3247 ( 3248 cd 10b && 3249 3250 mkdir z && 3251 echo b >z/b && 3252 echo c >z/c && 3253 git add z && 3254 test_tick && 3255 git commit -m "O" && 3256 3257 git branch O && 3258 git branch A && 3259 git branch B && 3260 3261 git checkout A && 3262 git rm z/c && 3263 git mv z/ y/ && 3264 test_tick && 3265 git commit -m "A" && 3266 3267 git checkout B && 3268 git mv z/c z/d && 3269 echo e >z/e && 3270 git add z/e && 3271 test_tick && 3272 git commit -m "B" 3273 ) 3274} 3275 3276test_expect_success '10b: Overwrite untracked with dir rename + delete' ' 3277 test_setup_10b && 3278 ( 3279 cd 10b && 3280 3281 git checkout A^0 && 3282 echo very >y/c && 3283 echo important >y/d && 3284 echo contents >y/e && 3285 3286 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3287 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3288 then 3289 test_path_is_missing .git/MERGE_HEAD && 3290 test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err && 3291 3292 git ls-files -s >out && 3293 test_line_count = 1 out && 3294 git ls-files -u >out && 3295 test_line_count = 0 out && 3296 git ls-files -o >out && 3297 test_line_count = 5 out 3298 else 3299 test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out && 3300 test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out && 3301 3302 git ls-files -s >out && 3303 test_line_count = 3 out && 3304 git ls-files -u >out && 3305 test_line_count = 2 out && 3306 git ls-files -o >out && 3307 test_line_count = 5 out && 3308 3309 git rev-parse >actual \ 3310 :0:y/b :3:y/d :3:y/e && 3311 git rev-parse >expect \ 3312 O:z/b O:z/c B:z/e && 3313 test_cmp expect actual 3314 fi && 3315 3316 echo very >expect && 3317 test_cmp expect y/c && 3318 3319 echo important >expect && 3320 test_cmp expect y/d && 3321 3322 echo contents >expect && 3323 test_cmp expect y/e 3324 ) 3325' 3326 3327# Testcase 10c, Overwrite untracked: dir rename/rename(1to2) 3328# Commit O: z/{a,b}, x/{c,d} 3329# Commit A: y/{a,b}, w/c, x/d + different untracked y/c 3330# Commit B: z/{a,b,c}, x/d 3331# Expected: Failed Merge; y/{a,b} + x/d + untracked y/c + 3332# CONFLICT(rename/rename) x/c -> w/c vs y/c + 3333# y/c~B^0 + 3334# ERROR_MSG(Refusing to lose untracked file at y/c) 3335 3336test_setup_10c () { 3337 test_create_repo 10c_$1 && 3338 ( 3339 cd 10c_$1 && 3340 3341 mkdir z x && 3342 echo a >z/a && 3343 echo b >z/b && 3344 echo c >x/c && 3345 echo d >x/d && 3346 git add z x && 3347 test_tick && 3348 git commit -m "O" && 3349 3350 git branch O && 3351 git branch A && 3352 git branch B && 3353 3354 git checkout A && 3355 mkdir w && 3356 git mv x/c w/c && 3357 git mv z/ y/ && 3358 test_tick && 3359 git commit -m "A" && 3360 3361 git checkout B && 3362 git mv x/c z/ && 3363 test_tick && 3364 git commit -m "B" 3365 ) 3366} 3367 3368test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' ' 3369 test_setup_10c 1 && 3370 ( 3371 cd 10c_1 && 3372 3373 git checkout A^0 && 3374 echo important >y/c && 3375 3376 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3377 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3378 then 3379 test_path_is_missing .git/MERGE_HEAD && 3380 test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err && 3381 3382 git ls-files -s >out && 3383 test_line_count = 4 out && 3384 git ls-files -u >out && 3385 test_line_count = 0 out && 3386 git ls-files -o >out && 3387 test_line_count = 3 out 3388 else 3389 test_i18ngrep "CONFLICT (rename/rename)" out && 3390 test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out && 3391 3392 git ls-files -s >out && 3393 test_line_count = 6 out && 3394 git ls-files -u >out && 3395 test_line_count = 3 out && 3396 git ls-files -o >out && 3397 test_line_count = 3 out && 3398 3399 git rev-parse >actual \ 3400 :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :3:y/c && 3401 git rev-parse >expect \ 3402 O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c && 3403 test_cmp expect actual && 3404 3405 git hash-object y/c~B^0 >actual && 3406 git rev-parse O:x/c >expect && 3407 test_cmp expect actual 3408 fi && 3409 3410 echo important >expect && 3411 test_cmp expect y/c 3412 ) 3413' 3414 3415test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), other direction' ' 3416 test_setup_10c 2 && 3417 ( 3418 cd 10c_2 && 3419 3420 git reset --hard && 3421 git clean -fdqx && 3422 3423 git checkout B^0 && 3424 mkdir y && 3425 echo important >y/c && 3426 3427 test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err && 3428 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3429 then 3430 test_path_is_missing .git/MERGE_HEAD && 3431 test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err && 3432 3433 git ls-files -s >out && 3434 test_line_count = 4 out && 3435 git ls-files -u >out && 3436 test_line_count = 0 out && 3437 git ls-files -o >out && 3438 test_line_count = 3 out 3439 else 3440 test_i18ngrep "CONFLICT (rename/rename)" out && 3441 test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out && 3442 3443 git ls-files -s >out && 3444 test_line_count = 6 out && 3445 git ls-files -u >out && 3446 test_line_count = 3 out && 3447 git ls-files -o >out && 3448 test_line_count = 3 out && 3449 3450 git rev-parse >actual \ 3451 :0:y/a :0:y/b :0:x/d :1:x/c :3:w/c :2:y/c && 3452 git rev-parse >expect \ 3453 O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c && 3454 test_cmp expect actual && 3455 3456 git hash-object y/c~HEAD >actual && 3457 git rev-parse O:x/c >expect && 3458 test_cmp expect actual 3459 fi && 3460 3461 echo important >expect && 3462 test_cmp expect y/c 3463 ) 3464' 3465 3466# Testcase 10d, Delete untracked w/ dir rename/rename(2to1) 3467# Commit O: z/{a,b,c_1}, x/{d,e,f_2} 3468# Commit A: y/{a,b}, x/{d,e,f_2,wham_1} + untracked y/wham 3469# Commit B: z/{a,b,c_1,wham_2}, y/{d,e} 3470# Expected: Failed Merge; y/{a,b,d,e} + untracked y/{wham,wham~merged}+ 3471# CONFLICT(rename/rename) z/c_1 vs x/f_2 -> y/wham 3472# ERROR_MSG(Refusing to lose untracked file at y/wham) 3473 3474test_setup_10d () { 3475 test_create_repo 10d && 3476 ( 3477 cd 10d && 3478 3479 mkdir z x && 3480 echo a >z/a && 3481 echo b >z/b && 3482 echo c >z/c && 3483 echo d >x/d && 3484 echo e >x/e && 3485 echo f >x/f && 3486 git add z x && 3487 test_tick && 3488 git commit -m "O" && 3489 3490 git branch O && 3491 git branch A && 3492 git branch B && 3493 3494 git checkout A && 3495 git mv z/c x/wham && 3496 git mv z/ y/ && 3497 test_tick && 3498 git commit -m "A" && 3499 3500 git checkout B && 3501 git mv x/f z/wham && 3502 git mv x/ y/ && 3503 test_tick && 3504 git commit -m "B" 3505 ) 3506} 3507 3508test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' ' 3509 test_setup_10d && 3510 ( 3511 cd 10d && 3512 3513 git checkout A^0 && 3514 echo important >y/wham && 3515 3516 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3517 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3518 then 3519 test_path_is_missing .git/MERGE_HEAD && 3520 test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err && 3521 3522 git ls-files -s >out && 3523 test_line_count = 6 out && 3524 git ls-files -u >out && 3525 test_line_count = 0 out && 3526 git ls-files -o >out && 3527 test_line_count = 3 out 3528 else 3529 test_i18ngrep "CONFLICT (rename/rename)" out && 3530 test_i18ngrep "Refusing to lose untracked file at y/wham" out && 3531 3532 git ls-files -s >out && 3533 test_line_count = 6 out && 3534 git ls-files -u >out && 3535 test_line_count = 2 out && 3536 git ls-files -o >out && 3537 test_line_count = 3 out && 3538 3539 git rev-parse >actual \ 3540 :0:y/a :0:y/b :0:y/d :0:y/e :2:y/wham :3:y/wham && 3541 git rev-parse >expect \ 3542 O:z/a O:z/b O:x/d O:x/e O:z/c O:x/f && 3543 test_cmp expect actual && 3544 3545 test_must_fail git rev-parse :1:y/wham && 3546 3547 # Test that two-way merge in y/wham~merged is as expected 3548 git cat-file -p :2:y/wham >expect && 3549 git cat-file -p :3:y/wham >other && 3550 >empty && 3551 test_must_fail git merge-file \ 3552 -L "HEAD" \ 3553 -L "" \ 3554 -L "B^0" \ 3555 expect empty other && 3556 test_cmp expect y/wham~merged 3557 fi && 3558 3559 echo important >expect && 3560 test_cmp expect y/wham 3561 ) 3562' 3563 3564# Testcase 10e, Does git complain about untracked file that's not in the way? 3565# Commit O: z/{a,b} 3566# Commit A: y/{a,b} + untracked z/c 3567# Commit B: z/{a,b,c} 3568# Expected: y/{a,b,c} + untracked z/c 3569 3570test_setup_10e () { 3571 test_create_repo 10e && 3572 ( 3573 cd 10e && 3574 3575 mkdir z && 3576 echo a >z/a && 3577 echo b >z/b && 3578 git add z && 3579 test_tick && 3580 git commit -m "O" && 3581 3582 git branch O && 3583 git branch A && 3584 git branch B && 3585 3586 git checkout A && 3587 git mv z/ y/ && 3588 test_tick && 3589 git commit -m "A" && 3590 3591 git checkout B && 3592 echo c >z/c && 3593 git add z/c && 3594 test_tick && 3595 git commit -m "B" 3596 ) 3597} 3598 3599test_expect_merge_algorithm failure success '10e: Does git complain about untracked file that is not really in the way?' ' 3600 test_setup_10e && 3601 ( 3602 cd 10e && 3603 3604 git checkout A^0 && 3605 mkdir z && 3606 echo random >z/c && 3607 3608 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3609 test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err && 3610 3611 git ls-files -s >out && 3612 test_line_count = 3 out && 3613 git ls-files -u >out && 3614 test_line_count = 0 out && 3615 git ls-files -o >out && 3616 test_line_count = 3 out && 3617 3618 git rev-parse >actual \ 3619 :0:y/a :0:y/b :0:y/c && 3620 git rev-parse >expect \ 3621 O:z/a O:z/b B:z/c && 3622 test_cmp expect actual && 3623 3624 echo random >expect && 3625 test_cmp expect z/c 3626 ) 3627' 3628 3629########################################################################### 3630# SECTION 11: Handling dirty (not up-to-date) files 3631# 3632# unpack_trees(), upon which the recursive merge algorithm is based, aborts 3633# the operation if untracked or dirty files would be deleted or overwritten 3634# by the merge. Unfortunately, unpack_trees() does not understand renames, 3635# and if it doesn't abort, then it muddies up the working directory before 3636# we even get to the point of detecting renames, so we need some special 3637# handling. This was true even of normal renames, but there are additional 3638# codepaths that need special handling with directory renames. Add 3639# testcases for both renamed-by-directory-rename-detection and standard 3640# rename cases. 3641########################################################################### 3642 3643# Testcase 11a, Avoid losing dirty contents with simple rename 3644# Commit O: z/{a,b_v1}, 3645# Commit A: z/{a,c_v1}, and z/c_v1 has uncommitted mods 3646# Commit B: z/{a,b_v2} 3647# Expected: ERROR_MSG(Refusing to lose dirty file at z/c) + 3648# z/a, staged version of z/c has sha1sum matching B:z/b_v2, 3649# z/c~HEAD with contents of B:z/b_v2, 3650# z/c with uncommitted mods on top of A:z/c_v1 3651 3652test_setup_11a () { 3653 test_create_repo 11a && 3654 ( 3655 cd 11a && 3656 3657 mkdir z && 3658 echo a >z/a && 3659 test_seq 1 10 >z/b && 3660 git add z && 3661 test_tick && 3662 git commit -m "O" && 3663 3664 git branch O && 3665 git branch A && 3666 git branch B && 3667 3668 git checkout A && 3669 git mv z/b z/c && 3670 test_tick && 3671 git commit -m "A" && 3672 3673 git checkout B && 3674 echo 11 >>z/b && 3675 git add z/b && 3676 test_tick && 3677 git commit -m "B" 3678 ) 3679} 3680 3681test_expect_success '11a: Avoid losing dirty contents with simple rename' ' 3682 test_setup_11a && 3683 ( 3684 cd 11a && 3685 3686 git checkout A^0 && 3687 echo stuff >>z/c && 3688 3689 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3690 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3691 then 3692 test_path_is_missing .git/MERGE_HEAD && 3693 test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err 3694 else 3695 test_i18ngrep "Refusing to lose dirty file at z/c" out && 3696 3697 git ls-files -s >out && 3698 test_line_count = 2 out && 3699 git ls-files -u >out && 3700 test_line_count = 1 out && 3701 git ls-files -o >out && 3702 test_line_count = 3 out && 3703 3704 git rev-parse >actual \ 3705 :0:z/a :2:z/c && 3706 git rev-parse >expect \ 3707 O:z/a B:z/b && 3708 test_cmp expect actual && 3709 3710 git hash-object z/c~HEAD >actual && 3711 git rev-parse B:z/b >expect && 3712 test_cmp expect actual 3713 fi && 3714 3715 test_seq 1 10 >expected && 3716 echo stuff >>expected && 3717 test_cmp expected z/c 3718 3719 ) 3720' 3721 3722# Testcase 11b, Avoid losing dirty file involved in directory rename 3723# Commit O: z/a, x/{b,c_v1} 3724# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods 3725# Commit B: y/a, x/{b,c_v2} 3726# Expected: y/{a,c_v2}, x/b, z/c_v1 with uncommitted mods untracked, 3727# ERROR_MSG(Refusing to lose dirty file at z/c) 3728 3729 3730test_setup_11b () { 3731 test_create_repo 11b && 3732 ( 3733 cd 11b && 3734 3735 mkdir z x && 3736 echo a >z/a && 3737 echo b >x/b && 3738 test_seq 1 10 >x/c && 3739 git add z x && 3740 test_tick && 3741 git commit -m "O" && 3742 3743 git branch O && 3744 git branch A && 3745 git branch B && 3746 3747 git checkout A && 3748 git mv x/c z/c && 3749 test_tick && 3750 git commit -m "A" && 3751 3752 git checkout B && 3753 git mv z y && 3754 echo 11 >>x/c && 3755 git add x/c && 3756 test_tick && 3757 git commit -m "B" 3758 ) 3759} 3760 3761test_expect_success '11b: Avoid losing dirty file involved in directory rename' ' 3762 test_setup_11b && 3763 ( 3764 cd 11b && 3765 3766 git checkout A^0 && 3767 echo stuff >>z/c && 3768 3769 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3770 then 3771 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3772 test_path_is_missing .git/MERGE_HEAD && 3773 test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err 3774 else 3775 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3776 test_i18ngrep "Refusing to lose dirty file at z/c" out && 3777 3778 git ls-files -s >out && 3779 test_line_count = 3 out && 3780 git ls-files -u >out && 3781 test_line_count = 0 out && 3782 git ls-files -m >out && 3783 test_line_count = 0 out && 3784 git ls-files -o >out && 3785 test_line_count = 3 out && 3786 3787 git rev-parse >actual \ 3788 :0:x/b :0:y/a :0:y/c && 3789 git rev-parse >expect \ 3790 O:x/b O:z/a B:x/c && 3791 test_cmp expect actual && 3792 3793 git hash-object y/c >actual && 3794 git rev-parse B:x/c >expect && 3795 test_cmp expect actual 3796 fi && 3797 3798 grep -q stuff z/c && 3799 test_seq 1 10 >expected && 3800 echo stuff >>expected && 3801 test_cmp expected z/c 3802 ) 3803' 3804 3805# Testcase 11c, Avoid losing not-up-to-date with rename + D/F conflict 3806# Commit O: y/a, x/{b,c_v1} 3807# Commit A: y/{a,c_v1}, x/b, and y/c_v1 has uncommitted mods 3808# Commit B: y/{a,c/d}, x/{b,c_v2} 3809# Expected: Abort_msg("following files would be overwritten by merge") + 3810# y/c left untouched (still has uncommitted mods) 3811 3812test_setup_11c () { 3813 test_create_repo 11c && 3814 ( 3815 cd 11c && 3816 3817 mkdir y x && 3818 echo a >y/a && 3819 echo b >x/b && 3820 test_seq 1 10 >x/c && 3821 git add y x && 3822 test_tick && 3823 git commit -m "O" && 3824 3825 git branch O && 3826 git branch A && 3827 git branch B && 3828 3829 git checkout A && 3830 git mv x/c y/c && 3831 test_tick && 3832 git commit -m "A" && 3833 3834 git checkout B && 3835 mkdir y/c && 3836 echo d >y/c/d && 3837 echo 11 >>x/c && 3838 git add x/c y/c/d && 3839 test_tick && 3840 git commit -m "B" 3841 ) 3842} 3843 3844test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict' ' 3845 test_setup_11c && 3846 ( 3847 cd 11c && 3848 3849 git checkout A^0 && 3850 echo stuff >>y/c && 3851 3852 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3853 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3854 then 3855 test_path_is_missing .git/MERGE_HEAD && 3856 test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err 3857 else 3858 test_i18ngrep "following files would be overwritten by merge" err 3859 fi && 3860 3861 grep -q stuff y/c && 3862 test_seq 1 10 >expected && 3863 echo stuff >>expected && 3864 test_cmp expected y/c && 3865 3866 git ls-files -s >out && 3867 test_line_count = 3 out && 3868 git ls-files -u >out && 3869 test_line_count = 0 out && 3870 git ls-files -m >out && 3871 test_line_count = 1 out && 3872 git ls-files -o >out && 3873 test_line_count = 3 out 3874 ) 3875' 3876 3877# Testcase 11d, Avoid losing not-up-to-date with rename + D/F conflict 3878# Commit O: z/a, x/{b,c_v1} 3879# Commit A: z/{a,c_v1}, x/b, and z/c_v1 has uncommitted mods 3880# Commit B: y/{a,c/d}, x/{b,c_v2} 3881# Expected: D/F: y/c_v2 vs y/c/d) + 3882# Warning_Msg("Refusing to lose dirty file at z/c) + 3883# y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods 3884 3885test_setup_11d () { 3886 test_create_repo 11d && 3887 ( 3888 cd 11d && 3889 3890 mkdir z x && 3891 echo a >z/a && 3892 echo b >x/b && 3893 test_seq 1 10 >x/c && 3894 git add z x && 3895 test_tick && 3896 git commit -m "O" && 3897 3898 git branch O && 3899 git branch A && 3900 git branch B && 3901 3902 git checkout A && 3903 git mv x/c z/c && 3904 test_tick && 3905 git commit -m "A" && 3906 3907 git checkout B && 3908 git mv z y && 3909 mkdir y/c && 3910 echo d >y/c/d && 3911 echo 11 >>x/c && 3912 git add x/c y/c/d && 3913 test_tick && 3914 git commit -m "B" 3915 ) 3916} 3917 3918test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict' ' 3919 test_setup_11d && 3920 ( 3921 cd 11d && 3922 3923 git checkout A^0 && 3924 echo stuff >>z/c && 3925 3926 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 3927 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 3928 then 3929 test_path_is_missing .git/MERGE_HEAD && 3930 test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err 3931 else 3932 test_i18ngrep "Refusing to lose dirty file at z/c" out && 3933 3934 git ls-files -s >out && 3935 test_line_count = 4 out && 3936 git ls-files -u >out && 3937 test_line_count = 1 out && 3938 git ls-files -o >out && 3939 test_line_count = 4 out && 3940 3941 git rev-parse >actual \ 3942 :0:x/b :0:y/a :0:y/c/d :3:y/c && 3943 git rev-parse >expect \ 3944 O:x/b O:z/a B:y/c/d B:x/c && 3945 test_cmp expect actual && 3946 3947 git hash-object y/c~HEAD >actual && 3948 git rev-parse B:x/c >expect && 3949 test_cmp expect actual 3950 fi && 3951 3952 grep -q stuff z/c && 3953 test_seq 1 10 >expected && 3954 echo stuff >>expected && 3955 test_cmp expected z/c 3956 ) 3957' 3958 3959# Testcase 11e, Avoid deleting not-up-to-date with dir rename/rename(1to2)/add 3960# Commit O: z/{a,b}, x/{c_1,d} 3961# Commit A: y/{a,b,c_2}, x/d, w/c_1, and y/c_2 has uncommitted mods 3962# Commit B: z/{a,b,c_1}, x/d 3963# Expected: Failed Merge; y/{a,b} + x/d + 3964# CONFLICT(rename/rename) x/c_1 -> w/c_1 vs y/c_1 + 3965# ERROR_MSG(Refusing to lose dirty file at y/c) 3966# y/c~B^0 has O:x/c_1 contents 3967# y/c~HEAD has A:y/c_2 contents 3968# y/c has dirty file from before merge 3969 3970test_setup_11e () { 3971 test_create_repo 11e && 3972 ( 3973 cd 11e && 3974 3975 mkdir z x && 3976 echo a >z/a && 3977 echo b >z/b && 3978 echo c >x/c && 3979 echo d >x/d && 3980 git add z x && 3981 test_tick && 3982 git commit -m "O" && 3983 3984 git branch O && 3985 git branch A && 3986 git branch B && 3987 3988 git checkout A && 3989 git mv z/ y/ && 3990 echo different >y/c && 3991 mkdir w && 3992 git mv x/c w/ && 3993 git add y/c && 3994 test_tick && 3995 git commit -m "A" && 3996 3997 git checkout B && 3998 git mv x/c z/ && 3999 test_tick && 4000 git commit -m "B" 4001 ) 4002} 4003 4004test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' ' 4005 test_setup_11e && 4006 ( 4007 cd 11e && 4008 4009 git checkout A^0 && 4010 echo mods >>y/c && 4011 4012 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 4013 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 4014 then 4015 test_path_is_missing .git/MERGE_HEAD && 4016 test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err 4017 else 4018 test_i18ngrep "CONFLICT (rename/rename)" out && 4019 test_i18ngrep "Refusing to lose dirty file at y/c" out && 4020 4021 git ls-files -s >out && 4022 test_line_count = 7 out && 4023 git ls-files -u >out && 4024 test_line_count = 4 out && 4025 git ls-files -o >out && 4026 test_line_count = 3 out && 4027 4028 git rev-parse >actual \ 4029 :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :2:y/c :3:y/c && 4030 git rev-parse >expect \ 4031 O:z/a O:z/b O:x/d O:x/c O:x/c A:y/c O:x/c && 4032 test_cmp expect actual && 4033 4034 # See if y/c~merged has expected contents; requires manually 4035 # doing the expected file merge 4036 git cat-file -p A:y/c >c1 && 4037 git cat-file -p B:z/c >c2 && 4038 >empty && 4039 test_must_fail git merge-file \ 4040 -L "HEAD" \ 4041 -L "" \ 4042 -L "B^0" \ 4043 c1 empty c2 && 4044 test_cmp c1 y/c~merged 4045 fi && 4046 4047 echo different >expected && 4048 echo mods >>expected && 4049 test_cmp expected y/c 4050 ) 4051' 4052 4053# Testcase 11f, Avoid deleting not-up-to-date w/ dir rename/rename(2to1) 4054# Commit O: z/{a,b}, x/{c_1,d_2} 4055# Commit A: y/{a,b,wham_1}, x/d_2, except y/wham has uncommitted mods 4056# Commit B: z/{a,b,wham_2}, x/c_1 4057# Expected: Failed Merge; y/{a,b} + untracked y/{wham~merged} + 4058# y/wham with dirty changes from before merge + 4059# CONFLICT(rename/rename) x/c vs x/d -> y/wham 4060# ERROR_MSG(Refusing to lose dirty file at y/wham) 4061 4062test_setup_11f () { 4063 test_create_repo 11f && 4064 ( 4065 cd 11f && 4066 4067 mkdir z x && 4068 echo a >z/a && 4069 echo b >z/b && 4070 test_seq 1 10 >x/c && 4071 echo d >x/d && 4072 git add z x && 4073 test_tick && 4074 git commit -m "O" && 4075 4076 git branch O && 4077 git branch A && 4078 git branch B && 4079 4080 git checkout A && 4081 git mv z/ y/ && 4082 git mv x/c y/wham && 4083 test_tick && 4084 git commit -m "A" && 4085 4086 git checkout B && 4087 git mv x/d z/wham && 4088 test_tick && 4089 git commit -m "B" 4090 ) 4091} 4092 4093test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to1)' ' 4094 test_setup_11f && 4095 ( 4096 cd 11f && 4097 4098 git checkout A^0 && 4099 echo important >>y/wham && 4100 4101 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 4102 if test "$GIT_TEST_MERGE_ALGORITHM" = ort 4103 then 4104 test_path_is_missing .git/MERGE_HEAD && 4105 test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err 4106 else 4107 test_i18ngrep "CONFLICT (rename/rename)" out && 4108 test_i18ngrep "Refusing to lose dirty file at y/wham" out && 4109 4110 git ls-files -s >out && 4111 test_line_count = 4 out && 4112 git ls-files -u >out && 4113 test_line_count = 2 out && 4114 git ls-files -o >out && 4115 test_line_count = 3 out && 4116 4117 test_must_fail git rev-parse :1:y/wham && 4118 4119 git rev-parse >actual \ 4120 :0:y/a :0:y/b :2:y/wham :3:y/wham && 4121 git rev-parse >expect \ 4122 O:z/a O:z/b O:x/c O:x/d && 4123 test_cmp expect actual && 4124 4125 # Test that two-way merge in y/wham~merged is as expected 4126 git cat-file -p :2:y/wham >expect && 4127 git cat-file -p :3:y/wham >other && 4128 >empty && 4129 test_must_fail git merge-file \ 4130 -L "HEAD" \ 4131 -L "" \ 4132 -L "B^0" \ 4133 expect empty other && 4134 test_cmp expect y/wham~merged 4135 fi && 4136 4137 test_seq 1 10 >expected && 4138 echo important >>expected && 4139 test_cmp expected y/wham 4140 ) 4141' 4142 4143########################################################################### 4144# SECTION 12: Everything else 4145# 4146# Tests suggested by others. Tests added after implementation completed 4147# and submitted. Grab bag. 4148########################################################################### 4149 4150# Testcase 12a, Moving one directory hierarchy into another 4151# (Related to testcase 9a) 4152# Commit O: node1/{leaf1,leaf2}, node2/{leaf3,leaf4} 4153# Commit A: node1/{leaf1,leaf2,node2/{leaf3,leaf4}} 4154# Commit B: node1/{leaf1,leaf2,leaf5}, node2/{leaf3,leaf4,leaf6} 4155# Expected: node1/{leaf1,leaf2,leaf5,node2/{leaf3,leaf4,leaf6}} 4156 4157test_setup_12a () { 4158 test_create_repo 12a && 4159 ( 4160 cd 12a && 4161 4162 mkdir -p node1 node2 && 4163 echo leaf1 >node1/leaf1 && 4164 echo leaf2 >node1/leaf2 && 4165 echo leaf3 >node2/leaf3 && 4166 echo leaf4 >node2/leaf4 && 4167 git add node1 node2 && 4168 test_tick && 4169 git commit -m "O" && 4170 4171 git branch O && 4172 git branch A && 4173 git branch B && 4174 4175 git checkout A && 4176 git mv node2/ node1/ && 4177 test_tick && 4178 git commit -m "A" && 4179 4180 git checkout B && 4181 echo leaf5 >node1/leaf5 && 4182 echo leaf6 >node2/leaf6 && 4183 git add node1 node2 && 4184 test_tick && 4185 git commit -m "B" 4186 ) 4187} 4188 4189test_expect_success '12a: Moving one directory hierarchy into another' ' 4190 test_setup_12a && 4191 ( 4192 cd 12a && 4193 4194 git checkout A^0 && 4195 4196 git -c merge.directoryRenames=true merge -s recursive B^0 && 4197 4198 git ls-files -s >out && 4199 test_line_count = 6 out && 4200 4201 git rev-parse >actual \ 4202 HEAD:node1/leaf1 HEAD:node1/leaf2 HEAD:node1/leaf5 \ 4203 HEAD:node1/node2/leaf3 \ 4204 HEAD:node1/node2/leaf4 \ 4205 HEAD:node1/node2/leaf6 && 4206 git rev-parse >expect \ 4207 O:node1/leaf1 O:node1/leaf2 B:node1/leaf5 \ 4208 O:node2/leaf3 \ 4209 O:node2/leaf4 \ 4210 B:node2/leaf6 && 4211 test_cmp expect actual 4212 ) 4213' 4214 4215# Testcase 12b1, Moving two directory hierarchies into each other 4216# (Related to testcases 1c and 12c) 4217# Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4} 4218# Commit A: node1/{leaf1, leaf2, node2/{leaf3, leaf4}} 4219# Commit B: node2/{leaf3, leaf4, node1/{leaf1, leaf2}} 4220# Expected: node1/node2/{leaf3, leaf4} 4221# node2/node1/{leaf1, leaf2} 4222# NOTE: If there were new files added to the old node1/ or node2/ directories, 4223# then we would need to detect renames for those directories and would 4224# find that: 4225# commit A renames node2/ -> node1/node2/ 4226# commit B renames node1/ -> node2/node1/ 4227# Applying those directory renames to the initial result (making all 4228# four paths experience a transitive renaming), yields 4229# node1/node2/node1/{leaf1, leaf2} 4230# node2/node1/node2/{leaf3, leaf4} 4231# as the result. It may be really weird to have two directories 4232# rename each other, but simple rules give weird results when given 4233# weird inputs. HOWEVER, the "If" at the beginning of those NOTE was 4234# false; there were no new files added and thus there is no directory 4235# rename detection to perform. As such, we just have simple renames 4236# and the expected answer is: 4237# node1/node2/{leaf3, leaf4} 4238# node2/node1/{leaf1, leaf2} 4239 4240test_setup_12b1 () { 4241 test_create_repo 12b1 && 4242 ( 4243 cd 12b1 && 4244 4245 mkdir -p node1 node2 && 4246 echo leaf1 >node1/leaf1 && 4247 echo leaf2 >node1/leaf2 && 4248 echo leaf3 >node2/leaf3 && 4249 echo leaf4 >node2/leaf4 && 4250 git add node1 node2 && 4251 test_tick && 4252 git commit -m "O" && 4253 4254 git branch O && 4255 git branch A && 4256 git branch B && 4257 4258 git checkout A && 4259 git mv node2/ node1/ && 4260 test_tick && 4261 git commit -m "A" && 4262 4263 git checkout B && 4264 git mv node1/ node2/ && 4265 test_tick && 4266 git commit -m "B" 4267 ) 4268} 4269 4270test_expect_merge_algorithm failure success '12b1: Moving two directory hierarchies into each other' ' 4271 test_setup_12b1 && 4272 ( 4273 cd 12b1 && 4274 4275 git checkout A^0 && 4276 4277 git -c merge.directoryRenames=true merge -s recursive B^0 && 4278 4279 git ls-files -s >out && 4280 test_line_count = 4 out && 4281 4282 git rev-parse >actual \ 4283 HEAD:node2/node1/leaf1 \ 4284 HEAD:node2/node1/leaf2 \ 4285 HEAD:node1/node2/leaf3 \ 4286 HEAD:node1/node2/leaf4 && 4287 git rev-parse >expect \ 4288 O:node1/leaf1 \ 4289 O:node1/leaf2 \ 4290 O:node2/leaf3 \ 4291 O:node2/leaf4 && 4292 test_cmp expect actual 4293 ) 4294' 4295 4296# Testcase 12b2, Moving two directory hierarchies into each other 4297# (Related to testcases 1c and 12c) 4298# Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4} 4299# Commit A: node1/{leaf1, leaf2, leaf5, node2/{leaf3, leaf4}} 4300# Commit B: node2/{leaf3, leaf4, leaf6, node1/{leaf1, leaf2}} 4301# Expected: node1/node2/{node1/{leaf1, leaf2}, leaf6} 4302# node2/node1/{node2/{leaf3, leaf4}, leaf5} 4303# NOTE: Without directory renames, we would expect 4304# A: node2/leaf3 -> node1/node2/leaf3 4305# A: node2/leaf1 -> node1/node2/leaf4 4306# A: Adds node1/leaf5 4307# B: node1/leaf1 -> node2/node1/leaf1 4308# B: node1/leaf2 -> node2/node1/leaf2 4309# B: Adds node2/leaf6 4310# with directory rename detection, we note that 4311# commit A renames node2/ -> node1/node2/ 4312# commit B renames node1/ -> node2/node1/ 4313# therefore, applying A's directory rename to the paths added in B gives: 4314# B: node1/leaf1 -> node1/node2/node1/leaf1 4315# B: node1/leaf2 -> node1/node2/node1/leaf2 4316# B: Adds node1/node2/leaf6 4317# and applying B's directory rename to the paths added in A gives: 4318# A: node2/leaf3 -> node2/node1/node2/leaf3 4319# A: node2/leaf1 -> node2/node1/node2/leaf4 4320# A: Adds node2/node1/leaf5 4321# resulting in the expected 4322# node1/node2/{node1/{leaf1, leaf2}, leaf6} 4323# node2/node1/{node2/{leaf3, leaf4}, leaf5} 4324# 4325# You may ask, is it weird to have two directories rename each other? 4326# To which, I can do no more than shrug my shoulders and say that 4327# even simple rules give weird results when given weird inputs. 4328 4329test_setup_12b2 () { 4330 test_create_repo 12b2 && 4331 ( 4332 cd 12b2 && 4333 4334 mkdir -p node1 node2 && 4335 echo leaf1 >node1/leaf1 && 4336 echo leaf2 >node1/leaf2 && 4337 echo leaf3 >node2/leaf3 && 4338 echo leaf4 >node2/leaf4 && 4339 git add node1 node2 && 4340 test_tick && 4341 git commit -m "O" && 4342 4343 git branch O && 4344 git branch A && 4345 git branch B && 4346 4347 git checkout A && 4348 git mv node2/ node1/ && 4349 echo leaf5 >node1/leaf5 && 4350 git add node1/leaf5 && 4351 test_tick && 4352 git commit -m "A" && 4353 4354 git checkout B && 4355 git mv node1/ node2/ && 4356 echo leaf6 >node2/leaf6 && 4357 git add node2/leaf6 && 4358 test_tick && 4359 git commit -m "B" 4360 ) 4361} 4362 4363test_expect_success '12b2: Moving two directory hierarchies into each other' ' 4364 test_setup_12b2 && 4365 ( 4366 cd 12b2 && 4367 4368 git checkout A^0 && 4369 4370 git -c merge.directoryRenames=true merge -s recursive B^0 && 4371 4372 git ls-files -s >out && 4373 test_line_count = 6 out && 4374 4375 git rev-parse >actual \ 4376 HEAD:node1/node2/node1/leaf1 \ 4377 HEAD:node1/node2/node1/leaf2 \ 4378 HEAD:node2/node1/node2/leaf3 \ 4379 HEAD:node2/node1/node2/leaf4 \ 4380 HEAD:node2/node1/leaf5 \ 4381 HEAD:node1/node2/leaf6 && 4382 git rev-parse >expect \ 4383 O:node1/leaf1 \ 4384 O:node1/leaf2 \ 4385 O:node2/leaf3 \ 4386 O:node2/leaf4 \ 4387 A:node1/leaf5 \ 4388 B:node2/leaf6 && 4389 test_cmp expect actual 4390 ) 4391' 4392 4393# Testcase 12c1, Moving two directory hierarchies into each other w/ content merge 4394# (Related to testcase 12b) 4395# Commit O: node1/{ leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1} 4396# Commit A: node1/{ leaf1_2, leaf2_2, node2/{leaf3_2, leaf4_2}} 4397# Commit B: node2/{node1/{leaf1_3, leaf2_3}, leaf3_3, leaf4_3} 4398# Expected: Content merge conflicts for each of: 4399# node1/node2/node1/{leaf1, leaf2}, 4400# node2/node1/node2/{leaf3, leaf4} 4401# NOTE: This is *exactly* like 12b1, except that every path is modified on 4402# each side of the merge. 4403 4404test_setup_12c1 () { 4405 test_create_repo 12c1 && 4406 ( 4407 cd 12c1 && 4408 4409 mkdir -p node1 node2 && 4410 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 && 4411 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf2\n" >node1/leaf2 && 4412 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf3\n" >node2/leaf3 && 4413 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf4\n" >node2/leaf4 && 4414 git add node1 node2 && 4415 test_tick && 4416 git commit -m "O" && 4417 4418 git branch O && 4419 git branch A && 4420 git branch B && 4421 4422 git checkout A && 4423 git mv node2/ node1/ && 4424 for i in `git ls-files`; do echo side A >>$i; done && 4425 git add -u && 4426 test_tick && 4427 git commit -m "A" && 4428 4429 git checkout B && 4430 git mv node1/ node2/ && 4431 for i in `git ls-files`; do echo side B >>$i; done && 4432 git add -u && 4433 test_tick && 4434 git commit -m "B" 4435 ) 4436} 4437 4438test_expect_merge_algorithm failure success '12c1: Moving one directory hierarchy into another w/ content merge' ' 4439 test_setup_12c1 && 4440 ( 4441 cd 12c1 && 4442 4443 git checkout A^0 && 4444 4445 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 && 4446 4447 git ls-files -u >out && 4448 test_line_count = 12 out && 4449 4450 git rev-parse >actual \ 4451 :1:node2/node1/leaf1 \ 4452 :1:node2/node1/leaf2 \ 4453 :1:node1/node2/leaf3 \ 4454 :1:node1/node2/leaf4 \ 4455 :2:node2/node1/leaf1 \ 4456 :2:node2/node1/leaf2 \ 4457 :2:node1/node2/leaf3 \ 4458 :2:node1/node2/leaf4 \ 4459 :3:node2/node1/leaf1 \ 4460 :3:node2/node1/leaf2 \ 4461 :3:node1/node2/leaf3 \ 4462 :3:node1/node2/leaf4 && 4463 git rev-parse >expect \ 4464 O:node1/leaf1 \ 4465 O:node1/leaf2 \ 4466 O:node2/leaf3 \ 4467 O:node2/leaf4 \ 4468 A:node1/leaf1 \ 4469 A:node1/leaf2 \ 4470 A:node1/node2/leaf3 \ 4471 A:node1/node2/leaf4 \ 4472 B:node2/node1/leaf1 \ 4473 B:node2/node1/leaf2 \ 4474 B:node2/leaf3 \ 4475 B:node2/leaf4 && 4476 test_cmp expect actual 4477 ) 4478' 4479 4480# Testcase 12c2, Moving two directory hierarchies into each other w/ content merge 4481# (Related to testcase 12b) 4482# Commit O: node1/{ leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1} 4483# Commit A: node1/{ leaf1_2, leaf2_2, node2/{leaf3_2, leaf4_2}, leaf5} 4484# Commit B: node2/{node1/{leaf1_3, leaf2_3}, leaf3_3, leaf4_3, leaf6} 4485# Expected: Content merge conflicts for each of: 4486# node1/node2/node1/{leaf1, leaf2} 4487# node2/node1/node2/{leaf3, leaf4} 4488# plus 4489# node2/node1/leaf5 4490# node1/node2/leaf6 4491# NOTE: This is *exactly* like 12b2, except that every path from O is modified 4492# on each side of the merge. 4493 4494test_setup_12c2 () { 4495 test_create_repo 12c2 && 4496 ( 4497 cd 12c2 && 4498 4499 mkdir -p node1 node2 && 4500 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 && 4501 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf2\n" >node1/leaf2 && 4502 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf3\n" >node2/leaf3 && 4503 printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf4\n" >node2/leaf4 && 4504 git add node1 node2 && 4505 test_tick && 4506 git commit -m "O" && 4507 4508 git branch O && 4509 git branch A && 4510 git branch B && 4511 4512 git checkout A && 4513 git mv node2/ node1/ && 4514 for i in `git ls-files`; do echo side A >>$i; done && 4515 git add -u && 4516 echo leaf5 >node1/leaf5 && 4517 git add node1/leaf5 && 4518 test_tick && 4519 git commit -m "A" && 4520 4521 git checkout B && 4522 git mv node1/ node2/ && 4523 for i in `git ls-files`; do echo side B >>$i; done && 4524 git add -u && 4525 echo leaf6 >node2/leaf6 && 4526 git add node2/leaf6 && 4527 test_tick && 4528 git commit -m "B" 4529 ) 4530} 4531 4532test_expect_success '12c2: Moving one directory hierarchy into another w/ content merge' ' 4533 test_setup_12c2 && 4534 ( 4535 cd 12c2 && 4536 4537 git checkout A^0 && 4538 4539 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 && 4540 4541 git ls-files -s >out && 4542 test_line_count = 14 out && 4543 git ls-files -u >out && 4544 test_line_count = 12 out && 4545 4546 git rev-parse >actual \ 4547 :1:node1/node2/node1/leaf1 \ 4548 :1:node1/node2/node1/leaf2 \ 4549 :1:node2/node1/node2/leaf3 \ 4550 :1:node2/node1/node2/leaf4 \ 4551 :2:node1/node2/node1/leaf1 \ 4552 :2:node1/node2/node1/leaf2 \ 4553 :2:node2/node1/node2/leaf3 \ 4554 :2:node2/node1/node2/leaf4 \ 4555 :3:node1/node2/node1/leaf1 \ 4556 :3:node1/node2/node1/leaf2 \ 4557 :3:node2/node1/node2/leaf3 \ 4558 :3:node2/node1/node2/leaf4 \ 4559 :0:node2/node1/leaf5 \ 4560 :0:node1/node2/leaf6 && 4561 git rev-parse >expect \ 4562 O:node1/leaf1 \ 4563 O:node1/leaf2 \ 4564 O:node2/leaf3 \ 4565 O:node2/leaf4 \ 4566 A:node1/leaf1 \ 4567 A:node1/leaf2 \ 4568 A:node1/node2/leaf3 \ 4569 A:node1/node2/leaf4 \ 4570 B:node2/node1/leaf1 \ 4571 B:node2/node1/leaf2 \ 4572 B:node2/leaf3 \ 4573 B:node2/leaf4 \ 4574 A:node1/leaf5 \ 4575 B:node2/leaf6 && 4576 test_cmp expect actual 4577 ) 4578' 4579 4580# Testcase 12d, Rename/merge of subdirectory into the root 4581# Commit O: a/b/subdir/foo 4582# Commit A: subdir/foo 4583# Commit B: a/b/subdir/foo, a/b/bar 4584# Expected: subdir/foo, bar 4585 4586test_setup_12d () { 4587 test_create_repo 12d && 4588 ( 4589 cd 12d && 4590 4591 mkdir -p a/b/subdir && 4592 test_commit a/b/subdir/foo && 4593 4594 git branch O && 4595 git branch A && 4596 git branch B && 4597 4598 git checkout A && 4599 mkdir subdir && 4600 git mv a/b/subdir/foo.t subdir/foo.t && 4601 test_tick && 4602 git commit -m "A" && 4603 4604 git checkout B && 4605 test_commit a/b/bar 4606 ) 4607} 4608 4609test_expect_success '12d: Rename/merge subdir into the root, variant 1' ' 4610 test_setup_12d && 4611 ( 4612 cd 12d && 4613 4614 git checkout A^0 && 4615 4616 git -c merge.directoryRenames=true merge -s recursive B^0 && 4617 4618 git ls-files -s >out && 4619 test_line_count = 2 out && 4620 4621 git rev-parse >actual \ 4622 HEAD:subdir/foo.t HEAD:bar.t && 4623 git rev-parse >expect \ 4624 O:a/b/subdir/foo.t B:a/b/bar.t && 4625 test_cmp expect actual && 4626 4627 git hash-object bar.t >actual && 4628 git rev-parse B:a/b/bar.t >expect && 4629 test_cmp expect actual && 4630 4631 test_must_fail git rev-parse HEAD:a/b/subdir/foo.t && 4632 test_must_fail git rev-parse HEAD:a/b/bar.t && 4633 test_path_is_missing a/ && 4634 test_path_is_file bar.t 4635 ) 4636' 4637 4638# Testcase 12e, Rename/merge of subdirectory into the root 4639# Commit O: a/b/foo 4640# Commit A: foo 4641# Commit B: a/b/foo, a/b/bar 4642# Expected: foo, bar 4643 4644test_setup_12e () { 4645 test_create_repo 12e && 4646 ( 4647 cd 12e && 4648 4649 mkdir -p a/b && 4650 test_commit a/b/foo && 4651 4652 git branch O && 4653 git branch A && 4654 git branch B && 4655 4656 git checkout A && 4657 mkdir subdir && 4658 git mv a/b/foo.t foo.t && 4659 test_tick && 4660 git commit -m "A" && 4661 4662 git checkout B && 4663 test_commit a/b/bar 4664 ) 4665} 4666 4667test_expect_success '12e: Rename/merge subdir into the root, variant 2' ' 4668 test_setup_12e && 4669 ( 4670 cd 12e && 4671 4672 git checkout A^0 && 4673 4674 git -c merge.directoryRenames=true merge -s recursive B^0 && 4675 4676 git ls-files -s >out && 4677 test_line_count = 2 out && 4678 4679 git rev-parse >actual \ 4680 HEAD:foo.t HEAD:bar.t && 4681 git rev-parse >expect \ 4682 O:a/b/foo.t B:a/b/bar.t && 4683 test_cmp expect actual && 4684 4685 git hash-object bar.t >actual && 4686 git rev-parse B:a/b/bar.t >expect && 4687 test_cmp expect actual && 4688 4689 test_must_fail git rev-parse HEAD:a/b/foo.t && 4690 test_must_fail git rev-parse HEAD:a/b/bar.t && 4691 test_path_is_missing a/ && 4692 test_path_is_file bar.t 4693 ) 4694' 4695 4696# Testcase 12f, Rebase of patches with big directory rename 4697# Commit O: 4698# dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O} 4699# dir/subdir/tweaked/{f,g,h,Makefile_SUB_O} 4700# dir/unchanged/<LOTS OF FILES> 4701# Commit A: 4702# (Remove f & g, move e into newsubdir, rename dir/->folder/, modify files) 4703# folder/subdir/{a,b,c,d,Makefile_TOP_A} 4704# folder/subdir/newsubdir/e_A 4705# folder/subdir/tweaked/{h,Makefile_SUB_A} 4706# folder/unchanged/<LOTS OF FILES> 4707# Commit B1: 4708# (add newfile.{c,py}, modify underscored files) 4709# dir/{a,b,c,d,e_B1,Makefile_TOP_B1,newfile.c} 4710# dir/tweaked/{f,g,h,Makefile_SUB_B1,newfile.py} 4711# dir/unchanged/<LOTS OF FILES> 4712# Commit B2: 4713# (Modify e further, add newfile.rs) 4714# dir/{a,b,c,d,e_B2,Makefile_TOP_B1,newfile.c,newfile.rs} 4715# dir/tweaked/{f,g,h,Makefile_SUB_B1,newfile.py} 4716# dir/unchanged/<LOTS OF FILES> 4717# Expected: 4718# B1-picked: 4719# folder/subdir/{a,b,c,d,Makefile_TOP_Merge1,newfile.c} 4720# folder/subdir/newsubdir/e_Merge1 4721# folder/subdir/tweaked/{h,Makefile_SUB_Merge1,newfile.py} 4722# folder/unchanged/<LOTS OF FILES> 4723# B2-picked: 4724# folder/subdir/{a,b,c,d,Makefile_TOP_Merge1,newfile.c,newfile.rs} 4725# folder/subdir/newsubdir/e_Merge2 4726# folder/subdir/tweaked/{h,Makefile_SUB_Merge1,newfile.py} 4727# folder/unchanged/<LOTS OF FILES> 4728# Things being checked here: 4729# 1. dir/subdir/newfile.c does not get pushed into folder/subdir/newsubdir/. 4730# dir/subdir/{a,b,c,d} -> folder/subdir/{a,b,c,d} looks like 4731# dir/ -> folder/, 4732# whereas dir/subdir/e -> folder/subdir/newsubdir/e looks like 4733# dir/subdir/ -> folder/subdir/newsubdir/ 4734# and if we note that newfile.c is found in dir/subdir/, we might overlook 4735# the dir/ -> folder/ rule that has more weight. Older git versions did 4736# this. 4737# 2. The code to do trivial directory resolves. Note that 4738# dir/subdir/unchanged/ is unchanged and can be deleted, and files in the 4739# new folder/subdir/unchanged/ are not needed as a target to any renames. 4740# Thus, in the second collect_merge_info_callback() we can just resolve 4741# these two directories trivially without recursing.) 4742# 3. Exercising the codepaths for caching renames and deletes from one cherry 4743# pick and re-applying them in the subsequent one. 4744 4745test_setup_12f () { 4746 test_create_repo 12f && 4747 ( 4748 cd 12f && 4749 4750 mkdir -p dir/unchanged && 4751 mkdir -p dir/subdir/tweaked && 4752 echo a >dir/subdir/a && 4753 echo b >dir/subdir/b && 4754 echo c >dir/subdir/c && 4755 echo d >dir/subdir/d && 4756 test_seq 1 10 >dir/subdir/e && 4757 test_seq 10 20 >dir/subdir/Makefile && 4758 echo f >dir/subdir/tweaked/f && 4759 echo g >dir/subdir/tweaked/g && 4760 echo h >dir/subdir/tweaked/h && 4761 test_seq 20 30 >dir/subdir/tweaked/Makefile && 4762 for i in `test_seq 1 88`; do 4763 echo content $i >dir/unchanged/file_$i 4764 done && 4765 git add . && 4766 git commit -m "O" && 4767 4768 git branch O && 4769 git branch A && 4770 git branch B && 4771 4772 git switch A && 4773 git rm dir/subdir/tweaked/f dir/subdir/tweaked/g && 4774 test_seq 2 10 >dir/subdir/e && 4775 test_seq 11 20 >dir/subdir/Makefile && 4776 test_seq 21 30 >dir/subdir/tweaked/Makefile && 4777 mkdir dir/subdir/newsubdir && 4778 git mv dir/subdir/e dir/subdir/newsubdir/ && 4779 git mv dir folder && 4780 git add . && 4781 git commit -m "A" && 4782 4783 git switch B && 4784 mkdir dir/subdir/newsubdir/ && 4785 echo c code >dir/subdir/newfile.c && 4786 echo python code >dir/subdir/newsubdir/newfile.py && 4787 test_seq 1 11 >dir/subdir/e && 4788 test_seq 10 21 >dir/subdir/Makefile && 4789 test_seq 20 31 >dir/subdir/tweaked/Makefile && 4790 git add . && 4791 git commit -m "B1" && 4792 4793 echo rust code >dir/subdir/newfile.rs && 4794 test_seq 1 12 >dir/subdir/e && 4795 git add . && 4796 git commit -m "B2" 4797 ) 4798} 4799 4800test_expect_merge_algorithm failure success '12f: Trivial directory resolve, caching, all kinds of fun' ' 4801 test_setup_12f && 4802 ( 4803 cd 12f && 4804 4805 git checkout A^0 && 4806 git branch Bmod B && 4807 4808 GIT_TRACE2_PERF="$(pwd)/trace.output" git -c merge.directoryRenames=true rebase A Bmod && 4809 4810 echo Checking the pick of B1... && 4811 4812 test_must_fail git rev-parse Bmod~1:dir && 4813 4814 git ls-tree -r Bmod~1 >out && 4815 test_line_count = 98 out && 4816 4817 git diff --name-status A Bmod~1 >actual && 4818 q_to_tab >expect <<-\EOF && 4819 MQfolder/subdir/Makefile 4820 AQfolder/subdir/newfile.c 4821 MQfolder/subdir/newsubdir/e 4822 AQfolder/subdir/newsubdir/newfile.py 4823 MQfolder/subdir/tweaked/Makefile 4824 EOF 4825 test_cmp expect actual && 4826 4827 # Three-way merged files 4828 test_seq 2 11 >e_Merge1 && 4829 test_seq 11 21 >Makefile_TOP && 4830 test_seq 21 31 >Makefile_SUB && 4831 git hash-object >expect \ 4832 e_Merge1 \ 4833 Makefile_TOP \ 4834 Makefile_SUB && 4835 git rev-parse >actual \ 4836 Bmod~1:folder/subdir/newsubdir/e \ 4837 Bmod~1:folder/subdir/Makefile \ 4838 Bmod~1:folder/subdir/tweaked/Makefile && 4839 test_cmp expect actual && 4840 4841 # New files showed up at the right location with right contents 4842 git rev-parse >expect \ 4843 B~1:dir/subdir/newfile.c \ 4844 B~1:dir/subdir/newsubdir/newfile.py && 4845 git rev-parse >actual \ 4846 Bmod~1:folder/subdir/newfile.c \ 4847 Bmod~1:folder/subdir/newsubdir/newfile.py && 4848 test_cmp expect actual && 4849 4850 # Removed files 4851 test_path_is_missing folder/subdir/tweaked/f && 4852 test_path_is_missing folder/subdir/tweaked/g && 4853 4854 # Unchanged files or directories 4855 git rev-parse >actual \ 4856 Bmod~1:folder/subdir/a \ 4857 Bmod~1:folder/subdir/b \ 4858 Bmod~1:folder/subdir/c \ 4859 Bmod~1:folder/subdir/d \ 4860 Bmod~1:folder/unchanged \ 4861 Bmod~1:folder/subdir/tweaked/h && 4862 git rev-parse >expect \ 4863 O:dir/subdir/a \ 4864 O:dir/subdir/b \ 4865 O:dir/subdir/c \ 4866 O:dir/subdir/d \ 4867 O:dir/unchanged \ 4868 O:dir/subdir/tweaked/h && 4869 test_cmp expect actual && 4870 4871 echo Checking the pick of B2... && 4872 4873 test_must_fail git rev-parse Bmod:dir && 4874 4875 git ls-tree -r Bmod >out && 4876 test_line_count = 99 out && 4877 4878 git diff --name-status Bmod~1 Bmod >actual && 4879 q_to_tab >expect <<-\EOF && 4880 AQfolder/subdir/newfile.rs 4881 MQfolder/subdir/newsubdir/e 4882 EOF 4883 test_cmp expect actual && 4884 4885 # Three-way merged file 4886 test_seq 2 12 >e_Merge2 && 4887 git hash-object e_Merge2 >expect && 4888 git rev-parse Bmod:folder/subdir/newsubdir/e >actual && 4889 test_cmp expect actual && 4890 4891 grep region_enter.*collect_merge_info trace.output >collect && 4892 test_line_count = 4 collect && 4893 grep region_enter.*process_entries$ trace.output >process && 4894 test_line_count = 2 process 4895 ) 4896' 4897 4898# Testcase 12g, Testcase with two kinds of "relevant" renames 4899# Commit O: somefile_O, subdir/{a_O,b_O} 4900# Commit A: somefile_A, subdir/{a_O,b_O,c_A} 4901# Commit B: newfile_B, newdir/{a_B,b_B} 4902# Expected: newfile_{merged}, newdir/{a_B,b_B,c_A} 4903 4904test_setup_12g () { 4905 test_create_repo 12g && 4906 ( 4907 cd 12g && 4908 4909 mkdir -p subdir && 4910 test_write_lines upon a time there was a >somefile && 4911 test_write_lines 1 2 3 4 5 6 7 8 9 10 >subdir/a && 4912 test_write_lines one two three four five six >subdir/b && 4913 git add . && 4914 test_tick && 4915 git commit -m "O" && 4916 4917 git branch O && 4918 git branch A && 4919 git branch B && 4920 4921 git switch A && 4922 test_write_lines once upon a time there was a >somefile && 4923 > subdir/c && 4924 git add somefile subdir/c && 4925 test_tick && 4926 git commit -m "A" && 4927 4928 git checkout B && 4929 git mv somefile newfile && 4930 git mv subdir newdir && 4931 echo repo >>newfile && 4932 test_write_lines 1 2 3 4 5 6 7 8 9 10 11 >newdir/a && 4933 test_write_lines one two three four five six seven >newdir/b && 4934 git add newfile newdir && 4935 test_tick && 4936 git commit -m "B" 4937 ) 4938} 4939 4940test_expect_success '12g: Testcase with two kinds of "relevant" renames' ' 4941 test_setup_12g && 4942 ( 4943 cd 12g && 4944 4945 git checkout A^0 && 4946 4947 git -c merge.directoryRenames=true merge -s recursive B^0 && 4948 4949 test_write_lines once upon a time there was a repo >expect && 4950 test_cmp expect newfile && 4951 4952 git ls-files -s >out && 4953 test_line_count = 4 out && 4954 4955 git rev-parse >actual \ 4956 HEAD:newdir/a HEAD:newdir/b HEAD:newdir/c && 4957 git rev-parse >expect \ 4958 B:newdir/a B:newdir/b A:subdir/c && 4959 test_cmp expect actual && 4960 4961 test_must_fail git rev-parse HEAD:subdir/a && 4962 test_must_fail git rev-parse HEAD:subdir/b && 4963 test_must_fail git rev-parse HEAD:subdir/c && 4964 test_path_is_missing subdir/ && 4965 test_path_is_file newdir/c 4966 ) 4967' 4968 4969# Testcase 12h, Testcase with two kinds of "relevant" renames 4970# Commit O: olddir/{a_1, b} 4971# Commit A: newdir/{a_2, b} 4972# Commit B: olddir/{alpha_1, b} 4973# Expected: newdir/{alpha_2, b} 4974 4975test_setup_12h () { 4976 test_create_repo 12h && 4977 ( 4978 cd 12h && 4979 4980 mkdir olddir && 4981 test_seq 3 8 >olddir/a && 4982 >olddir/b && 4983 git add olddir && 4984 git commit -m orig && 4985 4986 git branch O && 4987 git branch A && 4988 git branch B && 4989 4990 git switch A && 4991 test_seq 3 10 >olddir/a && 4992 git add olddir/a && 4993 git mv olddir newdir && 4994 git commit -m A && 4995 4996 git switch B && 4997 4998 git mv olddir/a olddir/alpha && 4999 git commit -m B 5000 ) 5001} 5002 5003test_expect_failure '12h: renaming a file within a renamed directory' ' 5004 test_setup_12h && 5005 ( 5006 cd 12h && 5007 5008 git checkout A^0 && 5009 5010 test_might_fail git -c merge.directoryRenames=true merge -s recursive B^0 && 5011 5012 git ls-files >tracked && 5013 test_line_count = 2 tracked && 5014 5015 test_path_is_missing olddir/a && 5016 test_path_is_file newdir/alpha && 5017 test_path_is_file newdir/b && 5018 5019 git rev-parse >actual \ 5020 HEAD:newdir/alpha HEAD:newdir/b && 5021 git rev-parse >expect \ 5022 A:newdir/a O:oldir/b && 5023 test_cmp expect actual 5024 ) 5025' 5026 5027# Testcase 12i, Directory rename causes rename-to-self 5028# Commit O: source/{subdir/foo, bar, baz_1} 5029# Commit A: source/{foo, bar, baz_1} 5030# Commit B: source/{subdir/{foo, bar}, baz_2} 5031# Expected: source/{foo, bar, baz_2}, with conflicts on 5032# source/bar vs. source/subdir/bar 5033 5034test_setup_12i () { 5035 test_create_repo 12i && 5036 ( 5037 cd 12i && 5038 5039 mkdir -p source/subdir && 5040 echo foo >source/subdir/foo && 5041 echo bar >source/bar && 5042 echo baz >source/baz && 5043 git add source && 5044 git commit -m orig && 5045 5046 git branch O && 5047 git branch A && 5048 git branch B && 5049 5050 git switch A && 5051 git mv source/subdir/foo source/foo && 5052 git commit -m A && 5053 5054 git switch B && 5055 git mv source/bar source/subdir/bar && 5056 echo more baz >>source/baz && 5057 git commit -m B 5058 ) 5059} 5060 5061test_expect_success '12i: Directory rename causes rename-to-self' ' 5062 test_setup_12i && 5063 ( 5064 cd 12i && 5065 5066 git checkout A^0 && 5067 5068 test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 && 5069 5070 test_path_is_missing source/subdir && 5071 test_path_is_file source/bar && 5072 test_path_is_file source/baz && 5073 5074 git ls-files | uniq >tracked && 5075 test_line_count = 3 tracked && 5076 5077 git status --porcelain -uno >actual && 5078 cat >expect <<-\EOF && 5079 UU source/bar 5080 M source/baz 5081 EOF 5082 test_cmp expect actual 5083 ) 5084' 5085 5086# Testcase 12j, Directory rename to root causes rename-to-self 5087# Commit O: {subdir/foo, bar, baz_1} 5088# Commit A: {foo, bar, baz_1} 5089# Commit B: {subdir/{foo, bar}, baz_2} 5090# Expected: {foo, bar, baz_2}, with conflicts on bar vs. subdir/bar 5091 5092test_setup_12j () { 5093 test_create_repo 12j && 5094 ( 5095 cd 12j && 5096 5097 mkdir -p subdir && 5098 echo foo >subdir/foo && 5099 echo bar >bar && 5100 echo baz >baz && 5101 git add . && 5102 git commit -m orig && 5103 5104 git branch O && 5105 git branch A && 5106 git branch B && 5107 5108 git switch A && 5109 git mv subdir/foo foo && 5110 git commit -m A && 5111 5112 git switch B && 5113 git mv bar subdir/bar && 5114 echo more baz >>baz && 5115 git commit -m B 5116 ) 5117} 5118 5119test_expect_success '12j: Directory rename to root causes rename-to-self' ' 5120 test_setup_12j && 5121 ( 5122 cd 12j && 5123 5124 git checkout A^0 && 5125 5126 test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 && 5127 5128 test_path_is_missing subdir && 5129 test_path_is_file bar && 5130 test_path_is_file baz && 5131 5132 git ls-files | uniq >tracked && 5133 test_line_count = 3 tracked && 5134 5135 git status --porcelain -uno >actual && 5136 cat >expect <<-\EOF && 5137 UU bar 5138 M baz 5139 EOF 5140 test_cmp expect actual 5141 ) 5142' 5143 5144# Testcase 12k, Directory rename with sibling causes rename-to-self 5145# Commit O: dirB/foo, dirA/{bar, baz_1} 5146# Commit A: dirA/{foo, bar, baz_1} 5147# Commit B: dirB/{foo, bar}, dirA/baz_2 5148# Expected: dirA/{foo, bar, baz_2}, with conflicts on dirA/bar vs. dirB/bar 5149 5150test_setup_12k () { 5151 test_create_repo 12k && 5152 ( 5153 cd 12k && 5154 5155 mkdir dirA dirB && 5156 echo foo >dirB/foo && 5157 echo bar >dirA/bar && 5158 echo baz >dirA/baz && 5159 git add . && 5160 git commit -m orig && 5161 5162 git branch O && 5163 git branch A && 5164 git branch B && 5165 5166 git switch A && 5167 git mv dirB/* dirA/ && 5168 git commit -m A && 5169 5170 git switch B && 5171 git mv dirA/bar dirB/bar && 5172 echo more baz >>dirA/baz && 5173 git commit -m B 5174 ) 5175} 5176 5177test_expect_success '12k: Directory rename with sibling causes rename-to-self' ' 5178 test_setup_12k && 5179 ( 5180 cd 12k && 5181 5182 git checkout A^0 && 5183 5184 test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 && 5185 5186 test_path_is_missing dirB && 5187 test_path_is_file dirA/bar && 5188 test_path_is_file dirA/baz && 5189 5190 git ls-files | uniq >tracked && 5191 test_line_count = 3 tracked && 5192 5193 git status --porcelain -uno >actual && 5194 cat >expect <<-\EOF && 5195 UU dirA/bar 5196 M dirA/baz 5197 EOF 5198 test_cmp expect actual 5199 ) 5200' 5201 5202########################################################################### 5203# SECTION 13: Checking informational and conflict messages 5204# 5205# A year after directory rename detection became the default, it was 5206# instead decided to report conflicts on the pathname on the basis that 5207# some users may expect the new files added or moved into a directory to 5208# be unrelated to all the other files in that directory, and thus that 5209# directory rename detection is unexpected. Test that the messages printed 5210# match our expectation. 5211########################################################################### 5212 5213# Testcase 13a, Basic directory rename with newly added files 5214# Commit O: z/{b,c} 5215# Commit A: y/{b,c} 5216# Commit B: z/{b,c,d,e/f} 5217# Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f 5218 5219test_setup_13a () { 5220 test_create_repo 13a_$1 && 5221 ( 5222 cd 13a_$1 && 5223 5224 mkdir z && 5225 echo b >z/b && 5226 echo c >z/c && 5227 git add z && 5228 test_tick && 5229 git commit -m "O" && 5230 5231 git branch O && 5232 git branch A && 5233 git branch B && 5234 5235 git checkout A && 5236 git mv z y && 5237 test_tick && 5238 git commit -m "A" && 5239 5240 git checkout B && 5241 echo d >z/d && 5242 mkdir z/e && 5243 echo f >z/e/f && 5244 git add z/d z/e/f && 5245 test_tick && 5246 git commit -m "B" 5247 ) 5248} 5249 5250test_expect_success '13a(conflict): messages for newly added files' ' 5251 test_setup_13a conflict && 5252 ( 5253 cd 13a_conflict && 5254 5255 git checkout A^0 && 5256 5257 test_must_fail git merge -s recursive B^0 >out 2>err && 5258 5259 test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out && 5260 test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out && 5261 5262 git ls-files >paths && 5263 ! grep z/ paths && 5264 grep "y/[de]" paths && 5265 5266 test_path_is_missing z/d && 5267 test_path_is_file y/d && 5268 test_path_is_missing z/e/f && 5269 test_path_is_file y/e/f 5270 ) 5271' 5272 5273test_expect_success '13a(info): messages for newly added files' ' 5274 test_setup_13a info && 5275 ( 5276 cd 13a_info && 5277 5278 git reset --hard && 5279 git checkout A^0 && 5280 5281 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 5282 5283 test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out && 5284 test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out && 5285 5286 git ls-files >paths && 5287 ! grep z/ paths && 5288 grep "y/[de]" paths && 5289 5290 test_path_is_missing z/d && 5291 test_path_is_file y/d && 5292 test_path_is_missing z/e/f && 5293 test_path_is_file y/e/f 5294 ) 5295' 5296 5297# Testcase 13b, Transitive rename with conflicted content merge and default 5298# "conflict" setting 5299# (Related to testcase 1c, 9b) 5300# Commit O: z/{b,c}, x/d_1 5301# Commit A: y/{b,c}, x/d_2 5302# Commit B: z/{b,c,d_3} 5303# Expected: y/{b,c,d_merged}, with two conflict messages for y/d, 5304# one about content, and one about file location 5305 5306test_setup_13b () { 5307 test_create_repo 13b_$1 && 5308 ( 5309 cd 13b_$1 && 5310 5311 mkdir x && 5312 mkdir z && 5313 test_seq 1 10 >x/d && 5314 echo b >z/b && 5315 echo c >z/c && 5316 git add x z && 5317 test_tick && 5318 git commit -m "O" && 5319 5320 git branch O && 5321 git branch A && 5322 git branch B && 5323 5324 git checkout A && 5325 git mv z y && 5326 echo 11 >>x/d && 5327 git add x/d && 5328 test_tick && 5329 git commit -m "A" && 5330 5331 git checkout B && 5332 echo eleven >>x/d && 5333 git mv x/d z/d && 5334 git add z/d && 5335 test_tick && 5336 git commit -m "B" 5337 ) 5338} 5339 5340test_expect_success '13b(conflict): messages for transitive rename with conflicted content' ' 5341 test_setup_13b conflict && 5342 ( 5343 cd 13b_conflict && 5344 5345 git checkout A^0 && 5346 5347 test_must_fail git merge -s recursive B^0 >out 2>err && 5348 5349 test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out && 5350 test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out && 5351 5352 git ls-files >paths && 5353 ! grep z/ paths && 5354 grep "y/d" paths && 5355 5356 test_path_is_missing z/d && 5357 test_path_is_file y/d 5358 ) 5359' 5360 5361test_expect_success '13b(info): messages for transitive rename with conflicted content' ' 5362 test_setup_13b info && 5363 ( 5364 cd 13b_info && 5365 5366 git reset --hard && 5367 git checkout A^0 && 5368 5369 test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 5370 5371 test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out && 5372 test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out && 5373 5374 git ls-files >paths && 5375 ! grep z/ paths && 5376 grep "y/d" paths && 5377 5378 test_path_is_missing z/d && 5379 test_path_is_file y/d 5380 ) 5381' 5382 5383# Testcase 13c, Rename/rename(1to1) due to directory rename 5384# Commit O: z/{b,c}, x/{d,e} 5385# Commit A: y/{b,c,d}, x/e 5386# Commit B: z/{b,c,d}, x/e 5387# Expected: y/{b,c,d}, x/e, with info or conflict messages for d 5388# A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d 5389# One could argue A had partial knowledge of what was done with 5390# d and B had full knowledge, but that's a slippery slope as 5391# shown in testcase 13d. 5392 5393test_setup_13c () { 5394 test_create_repo 13c_$1 && 5395 ( 5396 cd 13c_$1 && 5397 5398 mkdir x && 5399 mkdir z && 5400 test_seq 1 10 >x/d && 5401 echo e >x/e && 5402 echo b >z/b && 5403 echo c >z/c && 5404 git add x z && 5405 test_tick && 5406 git commit -m "O" && 5407 5408 git branch O && 5409 git branch A && 5410 git branch B && 5411 5412 git checkout A && 5413 git mv z y && 5414 git mv x/d y/ && 5415 test_tick && 5416 git commit -m "A" && 5417 5418 git checkout B && 5419 git mv x/d z/d && 5420 git add z/d && 5421 test_tick && 5422 git commit -m "B" 5423 ) 5424} 5425 5426test_expect_success '13c(conflict): messages for rename/rename(1to1) via transitive rename' ' 5427 test_setup_13c conflict && 5428 ( 5429 cd 13c_conflict && 5430 5431 git checkout A^0 && 5432 5433 test_must_fail git merge -s recursive B^0 >out 2>err && 5434 5435 test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out && 5436 5437 git ls-files >paths && 5438 ! grep z/ paths && 5439 grep "y/d" paths && 5440 5441 test_path_is_missing z/d && 5442 test_path_is_file y/d 5443 ) 5444' 5445 5446test_expect_success '13c(info): messages for rename/rename(1to1) via transitive rename' ' 5447 test_setup_13c info && 5448 ( 5449 cd 13c_info && 5450 5451 git reset --hard && 5452 git checkout A^0 && 5453 5454 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 5455 5456 test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out && 5457 5458 git ls-files >paths && 5459 ! grep z/ paths && 5460 grep "y/d" paths && 5461 5462 test_path_is_missing z/d && 5463 test_path_is_file y/d 5464 ) 5465' 5466 5467# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides 5468# Commit O: a/{z,y}, b/x, c/w 5469# Commit A: a/z, b/{y,x}, d/w 5470# Commit B: a/z, d/x, c/{y,w} 5471# Expected: a/z, d/{y,x,w} with no file location conflict for x 5472# Easy cases: 5473# * z is always in a; so it stays in a. 5474# * x starts in b, only modified on one side to move into d/ 5475# * w starts in c, only modified on one side to move into d/ 5476# Hard case: 5477# * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y 5478# * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y 5479# No conflict in where a/y ends up, so put it in d/y. 5480 5481test_setup_13d () { 5482 test_create_repo 13d_$1 && 5483 ( 5484 cd 13d_$1 && 5485 5486 mkdir a && 5487 mkdir b && 5488 mkdir c && 5489 echo z >a/z && 5490 echo y >a/y && 5491 echo x >b/x && 5492 echo w >c/w && 5493 git add a b c && 5494 test_tick && 5495 git commit -m "O" && 5496 5497 git branch O && 5498 git branch A && 5499 git branch B && 5500 5501 git checkout A && 5502 git mv a/y b/ && 5503 git mv c/ d/ && 5504 test_tick && 5505 git commit -m "A" && 5506 5507 git checkout B && 5508 git mv a/y c/ && 5509 git mv b/ d/ && 5510 test_tick && 5511 git commit -m "B" 5512 ) 5513} 5514 5515test_expect_success '13d(conflict): messages for rename/rename(1to1) via dual transitive rename' ' 5516 test_setup_13d conflict && 5517 ( 5518 cd 13d_conflict && 5519 5520 git checkout A^0 && 5521 5522 test_must_fail git merge -s recursive B^0 >out 2>err && 5523 5524 test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out && 5525 test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out && 5526 5527 git ls-files >paths && 5528 ! grep b/ paths && 5529 ! grep c/ paths && 5530 grep "d/y" paths && 5531 5532 test_path_is_missing b/y && 5533 test_path_is_missing c/y && 5534 test_path_is_file d/y 5535 ) 5536' 5537 5538test_expect_success '13d(info): messages for rename/rename(1to1) via dual transitive rename' ' 5539 test_setup_13d info && 5540 ( 5541 cd 13d_info && 5542 5543 git reset --hard && 5544 git checkout A^0 && 5545 5546 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err && 5547 5548 test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out && 5549 test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out && 5550 5551 git ls-files >paths && 5552 ! grep b/ paths && 5553 ! grep c/ paths && 5554 grep "d/y" paths && 5555 5556 test_path_is_missing b/y && 5557 test_path_is_missing c/y && 5558 test_path_is_file d/y 5559 ) 5560' 5561 5562# Testcase 13e, directory rename in virtual merge base 5563# 5564# This testcase has a slightly different setup than all the above cases, in 5565# order to include a recursive case: 5566# 5567# A C 5568# o - o 5569# / \ / \ 5570# O o X ? 5571# \ / \ / 5572# o o 5573# B D 5574# 5575# Commit O: a/{z,y} 5576# Commit A: b/{z,y} 5577# Commit B: a/{z,y,x} 5578# Commit C: b/{z,y,x} 5579# Commit D: b/{z,y}, a/x 5580# Expected: b/{z,y,x} (sort of; see below for why this might not be expected) 5581# 5582# NOTES: 'X' represents a virtual merge base. With the default of 5583# directory rename detection yielding conflicts, merging A and B 5584# results in a conflict complaining about whether 'x' should be 5585# under 'a/' or 'b/'. However, when creating the virtual merge 5586# base 'X', since virtual merge bases need to be written out as a 5587# tree, we cannot have a conflict, so some resolution has to be 5588# picked. 5589# 5590# In choosing the right resolution, it's worth noting here that 5591# commits C & D are merges of A & B that choose different 5592# locations for 'x' (i.e. they resolve the conflict differently), 5593# and so it would be nice when merging C & D if git could detect 5594# this difference of opinion and report a conflict. But the only 5595# way to do so that I can think of would be to have the virtual 5596# merge base place 'x' in some directory other than either 'a/' or 5597# 'b/', which seems a little weird -- especially since it'd result 5598# in a rename/rename(1to2) conflict with a source path that never 5599# existed in any version. 5600# 5601# So, for now, when directory rename detection is set to 5602# 'conflict' just avoid doing directory rename detection at all in 5603# the recursive case. This will not allow us to detect a conflict 5604# in the outer merge for this special kind of setup, but it at 5605# least avoids hitting a BUG(). 5606# 5607test_setup_13e () { 5608 test_create_repo 13e && 5609 ( 5610 cd 13e && 5611 5612 mkdir a && 5613 echo z >a/z && 5614 echo y >a/y && 5615 git add a && 5616 test_tick && 5617 git commit -m "O" && 5618 5619 git branch O && 5620 git branch A && 5621 git branch B && 5622 5623 git checkout A && 5624 git mv a/ b/ && 5625 test_tick && 5626 git commit -m "A" && 5627 5628 git checkout B && 5629 echo x >a/x && 5630 git add a && 5631 test_tick && 5632 git commit -m "B" && 5633 5634 git branch C A && 5635 git branch D B && 5636 5637 git checkout C && 5638 test_must_fail git -c merge.directoryRenames=conflict merge B && 5639 git add b/x && 5640 test_tick && 5641 git commit -m "C" && 5642 5643 5644 git checkout D && 5645 test_must_fail git -c merge.directoryRenames=conflict merge A && 5646 git add b/x && 5647 mkdir a && 5648 git mv b/x a/x && 5649 test_tick && 5650 git commit -m "D" 5651 ) 5652} 5653 5654test_expect_success '13e: directory rename detection in recursive case' ' 5655 test_setup_13e && 5656 ( 5657 cd 13e && 5658 5659 git checkout --quiet D^0 && 5660 5661 git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err && 5662 5663 test_i18ngrep ! CONFLICT out && 5664 test_i18ngrep ! BUG: err && 5665 test_i18ngrep ! core.dumped err && 5666 test_must_be_empty err && 5667 5668 git ls-files >paths && 5669 ! grep a/x paths && 5670 grep b/x paths 5671 ) 5672' 5673 5674test_done 5675