Git fork
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

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