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