t / t6043-merge-rename-directories.shon commit Merge branch 'mt/dir-iterator-updates' (9179380)
   1#!/bin/sh
   2
   3test_description="recursive merge with directory renames"
   4# includes checking of many corner cases, with a similar methodology to:
   5#   t6042: corner cases with renames but not criss-cross merges
   6#   t6036: corner cases with both renames and criss-cross merges
   7#
   8# The setup for all of them, pictorially, is:
   9#
  10#      A
  11#      o
  12#     / \
  13#  O o   ?
  14#     \ /
  15#      o
  16#      B
  17#
  18# To help make it easier to follow the flow of tests, they have been
  19# divided into sections and each test will start with a quick explanation
  20# of what commits O, A, and B contain.
  21#
  22# Notation:
  23#    z/{b,c}   means  files z/b and z/c both exist
  24#    x/d_1     means  file x/d exists with content d1.  (Purpose of the
  25#                     underscore notation is to differentiate different
  26#                     files that might be renamed into each other's paths.)
  27
  28. ./test-lib.sh
  29
  30
  31###########################################################################
  32# SECTION 1: Basic cases we should be able to handle
  33###########################################################################
  34
  35# Testcase 1a, Basic directory rename.
  36#   Commit O: z/{b,c}
  37#   Commit A: y/{b,c}
  38#   Commit B: z/{b,c,d,e/f}
  39#   Expected: y/{b,c,d,e/f}
  40
  41test_expect_success '1a-setup: Simple directory rename detection' '
  42        test_create_repo 1a &&
  43        (
  44                cd 1a &&
  45
  46                mkdir z &&
  47                echo b >z/b &&
  48                echo c >z/c &&
  49                git add z &&
  50                test_tick &&
  51                git commit -m "O" &&
  52
  53                git branch O &&
  54                git branch A &&
  55                git branch B &&
  56
  57                git checkout A &&
  58                git mv z y &&
  59                test_tick &&
  60                git commit -m "A" &&
  61
  62                git checkout B &&
  63                echo d >z/d &&
  64                mkdir z/e &&
  65                echo f >z/e/f &&
  66                git add z/d z/e/f &&
  67                test_tick &&
  68                git commit -m "B"
  69        )
  70'
  71
  72test_expect_success '1a-check: Simple directory rename detection' '
  73        (
  74                cd 1a &&
  75
  76                git checkout A^0 &&
  77
  78                git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
  79
  80                git ls-files -s >out &&
  81                test_line_count = 4 out &&
  82
  83                git rev-parse >actual \
  84                        HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e/f &&
  85                git rev-parse >expect \
  86                        O:z/b    O:z/c    B:z/d    B:z/e/f &&
  87                test_cmp expect actual &&
  88
  89                git hash-object y/d >actual &&
  90                git rev-parse B:z/d >expect &&
  91                test_cmp expect actual &&
  92
  93                test_must_fail git rev-parse HEAD:z/d &&
  94                test_must_fail git rev-parse HEAD:z/e/f &&
  95                test_path_is_missing z/d &&
  96                test_path_is_missing z/e/f
  97        )
  98'
  99
 100# Testcase 1b, Merge a directory with another
 101#   Commit O: z/{b,c},   y/d
 102#   Commit A: z/{b,c,e}, y/d
 103#   Commit B: y/{b,c,d}
 104#   Expected: y/{b,c,d,e}
 105
 106test_expect_success '1b-setup: Merge a directory with another' '
 107        test_create_repo 1b &&
 108        (
 109                cd 1b &&
 110
 111                mkdir z &&
 112                echo b >z/b &&
 113                echo c >z/c &&
 114                mkdir y &&
 115                echo d >y/d &&
 116                git add z y &&
 117                test_tick &&
 118                git commit -m "O" &&
 119
 120                git branch O &&
 121                git branch A &&
 122                git branch B &&
 123
 124                git checkout A &&
 125                echo e >z/e &&
 126                git add z/e &&
 127                test_tick &&
 128                git commit -m "A" &&
 129
 130                git checkout B &&
 131                git mv z/b y &&
 132                git mv z/c y &&
 133                rmdir z &&
 134                test_tick &&
 135                git commit -m "B"
 136        )
 137'
 138
 139test_expect_success '1b-check: Merge a directory with another' '
 140        (
 141                cd 1b &&
 142
 143                git checkout A^0 &&
 144
 145                git -c merge.directoryRenames=true merge -s recursive B^0 &&
 146
 147                git ls-files -s >out &&
 148                test_line_count = 4 out &&
 149
 150                git rev-parse >actual \
 151                        HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e &&
 152                git rev-parse >expect \
 153                        O:z/b    O:z/c    O:y/d    A:z/e &&
 154                test_cmp expect actual &&
 155                test_must_fail git rev-parse HEAD:z/e
 156        )
 157'
 158
 159# Testcase 1c, Transitive renaming
 160#   (Related to testcases 3a and 6d -- when should a transitive rename apply?)
 161#   (Related to testcases 9c and 9d -- can transitivity repeat?)
 162#   (Related to testcase 12b -- joint-transitivity?)
 163#   Commit O: z/{b,c},   x/d
 164#   Commit A: y/{b,c},   x/d
 165#   Commit B: z/{b,c,d}
 166#   Expected: y/{b,c,d}  (because x/d -> z/d -> y/d)
 167
 168test_expect_success '1c-setup: Transitive renaming' '
 169        test_create_repo 1c &&
 170        (
 171                cd 1c &&
 172
 173                mkdir z &&
 174                echo b >z/b &&
 175                echo c >z/c &&
 176                mkdir x &&
 177                echo d >x/d &&
 178                git add z x &&
 179                test_tick &&
 180                git commit -m "O" &&
 181
 182                git branch O &&
 183                git branch A &&
 184                git branch B &&
 185
 186                git checkout A &&
 187                git mv z y &&
 188                test_tick &&
 189                git commit -m "A" &&
 190
 191                git checkout B &&
 192                git mv x/d z/d &&
 193                test_tick &&
 194                git commit -m "B"
 195        )
 196'
 197
 198test_expect_success '1c-check: Transitive renaming' '
 199        (
 200                cd 1c &&
 201
 202                git checkout A^0 &&
 203
 204                git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 205
 206                git ls-files -s >out &&
 207                test_line_count = 3 out &&
 208
 209                git rev-parse >actual \
 210                        HEAD:y/b HEAD:y/c HEAD:y/d &&
 211                git rev-parse >expect \
 212                        O:z/b    O:z/c    O:x/d &&
 213                test_cmp expect actual &&
 214                test_must_fail git rev-parse HEAD:x/d &&
 215                test_must_fail git rev-parse HEAD:z/d &&
 216                test_path_is_missing z/d
 217        )
 218'
 219
 220# Testcase 1d, Directory renames (merging two directories into one new one)
 221#              cause a rename/rename(2to1) conflict
 222#   (Related to testcases 1c and 7b)
 223#   Commit O. z/{b,c},        y/{d,e}
 224#   Commit A. x/{b,c},        y/{d,e,m,wham_1}
 225#   Commit B. z/{b,c,n,wham_2}, x/{d,e}
 226#   Expected: x/{b,c,d,e,m,n}, CONFLICT:(y/wham_1 & z/wham_2 -> x/wham)
 227#   Note: y/m & z/n should definitely move into x.  By the same token, both
 228#         y/wham_1 & z/wham_2 should too...giving us a conflict.
 229
 230test_expect_success '1d-setup: Directory renames cause a rename/rename(2to1) conflict' '
 231        test_create_repo 1d &&
 232        (
 233                cd 1d &&
 234
 235                mkdir z &&
 236                echo b >z/b &&
 237                echo c >z/c &&
 238                mkdir y &&
 239                echo d >y/d &&
 240                echo e >y/e &&
 241                git add z y &&
 242                test_tick &&
 243                git commit -m "O" &&
 244
 245                git branch O &&
 246                git branch A &&
 247                git branch B &&
 248
 249                git checkout A &&
 250                git mv z x &&
 251                echo m >y/m &&
 252                echo wham1 >y/wham &&
 253                git add y &&
 254                test_tick &&
 255                git commit -m "A" &&
 256
 257                git checkout B &&
 258                git mv y x &&
 259                echo n >z/n &&
 260                echo wham2 >z/wham &&
 261                git add z &&
 262                test_tick &&
 263                git commit -m "B"
 264        )
 265'
 266
 267test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) conflict' '
 268        (
 269                cd 1d &&
 270
 271                git checkout A^0 &&
 272
 273                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 274                test_i18ngrep "CONFLICT (rename/rename)" out &&
 275
 276                git ls-files -s >out &&
 277                test_line_count = 8 out &&
 278                git ls-files -u >out &&
 279                test_line_count = 2 out &&
 280                git ls-files -o >out &&
 281                test_line_count = 1 out &&
 282
 283                git rev-parse >actual \
 284                        :0:x/b :0:x/c :0:x/d :0:x/e :0:x/m :0:x/n &&
 285                git rev-parse >expect \
 286                         O:z/b  O:z/c  O:y/d  O:y/e  A:y/m  B:z/n &&
 287                test_cmp expect actual &&
 288
 289                test_must_fail git rev-parse :0:x/wham &&
 290                git rev-parse >actual \
 291                        :2:x/wham :3:x/wham &&
 292                git rev-parse >expect \
 293                         A:y/wham  B:z/wham &&
 294                test_cmp expect actual &&
 295
 296                # Test that the two-way merge in x/wham is as expected
 297                git cat-file -p :2:x/wham >expect &&
 298                git cat-file -p :3:x/wham >other &&
 299                >empty &&
 300                test_must_fail git merge-file \
 301                        -L "HEAD" \
 302                        -L "" \
 303                        -L "B^0" \
 304                        expect empty other &&
 305                test_cmp expect x/wham
 306        )
 307'
 308
 309# Testcase 1e, Renamed directory, with all filenames being renamed too
 310#   (Related to testcases 9f & 9g)
 311#   Commit O: z/{oldb,oldc}
 312#   Commit A: y/{newb,newc}
 313#   Commit B: z/{oldb,oldc,d}
 314#   Expected: y/{newb,newc,d}
 315
 316test_expect_success '1e-setup: Renamed directory, with all files being renamed too' '
 317        test_create_repo 1e &&
 318        (
 319                cd 1e &&
 320
 321                mkdir z &&
 322                echo b >z/oldb &&
 323                echo c >z/oldc &&
 324                git add z &&
 325                test_tick &&
 326                git commit -m "O" &&
 327
 328                git branch O &&
 329                git branch A &&
 330                git branch B &&
 331
 332                git checkout A &&
 333                mkdir y &&
 334                git mv z/oldb y/newb &&
 335                git mv z/oldc y/newc &&
 336                test_tick &&
 337                git commit -m "A" &&
 338
 339                git checkout B &&
 340                echo d >z/d &&
 341                git add z/d &&
 342                test_tick &&
 343                git commit -m "B"
 344        )
 345'
 346
 347test_expect_success '1e-check: Renamed directory, with all files being renamed too' '
 348        (
 349                cd 1e &&
 350
 351                git checkout A^0 &&
 352
 353                git -c merge.directoryRenames=true merge -s recursive B^0 &&
 354
 355                git ls-files -s >out &&
 356                test_line_count = 3 out &&
 357
 358                git rev-parse >actual \
 359                        HEAD:y/newb HEAD:y/newc HEAD:y/d &&
 360                git rev-parse >expect \
 361                        O:z/oldb    O:z/oldc    B:z/d &&
 362                test_cmp expect actual &&
 363                test_must_fail git rev-parse HEAD:z/d
 364        )
 365'
 366
 367# Testcase 1f, Split a directory into two other directories
 368#   (Related to testcases 3a, all of section 2, and all of section 4)
 369#   Commit O: z/{b,c,d,e,f}
 370#   Commit A: z/{b,c,d,e,f,g}
 371#   Commit B: y/{b,c}, x/{d,e,f}
 372#   Expected: y/{b,c}, x/{d,e,f,g}
 373
 374test_expect_success '1f-setup: Split a directory into two other directories' '
 375        test_create_repo 1f &&
 376        (
 377                cd 1f &&
 378
 379                mkdir z &&
 380                echo b >z/b &&
 381                echo c >z/c &&
 382                echo d >z/d &&
 383                echo e >z/e &&
 384                echo f >z/f &&
 385                git add z &&
 386                test_tick &&
 387                git commit -m "O" &&
 388
 389                git branch O &&
 390                git branch A &&
 391                git branch B &&
 392
 393                git checkout A &&
 394                echo g >z/g &&
 395                git add z/g &&
 396                test_tick &&
 397                git commit -m "A" &&
 398
 399                git checkout B &&
 400                mkdir y &&
 401                mkdir x &&
 402                git mv z/b y/ &&
 403                git mv z/c y/ &&
 404                git mv z/d x/ &&
 405                git mv z/e x/ &&
 406                git mv z/f x/ &&
 407                rmdir z &&
 408                test_tick &&
 409                git commit -m "B"
 410        )
 411'
 412
 413test_expect_success '1f-check: Split a directory into two other directories' '
 414        (
 415                cd 1f &&
 416
 417                git checkout A^0 &&
 418
 419                git -c merge.directoryRenames=true merge -s recursive B^0 &&
 420
 421                git ls-files -s >out &&
 422                test_line_count = 6 out &&
 423
 424                git rev-parse >actual \
 425                        HEAD:y/b HEAD:y/c HEAD:x/d HEAD:x/e HEAD:x/f HEAD:x/g &&
 426                git rev-parse >expect \
 427                        O:z/b    O:z/c    O:z/d    O:z/e    O:z/f    A:z/g &&
 428                test_cmp expect actual &&
 429                test_path_is_missing z/g &&
 430                test_must_fail git rev-parse HEAD:z/g
 431        )
 432'
 433
 434###########################################################################
 435# Rules suggested by testcases in section 1:
 436#
 437#   We should still detect the directory rename even if it wasn't just
 438#   the directory renamed, but the files within it. (see 1b)
 439#
 440#   If renames split a directory into two or more others, the directory
 441#   with the most renames, "wins" (see 1c).  However, see the testcases
 442#   in section 2, plus testcases 3a and 4a.
 443###########################################################################
 444
 445
 446###########################################################################
 447# SECTION 2: Split into multiple directories, with equal number of paths
 448#
 449# Explore the splitting-a-directory rules a bit; what happens in the
 450# edge cases?
 451#
 452# Note that there is a closely related case of a directory not being
 453# split on either side of history, but being renamed differently on
 454# each side.  See testcase 8e for that.
 455###########################################################################
 456
 457# Testcase 2a, Directory split into two on one side, with equal numbers of paths
 458#   Commit O: z/{b,c}
 459#   Commit A: y/b, w/c
 460#   Commit B: z/{b,c,d}
 461#   Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict
 462test_expect_success '2a-setup: Directory split into two on one side, with equal numbers of paths' '
 463        test_create_repo 2a &&
 464        (
 465                cd 2a &&
 466
 467                mkdir z &&
 468                echo b >z/b &&
 469                echo c >z/c &&
 470                git add z &&
 471                test_tick &&
 472                git commit -m "O" &&
 473
 474                git branch O &&
 475                git branch A &&
 476                git branch B &&
 477
 478                git checkout A &&
 479                mkdir y &&
 480                mkdir w &&
 481                git mv z/b y/ &&
 482                git mv z/c w/ &&
 483                test_tick &&
 484                git commit -m "A" &&
 485
 486                git checkout B &&
 487                echo d >z/d &&
 488                git add z/d &&
 489                test_tick &&
 490                git commit -m "B"
 491        )
 492'
 493
 494test_expect_success '2a-check: Directory split into two on one side, with equal numbers of paths' '
 495        (
 496                cd 2a &&
 497
 498                git checkout A^0 &&
 499
 500                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 501                test_i18ngrep "CONFLICT.*directory rename split" out &&
 502
 503                git ls-files -s >out &&
 504                test_line_count = 3 out &&
 505                git ls-files -u >out &&
 506                test_line_count = 0 out &&
 507                git ls-files -o >out &&
 508                test_line_count = 1 out &&
 509
 510                git rev-parse >actual \
 511                        :0:y/b :0:w/c :0:z/d &&
 512                git rev-parse >expect \
 513                         O:z/b  O:z/c  B:z/d &&
 514                test_cmp expect actual
 515        )
 516'
 517
 518# Testcase 2b, Directory split into two on one side, with equal numbers of paths
 519#   Commit O: z/{b,c}
 520#   Commit A: y/b, w/c
 521#   Commit B: z/{b,c}, x/d
 522#   Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict
 523test_expect_success '2b-setup: Directory split into two on one side, with equal numbers of paths' '
 524        test_create_repo 2b &&
 525        (
 526                cd 2b &&
 527
 528                mkdir z &&
 529                echo b >z/b &&
 530                echo c >z/c &&
 531                git add z &&
 532                test_tick &&
 533                git commit -m "O" &&
 534
 535                git branch O &&
 536                git branch A &&
 537                git branch B &&
 538
 539                git checkout A &&
 540                mkdir y &&
 541                mkdir w &&
 542                git mv z/b y/ &&
 543                git mv z/c w/ &&
 544                test_tick &&
 545                git commit -m "A" &&
 546
 547                git checkout B &&
 548                mkdir x &&
 549                echo d >x/d &&
 550                git add x/d &&
 551                test_tick &&
 552                git commit -m "B"
 553        )
 554'
 555
 556test_expect_success '2b-check: Directory split into two on one side, with equal numbers of paths' '
 557        (
 558                cd 2b &&
 559
 560                git checkout A^0 &&
 561
 562                git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 563
 564                git ls-files -s >out &&
 565                test_line_count = 3 out &&
 566                git ls-files -u >out &&
 567                test_line_count = 0 out &&
 568                git ls-files -o >out &&
 569                test_line_count = 1 out &&
 570
 571                git rev-parse >actual \
 572                        :0:y/b :0:w/c :0:x/d &&
 573                git rev-parse >expect \
 574                         O:z/b  O:z/c  B:x/d &&
 575                test_cmp expect actual &&
 576                test_i18ngrep ! "CONFLICT.*directory rename split" out
 577        )
 578'
 579
 580###########################################################################
 581# Rules suggested by section 2:
 582#
 583#   None; the rule was already covered in section 1.  These testcases are
 584#   here just to make sure the conflict resolution and necessary warning
 585#   messages are handled correctly.
 586###########################################################################
 587
 588
 589###########################################################################
 590# SECTION 3: Path in question is the source path for some rename already
 591#
 592# Combining cases from Section 1 and trying to handle them could lead to
 593# directory renaming detection being over-applied.  So, this section
 594# provides some good testcases to check that the implementation doesn't go
 595# too far.
 596###########################################################################
 597
 598# Testcase 3a, Avoid implicit rename if involved as source on other side
 599#   (Related to testcases 1c, 1f, and 9h)
 600#   Commit O: z/{b,c,d}
 601#   Commit A: z/{b,c,d} (no change)
 602#   Commit B: y/{b,c}, x/d
 603#   Expected: y/{b,c}, x/d
 604test_expect_success '3a-setup: Avoid implicit rename if involved as source on other side' '
 605        test_create_repo 3a &&
 606        (
 607                cd 3a &&
 608
 609                mkdir z &&
 610                echo b >z/b &&
 611                echo c >z/c &&
 612                echo d >z/d &&
 613                git add z &&
 614                test_tick &&
 615                git commit -m "O" &&
 616
 617                git branch O &&
 618                git branch A &&
 619                git branch B &&
 620
 621                git checkout A &&
 622                test_tick &&
 623                git commit --allow-empty -m "A" &&
 624
 625                git checkout B &&
 626                mkdir y &&
 627                mkdir x &&
 628                git mv z/b y/ &&
 629                git mv z/c y/ &&
 630                git mv z/d x/ &&
 631                rmdir z &&
 632                test_tick &&
 633                git commit -m "B"
 634        )
 635'
 636
 637test_expect_success '3a-check: Avoid implicit rename if involved as source on other side' '
 638        (
 639                cd 3a &&
 640
 641                git checkout A^0 &&
 642
 643                git -c merge.directoryRenames=true merge -s recursive B^0 &&
 644
 645                git ls-files -s >out &&
 646                test_line_count = 3 out &&
 647
 648                git rev-parse >actual \
 649                        HEAD:y/b HEAD:y/c HEAD:x/d &&
 650                git rev-parse >expect \
 651                        O:z/b    O:z/c    O:z/d &&
 652                test_cmp expect actual
 653        )
 654'
 655
 656# Testcase 3b, Avoid implicit rename if involved as source on other side
 657#   (Related to testcases 5c and 7c, also kind of 1e and 1f)
 658#   Commit O: z/{b,c,d}
 659#   Commit A: y/{b,c}, x/d
 660#   Commit B: z/{b,c}, w/d
 661#   Expected: y/{b,c}, CONFLICT:(z/d -> x/d vs. w/d)
 662#   NOTE: We're particularly checking that since z/d is already involved as
 663#         a source in a file rename on the same side of history, that we don't
 664#         get it involved in directory rename detection.  If it were, we might
 665#         end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a
 666#         rename/rename/rename(1to3) conflict, which is just weird.
 667test_expect_success '3b-setup: Avoid implicit rename if involved as source on current side' '
 668        test_create_repo 3b &&
 669        (
 670                cd 3b &&
 671
 672                mkdir z &&
 673                echo b >z/b &&
 674                echo c >z/c &&
 675                echo d >z/d &&
 676                git add z &&
 677                test_tick &&
 678                git commit -m "O" &&
 679
 680                git branch O &&
 681                git branch A &&
 682                git branch B &&
 683
 684                git checkout A &&
 685                mkdir y &&
 686                mkdir x &&
 687                git mv z/b y/ &&
 688                git mv z/c y/ &&
 689                git mv z/d x/ &&
 690                rmdir z &&
 691                test_tick &&
 692                git commit -m "A" &&
 693
 694                git checkout B &&
 695                mkdir w &&
 696                git mv z/d w/ &&
 697                test_tick &&
 698                git commit -m "B"
 699        )
 700'
 701
 702test_expect_success '3b-check: Avoid implicit rename if involved as source on current side' '
 703        (
 704                cd 3b &&
 705
 706                git checkout A^0 &&
 707
 708                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 709                test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
 710                test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
 711
 712                git ls-files -s >out &&
 713                test_line_count = 5 out &&
 714                git ls-files -u >out &&
 715                test_line_count = 3 out &&
 716                git ls-files -o >out &&
 717                test_line_count = 1 out &&
 718
 719                git rev-parse >actual \
 720                        :0:y/b :0:y/c :1:z/d :2:x/d :3:w/d &&
 721                git rev-parse >expect \
 722                         O:z/b  O:z/c  O:z/d  O:z/d  O:z/d &&
 723                test_cmp expect actual &&
 724
 725                test_path_is_missing z/d &&
 726                git hash-object >actual \
 727                        x/d   w/d &&
 728                git rev-parse >expect \
 729                        O:z/d O:z/d &&
 730                test_cmp expect actual
 731        )
 732'
 733
 734###########################################################################
 735# Rules suggested by section 3:
 736#
 737#   Avoid directory-rename-detection for a path, if that path is the source
 738#   of a rename on either side of a merge.
 739###########################################################################
 740
 741
 742###########################################################################
 743# SECTION 4: Partially renamed directory; still exists on both sides of merge
 744#
 745# What if we were to attempt to do directory rename detection when someone
 746# "mostly" moved a directory but still left some files around, or,
 747# equivalently, fully renamed a directory in one commmit and then recreated
 748# that directory in a later commit adding some new files and then tried to
 749# merge?
 750#
 751# It's hard to divine user intent in these cases, because you can make an
 752# argument that, depending on the intermediate history of the side being
 753# merged, that some users will want files in that directory to
 754# automatically be detected and renamed, while users with a different
 755# intermediate history wouldn't want that rename to happen.
 756#
 757# I think that it is best to simply not have directory rename detection
 758# apply to such cases.  My reasoning for this is four-fold: (1) it's
 759# easiest for users in general to figure out what happened if we don't
 760# apply directory rename detection in any such case, (2) it's an easy rule
 761# to explain ["We don't do directory rename detection if the directory
 762# still exists on both sides of the merge"], (3) we can get some hairy
 763# edge/corner cases that would be really confusing and possibly not even
 764# representable in the index if we were to even try, and [related to 3] (4)
 765# attempting to resolve this issue of divining user intent by examining
 766# intermediate history goes against the spirit of three-way merges and is a
 767# path towards crazy corner cases that are far more complex than what we're
 768# already dealing with.
 769#
 770# Note that the wording of the rule ("We don't do directory rename
 771# detection if the directory still exists on both sides of the merge.")
 772# also excludes "renaming" of a directory into a subdirectory of itself
 773# (e.g. /some/dir/* -> /some/dir/subdir/*).  It may be possible to carve
 774# out an exception for "renaming"-beneath-itself cases without opening
 775# weird edge/corner cases for other partial directory renames, but for now
 776# we are keeping the rule simple.
 777#
 778# This section contains a test for a partially-renamed-directory case.
 779###########################################################################
 780
 781# Testcase 4a, Directory split, with original directory still present
 782#   (Related to testcase 1f)
 783#   Commit O: z/{b,c,d,e}
 784#   Commit A: y/{b,c,d}, z/e
 785#   Commit B: z/{b,c,d,e,f}
 786#   Expected: y/{b,c,d}, z/{e,f}
 787#   NOTE: Even though most files from z moved to y, we don't want f to follow.
 788
 789test_expect_success '4a-setup: Directory split, with original directory still present' '
 790        test_create_repo 4a &&
 791        (
 792                cd 4a &&
 793
 794                mkdir z &&
 795                echo b >z/b &&
 796                echo c >z/c &&
 797                echo d >z/d &&
 798                echo e >z/e &&
 799                git add z &&
 800                test_tick &&
 801                git commit -m "O" &&
 802
 803                git branch O &&
 804                git branch A &&
 805                git branch B &&
 806
 807                git checkout A &&
 808                mkdir y &&
 809                git mv z/b y/ &&
 810                git mv z/c y/ &&
 811                git mv z/d y/ &&
 812                test_tick &&
 813                git commit -m "A" &&
 814
 815                git checkout B &&
 816                echo f >z/f &&
 817                git add z/f &&
 818                test_tick &&
 819                git commit -m "B"
 820        )
 821'
 822
 823test_expect_success '4a-check: Directory split, with original directory still present' '
 824        (
 825                cd 4a &&
 826
 827                git checkout A^0 &&
 828
 829                git -c merge.directoryRenames=true merge -s recursive B^0 &&
 830
 831                git ls-files -s >out &&
 832                test_line_count = 5 out &&
 833                git ls-files -u >out &&
 834                test_line_count = 0 out &&
 835                git ls-files -o >out &&
 836                test_line_count = 1 out &&
 837
 838                git rev-parse >actual \
 839                        HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/e HEAD:z/f &&
 840                git rev-parse >expect \
 841                        O:z/b    O:z/c    O:z/d    O:z/e    B:z/f &&
 842                test_cmp expect actual
 843        )
 844'
 845
 846###########################################################################
 847# Rules suggested by section 4:
 848#
 849#   Directory-rename-detection should be turned off for any directories (as
 850#   a source for renames) that exist on both sides of the merge.  (The "as
 851#   a source for renames" clarification is due to cases like 1c where
 852#   the target directory exists on both sides and we do want the rename
 853#   detection.)  But, sadly, see testcase 8b.
 854###########################################################################
 855
 856
 857###########################################################################
 858# SECTION 5: Files/directories in the way of subset of to-be-renamed paths
 859#
 860# Implicitly renaming files due to a detected directory rename could run
 861# into problems if there are files or directories in the way of the paths
 862# we want to rename.  Explore such cases in this section.
 863###########################################################################
 864
 865# Testcase 5a, Merge directories, other side adds files to original and target
 866#   Commit O: z/{b,c},       y/d
 867#   Commit A: z/{b,c,e_1,f}, y/{d,e_2}
 868#   Commit B: y/{b,c,d}
 869#   Expected: z/e_1, y/{b,c,d,e_2,f} + CONFLICT warning
 870#   NOTE: While directory rename detection is active here causing z/f to
 871#         become y/f, we did not apply this for z/e_1 because that would
 872#         give us an add/add conflict for y/e_1 vs y/e_2.  This problem with
 873#         this add/add, is that both versions of y/e are from the same side
 874#         of history, giving us no way to represent this conflict in the
 875#         index.
 876
 877test_expect_success '5a-setup: Merge directories, other side adds files to original and target' '
 878        test_create_repo 5a &&
 879        (
 880                cd 5a &&
 881
 882                mkdir z &&
 883                echo b >z/b &&
 884                echo c >z/c &&
 885                mkdir y &&
 886                echo d >y/d &&
 887                git add z y &&
 888                test_tick &&
 889                git commit -m "O" &&
 890
 891                git branch O &&
 892                git branch A &&
 893                git branch B &&
 894
 895                git checkout A &&
 896                echo e1 >z/e &&
 897                echo f >z/f &&
 898                echo e2 >y/e &&
 899                git add z/e z/f y/e &&
 900                test_tick &&
 901                git commit -m "A" &&
 902
 903                git checkout B &&
 904                git mv z/b y/ &&
 905                git mv z/c y/ &&
 906                rmdir z &&
 907                test_tick &&
 908                git commit -m "B"
 909        )
 910'
 911
 912test_expect_success '5a-check: Merge directories, other side adds files to original and target' '
 913        (
 914                cd 5a &&
 915
 916                git checkout A^0 &&
 917
 918                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 919                test_i18ngrep "CONFLICT.*implicit dir rename" out &&
 920
 921                git ls-files -s >out &&
 922                test_line_count = 6 out &&
 923                git ls-files -u >out &&
 924                test_line_count = 0 out &&
 925                git ls-files -o >out &&
 926                test_line_count = 1 out &&
 927
 928                git rev-parse >actual \
 929                        :0:y/b :0:y/c :0:y/d :0:y/e :0:z/e :0:y/f &&
 930                git rev-parse >expect \
 931                         O:z/b  O:z/c  O:y/d  A:y/e  A:z/e  A:z/f &&
 932                test_cmp expect actual
 933        )
 934'
 935
 936# Testcase 5b, Rename/delete in order to get add/add/add conflict
 937#   (Related to testcase 8d; these may appear slightly inconsistent to users;
 938#    Also related to testcases 7d and 7e)
 939#   Commit O: z/{b,c,d_1}
 940#   Commit A: y/{b,c,d_2}
 941#   Commit B: z/{b,c,d_1,e}, y/d_3
 942#   Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3)
 943#   NOTE: If z/d_1 in commit B were to be involved in dir rename detection, as
 944#         we normaly would since z/ is being renamed to y/, then this would be
 945#         a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add
 946#         conflict of y/d_1 vs. y/d_2 vs. y/d_3.  Add/add/add is not
 947#         representable in the index, so the existence of y/d_3 needs to
 948#         cause us to bail on directory rename detection for that path, falling
 949#         back to git behavior without the directory rename detection.
 950
 951test_expect_success '5b-setup: Rename/delete in order to get add/add/add conflict' '
 952        test_create_repo 5b &&
 953        (
 954                cd 5b &&
 955
 956                mkdir z &&
 957                echo b >z/b &&
 958                echo c >z/c &&
 959                echo d1 >z/d &&
 960                git add z &&
 961                test_tick &&
 962                git commit -m "O" &&
 963
 964                git branch O &&
 965                git branch A &&
 966                git branch B &&
 967
 968                git checkout A &&
 969                git rm z/d &&
 970                git mv z y &&
 971                echo d2 >y/d &&
 972                git add y/d &&
 973                test_tick &&
 974                git commit -m "A" &&
 975
 976                git checkout B &&
 977                mkdir y &&
 978                echo d3 >y/d &&
 979                echo e >z/e &&
 980                git add y/d z/e &&
 981                test_tick &&
 982                git commit -m "B"
 983        )
 984'
 985
 986test_expect_success '5b-check: Rename/delete in order to get add/add/add conflict' '
 987        (
 988                cd 5b &&
 989
 990                git checkout A^0 &&
 991
 992                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 993                test_i18ngrep "CONFLICT (add/add).* y/d" out &&
 994
 995                git ls-files -s >out &&
 996                test_line_count = 5 out &&
 997                git ls-files -u >out &&
 998                test_line_count = 2 out &&
 999                git ls-files -o >out &&
1000                test_line_count = 1 out &&
1001
1002                git rev-parse >actual \
1003                        :0:y/b :0:y/c :0:y/e :2:y/d :3:y/d &&
1004                git rev-parse >expect \
1005                         O:z/b  O:z/c  B:z/e  A:y/d  B:y/d &&
1006                test_cmp expect actual &&
1007
1008                test_must_fail git rev-parse :1:y/d &&
1009                test_path_is_file y/d
1010        )
1011'
1012
1013# Testcase 5c, Transitive rename would cause rename/rename/rename/add/add/add
1014#   (Directory rename detection would result in transitive rename vs.
1015#    rename/rename(1to2) and turn it into a rename/rename(1to3).  Further,
1016#    rename paths conflict with separate adds on the other side)
1017#   (Related to testcases 3b and 7c)
1018#   Commit O: z/{b,c}, x/d_1
1019#   Commit A: y/{b,c,d_2}, w/d_1
1020#   Commit B: z/{b,c,d_1,e}, w/d_3, y/d_4
1021#   Expected: A mess, but only a rename/rename(1to2)/add/add mess.  Use the
1022#             presence of y/d_4 in B to avoid doing transitive rename of
1023#             x/d_1 -> z/d_1 -> y/d_1, so that the only paths we have at
1024#             y/d are y/d_2 and y/d_4.  We still do the move from z/e to y/e,
1025#             though, because it doesn't have anything in the way.
1026
1027test_expect_success '5c-setup: Transitive rename would cause rename/rename/rename/add/add/add' '
1028        test_create_repo 5c &&
1029        (
1030                cd 5c &&
1031
1032                mkdir z &&
1033                echo b >z/b &&
1034                echo c >z/c &&
1035                mkdir x &&
1036                echo d1 >x/d &&
1037                git add z x &&
1038                test_tick &&
1039                git commit -m "O" &&
1040
1041                git branch O &&
1042                git branch A &&
1043                git branch B &&
1044
1045                git checkout A &&
1046                git mv z y &&
1047                echo d2 >y/d &&
1048                git add y/d &&
1049                git mv x w &&
1050                test_tick &&
1051                git commit -m "A" &&
1052
1053                git checkout B &&
1054                git mv x/d z/ &&
1055                mkdir w &&
1056                mkdir y &&
1057                echo d3 >w/d &&
1058                echo d4 >y/d &&
1059                echo e >z/e &&
1060                git add w/ y/ z/e &&
1061                test_tick &&
1062                git commit -m "B"
1063        )
1064'
1065
1066test_expect_success '5c-check: Transitive rename would cause rename/rename/rename/add/add/add' '
1067        (
1068                cd 5c &&
1069
1070                git checkout A^0 &&
1071
1072                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1073                test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
1074                test_i18ngrep "CONFLICT (add/add).* y/d" out &&
1075
1076                git ls-files -s >out &&
1077                test_line_count = 9 out &&
1078                git ls-files -u >out &&
1079                test_line_count = 6 out &&
1080                git ls-files -o >out &&
1081                test_line_count = 1 out &&
1082
1083                git rev-parse >actual \
1084                        :0:y/b :0:y/c :0:y/e &&
1085                git rev-parse >expect \
1086                         O:z/b  O:z/c  B:z/e &&
1087                test_cmp expect actual &&
1088
1089                test_must_fail git rev-parse :1:y/d &&
1090                git rev-parse >actual \
1091                        :2:w/d :3:w/d :1:x/d :2:y/d :3:y/d :3:z/d &&
1092                git rev-parse >expect \
1093                         O:x/d  B:w/d  O:x/d  A:y/d  B:y/d  O:x/d &&
1094                test_cmp expect actual &&
1095
1096                git hash-object >actual \
1097                        z/d &&
1098                git rev-parse >expect \
1099                        O:x/d &&
1100                test_cmp expect actual &&
1101                test_path_is_missing x/d &&
1102                test_path_is_file y/d &&
1103                grep -q "<<<<" y/d  # conflict markers should be present
1104        )
1105'
1106
1107# Testcase 5d, Directory/file/file conflict due to directory rename
1108#   Commit O: z/{b,c}
1109#   Commit A: y/{b,c,d_1}
1110#   Commit B: z/{b,c,d_2,f}, y/d/e
1111#   Expected: y/{b,c,d/e,f}, z/d_2, CONFLICT(file/directory), y/d_1~HEAD
1112#   Note: The fact that y/d/ exists in B makes us bail on directory rename
1113#         detection for z/d_2, but that doesn't prevent us from applying the
1114#         directory rename detection for z/f -> y/f.
1115
1116test_expect_success '5d-setup: Directory/file/file conflict due to directory rename' '
1117        test_create_repo 5d &&
1118        (
1119                cd 5d &&
1120
1121                mkdir z &&
1122                echo b >z/b &&
1123                echo c >z/c &&
1124                git add z &&
1125                test_tick &&
1126                git commit -m "O" &&
1127
1128                git branch O &&
1129                git branch A &&
1130                git branch B &&
1131
1132                git checkout A &&
1133                git mv z y &&
1134                echo d1 >y/d &&
1135                git add y/d &&
1136                test_tick &&
1137                git commit -m "A" &&
1138
1139                git checkout B &&
1140                mkdir -p y/d &&
1141                echo e >y/d/e &&
1142                echo d2 >z/d &&
1143                echo f >z/f &&
1144                git add y/d/e z/d z/f &&
1145                test_tick &&
1146                git commit -m "B"
1147        )
1148'
1149
1150test_expect_success '5d-check: Directory/file/file conflict due to directory rename' '
1151        (
1152                cd 5d &&
1153
1154                git checkout A^0 &&
1155
1156                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1157                test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
1158
1159                git ls-files -s >out &&
1160                test_line_count = 6 out &&
1161                git ls-files -u >out &&
1162                test_line_count = 1 out &&
1163                git ls-files -o >out &&
1164                test_line_count = 2 out &&
1165
1166                git rev-parse >actual \
1167                        :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e &&
1168                git rev-parse >expect \
1169                         O:z/b  O:z/c  B:z/d  B:z/f  A:y/d  B:y/d/e &&
1170                test_cmp expect actual &&
1171
1172                git hash-object y/d~HEAD >actual &&
1173                git rev-parse A:y/d >expect &&
1174                test_cmp expect actual
1175        )
1176'
1177
1178###########################################################################
1179# Rules suggested by section 5:
1180#
1181#   If a subset of to-be-renamed files have a file or directory in the way,
1182#   "turn off" the directory rename for those specific sub-paths, falling
1183#   back to old handling.  But, sadly, see testcases 8a and 8b.
1184###########################################################################
1185
1186
1187###########################################################################
1188# SECTION 6: Same side of the merge was the one that did the rename
1189#
1190# It may sound obvious that you only want to apply implicit directory
1191# renames to directories if the _other_ side of history did the renaming.
1192# If you did make an implementation that didn't explicitly enforce this
1193# rule, the majority of cases that would fall under this section would
1194# also be solved by following the rules from the above sections.  But
1195# there are still a few that stick out, so this section covers them just
1196# to make sure we also get them right.
1197###########################################################################
1198
1199# Testcase 6a, Tricky rename/delete
1200#   Commit O: z/{b,c,d}
1201#   Commit A: z/b
1202#   Commit B: y/{b,c}, z/d
1203#   Expected: y/b, CONFLICT(rename/delete, z/c -> y/c vs. NULL)
1204#   Note: We're just checking here that the rename of z/b and z/c to put
1205#         them under y/ doesn't accidentally catch z/d and make it look like
1206#         it is also involved in a rename/delete conflict.
1207
1208test_expect_success '6a-setup: Tricky rename/delete' '
1209        test_create_repo 6a &&
1210        (
1211                cd 6a &&
1212
1213                mkdir z &&
1214                echo b >z/b &&
1215                echo c >z/c &&
1216                echo d >z/d &&
1217                git add z &&
1218                test_tick &&
1219                git commit -m "O" &&
1220
1221                git branch O &&
1222                git branch A &&
1223                git branch B &&
1224
1225                git checkout A &&
1226                git rm z/c &&
1227                git rm z/d &&
1228                test_tick &&
1229                git commit -m "A" &&
1230
1231                git checkout B &&
1232                mkdir y &&
1233                git mv z/b y/ &&
1234                git mv z/c y/ &&
1235                test_tick &&
1236                git commit -m "B"
1237        )
1238'
1239
1240test_expect_success '6a-check: Tricky rename/delete' '
1241        (
1242                cd 6a &&
1243
1244                git checkout A^0 &&
1245
1246                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1247                test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&
1248
1249                git ls-files -s >out &&
1250                test_line_count = 2 out &&
1251                git ls-files -u >out &&
1252                test_line_count = 1 out &&
1253                git ls-files -o >out &&
1254                test_line_count = 1 out &&
1255
1256                git rev-parse >actual \
1257                        :0:y/b :3:y/c &&
1258                git rev-parse >expect \
1259                         O:z/b  O:z/c &&
1260                test_cmp expect actual
1261        )
1262'
1263
1264# Testcase 6b, Same rename done on both sides
1265#   (Related to testcases 6c and 8e)
1266#   Commit O: z/{b,c}
1267#   Commit A: y/{b,c}
1268#   Commit B: y/{b,c}, z/d
1269#   Expected: y/{b,c}, z/d
1270#   Note: If we did directory rename detection here, we'd move z/d into y/,
1271#         but B did that rename and still decided to put the file into z/,
1272#         so we probably shouldn't apply directory rename detection for it.
1273
1274test_expect_success '6b-setup: Same rename done on both sides' '
1275        test_create_repo 6b &&
1276        (
1277                cd 6b &&
1278
1279                mkdir z &&
1280                echo b >z/b &&
1281                echo c >z/c &&
1282                git add z &&
1283                test_tick &&
1284                git commit -m "O" &&
1285
1286                git branch O &&
1287                git branch A &&
1288                git branch B &&
1289
1290                git checkout A &&
1291                git mv z y &&
1292                test_tick &&
1293                git commit -m "A" &&
1294
1295                git checkout B &&
1296                git mv z y &&
1297                mkdir z &&
1298                echo d >z/d &&
1299                git add z/d &&
1300                test_tick &&
1301                git commit -m "B"
1302        )
1303'
1304
1305test_expect_success '6b-check: Same rename done on both sides' '
1306        (
1307                cd 6b &&
1308
1309                git checkout A^0 &&
1310
1311                git -c merge.directoryRenames=true merge -s recursive B^0 &&
1312
1313                git ls-files -s >out &&
1314                test_line_count = 3 out &&
1315                git ls-files -u >out &&
1316                test_line_count = 0 out &&
1317                git ls-files -o >out &&
1318                test_line_count = 1 out &&
1319
1320                git rev-parse >actual \
1321                        HEAD:y/b HEAD:y/c HEAD:z/d &&
1322                git rev-parse >expect \
1323                        O:z/b    O:z/c    B:z/d &&
1324                test_cmp expect actual
1325        )
1326'
1327
1328# Testcase 6c, Rename only done on same side
1329#   (Related to testcases 6b and 8e)
1330#   Commit O: z/{b,c}
1331#   Commit A: z/{b,c} (no change)
1332#   Commit B: y/{b,c}, z/d
1333#   Expected: y/{b,c}, z/d
1334#   NOTE: Seems obvious, but just checking that the implementation doesn't
1335#         "accidentally detect a rename" and give us y/{b,c,d}.
1336
1337test_expect_success '6c-setup: Rename only done on same side' '
1338        test_create_repo 6c &&
1339        (
1340                cd 6c &&
1341
1342                mkdir z &&
1343                echo b >z/b &&
1344                echo c >z/c &&
1345                git add z &&
1346                test_tick &&
1347                git commit -m "O" &&
1348
1349                git branch O &&
1350                git branch A &&
1351                git branch B &&
1352
1353                git checkout A &&
1354                test_tick &&
1355                git commit --allow-empty -m "A" &&
1356
1357                git checkout B &&
1358                git mv z y &&
1359                mkdir z &&
1360                echo d >z/d &&
1361                git add z/d &&
1362                test_tick &&
1363                git commit -m "B"
1364        )
1365'
1366
1367test_expect_success '6c-check: Rename only done on same side' '
1368        (
1369                cd 6c &&
1370
1371                git checkout A^0 &&
1372
1373                git -c merge.directoryRenames=true merge -s recursive B^0 &&
1374
1375                git ls-files -s >out &&
1376                test_line_count = 3 out &&
1377                git ls-files -u >out &&
1378                test_line_count = 0 out &&
1379                git ls-files -o >out &&
1380                test_line_count = 1 out &&
1381
1382                git rev-parse >actual \
1383                        HEAD:y/b HEAD:y/c HEAD:z/d &&
1384                git rev-parse >expect \
1385                        O:z/b    O:z/c    B:z/d &&
1386                test_cmp expect actual
1387        )
1388'
1389
1390# Testcase 6d, We don't always want transitive renaming
1391#   (Related to testcase 1c)
1392#   Commit O: z/{b,c}, x/d
1393#   Commit A: z/{b,c}, x/d (no change)
1394#   Commit B: y/{b,c}, z/d
1395#   Expected: y/{b,c}, z/d
1396#   NOTE: Again, this seems obvious but just checking that the implementation
1397#         doesn't "accidentally detect a rename" and give us y/{b,c,d}.
1398
1399test_expect_success '6d-setup: We do not always want transitive renaming' '
1400        test_create_repo 6d &&
1401        (
1402                cd 6d &&
1403
1404                mkdir z &&
1405                echo b >z/b &&
1406                echo c >z/c &&
1407                mkdir x &&
1408                echo d >x/d &&
1409                git add z x &&
1410                test_tick &&
1411                git commit -m "O" &&
1412
1413                git branch O &&
1414                git branch A &&
1415                git branch B &&
1416
1417                git checkout A &&
1418                test_tick &&
1419                git commit --allow-empty -m "A" &&
1420
1421                git checkout B &&
1422                git mv z y &&
1423                git mv x z &&
1424                test_tick &&
1425                git commit -m "B"
1426        )
1427'
1428
1429test_expect_success '6d-check: We do not always want transitive renaming' '
1430        (
1431                cd 6d &&
1432
1433                git checkout A^0 &&
1434
1435                git -c merge.directoryRenames=true merge -s recursive B^0 &&
1436
1437                git ls-files -s >out &&
1438                test_line_count = 3 out &&
1439                git ls-files -u >out &&
1440                test_line_count = 0 out &&
1441                git ls-files -o >out &&
1442                test_line_count = 1 out &&
1443
1444                git rev-parse >actual \
1445                        HEAD:y/b HEAD:y/c HEAD:z/d &&
1446                git rev-parse >expect \
1447                        O:z/b    O:z/c    O:x/d &&
1448                test_cmp expect actual
1449        )
1450'
1451
1452# Testcase 6e, Add/add from one-side
1453#   Commit O: z/{b,c}
1454#   Commit A: z/{b,c} (no change)
1455#   Commit B: y/{b,c,d_1}, z/d_2
1456#   Expected: y/{b,c,d_1}, z/d_2
1457#   NOTE: Again, this seems obvious but just checking that the implementation
1458#         doesn't "accidentally detect a rename" and give us y/{b,c} +
1459#         add/add conflict on y/d_1 vs y/d_2.
1460
1461test_expect_success '6e-setup: Add/add from one side' '
1462        test_create_repo 6e &&
1463        (
1464                cd 6e &&
1465
1466                mkdir z &&
1467                echo b >z/b &&
1468                echo c >z/c &&
1469                git add z &&
1470                test_tick &&
1471                git commit -m "O" &&
1472
1473                git branch O &&
1474                git branch A &&
1475                git branch B &&
1476
1477                git checkout A &&
1478                test_tick &&
1479                git commit --allow-empty -m "A" &&
1480
1481                git checkout B &&
1482                git mv z y &&
1483                echo d1 > y/d &&
1484                mkdir z &&
1485                echo d2 > z/d &&
1486                git add y/d z/d &&
1487                test_tick &&
1488                git commit -m "B"
1489        )
1490'
1491
1492test_expect_success '6e-check: Add/add from one side' '
1493        (
1494                cd 6e &&
1495
1496                git checkout A^0 &&
1497
1498                git -c merge.directoryRenames=true merge -s recursive B^0 &&
1499
1500                git ls-files -s >out &&
1501                test_line_count = 4 out &&
1502                git ls-files -u >out &&
1503                test_line_count = 0 out &&
1504                git ls-files -o >out &&
1505                test_line_count = 1 out &&
1506
1507                git rev-parse >actual \
1508                        HEAD:y/b HEAD:y/c HEAD:y/d HEAD:z/d &&
1509                git rev-parse >expect \
1510                        O:z/b    O:z/c    B:y/d    B:z/d &&
1511                test_cmp expect actual
1512        )
1513'
1514
1515###########################################################################
1516# Rules suggested by section 6:
1517#
1518#   Only apply implicit directory renames to directories if the other
1519#   side of history is the one doing the renaming.
1520###########################################################################
1521
1522
1523###########################################################################
1524# SECTION 7: More involved Edge/Corner cases
1525#
1526# The ruleset we have generated in the above sections seems to provide
1527# well-defined merges.  But can we find edge/corner cases that either (a)
1528# are harder for users to understand, or (b) have a resolution that is
1529# non-intuitive or suboptimal?
1530#
1531# The testcases in this section dive into cases that I've tried to craft in
1532# a way to find some that might be surprising to users or difficult for
1533# them to understand (the next section will look at non-intuitive or
1534# suboptimal merge results).  Some of the testcases are similar to ones
1535# from past sections, but have been simplified to try to highlight error
1536# messages using a "modified" path (due to the directory rename).  Are
1537# users okay with these?
1538#
1539# In my opinion, testcases that are difficult to understand from this
1540# section is due to difficulty in the testcase rather than the directory
1541# renaming (similar to how t6042 and t6036 have difficult resolutions due
1542# to the problem setup itself being complex).  And I don't think the
1543# error messages are a problem.
1544#
1545# On the other hand, the testcases in section 8 worry me slightly more...
1546###########################################################################
1547
1548# Testcase 7a, rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file
1549#   Commit O: z/{b,c}
1550#   Commit A: y/{b,c}
1551#   Commit B: w/b, x/c, z/d
1552#   Expected: y/d, CONFLICT(rename/rename for both z/b and z/c)
1553#   NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d.
1554
1555test_expect_success '7a-setup: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file' '
1556        test_create_repo 7a &&
1557        (
1558                cd 7a &&
1559
1560                mkdir z &&
1561                echo b >z/b &&
1562                echo c >z/c &&
1563                git add z &&
1564                test_tick &&
1565                git commit -m "O" &&
1566
1567                git branch O &&
1568                git branch A &&
1569                git branch B &&
1570
1571                git checkout A &&
1572                git mv z y &&
1573                test_tick &&
1574                git commit -m "A" &&
1575
1576                git checkout B &&
1577                mkdir w &&
1578                mkdir x &&
1579                git mv z/b w/ &&
1580                git mv z/c x/ &&
1581                echo d > z/d &&
1582                git add z/d &&
1583                test_tick &&
1584                git commit -m "B"
1585        )
1586'
1587
1588test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS add-other-file' '
1589        (
1590                cd 7a &&
1591
1592                git checkout A^0 &&
1593
1594                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1595                test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
1596                test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
1597
1598                git ls-files -s >out &&
1599                test_line_count = 7 out &&
1600                git ls-files -u >out &&
1601                test_line_count = 6 out &&
1602                git ls-files -o >out &&
1603                test_line_count = 1 out &&
1604
1605                git rev-parse >actual \
1606                        :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:x/c :0:y/d &&
1607                git rev-parse >expect \
1608                         O:z/b  O:z/b  O:z/b  O:z/c  O:z/c  O:z/c  B:z/d &&
1609                test_cmp expect actual &&
1610
1611                git hash-object >actual \
1612                        y/b   w/b   y/c   x/c &&
1613                git rev-parse >expect \
1614                        O:z/b O:z/b O:z/c O:z/c &&
1615                test_cmp expect actual
1616        )
1617'
1618
1619# Testcase 7b, rename/rename(2to1), but only due to transitive rename
1620#   (Related to testcase 1d)
1621#   Commit O: z/{b,c},     x/d_1, w/d_2
1622#   Commit A: y/{b,c,d_2}, x/d_1
1623#   Commit B: z/{b,c,d_1},        w/d_2
1624#   Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d)
1625
1626test_expect_success '7b-setup: rename/rename(2to1), but only due to transitive rename' '
1627        test_create_repo 7b &&
1628        (
1629                cd 7b &&
1630
1631                mkdir z &&
1632                mkdir x &&
1633                mkdir w &&
1634                echo b >z/b &&
1635                echo c >z/c &&
1636                echo d1 > x/d &&
1637                echo d2 > w/d &&
1638                git add z x w &&
1639                test_tick &&
1640                git commit -m "O" &&
1641
1642                git branch O &&
1643                git branch A &&
1644                git branch B &&
1645
1646                git checkout A &&
1647                git mv z y &&
1648                git mv w/d y/ &&
1649                test_tick &&
1650                git commit -m "A" &&
1651
1652                git checkout B &&
1653                git mv x/d z/ &&
1654                rmdir x &&
1655                test_tick &&
1656                git commit -m "B"
1657        )
1658'
1659
1660test_expect_success '7b-check: rename/rename(2to1), but only due to transitive rename' '
1661        (
1662                cd 7b &&
1663
1664                git checkout A^0 &&
1665
1666                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1667                test_i18ngrep "CONFLICT (rename/rename)" out &&
1668
1669                git ls-files -s >out &&
1670                test_line_count = 4 out &&
1671                git ls-files -u >out &&
1672                test_line_count = 2 out &&
1673                git ls-files -o >out &&
1674                test_line_count = 1 out &&
1675
1676                git rev-parse >actual \
1677                        :0:y/b :0:y/c :2:y/d :3:y/d &&
1678                git rev-parse >expect \
1679                         O:z/b  O:z/c  O:w/d  O:x/d &&
1680                test_cmp expect actual &&
1681
1682                # Test that the two-way merge in y/d is as expected
1683                git cat-file -p :2:y/d >expect &&
1684                git cat-file -p :3:y/d >other &&
1685                >empty &&
1686                test_must_fail git merge-file \
1687                        -L "HEAD" \
1688                        -L "" \
1689                        -L "B^0" \
1690                        expect empty other &&
1691                test_cmp expect y/d
1692        )
1693'
1694
1695# Testcase 7c, rename/rename(1to...2or3); transitive rename may add complexity
1696#   (Related to testcases 3b and 5c)
1697#   Commit O: z/{b,c}, x/d
1698#   Commit A: y/{b,c}, w/d
1699#   Commit B: z/{b,c,d}
1700#   Expected: y/{b,c}, CONFLICT(x/d -> w/d vs. y/d)
1701#   NOTE: z/ was renamed to y/ so we do want to report
1702#         neither CONFLICT(x/d -> w/d vs. z/d)
1703#         nor CONFLiCT x/d -> w/d vs. y/d vs. z/d)
1704
1705test_expect_success '7c-setup: rename/rename(1to...2or3); transitive rename may add complexity' '
1706        test_create_repo 7c &&
1707        (
1708                cd 7c &&
1709
1710                mkdir z &&
1711                echo b >z/b &&
1712                echo c >z/c &&
1713                mkdir x &&
1714                echo d >x/d &&
1715                git add z x &&
1716                test_tick &&
1717                git commit -m "O" &&
1718
1719                git branch O &&
1720                git branch A &&
1721                git branch B &&
1722
1723                git checkout A &&
1724                git mv z y &&
1725                git mv x w &&
1726                test_tick &&
1727                git commit -m "A" &&
1728
1729                git checkout B &&
1730                git mv x/d z/ &&
1731                rmdir x &&
1732                test_tick &&
1733                git commit -m "B"
1734        )
1735'
1736
1737test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may add complexity' '
1738        (
1739                cd 7c &&
1740
1741                git checkout A^0 &&
1742
1743                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1744                test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
1745
1746                git ls-files -s >out &&
1747                test_line_count = 5 out &&
1748                git ls-files -u >out &&
1749                test_line_count = 3 out &&
1750                git ls-files -o >out &&
1751                test_line_count = 1 out &&
1752
1753                git rev-parse >actual \
1754                        :0:y/b :0:y/c :1:x/d :2:w/d :3:y/d &&
1755                git rev-parse >expect \
1756                         O:z/b  O:z/c  O:x/d  O:x/d  O:x/d &&
1757                test_cmp expect actual
1758        )
1759'
1760
1761# Testcase 7d, transitive rename involved in rename/delete; how is it reported?
1762#   (Related somewhat to testcases 5b and 8d)
1763#   Commit O: z/{b,c}, x/d
1764#   Commit A: y/{b,c}
1765#   Commit B: z/{b,c,d}
1766#   Expected: y/{b,c}, CONFLICT(delete x/d vs rename to y/d)
1767#   NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d)
1768
1769test_expect_success '7d-setup: transitive rename involved in rename/delete; how is it reported?' '
1770        test_create_repo 7d &&
1771        (
1772                cd 7d &&
1773
1774                mkdir z &&
1775                echo b >z/b &&
1776                echo c >z/c &&
1777                mkdir x &&
1778                echo d >x/d &&
1779                git add z x &&
1780                test_tick &&
1781                git commit -m "O" &&
1782
1783                git branch O &&
1784                git branch A &&
1785                git branch B &&
1786
1787                git checkout A &&
1788                git mv z y &&
1789                git rm -rf x &&
1790                test_tick &&
1791                git commit -m "A" &&
1792
1793                git checkout B &&
1794                git mv x/d z/ &&
1795                rmdir x &&
1796                test_tick &&
1797                git commit -m "B"
1798        )
1799'
1800
1801test_expect_success '7d-check: transitive rename involved in rename/delete; how is it reported?' '
1802        (
1803                cd 7d &&
1804
1805                git checkout A^0 &&
1806
1807                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1808                test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
1809
1810                git ls-files -s >out &&
1811                test_line_count = 3 out &&
1812                git ls-files -u >out &&
1813                test_line_count = 1 out &&
1814                git ls-files -o >out &&
1815                test_line_count = 1 out &&
1816
1817                git rev-parse >actual \
1818                        :0:y/b :0:y/c :3:y/d &&
1819                git rev-parse >expect \
1820                         O:z/b  O:z/c  O:x/d &&
1821                test_cmp expect actual
1822        )
1823'
1824
1825# Testcase 7e, transitive rename in rename/delete AND dirs in the way
1826#   (Very similar to 'both rename source and destination involved in D/F conflict' from t6022-merge-rename.sh)
1827#   (Also related to testcases 9c and 9d)
1828#   Commit O: z/{b,c},     x/d_1
1829#   Commit A: y/{b,c,d/g}, x/d/f
1830#   Commit B: z/{b,c,d_1}
1831#   Expected: rename/delete(x/d_1->y/d_1 vs. None) + D/F conflict on y/d
1832#             y/{b,c,d/g}, y/d_1~B^0, x/d/f
1833
1834#   NOTE: The main path of interest here is d_1 and where it ends up, but
1835#         this is actually a case that has two potential directory renames
1836#         involved and D/F conflict(s), so it makes sense to walk through
1837#         each step.
1838#
1839#         Commit A renames z/ -> y/.  Thus everything that B adds to z/
1840#         should be instead moved to y/.  This gives us the D/F conflict on
1841#         y/d because x/d_1 -> z/d_1 -> y/d_1 conflicts with y/d/g.
1842#
1843#         Further, commit B renames x/ -> z/, thus everything A adds to x/
1844#         should instead be moved to z/...BUT we removed z/ and renamed it
1845#         to y/, so maybe everything should move not from x/ to z/, but
1846#         from x/ to z/ to y/.  Doing so might make sense from the logic so
1847#         far, but note that commit A had both an x/ and a y/; it did the
1848#         renaming of z/ to y/ and created x/d/f and it clearly made these
1849#         things separate, so it doesn't make much sense to push these
1850#         together.  Doing so is what I'd call a doubly transitive rename;
1851#         see testcases 9c and 9d for further discussion of this issue and
1852#         how it's resolved.
1853
1854test_expect_success '7e-setup: transitive rename in rename/delete AND dirs in the way' '
1855        test_create_repo 7e &&
1856        (
1857                cd 7e &&
1858
1859                mkdir z &&
1860                echo b >z/b &&
1861                echo c >z/c &&
1862                mkdir x &&
1863                echo d1 >x/d &&
1864                git add z x &&
1865                test_tick &&
1866                git commit -m "O" &&
1867
1868                git branch O &&
1869                git branch A &&
1870                git branch B &&
1871
1872                git checkout A &&
1873                git mv z y &&
1874                git rm x/d &&
1875                mkdir -p x/d &&
1876                mkdir -p y/d &&
1877                echo f >x/d/f &&
1878                echo g >y/d/g &&
1879                git add x/d/f y/d/g &&
1880                test_tick &&
1881                git commit -m "A" &&
1882
1883                git checkout B &&
1884                git mv x/d z/ &&
1885                rmdir x &&
1886                test_tick &&
1887                git commit -m "B"
1888        )
1889'
1890
1891test_expect_success '7e-check: transitive rename in rename/delete AND dirs in the way' '
1892        (
1893                cd 7e &&
1894
1895                git checkout A^0 &&
1896
1897                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
1898                test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
1899
1900                git ls-files -s >out &&
1901                test_line_count = 5 out &&
1902                git ls-files -u >out &&
1903                test_line_count = 1 out &&
1904                git ls-files -o >out &&
1905                test_line_count = 2 out &&
1906
1907                git rev-parse >actual \
1908                        :0:x/d/f :0:y/d/g :0:y/b :0:y/c :3:y/d &&
1909                git rev-parse >expect \
1910                         A:x/d/f  A:y/d/g  O:z/b  O:z/c  O:x/d &&
1911                test_cmp expect actual &&
1912
1913                git hash-object y/d~B^0 >actual &&
1914                git rev-parse O:x/d >expect &&
1915                test_cmp expect actual
1916        )
1917'
1918
1919###########################################################################
1920# SECTION 8: Suboptimal merges
1921#
1922# As alluded to in the last section, the ruleset we have built up for
1923# detecting directory renames unfortunately has some special cases where it
1924# results in slightly suboptimal or non-intuitive behavior.  This section
1925# explores these cases.
1926#
1927# To be fair, we already had non-intuitive or suboptimal behavior for most
1928# of these cases in git before introducing implicit directory rename
1929# detection, but it'd be nice if there was a modified ruleset out there
1930# that handled these cases a bit better.
1931###########################################################################
1932
1933# Testcase 8a, Dual-directory rename, one into the others' way
1934#   Commit O. x/{a,b},   y/{c,d}
1935#   Commit A. x/{a,b,e}, y/{c,d,f}
1936#   Commit B. y/{a,b},   z/{c,d}
1937#
1938# Possible Resolutions:
1939#   w/o dir-rename detection: y/{a,b,f},   z/{c,d},   x/e
1940#   Currently expected:       y/{a,b,e,f}, z/{c,d}
1941#   Optimal:                  y/{a,b,e},   z/{c,d,f}
1942#
1943# Note: Both x and y got renamed and it'd be nice to detect both, and we do
1944# better with directory rename detection than git did without, but the
1945# simple rule from section 5 prevents me from handling this as optimally as
1946# we potentially could.
1947
1948test_expect_success '8a-setup: Dual-directory rename, one into the others way' '
1949        test_create_repo 8a &&
1950        (
1951                cd 8a &&
1952
1953                mkdir x &&
1954                mkdir y &&
1955                echo a >x/a &&
1956                echo b >x/b &&
1957                echo c >y/c &&
1958                echo d >y/d &&
1959                git add x y &&
1960                test_tick &&
1961                git commit -m "O" &&
1962
1963                git branch O &&
1964                git branch A &&
1965                git branch B &&
1966
1967                git checkout A &&
1968                echo e >x/e &&
1969                echo f >y/f &&
1970                git add x/e y/f &&
1971                test_tick &&
1972                git commit -m "A" &&
1973
1974                git checkout B &&
1975                git mv y z &&
1976                git mv x y &&
1977                test_tick &&
1978                git commit -m "B"
1979        )
1980'
1981
1982test_expect_success '8a-check: Dual-directory rename, one into the others way' '
1983        (
1984                cd 8a &&
1985
1986                git checkout A^0 &&
1987
1988                git -c merge.directoryRenames=true merge -s recursive B^0 &&
1989
1990                git ls-files -s >out &&
1991                test_line_count = 6 out &&
1992                git ls-files -u >out &&
1993                test_line_count = 0 out &&
1994                git ls-files -o >out &&
1995                test_line_count = 1 out &&
1996
1997                git rev-parse >actual \
1998                        HEAD:y/a HEAD:y/b HEAD:y/e HEAD:y/f HEAD:z/c HEAD:z/d &&
1999                git rev-parse >expect \
2000                        O:x/a    O:x/b    A:x/e    A:y/f    O:y/c    O:y/d &&
2001                test_cmp expect actual
2002        )
2003'
2004
2005# Testcase 8b, Dual-directory rename, one into the others' way, with conflicting filenames
2006#   Commit O. x/{a_1,b_1},     y/{a_2,b_2}
2007#   Commit A. x/{a_1,b_1,e_1}, y/{a_2,b_2,e_2}
2008#   Commit B. y/{a_1,b_1},     z/{a_2,b_2}
2009#
2010#   w/o dir-rename detection: y/{a_1,b_1,e_2}, z/{a_2,b_2}, x/e_1
2011#   Currently expected:       <same>
2012#   Scary:                    y/{a_1,b_1},     z/{a_2,b_2}, CONFLICT(add/add, e_1 vs. e_2)
2013#   Optimal:                  y/{a_1,b_1,e_1}, z/{a_2,b_2,e_2}
2014#
2015# Note: Very similar to 8a, except instead of 'e' and 'f' in directories x and
2016# y, both are named 'e'.  Without directory rename detection, neither file
2017# moves directories.  Implement directory rename detection suboptimally, and
2018# you get an add/add conflict, but both files were added in commit A, so this
2019# is an add/add conflict where one side of history added both files --
2020# something we can't represent in the index.  Obviously, we'd prefer the last
2021# resolution, but our previous rules are too coarse to allow it.  Using both
2022# the rules from section 4 and section 5 save us from the Scary resolution,
2023# making us fall back to pre-directory-rename-detection behavior for both
2024# e_1 and e_2.
2025
2026test_expect_success '8b-setup: Dual-directory rename, one into the others way, with conflicting filenames' '
2027        test_create_repo 8b &&
2028        (
2029                cd 8b &&
2030
2031                mkdir x &&
2032                mkdir y &&
2033                echo a1 >x/a &&
2034                echo b1 >x/b &&
2035                echo a2 >y/a &&
2036                echo b2 >y/b &&
2037                git add x y &&
2038                test_tick &&
2039                git commit -m "O" &&
2040
2041                git branch O &&
2042                git branch A &&
2043                git branch B &&
2044
2045                git checkout A &&
2046                echo e1 >x/e &&
2047                echo e2 >y/e &&
2048                git add x/e y/e &&
2049                test_tick &&
2050                git commit -m "A" &&
2051
2052                git checkout B &&
2053                git mv y z &&
2054                git mv x y &&
2055                test_tick &&
2056                git commit -m "B"
2057        )
2058'
2059
2060test_expect_success '8b-check: Dual-directory rename, one into the others way, with conflicting filenames' '
2061        (
2062                cd 8b &&
2063
2064                git checkout A^0 &&
2065
2066                git -c merge.directoryRenames=true merge -s recursive B^0 &&
2067
2068                git ls-files -s >out &&
2069                test_line_count = 6 out &&
2070                git ls-files -u >out &&
2071                test_line_count = 0 out &&
2072                git ls-files -o >out &&
2073                test_line_count = 1 out &&
2074
2075                git rev-parse >actual \
2076                        HEAD:y/a HEAD:y/b HEAD:z/a HEAD:z/b HEAD:x/e HEAD:y/e &&
2077                git rev-parse >expect \
2078                        O:x/a    O:x/b    O:y/a    O:y/b    A:x/e    A:y/e &&
2079                test_cmp expect actual
2080        )
2081'
2082
2083# Testcase 8c, modify/delete or rename+modify/delete?
2084#   (Related to testcases 5b, 8d, and 9h)
2085#   Commit O: z/{b,c,d}
2086#   Commit A: y/{b,c}
2087#   Commit B: z/{b,c,d_modified,e}
2088#   Expected: y/{b,c,e}, CONFLICT(modify/delete: on z/d)
2089#
2090#   Note: It could easily be argued that the correct resolution here is
2091#         y/{b,c,e}, CONFLICT(rename/delete: z/d -> y/d vs deleted)
2092#         and that the modifed version of d should be present in y/ after
2093#         the merge, just marked as conflicted.  Indeed, I previously did
2094#         argue that.  But applying directory renames to the side of
2095#         history where a file is merely modified results in spurious
2096#         rename/rename(1to2) conflicts -- see testcase 9h.  See also
2097#         notes in 8d.
2098
2099test_expect_success '8c-setup: modify/delete or rename+modify/delete?' '
2100        test_create_repo 8c &&
2101        (
2102                cd 8c &&
2103
2104                mkdir z &&
2105                echo b >z/b &&
2106                echo c >z/c &&
2107                test_seq 1 10 >z/d &&
2108                git add z &&
2109                test_tick &&
2110                git commit -m "O" &&
2111
2112                git branch O &&
2113                git branch A &&
2114                git branch B &&
2115
2116                git checkout A &&
2117                git rm z/d &&
2118                git mv z y &&
2119                test_tick &&
2120                git commit -m "A" &&
2121
2122                git checkout B &&
2123                echo 11 >z/d &&
2124                test_chmod +x z/d &&
2125                echo e >z/e &&
2126                git add z/d z/e &&
2127                test_tick &&
2128                git commit -m "B"
2129        )
2130'
2131
2132test_expect_success '8c-check: modify/delete or rename+modify/delete' '
2133        (
2134                cd 8c &&
2135
2136                git checkout A^0 &&
2137
2138                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
2139                test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&
2140
2141                git ls-files -s >out &&
2142                test_line_count = 5 out &&
2143                git ls-files -u >out &&
2144                test_line_count = 2 out &&
2145                git ls-files -o >out &&
2146                test_line_count = 1 out &&
2147
2148                git rev-parse >actual \
2149                        :0:y/b :0:y/c :0:y/e :1:z/d :3:z/d &&
2150                git rev-parse >expect \
2151                         O:z/b  O:z/c  B:z/e  O:z/d  B:z/d &&
2152                test_cmp expect actual &&
2153
2154                test_must_fail git rev-parse :2:z/d &&
2155                git ls-files -s z/d | grep ^100755 &&
2156                test_path_is_file z/d &&
2157                test_path_is_missing y/d
2158        )
2159'
2160
2161# Testcase 8d, rename/delete...or not?
2162#   (Related to testcase 5b; these may appear slightly inconsistent to users;
2163#    Also related to testcases 7d and 7e)
2164#   Commit O: z/{b,c,d}
2165#   Commit A: y/{b,c}
2166#   Commit B: z/{b,c,d,e}
2167#   Expected: y/{b,c,e}
2168#
2169#   Note: It would also be somewhat reasonable to resolve this as
2170#             y/{b,c,e}, CONFLICT(rename/delete: x/d -> y/d or deleted)
2171#
2172#   In this case, I'm leaning towards: commit A was the one that deleted z/d
2173#   and it did the rename of z to y, so the two "conflicts" (rename vs.
2174#   delete) are both coming from commit A, which is illogical.  Conflicts
2175#   during merging are supposed to be about opposite sides doing things
2176#   differently.
2177
2178test_expect_success '8d-setup: rename/delete...or not?' '
2179        test_create_repo 8d &&
2180        (
2181                cd 8d &&
2182
2183                mkdir z &&
2184                echo b >z/b &&
2185                echo c >z/c &&
2186                test_seq 1 10 >z/d &&
2187                git add z &&
2188                test_tick &&
2189                git commit -m "O" &&
2190
2191                git branch O &&
2192                git branch A &&
2193                git branch B &&
2194
2195                git checkout A &&
2196                git rm z/d &&
2197                git mv z y &&
2198                test_tick &&
2199                git commit -m "A" &&
2200
2201                git checkout B &&
2202                echo e >z/e &&
2203                git add z/e &&
2204                test_tick &&
2205                git commit -m "B"
2206        )
2207'
2208
2209test_expect_success '8d-check: rename/delete...or not?' '
2210        (
2211                cd 8d &&
2212
2213                git checkout A^0 &&
2214
2215                git -c merge.directoryRenames=true merge -s recursive B^0 &&
2216
2217                git ls-files -s >out &&
2218                test_line_count = 3 out &&
2219
2220                git rev-parse >actual \
2221                        HEAD:y/b HEAD:y/c HEAD:y/e &&
2222                git rev-parse >expect \
2223                        O:z/b    O:z/c    B:z/e &&
2224                test_cmp expect actual
2225        )
2226'
2227
2228# Testcase 8e, Both sides rename, one side adds to original directory
2229#   Commit O: z/{b,c}
2230#   Commit A: y/{b,c}
2231#   Commit B: w/{b,c}, z/d
2232#
2233# Possible Resolutions:
2234#   w/o dir-rename detection: z/d, CONFLICT(z/b -> y/b vs. w/b),
2235#                                  CONFLICT(z/c -> y/c vs. w/c)
2236#   Currently expected:       y/d, CONFLICT(z/b -> y/b vs. w/b),
2237#                                  CONFLICT(z/c -> y/c vs. w/c)
2238#   Optimal:                  ??
2239#
2240# Notes: In commit A, directory z got renamed to y.  In commit B, directory z
2241#        did NOT get renamed; the directory is still present; instead it is
2242#        considered to have just renamed a subset of paths in directory z
2243#        elsewhere.  Therefore, the directory rename done in commit A to z/
2244#        applies to z/d and maps it to y/d.
2245#
2246#        It's possible that users would get confused about this, but what
2247#        should we do instead?  Silently leaving at z/d seems just as bad or
2248#        maybe even worse.  Perhaps we could print a big warning about z/d
2249#        and how we're moving to y/d in this case, but when I started thinking
2250#        about the ramifications of doing that, I didn't know how to rule out
2251#        that opening other weird edge and corner cases so I just punted.
2252
2253test_expect_success '8e-setup: Both sides rename, one side adds to original directory' '
2254        test_create_repo 8e &&
2255        (
2256                cd 8e &&
2257
2258                mkdir z &&
2259                echo b >z/b &&
2260                echo c >z/c &&
2261                git add z &&
2262                test_tick &&
2263                git commit -m "O" &&
2264
2265                git branch O &&
2266                git branch A &&
2267                git branch B &&
2268
2269                git checkout A &&
2270                git mv z y &&
2271                test_tick &&
2272                git commit -m "A" &&
2273
2274                git checkout B &&
2275                git mv z w &&
2276                mkdir z &&
2277                echo d >z/d &&
2278                git add z/d &&
2279                test_tick &&
2280                git commit -m "B"
2281        )
2282'
2283
2284test_expect_success '8e-check: Both sides rename, one side adds to original directory' '
2285        (
2286                cd 8e &&
2287
2288                git checkout A^0 &&
2289
2290                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
2291                test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
2292                test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
2293
2294                git ls-files -s >out &&
2295                test_line_count = 7 out &&
2296                git ls-files -u >out &&
2297                test_line_count = 6 out &&
2298                git ls-files -o >out &&
2299                test_line_count = 2 out &&
2300
2301                git rev-parse >actual \
2302                        :1:z/b :2:y/b :3:w/b :1:z/c :2:y/c :3:w/c :0:y/d &&
2303                git rev-parse >expect \
2304                         O:z/b  O:z/b  O:z/b  O:z/c  O:z/c  O:z/c  B:z/d &&
2305                test_cmp expect actual &&
2306
2307                git hash-object >actual \
2308                        y/b   w/b   y/c   w/c &&
2309                git rev-parse >expect \
2310                        O:z/b O:z/b O:z/c O:z/c &&
2311                test_cmp expect actual &&
2312
2313                test_path_is_missing z/b &&
2314                test_path_is_missing z/c
2315        )
2316'
2317
2318###########################################################################
2319# SECTION 9: Other testcases
2320#
2321# This section consists of miscellaneous testcases I thought of during
2322# the implementation which round out the testing.
2323###########################################################################
2324
2325# Testcase 9a, Inner renamed directory within outer renamed directory
2326#   (Related to testcase 1f)
2327#   Commit O: z/{b,c,d/{e,f,g}}
2328#   Commit A: y/{b,c}, x/w/{e,f,g}
2329#   Commit B: z/{b,c,d/{e,f,g,h},i}
2330#   Expected: y/{b,c,i}, x/w/{e,f,g,h}
2331#   NOTE: The only reason this one is interesting is because when a directory
2332#         is split into multiple other directories, we determine by the weight
2333#         of which one had the most paths going to it.  A naive implementation
2334#         of that could take the new file in commit B at z/i to x/w/i or x/i.
2335
2336test_expect_success '9a-setup: Inner renamed directory within outer renamed directory' '
2337        test_create_repo 9a &&
2338        (
2339                cd 9a &&
2340
2341                mkdir -p z/d &&
2342                echo b >z/b &&
2343                echo c >z/c &&
2344                echo e >z/d/e &&
2345                echo f >z/d/f &&
2346                echo g >z/d/g &&
2347                git add z &&
2348                test_tick &&
2349                git commit -m "O" &&
2350
2351                git branch O &&
2352                git branch A &&
2353                git branch B &&
2354
2355                git checkout A &&
2356                mkdir x &&
2357                git mv z/d x/w &&
2358                git mv z y &&
2359                test_tick &&
2360                git commit -m "A" &&
2361
2362                git checkout B &&
2363                echo h >z/d/h &&
2364                echo i >z/i &&
2365                git add z &&
2366                test_tick &&
2367                git commit -m "B"
2368        )
2369'
2370
2371test_expect_success '9a-check: Inner renamed directory within outer renamed directory' '
2372        (
2373                cd 9a &&
2374
2375                git checkout A^0 &&
2376
2377                git -c merge.directoryRenames=true merge -s recursive B^0 &&
2378
2379                git ls-files -s >out &&
2380                test_line_count = 7 out &&
2381                git ls-files -u >out &&
2382                test_line_count = 0 out &&
2383                git ls-files -o >out &&
2384                test_line_count = 1 out &&
2385
2386                git rev-parse >actual \
2387                        HEAD:y/b HEAD:y/c HEAD:y/i &&
2388                git rev-parse >expect \
2389                        O:z/b    O:z/c    B:z/i &&
2390                test_cmp expect actual &&
2391
2392                git rev-parse >actual \
2393                        HEAD:x/w/e HEAD:x/w/f HEAD:x/w/g HEAD:x/w/h &&
2394                git rev-parse >expect \
2395                        O:z/d/e    O:z/d/f    O:z/d/g    B:z/d/h &&
2396                test_cmp expect actual
2397        )
2398'
2399
2400# Testcase 9b, Transitive rename with content merge
2401#   (Related to testcase 1c)
2402#   Commit O: z/{b,c},   x/d_1
2403#   Commit A: y/{b,c},   x/d_2
2404#   Commit B: z/{b,c,d_3}
2405#   Expected: y/{b,c,d_merged}
2406
2407test_expect_success '9b-setup: Transitive rename with content merge' '
2408        test_create_repo 9b &&
2409        (
2410                cd 9b &&
2411
2412                mkdir z &&
2413                echo b >z/b &&
2414                echo c >z/c &&
2415                mkdir x &&
2416                test_seq 1 10 >x/d &&
2417                git add z x &&
2418                test_tick &&
2419                git commit -m "O" &&
2420
2421                git branch O &&
2422                git branch A &&
2423                git branch B &&
2424
2425                git checkout A &&
2426                git mv z y &&
2427                test_seq 1 11 >x/d &&
2428                git add x/d &&
2429                test_tick &&
2430                git commit -m "A" &&
2431
2432                git checkout B &&
2433                test_seq 0 10 >x/d &&
2434                git mv x/d z/d &&
2435                git add z/d &&
2436                test_tick &&
2437                git commit -m "B"
2438        )
2439'
2440
2441test_expect_success '9b-check: Transitive rename with content merge' '
2442        (
2443                cd 9b &&
2444
2445                git checkout A^0 &&
2446
2447                git -c merge.directoryRenames=true merge -s recursive B^0 &&
2448
2449                git ls-files -s >out &&
2450                test_line_count = 3 out &&
2451
2452                test_seq 0 11 >expected &&
2453                test_cmp expected y/d &&
2454                git add expected &&
2455                git rev-parse >actual \
2456                        HEAD:y/b HEAD:y/c HEAD:y/d &&
2457                git rev-parse >expect \
2458                        O:z/b    O:z/c    :0:expected &&
2459                test_cmp expect actual &&
2460                test_must_fail git rev-parse HEAD:x/d &&
2461                test_must_fail git rev-parse HEAD:z/d &&
2462                test_path_is_missing z/d &&
2463
2464                test $(git rev-parse HEAD:y/d) != $(git rev-parse O:x/d) &&
2465                test $(git rev-parse HEAD:y/d) != $(git rev-parse A:x/d) &&
2466                test $(git rev-parse HEAD:y/d) != $(git rev-parse B:z/d)
2467        )
2468'
2469
2470# Testcase 9c, Doubly transitive rename?
2471#   (Related to testcase 1c, 7e, and 9d)
2472#   Commit O: z/{b,c},     x/{d,e},    w/f
2473#   Commit A: y/{b,c},     x/{d,e,f,g}
2474#   Commit B: z/{b,c,d,e},             w/f
2475#   Expected: y/{b,c,d,e}, x/{f,g}
2476#
2477#   NOTE: x/f and x/g may be slightly confusing here.  The rename from w/f to
2478#         x/f is clear.  Let's look beyond that.  Here's the logic:
2479#            Commit B renamed x/ -> z/
2480#            Commit A renamed z/ -> y/
2481#         So, we could possibly further rename x/f to z/f to y/f, a doubly
2482#         transient rename.  However, where does it end?  We can chain these
2483#         indefinitely (see testcase 9d).  What if there is a D/F conflict
2484#         at z/f/ or y/f/?  Or just another file conflict at one of those
2485#         paths?  In the case of an N-long chain of transient renamings,
2486#         where do we "abort" the rename at?  Can the user make sense of
2487#         the resulting conflict and resolve it?
2488#
2489#         To avoid this confusion I use the simple rule that if the other side
2490#         of history did a directory rename to a path that your side renamed
2491#         away, then ignore that particular rename from the other side of
2492#         history for any implicit directory renames.
2493
2494test_expect_success '9c-setup: Doubly transitive rename?' '
2495        test_create_repo 9c &&
2496        (
2497                cd 9c &&
2498
2499                mkdir z &&
2500                echo b >z/b &&
2501                echo c >z/c &&
2502                mkdir x &&
2503                echo d >x/d &&
2504                echo e >x/e &&
2505                mkdir w &&
2506                echo f >w/f &&
2507                git add z x w &&
2508                test_tick &&
2509                git commit -m "O" &&
2510
2511                git branch O &&
2512                git branch A &&
2513                git branch B &&
2514
2515                git checkout A &&
2516                git mv z y &&
2517                git mv w/f x/ &&
2518                echo g >x/g &&
2519                git add x/g &&
2520                test_tick &&
2521                git commit -m "A" &&
2522
2523                git checkout B &&
2524                git mv x/d z/d &&
2525                git mv x/e z/e &&
2526                test_tick &&
2527                git commit -m "B"
2528        )
2529'
2530
2531test_expect_success '9c-check: Doubly transitive rename?' '
2532        (
2533                cd 9c &&
2534
2535                git checkout A^0 &&
2536
2537                git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
2538                test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&
2539
2540                git ls-files -s >out &&
2541                test_line_count = 6 out &&
2542                git ls-files -o >out &&
2543                test_line_count = 1 out &&
2544
2545                git rev-parse >actual \
2546                        HEAD:y/b HEAD:y/c HEAD:y/d HEAD:y/e HEAD:x/f HEAD:x/g &&
2547                git rev-parse >expect \
2548                        O:z/b    O:z/c    O:x/d    O:x/e    O:w/f    A:x/g &&
2549                test_cmp expect actual
2550        )
2551'
2552
2553# Testcase 9d, N-fold transitive rename?
2554#   (Related to testcase 9c...and 1c and 7e)
2555#   Commit O: z/a, y/b, x/c, w/d, v/e, u/f
2556#   Commit A:  y/{a,b},  w/{c,d},  u/{e,f}
2557#   Commit B: z/{a,t}, x/{b,c}, v/{d,e}, u/f
2558#   Expected: <see NOTE first>
2559#
2560#   NOTE: z/ -> y/ (in commit A)
2561#         y/ -> x/ (in commit B)
2562#         x/ -> w/ (in commit A)
2563#         w/ -> v/ (in commit B)
2564#         v/ -> u/ (in commit A)
2565#         So, if we add a file to z, say z/t, where should it end up?  In u?
2566#         What if there's another file or directory named 't' in one of the
2567#         intervening directories and/or in u itself?  Also, shouldn't the
2568#         same logic that places 't' in u/ also move ALL other files to u/?
2569#         What if there are file or directory conflicts in any of them?  If
2570#         we attempted to do N-way (N-fold? N-ary? N-uple?) transitive renames
2571#         like this, would the user have any hope of understanding any
2572#         conflicts or how their working tree ended up?  I think not, so I'm
2573#         ruling out N-ary transitive renames for N>1.
2574#
2575#   Therefore our expected result is:
2576#     z/t, y/a, x/b, w/c, u/d, u/e, u/f
2577#   The reason that v/d DOES get transitively renamed to u/d is that u/ isn't
2578#   renamed somewhere.  A slightly sub-optimal result, but it uses fairly
2579#   simple rules that are consistent with what we need for all the other
2580#   testcases and simplifies things for the user.
2581
2582test_expect_success '9d-setup: N-way transitive rename?' '
2583        test_create_repo 9d &&
2584        (
2585                cd 9d &&
2586
2587                mkdir z y x w v u &&
2588                echo a >z/a &&
2589                echo b >y/b &&
2590                echo c >x/c &&
2591                echo d >w/d &&
2592                echo e >v/e &&
2593                echo f >u/f &&
2594                git add z y x w v u &&
2595                test_tick &&
2596                git commit -m "O" &&
2597
2598                git branch O &&
2599                git branch A &&
2600                git branch B &&
2601
2602                git checkout A &&
2603                git mv z/a y/ &&
2604                git mv x/c w/ &&
2605                git mv v/e u/ &&
2606                test_tick &&
2607                git commit -m "A" &&
2608
2609                git checkout B &&
2610                echo t >z/t &&
2611                git mv y/b x/ &&
2612                git mv w/d v/ &&
2613                git add z/t &&
2614                test_tick &&
2615                git commit -m "B"
2616        )
2617'
2618
2619test_expect_success '9d-check: N-way transitive rename?' '
2620        (
2621                cd 9d &&
2622
2623                git checkout A^0 &&
2624
2625                git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
2626                test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&
2627                test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&
2628                test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&
2629                test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out &&
2630
2631                git ls-files -s >out &&
2632                test_line_count = 7 out &&
2633                git ls-files -o >out &&
2634                test_line_count = 1 out &&
2635
2636                git rev-parse >actual \
2637                        HEAD:z/t \
2638                        HEAD:y/a HEAD:x/b HEAD:w/c \
2639                        HEAD:u/d HEAD:u/e HEAD:u/f &&
2640                git rev-parse >expect \
2641                        B:z/t    \
2642                        O:z/a    O:y/b    O:x/c    \
2643                        O:w/d    O:v/e    A:u/f &&
2644                test_cmp expect actual
2645        )
2646'
2647
2648# Testcase 9e, N-to-1 whammo
2649#   (Related to testcase 9c...and 1c and 7e)
2650#   Commit O: dir1/{a,b}, dir2/{d,e}, dir3/{g,h}, dirN/{j,k}
2651#   Commit A: dir1/{a,b,c,yo}, dir2/{d,e,f,yo}, dir3/{g,h,i,yo}, dirN/{j,k,l,yo}
2652#   Commit B: combined/{a,b,d,e,g,h,j,k}
2653#   Expected: combined/{a,b,c,d,e,f,g,h,i,j,k,l}, CONFLICT(Nto1) warnings,
2654#             dir1/yo, dir2/yo, dir3/yo, dirN/yo
2655
2656test_expect_success '9e-setup: N-to-1 whammo' '
2657        test_create_repo 9e &&
2658        (
2659                cd 9e &&
2660
2661                mkdir dir1 dir2 dir3 dirN &&
2662                echo a >dir1/a &&
2663                echo b >dir1/b &&
2664                echo d >dir2/d &&
2665                echo e >dir2/e &&
2666                echo g >dir3/g &&
2667                echo h >dir3/h &&
2668                echo j >dirN/j &&
2669                echo k >dirN/k &&
2670                git add dir* &&
2671                test_tick &&
2672                git commit -m "O" &&
2673
2674                git branch O &&
2675                git branch A &&
2676                git branch B &&
2677
2678                git checkout A &&
2679                echo c  >dir1/c &&
2680                echo yo >dir1/yo &&
2681                echo f  >dir2/f &&
2682                echo yo >dir2/yo &&
2683                echo i  >dir3/i &&
2684                echo yo >dir3/yo &&
2685                echo l  >dirN/l &&
2686                echo yo >dirN/yo &&
2687                git add dir* &&
2688                test_tick &&
2689                git commit -m "A" &&
2690
2691                git checkout B &&
2692                git mv dir1 combined &&
2693                git mv dir2/* combined/ &&
2694                git mv dir3/* combined/ &&
2695                git mv dirN/* combined/ &&
2696                test_tick &&
2697                git commit -m "B"
2698        )
2699'
2700
2701test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo' '
2702        (
2703                cd 9e &&
2704
2705                git checkout A^0 &&
2706
2707                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
2708                grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line &&
2709                grep -q dir1/yo error_line &&
2710                grep -q dir2/yo error_line &&
2711                grep -q dir3/yo error_line &&
2712                grep -q dirN/yo error_line &&
2713
2714                git ls-files -s >out &&
2715                test_line_count = 16 out &&
2716                git ls-files -u >out &&
2717                test_line_count = 0 out &&
2718                git ls-files -o >out &&
2719                test_line_count = 2 out &&
2720
2721                git rev-parse >actual \
2722                        :0:combined/a :0:combined/b :0:combined/c \
2723                        :0:combined/d :0:combined/e :0:combined/f \
2724                        :0:combined/g :0:combined/h :0:combined/i \
2725                        :0:combined/j :0:combined/k :0:combined/l &&
2726                git rev-parse >expect \
2727                         O:dir1/a      O:dir1/b      A:dir1/c \
2728                         O:dir2/d      O:dir2/e      A:dir2/f \
2729                         O:dir3/g      O:dir3/h      A:dir3/i \
2730                         O:dirN/j      O:dirN/k      A:dirN/l &&
2731                test_cmp expect actual &&
2732
2733                git rev-parse >actual \
2734                        :0:dir1/yo :0:dir2/yo :0:dir3/yo :0:dirN/yo &&
2735                git rev-parse >expect \
2736                         A:dir1/yo  A:dir2/yo  A:dir3/yo  A:dirN/yo &&
2737                test_cmp expect actual
2738        )
2739'
2740
2741# Testcase 9f, Renamed directory that only contained immediate subdirs
2742#   (Related to testcases 1e & 9g)
2743#   Commit O: goal/{a,b}/$more_files
2744#   Commit A: priority/{a,b}/$more_files
2745#   Commit B: goal/{a,b}/$more_files, goal/c
2746#   Expected: priority/{a,b}/$more_files, priority/c
2747
2748test_expect_success '9f-setup: Renamed directory that only contained immediate subdirs' '
2749        test_create_repo 9f &&
2750        (
2751                cd 9f &&
2752
2753                mkdir -p goal/a &&
2754                mkdir -p goal/b &&
2755                echo foo >goal/a/foo &&
2756                echo bar >goal/b/bar &&
2757                echo baz >goal/b/baz &&
2758                git add goal &&
2759                test_tick &&
2760                git commit -m "O" &&
2761
2762                git branch O &&
2763                git branch A &&
2764                git branch B &&
2765
2766                git checkout A &&
2767                git mv goal/ priority &&
2768                test_tick &&
2769                git commit -m "A" &&
2770
2771                git checkout B &&
2772                echo c >goal/c &&
2773                git add goal/c &&
2774                test_tick &&
2775                git commit -m "B"
2776        )
2777'
2778
2779test_expect_success '9f-check: Renamed directory that only contained immediate subdirs' '
2780        (
2781                cd 9f &&
2782
2783                git checkout A^0 &&
2784
2785                git -c merge.directoryRenames=true merge -s recursive B^0 &&
2786
2787                git ls-files -s >out &&
2788                test_line_count = 4 out &&
2789
2790                git rev-parse >actual \
2791                        HEAD:priority/a/foo \
2792                        HEAD:priority/b/bar \
2793                        HEAD:priority/b/baz \
2794                        HEAD:priority/c &&
2795                git rev-parse >expect \
2796                        O:goal/a/foo \
2797                        O:goal/b/bar \
2798                        O:goal/b/baz \
2799                        B:goal/c &&
2800                test_cmp expect actual &&
2801                test_must_fail git rev-parse HEAD:goal/c
2802        )
2803'
2804
2805# Testcase 9g, Renamed directory that only contained immediate subdirs, immediate subdirs renamed
2806#   (Related to testcases 1e & 9f)
2807#   Commit O: goal/{a,b}/$more_files
2808#   Commit A: priority/{alpha,bravo}/$more_files
2809#   Commit B: goal/{a,b}/$more_files, goal/c
2810#   Expected: priority/{alpha,bravo}/$more_files, priority/c
2811
2812test_expect_success '9g-setup: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' '
2813        test_create_repo 9g &&
2814        (
2815                cd 9g &&
2816
2817                mkdir -p goal/a &&
2818                mkdir -p goal/b &&
2819                echo foo >goal/a/foo &&
2820                echo bar >goal/b/bar &&
2821                echo baz >goal/b/baz &&
2822                git add goal &&
2823                test_tick &&
2824                git commit -m "O" &&
2825
2826                git branch O &&
2827                git branch A &&
2828                git branch B &&
2829
2830                git checkout A &&
2831                mkdir priority &&
2832                git mv goal/a/ priority/alpha &&
2833                git mv goal/b/ priority/beta &&
2834                rmdir goal/ &&
2835                test_tick &&
2836                git commit -m "A" &&
2837
2838                git checkout B &&
2839                echo c >goal/c &&
2840                git add goal/c &&
2841                test_tick &&
2842                git commit -m "B"
2843        )
2844'
2845
2846test_expect_failure '9g-check: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' '
2847        (
2848                cd 9g &&
2849
2850                git checkout A^0 &&
2851
2852                git -c merge.directoryRenames=true merge -s recursive B^0 &&
2853
2854                git ls-files -s >out &&
2855                test_line_count = 4 out &&
2856
2857                git rev-parse >actual \
2858                        HEAD:priority/alpha/foo \
2859                        HEAD:priority/beta/bar  \
2860                        HEAD:priority/beta/baz  \
2861                        HEAD:priority/c &&
2862                git rev-parse >expect \
2863                        O:goal/a/foo \
2864                        O:goal/b/bar \
2865                        O:goal/b/baz \
2866                        B:goal/c &&
2867                test_cmp expect actual &&
2868                test_must_fail git rev-parse HEAD:goal/c
2869        )
2870'
2871
2872# Testcase 9h, Avoid implicit rename if involved as source on other side
2873#   (Extremely closely related to testcase 3a)
2874#   Commit O: z/{b,c,d_1}
2875#   Commit A: z/{b,c,d_2}
2876#   Commit B: y/{b,c}, x/d_1
2877#   Expected: y/{b,c}, x/d_2
2878#   NOTE: If we applied the z/ -> y/ rename to z/d, then we'd end up with
2879#         a rename/rename(1to2) conflict (z/d -> y/d vs. x/d)
2880test_expect_success '9h-setup: Avoid dir rename on merely modified path' '
2881        test_create_repo 9h &&
2882        (
2883                cd 9h &&
2884
2885                mkdir z &&
2886                echo b >z/b &&
2887                echo c >z/c &&
2888                printf "1\n2\n3\n4\n5\n6\n7\n8\nd\n" >z/d &&
2889                git add z &&
2890                test_tick &&
2891                git commit -m "O" &&
2892
2893                git branch O &&
2894                git branch A &&
2895                git branch B &&
2896
2897                git checkout A &&
2898                test_tick &&
2899                echo more >>z/d &&
2900                git add z/d &&
2901                git commit -m "A" &&
2902
2903                git checkout B &&
2904                mkdir y &&
2905                mkdir x &&
2906                git mv z/b y/ &&
2907                git mv z/c y/ &&
2908                git mv z/d x/ &&
2909                rmdir z &&
2910                test_tick &&
2911                git commit -m "B"
2912        )
2913'
2914
2915test_expect_success '9h-check: Avoid dir rename on merely modified path' '
2916        (
2917                cd 9h &&
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 = 3 out &&
2925
2926                git rev-parse >actual \
2927                        HEAD:y/b HEAD:y/c HEAD:x/d &&
2928                git rev-parse >expect \
2929                        O:z/b    O:z/c    A:z/d &&
2930                test_cmp expect actual
2931        )
2932'
2933
2934###########################################################################
2935# Rules suggested by section 9:
2936#
2937#   If the other side of history did a directory rename to a path that your
2938#   side renamed away, then ignore that particular rename from the other
2939#   side of history for any implicit directory renames.
2940###########################################################################
2941
2942###########################################################################
2943# SECTION 10: Handling untracked files
2944#
2945# unpack_trees(), upon which the recursive merge algorithm is based, aborts
2946# the operation if untracked or dirty files would be deleted or overwritten
2947# by the merge.  Unfortunately, unpack_trees() does not understand renames,
2948# and if it doesn't abort, then it muddies up the working directory before
2949# we even get to the point of detecting renames, so we need some special
2950# handling, at least in the case of directory renames.
2951###########################################################################
2952
2953# Testcase 10a, Overwrite untracked: normal rename/delete
2954#   Commit O: z/{b,c_1}
2955#   Commit A: z/b + untracked z/c + untracked z/d
2956#   Commit B: z/{b,d_1}
2957#   Expected: Aborted Merge +
2958#       ERROR_MSG(untracked working tree files would be overwritten by merge)
2959
2960test_expect_success '10a-setup: Overwrite untracked with normal rename/delete' '
2961        test_create_repo 10a &&
2962        (
2963                cd 10a &&
2964
2965                mkdir z &&
2966                echo b >z/b &&
2967                echo c >z/c &&
2968                git add z &&
2969                test_tick &&
2970                git commit -m "O" &&
2971
2972                git branch O &&
2973                git branch A &&
2974                git branch B &&
2975
2976                git checkout A &&
2977                git rm z/c &&
2978                test_tick &&
2979                git commit -m "A" &&
2980
2981                git checkout B &&
2982                git mv z/c z/d &&
2983                test_tick &&
2984                git commit -m "B"
2985        )
2986'
2987
2988test_expect_success '10a-check: Overwrite untracked with normal rename/delete' '
2989        (
2990                cd 10a &&
2991
2992                git checkout A^0 &&
2993                echo very >z/c &&
2994                echo important >z/d &&
2995
2996                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
2997                test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&
2998
2999                git ls-files -s >out &&
3000                test_line_count = 1 out &&
3001                git ls-files -o >out &&
3002                test_line_count = 4 out &&
3003
3004                echo very >expect &&
3005                test_cmp expect z/c &&
3006
3007                echo important >expect &&
3008                test_cmp expect z/d &&
3009
3010                git rev-parse HEAD:z/b >actual &&
3011                git rev-parse O:z/b >expect &&
3012                test_cmp expect actual
3013        )
3014'
3015
3016# Testcase 10b, Overwrite untracked: dir rename + delete
3017#   Commit O: z/{b,c_1}
3018#   Commit A: y/b + untracked y/{c,d,e}
3019#   Commit B: z/{b,d_1,e}
3020#   Expected: Failed Merge; y/b + untracked y/c + untracked y/d on disk +
3021#             z/c_1 -> z/d_1 rename recorded at stage 3 for y/d +
3022#       ERROR_MSG(refusing to lose untracked file at 'y/d')
3023
3024test_expect_success '10b-setup: Overwrite untracked with dir rename + delete' '
3025        test_create_repo 10b &&
3026        (
3027                cd 10b &&
3028
3029                mkdir z &&
3030                echo b >z/b &&
3031                echo c >z/c &&
3032                git add z &&
3033                test_tick &&
3034                git commit -m "O" &&
3035
3036                git branch O &&
3037                git branch A &&
3038                git branch B &&
3039
3040                git checkout A &&
3041                git rm z/c &&
3042                git mv z/ y/ &&
3043                test_tick &&
3044                git commit -m "A" &&
3045
3046                git checkout B &&
3047                git mv z/c z/d &&
3048                echo e >z/e &&
3049                git add z/e &&
3050                test_tick &&
3051                git commit -m "B"
3052        )
3053'
3054
3055test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '
3056        (
3057                cd 10b &&
3058
3059                git checkout A^0 &&
3060                echo very >y/c &&
3061                echo important >y/d &&
3062                echo contents >y/e &&
3063
3064                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3065                test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
3066                test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
3067
3068                git ls-files -s >out &&
3069                test_line_count = 3 out &&
3070                git ls-files -u >out &&
3071                test_line_count = 2 out &&
3072                git ls-files -o >out &&
3073                test_line_count = 5 out &&
3074
3075                git rev-parse >actual \
3076                        :0:y/b :3:y/d :3:y/e &&
3077                git rev-parse >expect \
3078                        O:z/b  O:z/c  B:z/e &&
3079                test_cmp expect actual &&
3080
3081                echo very >expect &&
3082                test_cmp expect y/c &&
3083
3084                echo important >expect &&
3085                test_cmp expect y/d &&
3086
3087                echo contents >expect &&
3088                test_cmp expect y/e
3089        )
3090'
3091
3092# Testcase 10c, Overwrite untracked: dir rename/rename(1to2)
3093#   Commit O: z/{a,b}, x/{c,d}
3094#   Commit A: y/{a,b}, w/c, x/d + different untracked y/c
3095#   Commit B: z/{a,b,c}, x/d
3096#   Expected: Failed Merge; y/{a,b} + x/d + untracked y/c +
3097#             CONFLICT(rename/rename) x/c -> w/c vs y/c +
3098#             y/c~B^0 +
3099#             ERROR_MSG(Refusing to lose untracked file at y/c)
3100
3101test_expect_success '10c-setup: Overwrite untracked with dir rename/rename(1to2)' '
3102        test_create_repo 10c &&
3103        (
3104                cd 10c &&
3105
3106                mkdir z x &&
3107                echo a >z/a &&
3108                echo b >z/b &&
3109                echo c >x/c &&
3110                echo d >x/d &&
3111                git add z x &&
3112                test_tick &&
3113                git commit -m "O" &&
3114
3115                git branch O &&
3116                git branch A &&
3117                git branch B &&
3118
3119                git checkout A &&
3120                mkdir w &&
3121                git mv x/c w/c &&
3122                git mv z/ y/ &&
3123                test_tick &&
3124                git commit -m "A" &&
3125
3126                git checkout B &&
3127                git mv x/c z/ &&
3128                test_tick &&
3129                git commit -m "B"
3130        )
3131'
3132
3133test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)' '
3134        (
3135                cd 10c &&
3136
3137                git checkout A^0 &&
3138                echo important >y/c &&
3139
3140                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3141                test_i18ngrep "CONFLICT (rename/rename)" out &&
3142                test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
3143
3144                git ls-files -s >out &&
3145                test_line_count = 6 out &&
3146                git ls-files -u >out &&
3147                test_line_count = 3 out &&
3148                git ls-files -o >out &&
3149                test_line_count = 3 out &&
3150
3151                git rev-parse >actual \
3152                        :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :3:y/c &&
3153                git rev-parse >expect \
3154                         O:z/a  O:z/b  O:x/d  O:x/c  O:x/c  O:x/c &&
3155                test_cmp expect actual &&
3156
3157                git hash-object y/c~B^0 >actual &&
3158                git rev-parse O:x/c >expect &&
3159                test_cmp expect actual &&
3160
3161                echo important >expect &&
3162                test_cmp expect y/c
3163        )
3164'
3165
3166test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2), other direction' '
3167        (
3168                cd 10c &&
3169
3170                git reset --hard &&
3171                git clean -fdqx &&
3172
3173                git checkout B^0 &&
3174                mkdir y &&
3175                echo important >y/c &&
3176
3177                test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
3178                test_i18ngrep "CONFLICT (rename/rename)" out &&
3179                test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
3180
3181                git ls-files -s >out &&
3182                test_line_count = 6 out &&
3183                git ls-files -u >out &&
3184                test_line_count = 3 out &&
3185                git ls-files -o >out &&
3186                test_line_count = 3 out &&
3187
3188                git rev-parse >actual \
3189                        :0:y/a :0:y/b :0:x/d :1:x/c :3:w/c :2:y/c &&
3190                git rev-parse >expect \
3191                         O:z/a  O:z/b  O:x/d  O:x/c  O:x/c  O:x/c &&
3192                test_cmp expect actual &&
3193
3194                git hash-object y/c~HEAD >actual &&
3195                git rev-parse O:x/c >expect &&
3196                test_cmp expect actual &&
3197
3198                echo important >expect &&
3199                test_cmp expect y/c
3200        )
3201'
3202
3203# Testcase 10d, Delete untracked w/ dir rename/rename(2to1)
3204#   Commit O: z/{a,b,c_1},        x/{d,e,f_2}
3205#   Commit A: y/{a,b},            x/{d,e,f_2,wham_1} + untracked y/wham
3206#   Commit B: z/{a,b,c_1,wham_2}, y/{d,e}
3207#   Expected: Failed Merge; y/{a,b,d,e} + untracked y/{wham,wham~merged}+
3208#             CONFLICT(rename/rename) z/c_1 vs x/f_2 -> y/wham
3209#             ERROR_MSG(Refusing to lose untracked file at y/wham)
3210
3211test_expect_success '10d-setup: Delete untracked with dir rename/rename(2to1)' '
3212        test_create_repo 10d &&
3213        (
3214                cd 10d &&
3215
3216                mkdir z x &&
3217                echo a >z/a &&
3218                echo b >z/b &&
3219                echo c >z/c &&
3220                echo d >x/d &&
3221                echo e >x/e &&
3222                echo f >x/f &&
3223                git add z x &&
3224                test_tick &&
3225                git commit -m "O" &&
3226
3227                git branch O &&
3228                git branch A &&
3229                git branch B &&
3230
3231                git checkout A &&
3232                git mv z/c x/wham &&
3233                git mv z/ y/ &&
3234                test_tick &&
3235                git commit -m "A" &&
3236
3237                git checkout B &&
3238                git mv x/f z/wham &&
3239                git mv x/ y/ &&
3240                test_tick &&
3241                git commit -m "B"
3242        )
3243'
3244
3245test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '
3246        (
3247                cd 10d &&
3248
3249                git checkout A^0 &&
3250                echo important >y/wham &&
3251
3252                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3253                test_i18ngrep "CONFLICT (rename/rename)" out &&
3254                test_i18ngrep "Refusing to lose untracked file at y/wham" out &&
3255
3256                git ls-files -s >out &&
3257                test_line_count = 6 out &&
3258                git ls-files -u >out &&
3259                test_line_count = 2 out &&
3260                git ls-files -o >out &&
3261                test_line_count = 3 out &&
3262
3263                git rev-parse >actual \
3264                        :0:y/a :0:y/b :0:y/d :0:y/e :2:y/wham :3:y/wham &&
3265                git rev-parse >expect \
3266                         O:z/a  O:z/b  O:x/d  O:x/e  O:z/c     O:x/f &&
3267                test_cmp expect actual &&
3268
3269                test_must_fail git rev-parse :1:y/wham &&
3270
3271                echo important >expect &&
3272                test_cmp expect y/wham &&
3273
3274                # Test that the two-way merge in y/wham~merged is as expected
3275                git cat-file -p :2:y/wham >expect &&
3276                git cat-file -p :3:y/wham >other &&
3277                >empty &&
3278                test_must_fail git merge-file \
3279                        -L "HEAD" \
3280                        -L "" \
3281                        -L "B^0" \
3282                        expect empty other &&
3283                test_cmp expect y/wham~merged
3284        )
3285'
3286
3287# Testcase 10e, Does git complain about untracked file that's not in the way?
3288#   Commit O: z/{a,b}
3289#   Commit A: y/{a,b} + untracked z/c
3290#   Commit B: z/{a,b,c}
3291#   Expected: y/{a,b,c} + untracked z/c
3292
3293test_expect_success '10e-setup: Does git complain about untracked file that is not really in the way?' '
3294        test_create_repo 10e &&
3295        (
3296                cd 10e &&
3297
3298                mkdir z &&
3299                echo a >z/a &&
3300                echo b >z/b &&
3301                git add z &&
3302                test_tick &&
3303                git commit -m "O" &&
3304
3305                git branch O &&
3306                git branch A &&
3307                git branch B &&
3308
3309                git checkout A &&
3310                git mv z/ y/ &&
3311                test_tick &&
3312                git commit -m "A" &&
3313
3314                git checkout B &&
3315                echo c >z/c &&
3316                git add z/c &&
3317                test_tick &&
3318                git commit -m "B"
3319        )
3320'
3321
3322test_expect_failure '10e-check: Does git complain about untracked file that is not really in the way?' '
3323        (
3324                cd 10e &&
3325
3326                git checkout A^0 &&
3327                mkdir z &&
3328                echo random >z/c &&
3329
3330                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3331                test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
3332
3333                git ls-files -s >out &&
3334                test_line_count = 3 out &&
3335                git ls-files -u >out &&
3336                test_line_count = 0 out &&
3337                git ls-files -o >out &&
3338                test_line_count = 3 out &&
3339
3340                git rev-parse >actual \
3341                        :0:y/a :0:y/b :0:y/c &&
3342                git rev-parse >expect \
3343                         O:z/a  O:z/b  B:z/c &&
3344                test_cmp expect actual &&
3345
3346                echo random >expect &&
3347                test_cmp expect z/c
3348        )
3349'
3350
3351###########################################################################
3352# SECTION 11: Handling dirty (not up-to-date) files
3353#
3354# unpack_trees(), upon which the recursive merge algorithm is based, aborts
3355# the operation if untracked or dirty files would be deleted or overwritten
3356# by the merge.  Unfortunately, unpack_trees() does not understand renames,
3357# and if it doesn't abort, then it muddies up the working directory before
3358# we even get to the point of detecting renames, so we need some special
3359# handling.  This was true even of normal renames, but there are additional
3360# codepaths that need special handling with directory renames.  Add
3361# testcases for both renamed-by-directory-rename-detection and standard
3362# rename cases.
3363###########################################################################
3364
3365# Testcase 11a, Avoid losing dirty contents with simple rename
3366#   Commit O: z/{a,b_v1},
3367#   Commit A: z/{a,c_v1}, and z/c_v1 has uncommitted mods
3368#   Commit B: z/{a,b_v2}
3369#   Expected: ERROR_MSG(Refusing to lose dirty file at z/c) +
3370#             z/a, staged version of z/c has sha1sum matching B:z/b_v2,
3371#             z/c~HEAD with contents of B:z/b_v2,
3372#             z/c with uncommitted mods on top of A:z/c_v1
3373
3374test_expect_success '11a-setup: Avoid losing dirty contents with simple rename' '
3375        test_create_repo 11a &&
3376        (
3377                cd 11a &&
3378
3379                mkdir z &&
3380                echo a >z/a &&
3381                test_seq 1 10 >z/b &&
3382                git add z &&
3383                test_tick &&
3384                git commit -m "O" &&
3385
3386                git branch O &&
3387                git branch A &&
3388                git branch B &&
3389
3390                git checkout A &&
3391                git mv z/b z/c &&
3392                test_tick &&
3393                git commit -m "A" &&
3394
3395                git checkout B &&
3396                echo 11 >>z/b &&
3397                git add z/b &&
3398                test_tick &&
3399                git commit -m "B"
3400        )
3401'
3402
3403test_expect_success '11a-check: Avoid losing dirty contents with simple rename' '
3404        (
3405                cd 11a &&
3406
3407                git checkout A^0 &&
3408                echo stuff >>z/c &&
3409
3410                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3411                test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3412
3413                test_seq 1 10 >expected &&
3414                echo stuff >>expected &&
3415                test_cmp expected z/c &&
3416
3417                git ls-files -s >out &&
3418                test_line_count = 2 out &&
3419                git ls-files -u >out &&
3420                test_line_count = 1 out &&
3421                git ls-files -o >out &&
3422                test_line_count = 4 out &&
3423
3424                git rev-parse >actual \
3425                        :0:z/a :2:z/c &&
3426                git rev-parse >expect \
3427                         O:z/a  B:z/b &&
3428                test_cmp expect actual &&
3429
3430                git hash-object z/c~HEAD >actual &&
3431                git rev-parse B:z/b >expect &&
3432                test_cmp expect actual
3433        )
3434'
3435
3436# Testcase 11b, Avoid losing dirty file involved in directory rename
3437#   Commit O: z/a,         x/{b,c_v1}
3438#   Commit A: z/{a,c_v1},  x/b,       and z/c_v1 has uncommitted mods
3439#   Commit B: y/a,         x/{b,c_v2}
3440#   Expected: y/{a,c_v2}, x/b, z/c_v1 with uncommitted mods untracked,
3441#             ERROR_MSG(Refusing to lose dirty file at z/c)
3442
3443
3444test_expect_success '11b-setup: Avoid losing dirty file involved in directory rename' '
3445        test_create_repo 11b &&
3446        (
3447                cd 11b &&
3448
3449                mkdir z x &&
3450                echo a >z/a &&
3451                echo b >x/b &&
3452                test_seq 1 10 >x/c &&
3453                git add z x &&
3454                test_tick &&
3455                git commit -m "O" &&
3456
3457                git branch O &&
3458                git branch A &&
3459                git branch B &&
3460
3461                git checkout A &&
3462                git mv x/c z/c &&
3463                test_tick &&
3464                git commit -m "A" &&
3465
3466                git checkout B &&
3467                git mv z y &&
3468                echo 11 >>x/c &&
3469                git add x/c &&
3470                test_tick &&
3471                git commit -m "B"
3472        )
3473'
3474
3475test_expect_success '11b-check: Avoid losing dirty file involved in directory rename' '
3476        (
3477                cd 11b &&
3478
3479                git checkout A^0 &&
3480                echo stuff >>z/c &&
3481
3482                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3483                test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3484
3485                grep -q stuff z/c &&
3486                test_seq 1 10 >expected &&
3487                echo stuff >>expected &&
3488                test_cmp expected z/c &&
3489
3490                git ls-files -s >out &&
3491                test_line_count = 3 out &&
3492                git ls-files -u >out &&
3493                test_line_count = 0 out &&
3494                git ls-files -m >out &&
3495                test_line_count = 0 out &&
3496                git ls-files -o >out &&
3497                test_line_count = 4 out &&
3498
3499                git rev-parse >actual \
3500                        :0:x/b :0:y/a :0:y/c &&
3501                git rev-parse >expect \
3502                         O:x/b  O:z/a  B:x/c &&
3503                test_cmp expect actual &&
3504
3505                git hash-object y/c >actual &&
3506                git rev-parse B:x/c >expect &&
3507                test_cmp expect actual
3508        )
3509'
3510
3511# Testcase 11c, Avoid losing not-up-to-date with rename + D/F conflict
3512#   Commit O: y/a,         x/{b,c_v1}
3513#   Commit A: y/{a,c_v1},  x/b,       and y/c_v1 has uncommitted mods
3514#   Commit B: y/{a,c/d},   x/{b,c_v2}
3515#   Expected: Abort_msg("following files would be overwritten by merge") +
3516#             y/c left untouched (still has uncommitted mods)
3517
3518test_expect_success '11c-setup: Avoid losing not-uptodate with rename + D/F conflict' '
3519        test_create_repo 11c &&
3520        (
3521                cd 11c &&
3522
3523                mkdir y x &&
3524                echo a >y/a &&
3525                echo b >x/b &&
3526                test_seq 1 10 >x/c &&
3527                git add y x &&
3528                test_tick &&
3529                git commit -m "O" &&
3530
3531                git branch O &&
3532                git branch A &&
3533                git branch B &&
3534
3535                git checkout A &&
3536                git mv x/c y/c &&
3537                test_tick &&
3538                git commit -m "A" &&
3539
3540                git checkout B &&
3541                mkdir y/c &&
3542                echo d >y/c/d &&
3543                echo 11 >>x/c &&
3544                git add x/c y/c/d &&
3545                test_tick &&
3546                git commit -m "B"
3547        )
3548'
3549
3550test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conflict' '
3551        (
3552                cd 11c &&
3553
3554                git checkout A^0 &&
3555                echo stuff >>y/c &&
3556
3557                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3558                test_i18ngrep "following files would be overwritten by merge" err &&
3559
3560                grep -q stuff y/c &&
3561                test_seq 1 10 >expected &&
3562                echo stuff >>expected &&
3563                test_cmp expected y/c &&
3564
3565                git ls-files -s >out &&
3566                test_line_count = 3 out &&
3567                git ls-files -u >out &&
3568                test_line_count = 0 out &&
3569                git ls-files -m >out &&
3570                test_line_count = 1 out &&
3571                git ls-files -o >out &&
3572                test_line_count = 3 out
3573        )
3574'
3575
3576# Testcase 11d, Avoid losing not-up-to-date with rename + D/F conflict
3577#   Commit O: z/a,         x/{b,c_v1}
3578#   Commit A: z/{a,c_v1},  x/b,       and z/c_v1 has uncommitted mods
3579#   Commit B: y/{a,c/d},   x/{b,c_v2}
3580#   Expected: D/F: y/c_v2 vs y/c/d) +
3581#             Warning_Msg("Refusing to lose dirty file at z/c) +
3582#             y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods
3583
3584test_expect_success '11d-setup: Avoid losing not-uptodate with rename + D/F conflict' '
3585        test_create_repo 11d &&
3586        (
3587                cd 11d &&
3588
3589                mkdir z x &&
3590                echo a >z/a &&
3591                echo b >x/b &&
3592                test_seq 1 10 >x/c &&
3593                git add z x &&
3594                test_tick &&
3595                git commit -m "O" &&
3596
3597                git branch O &&
3598                git branch A &&
3599                git branch B &&
3600
3601                git checkout A &&
3602                git mv x/c z/c &&
3603                test_tick &&
3604                git commit -m "A" &&
3605
3606                git checkout B &&
3607                git mv z y &&
3608                mkdir y/c &&
3609                echo d >y/c/d &&
3610                echo 11 >>x/c &&
3611                git add x/c y/c/d &&
3612                test_tick &&
3613                git commit -m "B"
3614        )
3615'
3616
3617test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conflict' '
3618        (
3619                cd 11d &&
3620
3621                git checkout A^0 &&
3622                echo stuff >>z/c &&
3623
3624                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3625                test_i18ngrep "Refusing to lose dirty file at z/c" out &&
3626
3627                grep -q stuff z/c &&
3628                test_seq 1 10 >expected &&
3629                echo stuff >>expected &&
3630                test_cmp expected z/c &&
3631
3632                git ls-files -s >out &&
3633                test_line_count = 4 out &&
3634                git ls-files -u >out &&
3635                test_line_count = 1 out &&
3636                git ls-files -o >out &&
3637                test_line_count = 5 out &&
3638
3639                git rev-parse >actual \
3640                        :0:x/b :0:y/a :0:y/c/d :3:y/c &&
3641                git rev-parse >expect \
3642                         O:x/b  O:z/a  B:y/c/d  B:x/c &&
3643                test_cmp expect actual &&
3644
3645                git hash-object y/c~HEAD >actual &&
3646                git rev-parse B:x/c >expect &&
3647                test_cmp expect actual
3648        )
3649'
3650
3651# Testcase 11e, Avoid deleting not-up-to-date with dir rename/rename(1to2)/add
3652#   Commit O: z/{a,b},      x/{c_1,d}
3653#   Commit A: y/{a,b,c_2},  x/d, w/c_1, and y/c_2 has uncommitted mods
3654#   Commit B: z/{a,b,c_1},  x/d
3655#   Expected: Failed Merge; y/{a,b} + x/d +
3656#             CONFLICT(rename/rename) x/c_1 -> w/c_1 vs y/c_1 +
3657#             ERROR_MSG(Refusing to lose dirty file at y/c)
3658#             y/c~B^0 has O:x/c_1 contents
3659#             y/c~HEAD has A:y/c_2 contents
3660#             y/c has dirty file from before merge
3661
3662test_expect_success '11e-setup: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
3663        test_create_repo 11e &&
3664        (
3665                cd 11e &&
3666
3667                mkdir z x &&
3668                echo a >z/a &&
3669                echo b >z/b &&
3670                echo c >x/c &&
3671                echo d >x/d &&
3672                git add z x &&
3673                test_tick &&
3674                git commit -m "O" &&
3675
3676                git branch O &&
3677                git branch A &&
3678                git branch B &&
3679
3680                git checkout A &&
3681                git mv z/ y/ &&
3682                echo different >y/c &&
3683                mkdir w &&
3684                git mv x/c w/ &&
3685                git add y/c &&
3686                test_tick &&
3687                git commit -m "A" &&
3688
3689                git checkout B &&
3690                git mv x/c z/ &&
3691                test_tick &&
3692                git commit -m "B"
3693        )
3694'
3695
3696test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rename(1to2)/add' '
3697        (
3698                cd 11e &&
3699
3700                git checkout A^0 &&
3701                echo mods >>y/c &&
3702
3703                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3704                test_i18ngrep "CONFLICT (rename/rename)" out &&
3705                test_i18ngrep "Refusing to lose dirty file at y/c" out &&
3706
3707                git ls-files -s >out &&
3708                test_line_count = 7 out &&
3709                git ls-files -u >out &&
3710                test_line_count = 4 out &&
3711                git ls-files -o >out &&
3712                test_line_count = 3 out &&
3713
3714                echo different >expected &&
3715                echo mods >>expected &&
3716                test_cmp expected y/c &&
3717
3718                git rev-parse >actual \
3719                        :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :2:y/c :3:y/c &&
3720                git rev-parse >expect \
3721                         O:z/a  O:z/b  O:x/d  O:x/c  O:x/c  A:y/c  O:x/c &&
3722                test_cmp expect actual &&
3723
3724                # See if y/c~merged has expected contents; requires manually
3725                # doing the expected file merge
3726                git cat-file -p A:y/c >c1 &&
3727                git cat-file -p B:z/c >c2 &&
3728                >empty &&
3729                test_must_fail git merge-file \
3730                        -L "HEAD" \
3731                        -L "" \
3732                        -L "B^0" \
3733                        c1 empty c2 &&
3734                test_cmp c1 y/c~merged
3735        )
3736'
3737
3738# Testcase 11f, Avoid deleting not-up-to-date w/ dir rename/rename(2to1)
3739#   Commit O: z/{a,b},        x/{c_1,d_2}
3740#   Commit A: y/{a,b,wham_1}, x/d_2, except y/wham has uncommitted mods
3741#   Commit B: z/{a,b,wham_2}, x/c_1
3742#   Expected: Failed Merge; y/{a,b} + untracked y/{wham~merged} +
3743#             y/wham with dirty changes from before merge +
3744#             CONFLICT(rename/rename) x/c vs x/d -> y/wham
3745#             ERROR_MSG(Refusing to lose dirty file at y/wham)
3746
3747test_expect_success '11f-setup: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
3748        test_create_repo 11f &&
3749        (
3750                cd 11f &&
3751
3752                mkdir z x &&
3753                echo a >z/a &&
3754                echo b >z/b &&
3755                test_seq 1 10 >x/c &&
3756                echo d >x/d &&
3757                git add z x &&
3758                test_tick &&
3759                git commit -m "O" &&
3760
3761                git branch O &&
3762                git branch A &&
3763                git branch B &&
3764
3765                git checkout A &&
3766                git mv z/ y/ &&
3767                git mv x/c y/wham &&
3768                test_tick &&
3769                git commit -m "A" &&
3770
3771                git checkout B &&
3772                git mv x/d z/wham &&
3773                test_tick &&
3774                git commit -m "B"
3775        )
3776'
3777
3778test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rename(2to1)' '
3779        (
3780                cd 11f &&
3781
3782                git checkout A^0 &&
3783                echo important >>y/wham &&
3784
3785                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
3786                test_i18ngrep "CONFLICT (rename/rename)" out &&
3787                test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
3788
3789                git ls-files -s >out &&
3790                test_line_count = 4 out &&
3791                git ls-files -u >out &&
3792                test_line_count = 2 out &&
3793                git ls-files -o >out &&
3794                test_line_count = 3 out &&
3795
3796                test_seq 1 10 >expected &&
3797                echo important >>expected &&
3798                test_cmp expected y/wham &&
3799
3800                test_must_fail git rev-parse :1:y/wham &&
3801
3802                git rev-parse >actual \
3803                        :0:y/a :0:y/b :2:y/wham :3:y/wham &&
3804                git rev-parse >expect \
3805                         O:z/a  O:z/b  O:x/c     O:x/d &&
3806                test_cmp expect actual &&
3807
3808                # Test that the two-way merge in y/wham~merged is as expected
3809                git cat-file -p :2:y/wham >expect &&
3810                git cat-file -p :3:y/wham >other &&
3811                >empty &&
3812                test_must_fail git merge-file \
3813                        -L "HEAD" \
3814                        -L "" \
3815                        -L "B^0" \
3816                        expect empty other &&
3817                test_cmp expect y/wham~merged
3818        )
3819'
3820
3821###########################################################################
3822# SECTION 12: Everything else
3823#
3824# Tests suggested by others.  Tests added after implementation completed
3825# and submitted.  Grab bag.
3826###########################################################################
3827
3828# Testcase 12a, Moving one directory hierarchy into another
3829#   (Related to testcase 9a)
3830#   Commit O: node1/{leaf1,leaf2}, node2/{leaf3,leaf4}
3831#   Commit A: node1/{leaf1,leaf2,node2/{leaf3,leaf4}}
3832#   Commit B: node1/{leaf1,leaf2,leaf5}, node2/{leaf3,leaf4,leaf6}
3833#   Expected: node1/{leaf1,leaf2,leaf5,node2/{leaf3,leaf4,leaf6}}
3834
3835test_expect_success '12a-setup: Moving one directory hierarchy into another' '
3836        test_create_repo 12a &&
3837        (
3838                cd 12a &&
3839
3840                mkdir -p node1 node2 &&
3841                echo leaf1 >node1/leaf1 &&
3842                echo leaf2 >node1/leaf2 &&
3843                echo leaf3 >node2/leaf3 &&
3844                echo leaf4 >node2/leaf4 &&
3845                git add node1 node2 &&
3846                test_tick &&
3847                git commit -m "O" &&
3848
3849                git branch O &&
3850                git branch A &&
3851                git branch B &&
3852
3853                git checkout A &&
3854                git mv node2/ node1/ &&
3855                test_tick &&
3856                git commit -m "A" &&
3857
3858                git checkout B &&
3859                echo leaf5 >node1/leaf5 &&
3860                echo leaf6 >node2/leaf6 &&
3861                git add node1 node2 &&
3862                test_tick &&
3863                git commit -m "B"
3864        )
3865'
3866
3867test_expect_success '12a-check: Moving one directory hierarchy into another' '
3868        (
3869                cd 12a &&
3870
3871                git checkout A^0 &&
3872
3873                git -c merge.directoryRenames=true merge -s recursive B^0 &&
3874
3875                git ls-files -s >out &&
3876                test_line_count = 6 out &&
3877
3878                git rev-parse >actual \
3879                        HEAD:node1/leaf1 HEAD:node1/leaf2 HEAD:node1/leaf5 \
3880                        HEAD:node1/node2/leaf3 \
3881                        HEAD:node1/node2/leaf4 \
3882                        HEAD:node1/node2/leaf6 &&
3883                git rev-parse >expect \
3884                        O:node1/leaf1    O:node1/leaf2    B:node1/leaf5 \
3885                        O:node2/leaf3 \
3886                        O:node2/leaf4 \
3887                        B:node2/leaf6 &&
3888                test_cmp expect actual
3889        )
3890'
3891
3892# Testcase 12b, Moving two directory hierarchies into each other
3893#   (Related to testcases 1c and 12c)
3894#   Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4}
3895#   Commit A: node1/{leaf1, leaf2, node2/{leaf3, leaf4}}
3896#   Commit B: node2/{leaf3, leaf4, node1/{leaf1, leaf2}}
3897#   Expected: node1/node2/node1/{leaf1, leaf2},
3898#             node2/node1/node2/{leaf3, leaf4}
3899#   NOTE: Without directory renames, we would expect
3900#                   node2/node1/{leaf1, leaf2},
3901#                   node1/node2/{leaf3, leaf4}
3902#         with directory rename detection, we note that
3903#             commit A renames node2/ -> node1/node2/
3904#             commit B renames node1/ -> node2/node1/
3905#         therefore, applying those directory renames to the initial result
3906#         (making all four paths experience a transitive renaming), yields
3907#         the expected result.
3908#
3909#         You may ask, is it weird to have two directories rename each other?
3910#         To which, I can do no more than shrug my shoulders and say that
3911#         even simple rules give weird results when given weird inputs.
3912
3913test_expect_success '12b-setup: Moving two directory hierarchies into each other' '
3914        test_create_repo 12b &&
3915        (
3916                cd 12b &&
3917
3918                mkdir -p node1 node2 &&
3919                echo leaf1 >node1/leaf1 &&
3920                echo leaf2 >node1/leaf2 &&
3921                echo leaf3 >node2/leaf3 &&
3922                echo leaf4 >node2/leaf4 &&
3923                git add node1 node2 &&
3924                test_tick &&
3925                git commit -m "O" &&
3926
3927                git branch O &&
3928                git branch A &&
3929                git branch B &&
3930
3931                git checkout A &&
3932                git mv node2/ node1/ &&
3933                test_tick &&
3934                git commit -m "A" &&
3935
3936                git checkout B &&
3937                git mv node1/ node2/ &&
3938                test_tick &&
3939                git commit -m "B"
3940        )
3941'
3942
3943test_expect_success '12b-check: Moving two directory hierarchies into each other' '
3944        (
3945                cd 12b &&
3946
3947                git checkout A^0 &&
3948
3949                git -c merge.directoryRenames=true merge -s recursive B^0 &&
3950
3951                git ls-files -s >out &&
3952                test_line_count = 4 out &&
3953
3954                git rev-parse >actual \
3955                        HEAD:node1/node2/node1/leaf1 \
3956                        HEAD:node1/node2/node1/leaf2 \
3957                        HEAD:node2/node1/node2/leaf3 \
3958                        HEAD:node2/node1/node2/leaf4 &&
3959                git rev-parse >expect \
3960                        O:node1/leaf1 \
3961                        O:node1/leaf2 \
3962                        O:node2/leaf3 \
3963                        O:node2/leaf4 &&
3964                test_cmp expect actual
3965        )
3966'
3967
3968# Testcase 12c, Moving two directory hierarchies into each other w/ content merge
3969#   (Related to testcase 12b)
3970#   Commit O: node1/{       leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1}
3971#   Commit A: node1/{       leaf1_2, leaf2_2,  node2/{leaf3_2, leaf4_2}}
3972#   Commit B: node2/{node1/{leaf1_3, leaf2_3},        leaf3_3, leaf4_3}
3973#   Expected: Content merge conflicts for each of:
3974#               node1/node2/node1/{leaf1, leaf2},
3975#               node2/node1/node2/{leaf3, leaf4}
3976#   NOTE: This is *exactly* like 12c, except that every path is modified on
3977#         each side of the merge.
3978
3979test_expect_success '12c-setup: Moving one directory hierarchy into another w/ content merge' '
3980        test_create_repo 12c &&
3981        (
3982                cd 12c &&
3983
3984                mkdir -p node1 node2 &&
3985                printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 &&
3986                printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf2\n" >node1/leaf2 &&
3987                printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf3\n" >node2/leaf3 &&
3988                printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf4\n" >node2/leaf4 &&
3989                git add node1 node2 &&
3990                test_tick &&
3991                git commit -m "O" &&
3992
3993                git branch O &&
3994                git branch A &&
3995                git branch B &&
3996
3997                git checkout A &&
3998                git mv node2/ node1/ &&
3999                for i in `git ls-files`; do echo side A >>$i; done &&
4000                git add -u &&
4001                test_tick &&
4002                git commit -m "A" &&
4003
4004                git checkout B &&
4005                git mv node1/ node2/ &&
4006                for i in `git ls-files`; do echo side B >>$i; done &&
4007                git add -u &&
4008                test_tick &&
4009                git commit -m "B"
4010        )
4011'
4012
4013test_expect_success '12c-check: Moving one directory hierarchy into another w/ content merge' '
4014        (
4015                cd 12c &&
4016
4017                git checkout A^0 &&
4018
4019                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
4020
4021                git ls-files -u >out &&
4022                test_line_count = 12 out &&
4023
4024                git rev-parse >actual \
4025                        :1:node1/node2/node1/leaf1 \
4026                        :1:node1/node2/node1/leaf2 \
4027                        :1:node2/node1/node2/leaf3 \
4028                        :1:node2/node1/node2/leaf4 \
4029                        :2:node1/node2/node1/leaf1 \
4030                        :2:node1/node2/node1/leaf2 \
4031                        :2:node2/node1/node2/leaf3 \
4032                        :2:node2/node1/node2/leaf4 \
4033                        :3:node1/node2/node1/leaf1 \
4034                        :3:node1/node2/node1/leaf2 \
4035                        :3:node2/node1/node2/leaf3 \
4036                        :3:node2/node1/node2/leaf4 &&
4037                git rev-parse >expect \
4038                        O:node1/leaf1 \
4039                        O:node1/leaf2 \
4040                        O:node2/leaf3 \
4041                        O:node2/leaf4 \
4042                        A:node1/leaf1 \
4043                        A:node1/leaf2 \
4044                        A:node1/node2/leaf3 \
4045                        A:node1/node2/leaf4 \
4046                        B:node2/node1/leaf1 \
4047                        B:node2/node1/leaf2 \
4048                        B:node2/leaf3 \
4049                        B:node2/leaf4 &&
4050                test_cmp expect actual
4051        )
4052'
4053
4054###########################################################################
4055# SECTION 13: Checking informational and conflict messages
4056#
4057# A year after directory rename detection became the default, it was
4058# instead decided to report conflicts on the pathname on the basis that
4059# some users may expect the new files added or moved into a directory to
4060# be unrelated to all the other files in that directory, and thus that
4061# directory rename detection is unexpected.  Test that the messages printed
4062# match our expectation.
4063###########################################################################
4064
4065# Testcase 13a, Basic directory rename with newly added files
4066#   Commit O: z/{b,c}
4067#   Commit A: y/{b,c}
4068#   Commit B: z/{b,c,d,e/f}
4069#   Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f
4070
4071test_expect_success '13a-setup: messages for newly added files' '
4072        test_create_repo 13a &&
4073        (
4074                cd 13a &&
4075
4076                mkdir z &&
4077                echo b >z/b &&
4078                echo c >z/c &&
4079                git add z &&
4080                test_tick &&
4081                git commit -m "O" &&
4082
4083                git branch O &&
4084                git branch A &&
4085                git branch B &&
4086
4087                git checkout A &&
4088                git mv z y &&
4089                test_tick &&
4090                git commit -m "A" &&
4091
4092                git checkout B &&
4093                echo d >z/d &&
4094                mkdir z/e &&
4095                echo f >z/e/f &&
4096                git add z/d z/e/f &&
4097                test_tick &&
4098                git commit -m "B"
4099        )
4100'
4101
4102test_expect_success '13a-check(conflict): messages for newly added files' '
4103        (
4104                cd 13a &&
4105
4106                git checkout A^0 &&
4107
4108                test_must_fail git merge -s recursive B^0 >out 2>err &&
4109
4110                test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
4111                test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
4112
4113                git ls-files >paths &&
4114                ! grep z/ paths &&
4115                grep "y/[de]" paths &&
4116
4117                test_path_is_missing z/d &&
4118                test_path_is_file    y/d &&
4119                test_path_is_missing z/e/f &&
4120                test_path_is_file    y/e/f
4121        )
4122'
4123
4124test_expect_success '13a-check(info): messages for newly added files' '
4125        (
4126                cd 13a &&
4127
4128                git reset --hard &&
4129                git checkout A^0 &&
4130
4131                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
4132
4133                test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
4134                test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&
4135
4136                git ls-files >paths &&
4137                ! grep z/ paths &&
4138                grep "y/[de]" paths &&
4139
4140                test_path_is_missing z/d &&
4141                test_path_is_file    y/d &&
4142                test_path_is_missing z/e/f &&
4143                test_path_is_file    y/e/f
4144        )
4145'
4146
4147# Testcase 13b, Transitive rename with conflicted content merge and default
4148#               "conflict" setting
4149#   (Related to testcase 1c, 9b)
4150#   Commit O: z/{b,c},   x/d_1
4151#   Commit A: y/{b,c},   x/d_2
4152#   Commit B: z/{b,c,d_3}
4153#   Expected: y/{b,c,d_merged}, with two conflict messages for y/d,
4154#             one about content, and one about file location
4155
4156test_expect_success '13b-setup: messages for transitive rename with conflicted content' '
4157        test_create_repo 13b &&
4158        (
4159                cd 13b &&
4160
4161                mkdir x &&
4162                mkdir z &&
4163                test_seq 1 10 >x/d &&
4164                echo b >z/b &&
4165                echo c >z/c &&
4166                git add x z &&
4167                test_tick &&
4168                git commit -m "O" &&
4169
4170                git branch O &&
4171                git branch A &&
4172                git branch B &&
4173
4174                git checkout A &&
4175                git mv z y &&
4176                echo 11 >>x/d &&
4177                git add x/d &&
4178                test_tick &&
4179                git commit -m "A" &&
4180
4181                git checkout B &&
4182                echo eleven >>x/d &&
4183                git mv x/d z/d &&
4184                git add z/d &&
4185                test_tick &&
4186                git commit -m "B"
4187        )
4188'
4189
4190test_expect_success '13b-check(conflict): messages for transitive rename with conflicted content' '
4191        (
4192                cd 13b &&
4193
4194                git checkout A^0 &&
4195
4196                test_must_fail git merge -s recursive B^0 >out 2>err &&
4197
4198                test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
4199                test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
4200
4201                git ls-files >paths &&
4202                ! grep z/ paths &&
4203                grep "y/d" paths &&
4204
4205                test_path_is_missing z/d &&
4206                test_path_is_file    y/d
4207        )
4208'
4209
4210test_expect_success '13b-check(info): messages for transitive rename with conflicted content' '
4211        (
4212                cd 13b &&
4213
4214                git reset --hard &&
4215                git checkout A^0 &&
4216
4217                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
4218
4219                test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
4220                test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
4221
4222                git ls-files >paths &&
4223                ! grep z/ paths &&
4224                grep "y/d" paths &&
4225
4226                test_path_is_missing z/d &&
4227                test_path_is_file    y/d
4228        )
4229'
4230
4231# Testcase 13c, Rename/rename(1to1) due to directory rename
4232#   Commit O: z/{b,c},   x/{d,e}
4233#   Commit A: y/{b,c,d}, x/e
4234#   Commit B: z/{b,c,d}, x/e
4235#   Expected: y/{b,c,d}, with info or conflict messages for d (
4236#             A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d
4237#             One could argue A had partial knowledge of what was done with
4238#             d and B had full knowledge, but that's a slippery slope as
4239#             shown in testcase 13d.
4240
4241test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive rename' '
4242        test_create_repo 13c &&
4243        (
4244                cd 13c &&
4245
4246                mkdir x &&
4247                mkdir z &&
4248                test_seq 1 10 >x/d &&
4249                echo e >x/e &&
4250                echo b >z/b &&
4251                echo c >z/c &&
4252                git add x z &&
4253                test_tick &&
4254                git commit -m "O" &&
4255
4256                git branch O &&
4257                git branch A &&
4258                git branch B &&
4259
4260                git checkout A &&
4261                git mv z y &&
4262                git mv x/d y/ &&
4263                test_tick &&
4264                git commit -m "A" &&
4265
4266                git checkout B &&
4267                git mv x/d z/d &&
4268                git add z/d &&
4269                test_tick &&
4270                git commit -m "B"
4271        )
4272'
4273
4274test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via transitive rename' '
4275        (
4276                cd 13c &&
4277
4278                git checkout A^0 &&
4279
4280                test_must_fail git merge -s recursive B^0 >out 2>err &&
4281
4282                test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
4283
4284                git ls-files >paths &&
4285                ! grep z/ paths &&
4286                grep "y/d" paths &&
4287
4288                test_path_is_missing z/d &&
4289                test_path_is_file    y/d
4290        )
4291'
4292
4293test_expect_success '13c-check(info): messages for rename/rename(1to1) via transitive rename' '
4294        (
4295                cd 13c &&
4296
4297                git reset --hard &&
4298                git checkout A^0 &&
4299
4300                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
4301
4302                test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
4303
4304                git ls-files >paths &&
4305                ! grep z/ paths &&
4306                grep "y/d" paths &&
4307
4308                test_path_is_missing z/d &&
4309                test_path_is_file    y/d
4310        )
4311'
4312
4313# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides
4314#   Commit O: a/{z,y}, b/x,     c/w
4315#   Commit A: a/z,     b/{y,x}, d/w
4316#   Commit B: a/z,     d/x,     c/{y,w}
4317#   Expected: a/z, d/{y,x,w} with no file location conflict for x
4318#             Easy cases:
4319#               * z is always in a; so it stays in a.
4320#               * x starts in b, only modified on one side to move into d/
4321#               * w starts in c, only modified on one side to move into d/
4322#             Hard case:
4323#               * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y
4324#               * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y
4325#               No conflict in where a/y ends up, so put it in d/y.
4326
4327test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transitive rename' '
4328        test_create_repo 13d &&
4329        (
4330                cd 13d &&
4331
4332                mkdir a &&
4333                mkdir b &&
4334                mkdir c &&
4335                echo z >a/z &&
4336                echo y >a/y &&
4337                echo x >b/x &&
4338                echo w >c/w &&
4339                git add a b c &&
4340                test_tick &&
4341                git commit -m "O" &&
4342
4343                git branch O &&
4344                git branch A &&
4345                git branch B &&
4346
4347                git checkout A &&
4348                git mv a/y b/ &&
4349                git mv c/ d/ &&
4350                test_tick &&
4351                git commit -m "A" &&
4352
4353                git checkout B &&
4354                git mv a/y c/ &&
4355                git mv b/ d/ &&
4356                test_tick &&
4357                git commit -m "B"
4358        )
4359'
4360
4361test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via dual transitive rename' '
4362        (
4363                cd 13d &&
4364
4365                git checkout A^0 &&
4366
4367                test_must_fail git merge -s recursive B^0 >out 2>err &&
4368
4369                test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
4370                test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
4371
4372                git ls-files >paths &&
4373                ! grep b/ paths &&
4374                ! grep c/ paths &&
4375                grep "d/y" paths &&
4376
4377                test_path_is_missing b/y &&
4378                test_path_is_missing c/y &&
4379                test_path_is_file    d/y
4380        )
4381'
4382
4383test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual transitive rename' '
4384        (
4385                cd 13d &&
4386
4387                git reset --hard &&
4388                git checkout A^0 &&
4389
4390                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
4391
4392                test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
4393                test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
4394
4395                git ls-files >paths &&
4396                ! grep b/ paths &&
4397                ! grep c/ paths &&
4398                grep "d/y" paths &&
4399
4400                test_path_is_missing b/y &&
4401                test_path_is_missing c/y &&
4402                test_path_is_file    d/y
4403        )
4404'
4405
4406# Testcase 13e, directory rename in virtual merge base
4407#
4408# This testcase has a slightly different setup than all the above cases, in
4409# order to include a recursive case:
4410#
4411#      A   C
4412#      o - o
4413#     / \ / \
4414#  O o   X   ?
4415#     \ / \ /
4416#      o   o
4417#      B   D
4418#
4419#   Commit O: a/{z,y}
4420#   Commit A: b/{z,y}
4421#   Commit B: a/{z,y,x}
4422#   Commit C: b/{z,y,x}
4423#   Commit D: b/{z,y}, a/x
4424#   Expected: b/{z,y,x}  (sort of; see below for why this might not be expected)
4425#
4426#   NOTES: 'X' represents a virtual merge base.  With the default of
4427#          directory rename detection yielding conflicts, merging A and B
4428#          results in a conflict complaining about whether 'x' should be
4429#          under 'a/' or 'b/'.  However, when creating the virtual merge
4430#          base 'X', since virtual merge bases need to be written out as a
4431#          tree, we cannot have a conflict, so some resolution has to be
4432#          picked.
4433#
4434#          In choosing the right resolution, it's worth noting here that
4435#          commits C & D are merges of A & B that choose different
4436#          locations for 'x' (i.e. they resolve the conflict differently),
4437#          and so it would be nice when merging C & D if git could detect
4438#          this difference of opinion and report a conflict.  But the only
4439#          way to do so that I can think of would be to have the virtual
4440#          merge base place 'x' in some directory other than either 'a/' or
4441#          'b/', which seems a little weird -- especially since it'd result
4442#          in a rename/rename(1to2) conflict with a source path that never
4443#          existed in any version.
4444#
4445#          So, for now, when directory rename detection is set to
4446#          'conflict' just avoid doing directory rename detection at all in
4447#          the recursive case.  This will not allow us to detect a conflict
4448#          in the outer merge for this special kind of setup, but it at
4449#          least avoids hitting a BUG().
4450#
4451test_expect_success '13e-setup: directory rename detection in recursive case' '
4452        test_create_repo 13e &&
4453        (
4454                cd 13e &&
4455
4456                mkdir a &&
4457                echo z >a/z &&
4458                echo y >a/y &&
4459                git add a &&
4460                test_tick &&
4461                git commit -m "O" &&
4462
4463                git branch O &&
4464                git branch A &&
4465                git branch B &&
4466
4467                git checkout A &&
4468                git mv a/ b/ &&
4469                test_tick &&
4470                git commit -m "A" &&
4471
4472                git checkout B &&
4473                echo x >a/x &&
4474                git add a &&
4475                test_tick &&
4476                git commit -m "B" &&
4477
4478                git branch C A &&
4479                git branch D B &&
4480
4481                git checkout C &&
4482                test_must_fail git -c merge.directoryRenames=conflict merge B &&
4483                git add b/x &&
4484                test_tick &&
4485                git commit -m "C" &&
4486
4487
4488                git checkout D &&
4489                test_must_fail git -c merge.directoryRenames=conflict merge A &&
4490                git add b/x &&
4491                mkdir a &&
4492                git mv b/x a/x &&
4493                test_tick &&
4494                git commit -m "D"
4495        )
4496'
4497
4498test_expect_success '13e-check: directory rename detection in recursive case' '
4499        (
4500                cd 13e &&
4501
4502                git checkout --quiet D^0 &&
4503
4504                git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&
4505
4506                test_i18ngrep ! CONFLICT out &&
4507                test_i18ngrep ! BUG: err &&
4508                test_i18ngrep ! core.dumped err &&
4509                test_must_be_empty err &&
4510
4511                git ls-files >paths &&
4512                ! grep a/x paths &&
4513                grep b/x paths
4514        )
4515'
4516
4517test_done