t / t6036-recursive-corner-cases.shon commit merge-recursive: improve handling for rename/rename(2to1) conflicts (bbafc9c)
   1#!/bin/sh
   2
   3test_description='recursive merge corner cases involving criss-cross merges'
   4
   5. ./test-lib.sh
   6
   7#
   8#  L1  L2
   9#   o---o
  10#  / \ / \
  11# o   X   ?
  12#  \ / \ /
  13#   o---o
  14#  R1  R2
  15#
  16
  17test_expect_success 'setup basic criss-cross + rename with no modifications' '
  18        test_create_repo basic-rename &&
  19        (
  20                cd basic-rename &&
  21
  22                ten="0 1 2 3 4 5 6 7 8 9" &&
  23                for i in $ten
  24                do
  25                        echo line $i in a sample file
  26                done >one &&
  27                for i in $ten
  28                do
  29                        echo line $i in another sample file
  30                done >two &&
  31                git add one two &&
  32                test_tick && git commit -m initial &&
  33
  34                git branch L1 &&
  35                git checkout -b R1 &&
  36                git mv one three &&
  37                test_tick && git commit -m R1 &&
  38
  39                git checkout L1 &&
  40                git mv two three &&
  41                test_tick && git commit -m L1 &&
  42
  43                git checkout L1^0 &&
  44                test_tick && git merge -s ours R1 &&
  45                git tag L2 &&
  46
  47                git checkout R1^0 &&
  48                test_tick && git merge -s ours L1 &&
  49                git tag R2
  50        )
  51'
  52
  53test_expect_success 'merge simple rename+criss-cross with no modifications' '
  54        (
  55                cd basic-rename &&
  56
  57                git reset --hard &&
  58                git checkout L2^0 &&
  59
  60                test_must_fail git merge -s recursive R2^0 &&
  61
  62                git ls-files -s >out &&
  63                test_line_count = 2 out &&
  64                git ls-files -u >out &&
  65                test_line_count = 2 out &&
  66                git ls-files -o >out &&
  67                test_line_count = 1 out &&
  68
  69                git rev-parse >expect       \
  70                        L2:three   R2:three &&
  71                git rev-parse   >actual     \
  72                        :2:three   :3:three &&
  73                test_cmp expect actual
  74        )
  75'
  76
  77#
  78# Same as before, but modify L1 slightly:
  79#
  80#  L1m L2
  81#   o---o
  82#  / \ / \
  83# o   X   ?
  84#  \ / \ /
  85#   o---o
  86#  R1  R2
  87#
  88
  89test_expect_success 'setup criss-cross + rename merges with basic modification' '
  90        test_create_repo rename-modify &&
  91        (
  92                cd rename-modify &&
  93
  94                ten="0 1 2 3 4 5 6 7 8 9" &&
  95                for i in $ten
  96                do
  97                        echo line $i in a sample file
  98                done >one &&
  99                for i in $ten
 100                do
 101                        echo line $i in another sample file
 102                done >two &&
 103                git add one two &&
 104                test_tick && git commit -m initial &&
 105
 106                git branch L1 &&
 107                git checkout -b R1 &&
 108                git mv one three &&
 109                echo more >>two &&
 110                git add two &&
 111                test_tick && git commit -m R1 &&
 112
 113                git checkout L1 &&
 114                git mv two three &&
 115                test_tick && git commit -m L1 &&
 116
 117                git checkout L1^0 &&
 118                test_tick && git merge -s ours R1 &&
 119                git tag L2 &&
 120
 121                git checkout R1^0 &&
 122                test_tick && git merge -s ours L1 &&
 123                git tag R2
 124        )
 125'
 126
 127test_expect_success 'merge criss-cross + rename merges with basic modification' '
 128        (
 129                cd rename-modify &&
 130
 131                git checkout L2^0 &&
 132
 133                test_must_fail git merge -s recursive R2^0 &&
 134
 135                git ls-files -s >out &&
 136                test_line_count = 2 out &&
 137                git ls-files -u >out &&
 138                test_line_count = 2 out &&
 139                git ls-files -o >out &&
 140                test_line_count = 1 out &&
 141
 142                git rev-parse >expect       \
 143                        L2:three   R2:three &&
 144                git rev-parse   >actual     \
 145                        :2:three   :3:three &&
 146                test_cmp expect actual
 147        )
 148'
 149
 150#
 151# For the next test, we start with three commits in two lines of development
 152# which setup a rename/add conflict:
 153#   Commit A: File 'a' exists
 154#   Commit B: Rename 'a' -> 'new_a'
 155#   Commit C: Modify 'a', create different 'new_a'
 156# Later, two different people merge and resolve differently:
 157#   Commit D: Merge B & C, ignoring separately created 'new_a'
 158#   Commit E: Merge B & C making use of some piece of secondary 'new_a'
 159# Finally, someone goes to merge D & E.  Does git detect the conflict?
 160#
 161#      B   D
 162#      o---o
 163#     / \ / \
 164#  A o   X   ? F
 165#     \ / \ /
 166#      o---o
 167#      C   E
 168#
 169
 170test_expect_success 'setup differently handled merges of rename/add conflict' '
 171        test_create_repo rename-add &&
 172        (
 173                cd rename-add &&
 174
 175                printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
 176                git add a &&
 177                test_tick && git commit -m A &&
 178
 179                git branch B &&
 180                git checkout -b C &&
 181                echo 10 >>a &&
 182                test_write_lines 0 1 2 3 4 5 6 7 foobar >new_a &&
 183                git add a new_a &&
 184                test_tick && git commit -m C &&
 185
 186                git checkout B &&
 187                git mv a new_a &&
 188                test_tick && git commit -m B &&
 189
 190                git checkout B^0 &&
 191                test_must_fail git merge C &&
 192                git show :2:new_a >new_a &&
 193                git add new_a &&
 194                test_tick && git commit -m D &&
 195                git tag D &&
 196
 197                git checkout C^0 &&
 198                test_must_fail git merge B &&
 199                test_write_lines 0 1 2 3 4 5 6 7 bad_merge >new_a &&
 200                git add -u &&
 201                test_tick && git commit -m E &&
 202                git tag E
 203        )
 204'
 205
 206test_expect_success 'git detects differently handled merges conflict' '
 207        (
 208                cd rename-add &&
 209
 210                git checkout D^0 &&
 211
 212                test_must_fail git merge -s recursive E^0 &&
 213
 214                git ls-files -s >out &&
 215                test_line_count = 3 out &&
 216                git ls-files -u >out &&
 217                test_line_count = 3 out &&
 218                git ls-files -o >out &&
 219                test_line_count = 1 out &&
 220
 221                git rev-parse >expect       \
 222                        C:new_a  D:new_a  E:new_a &&
 223                git rev-parse   >actual     \
 224                        :1:new_a :2:new_a :3:new_a &&
 225                test_cmp expect actual &&
 226
 227                # Test that the two-way merge in new_a is as expected
 228                git cat-file -p D:new_a >ours &&
 229                git cat-file -p E:new_a >theirs &&
 230                >empty &&
 231                test_must_fail git merge-file \
 232                        -L "HEAD" \
 233                        -L "" \
 234                        -L "E^0" \
 235                        ours empty theirs &&
 236                sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
 237                git hash-object new_a >actual &&
 238                git hash-object ours  >expect &&
 239                test_cmp expect actual
 240        )
 241'
 242
 243#
 244# criss-cross + modify/delete:
 245#
 246#      B   D
 247#      o---o
 248#     / \ / \
 249#  A o   X   ? F
 250#     \ / \ /
 251#      o---o
 252#      C   E
 253#
 254#   Commit A: file with contents 'A\n'
 255#   Commit B: file with contents 'B\n'
 256#   Commit C: file not present
 257#   Commit D: file with contents 'B\n'
 258#   Commit E: file not present
 259#
 260# Merging commits D & E should result in modify/delete conflict.
 261
 262test_expect_success 'setup criss-cross + modify/delete resolved differently' '
 263        test_create_repo modify-delete &&
 264        (
 265                cd modify-delete &&
 266
 267                echo A >file &&
 268                git add file &&
 269                test_tick &&
 270                git commit -m A &&
 271
 272                git branch B &&
 273                git checkout -b C &&
 274                git rm file &&
 275                test_tick &&
 276                git commit -m C &&
 277
 278                git checkout B &&
 279                echo B >file &&
 280                git add file &&
 281                test_tick &&
 282                git commit -m B &&
 283
 284                git checkout B^0 &&
 285                test_must_fail git merge C &&
 286                echo B >file &&
 287                git add file &&
 288                test_tick &&
 289                git commit -m D &&
 290                git tag D &&
 291
 292                git checkout C^0 &&
 293                test_must_fail git merge B &&
 294                git rm file &&
 295                test_tick &&
 296                git commit -m E &&
 297                git tag E
 298        )
 299'
 300
 301test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
 302        (
 303                cd modify-delete &&
 304
 305                git checkout D^0 &&
 306
 307                test_must_fail git merge -s recursive E^0 &&
 308
 309                git ls-files -s >out &&
 310                test_line_count = 2 out &&
 311                git ls-files -u >out &&
 312                test_line_count = 2 out &&
 313
 314                git rev-parse >expect       \
 315                        master:file  B:file &&
 316                git rev-parse   >actual      \
 317                        :1:file      :2:file &&
 318                test_cmp expect actual
 319        )
 320'
 321
 322test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
 323        (
 324                cd modify-delete &&
 325
 326                git reset --hard &&
 327                git checkout E^0 &&
 328
 329                test_must_fail git merge -s recursive D^0 &&
 330
 331                git ls-files -s >out &&
 332                test_line_count = 2 out &&
 333                git ls-files -u >out &&
 334                test_line_count = 2 out &&
 335
 336                git rev-parse >expect       \
 337                        master:file  B:file &&
 338                git rev-parse   >actual      \
 339                        :1:file      :3:file &&
 340                test_cmp expect actual
 341        )
 342'
 343
 344#      SORRY FOR THE SUPER LONG DESCRIPTION, BUT THIS NEXT ONE IS HAIRY
 345#
 346# criss-cross + d/f conflict via add/add:
 347#   Commit A: Neither file 'a' nor directory 'a/' exists.
 348#   Commit B: Introduce 'a'
 349#   Commit C: Introduce 'a/file'
 350#   Commit D1: Merge B & C, keeping 'a'    and deleting 'a/'
 351#   Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
 352#
 353#      B   D1 or D2
 354#      o---o
 355#     / \ / \
 356#  A o   X   ? F
 357#     \ / \ /
 358#      o---o
 359#      C   E1 or E2 or E3
 360#
 361# I'll describe D2, E2, & E3 (which are alternatives for D1 & E1) more below...
 362#
 363# Merging D1 & E1 requires we first create a virtual merge base X from
 364# merging A & B in memory.  There are several possibilities for the merge-base:
 365#   1: Keep both 'a' and 'a/file' (assuming crazy filesystem allowing a tree
 366#      with a directory and file at same path): results in merge of D1 & E1
 367#      being clean with both files deleted.  Bad (no conflict detected).
 368#   2: Keep 'a' but not 'a/file': Merging D1 & E1 is clean and matches E1.  Bad.
 369#   3: Keep 'a/file' but not 'a': Merging D1 & E1 is clean and matches D1.  Bad.
 370#   4: Keep neither file: Merging D1 & E1 reports the D/F add/add conflict.
 371#
 372# So 4 sounds good for this case, but if we were to merge D1 & E3, where E3
 373# is defined as:
 374#   Commit E3: Merge B & C, keeping modified a, and deleting a/
 375# then we'd get an add/add conflict for 'a', which seems suboptimal.  A little
 376# creativity leads us to an alternate choice:
 377#   5: Keep 'a' as 'a~$UNIQUE' and a/file; results:
 378#        Merge D1 & E1: rename/delete conflict for 'a'; a/file silently deleted
 379#        Merge D1 & E3 is clean, as expected.
 380#
 381# So choice 5 at least provides some kind of conflict for the original case,
 382# and can merge cleanly as expected with D1 and E3.  It also made things just
 383# slightly funny for merging D1 and e$, where E4 is defined as:
 384#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
 385# in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE
 386# gets renamed to 'a' in D1 and to 'a2' in E4.  But that's better than having
 387# two files (both 'a' and 'a2') sitting around without the user being notified
 388# that we could detect they were related and need to be merged.  Also, choice
 389# 5 makes the handling of 'a/file' seem suboptimal.  What if we were to merge
 390# D2 and E4, where D2 is:
 391#   Commit D2: Merge B & C, renaming 'a'->'a2', keeping 'a/file'
 392# This would result in a clean merge with 'a2' having three-way merged
 393# contents (good), and deleting 'a/' (bad) -- it doesn't detect the
 394# conflict in how the different sides treated a/file differently.
 395# Continuing down the creative route:
 396#   6: Keep 'a' as 'a~$UNIQUE1' and keep 'a/' as 'a~$UNIQUE2/'; results:
 397#        Merge D1 & E1: rename/delete conflict for 'a' and each path under 'a/'.
 398#        Merge D1 & E3: clean, as expected.
 399#        Merge D1 & E4: rename/rename(1to2) conflict on 'a' vs 'a2'.
 400#        Merge D2 & E4: clean for 'a2', rename/delete for a/file
 401#
 402# Choice 6 could cause rename detection to take longer (providing more targets
 403# that need to be searched).  Also, the conflict message for each path under
 404# 'a/' might be annoying unless we can detect it at the directory level, print
 405# it once, and then suppress it for individual filepaths underneath.
 406#
 407#
 408# As of time of writing, git uses choice 5.  Directory rename detection and
 409# rename detection performance improvements might make choice 6 a desirable
 410# improvement.  But we can at least document where we fall short for now...
 411#
 412#
 413# Historically, this testcase also used:
 414#   Commit E2: Merge B & C, deleting 'a' but keeping slightly modified 'a/file'
 415# The merge of D1 & E2 is very similar to D1 & E1 -- it has similar issues for
 416# path 'a', but should always result in a modify/delete conflict for path
 417# 'a/file'.  These tests ran the two merges
 418#   D1 & E1
 419#   D1 & E2
 420# in both directions, to check for directional issues with D/F conflict
 421# handling. Later we added
 422#   D1 & E3
 423#   D1 & E4
 424#   D2 & E4
 425# for good measure, though we only ran those one way because we had pretty
 426# good confidence in merge-recursive's directional handling of D/F issues.
 427#
 428# Just to summarize all the intermediate merge commits:
 429#   Commit D1: Merge B & C, keeping a    and deleting a/
 430#   Commit D2: Merge B & C, renaming a->a2, keeping a/file
 431#   Commit E1: Merge B & C, deleting a but keeping a/file
 432#   Commit E2: Merge B & C, deleting a but keeping slightly modified a/file
 433#   Commit E3: Merge B & C, keeping modified a, and deleting a/
 434#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
 435#
 436
 437test_expect_success 'setup differently handled merges of directory/file conflict' '
 438        test_create_repo directory-file &&
 439        (
 440                cd directory-file &&
 441
 442                >ignore-me &&
 443                git add ignore-me &&
 444                test_tick &&
 445                git commit -m A &&
 446                git tag A &&
 447
 448                git branch B &&
 449                git checkout -b C &&
 450                mkdir a &&
 451                test_write_lines a b c d e f g >a/file &&
 452                git add a/file &&
 453                test_tick &&
 454                git commit -m C &&
 455
 456                git checkout B &&
 457                test_write_lines 1 2 3 4 5 6 7 >a &&
 458                git add a &&
 459                test_tick &&
 460                git commit -m B &&
 461
 462                git checkout B^0 &&
 463                git merge -s ours -m D1 C^0 &&
 464                git tag D1 &&
 465
 466                git checkout B^0 &&
 467                test_must_fail git merge C^0 &&
 468                git clean -fd &&
 469                git rm -rf a/ &&
 470                git rm a &&
 471                git cat-file -p B:a >a2 &&
 472                git add a2 &&
 473                git commit -m D2 &&
 474                git tag D2 &&
 475
 476                git checkout C^0 &&
 477                git merge -s ours -m E1 B^0 &&
 478                git tag E1 &&
 479
 480                git checkout C^0 &&
 481                git merge -s ours -m E2 B^0 &&
 482                test_write_lines a b c d e f g h >a/file &&
 483                git add a/file &&
 484                git commit --amend -C HEAD &&
 485                git tag E2 &&
 486
 487                git checkout C^0 &&
 488                test_must_fail git merge B^0 &&
 489                git clean -fd &&
 490                git rm -rf a/ &&
 491                test_write_lines 1 2 3 4 5 6 7 8 >a &&
 492                git add a &&
 493                git commit -m E3 &&
 494                git tag E3 &&
 495
 496                git checkout C^0 &&
 497                test_must_fail git merge B^0 &&
 498                git clean -fd &&
 499                git rm -rf a/ &&
 500                git rm a &&
 501                test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
 502                git add a2 &&
 503                git commit -m E4 &&
 504                git tag E4
 505        )
 506'
 507
 508test_expect_success 'merge of D1 & E1 fails but has appropriate contents' '
 509        test_when_finished "git -C directory-file reset --hard" &&
 510        test_when_finished "git -C directory-file clean -fdqx" &&
 511        (
 512                cd directory-file &&
 513
 514                git checkout D1^0 &&
 515
 516                test_must_fail git merge -s recursive E1^0 &&
 517
 518                git ls-files -s >out &&
 519                test_line_count = 2 out &&
 520                git ls-files -u >out &&
 521                test_line_count = 1 out &&
 522                git ls-files -o >out &&
 523                test_line_count = 1 out &&
 524
 525                git rev-parse >expect    \
 526                        A:ignore-me  B:a &&
 527                git rev-parse   >actual   \
 528                        :0:ignore-me :2:a &&
 529                test_cmp expect actual
 530        )
 531'
 532
 533test_expect_success 'merge of E1 & D1 fails but has appropriate contents' '
 534        test_when_finished "git -C directory-file reset --hard" &&
 535        test_when_finished "git -C directory-file clean -fdqx" &&
 536        (
 537                cd directory-file &&
 538
 539                git checkout E1^0 &&
 540
 541                test_must_fail git merge -s recursive D1^0 &&
 542
 543                git ls-files -s >out &&
 544                test_line_count = 2 out &&
 545                git ls-files -u >out &&
 546                test_line_count = 1 out &&
 547                git ls-files -o >out &&
 548                test_line_count = 1 out &&
 549
 550                git rev-parse >expect    \
 551                        A:ignore-me  B:a &&
 552                git rev-parse   >actual   \
 553                        :0:ignore-me :3:a &&
 554                test_cmp expect actual
 555        )
 556'
 557
 558test_expect_success 'merge of D1 & E2 fails but has appropriate contents' '
 559        test_when_finished "git -C directory-file reset --hard" &&
 560        test_when_finished "git -C directory-file clean -fdqx" &&
 561        (
 562                cd directory-file &&
 563
 564                git checkout D1^0 &&
 565
 566                test_must_fail git merge -s recursive E2^0 &&
 567
 568                git ls-files -s >out &&
 569                test_line_count = 4 out &&
 570                git ls-files -u >out &&
 571                test_line_count = 3 out &&
 572                git ls-files -o >out &&
 573                test_line_count = 2 out &&
 574
 575                git rev-parse >expect    \
 576                        B:a   E2:a/file  C:a/file   A:ignore-me &&
 577                git rev-parse   >actual   \
 578                        :2:a  :3:a/file  :1:a/file  :0:ignore-me &&
 579                test_cmp expect actual &&
 580
 581                test_path_is_file a~HEAD
 582        )
 583'
 584
 585test_expect_success 'merge of E2 & D1 fails but has appropriate contents' '
 586        test_when_finished "git -C directory-file reset --hard" &&
 587        test_when_finished "git -C directory-file clean -fdqx" &&
 588        (
 589                cd directory-file &&
 590
 591                git checkout E2^0 &&
 592
 593                test_must_fail git merge -s recursive D1^0 &&
 594
 595                git ls-files -s >out &&
 596                test_line_count = 4 out &&
 597                git ls-files -u >out &&
 598                test_line_count = 3 out &&
 599                git ls-files -o >out &&
 600                test_line_count = 2 out &&
 601
 602                git rev-parse >expect    \
 603                        B:a   E2:a/file  C:a/file   A:ignore-me &&
 604                git rev-parse   >actual   \
 605                        :3:a  :2:a/file  :1:a/file  :0:ignore-me &&
 606                test_cmp expect actual &&
 607
 608                test_path_is_file a~D1^0
 609        )
 610'
 611
 612test_expect_success 'merge of D1 & E3 succeeds' '
 613        test_when_finished "git -C directory-file reset --hard" &&
 614        test_when_finished "git -C directory-file clean -fdqx" &&
 615        (
 616                cd directory-file &&
 617
 618                git checkout D1^0 &&
 619
 620                git merge -s recursive E3^0 &&
 621
 622                git ls-files -s >out &&
 623                test_line_count = 2 out &&
 624                git ls-files -u >out &&
 625                test_line_count = 0 out &&
 626                git ls-files -o >out &&
 627                test_line_count = 1 out &&
 628
 629                git rev-parse >expect    \
 630                        A:ignore-me  E3:a &&
 631                git rev-parse   >actual   \
 632                        :0:ignore-me :0:a &&
 633                test_cmp expect actual
 634        )
 635'
 636
 637test_expect_success 'merge of D1 & E4 notifies user a and a2 are related' '
 638        test_when_finished "git -C directory-file reset --hard" &&
 639        test_when_finished "git -C directory-file clean -fdqx" &&
 640        (
 641                cd directory-file &&
 642
 643                git checkout D1^0 &&
 644
 645                test_must_fail git merge -s recursive E4^0 &&
 646
 647                git ls-files -s >out &&
 648                test_line_count = 4 out &&
 649                git ls-files -u >out &&
 650                test_line_count = 3 out &&
 651                git ls-files -o >out &&
 652                test_line_count = 1 out &&
 653
 654                git rev-parse >expect                  \
 655                        A:ignore-me  B:a   D1:a  E4:a2 &&
 656                git rev-parse   >actual                \
 657                        :0:ignore-me :1:a~Temporary\ merge\ branch\ 2  :2:a  :3:a2 &&
 658                test_cmp expect actual
 659        )
 660'
 661
 662test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file' '
 663        test_when_finished "git -C directory-file reset --hard" &&
 664        test_when_finished "git -C directory-file clean -fdqx" &&
 665        (
 666                cd directory-file &&
 667
 668                git checkout D2^0 &&
 669
 670                test_must_fail git merge -s recursive E4^0 &&
 671
 672                git ls-files -s >out &&
 673                test_line_count = 3 out &&
 674                git ls-files -u >out &&
 675                test_line_count = 1 out &&
 676                git ls-files -o >out &&
 677                test_line_count = 1 out &&
 678
 679                git rev-parse >expect                 \
 680                        A:ignore-me  E4:a2  D2:a/file &&
 681                git rev-parse   >actual               \
 682                        :0:ignore-me :0:a2  :2:a/file &&
 683                test_cmp expect actual
 684        )
 685'
 686
 687#
 688# criss-cross with rename/rename(1to2)/modify followed by
 689# rename/rename(2to1)/modify:
 690#
 691#      B   D
 692#      o---o
 693#     / \ / \
 694#  A o   X   ? F
 695#     \ / \ /
 696#      o---o
 697#      C   E
 698#
 699#   Commit A: new file: a
 700#   Commit B: rename a->b, modifying by adding a line
 701#   Commit C: rename a->c
 702#   Commit D: merge B&C, resolving conflict by keeping contents in newname
 703#   Commit E: merge B&C, resolving conflict similar to D but adding another line
 704#
 705# There is a conflict merging B & C, but one of filename not of file
 706# content.  Whoever created D and E chose specific resolutions for that
 707# conflict resolution.  Now, since: (1) there is no content conflict
 708# merging B & C, (2) D does not modify that merged content further, and (3)
 709# both D & E resolve the name conflict in the same way, the modification to
 710# newname in E should not cause any conflicts when it is merged with D.
 711# (Note that this can be accomplished by having the virtual merge base have
 712# the merged contents of b and c stored in a file named a, which seems like
 713# the most logical choice anyway.)
 714#
 715# Comment from Junio: I do not necessarily agree with the choice "a", but
 716# it feels sound to say "B and C do not agree what the final pathname
 717# should be, but we know this content was derived from the common A:a so we
 718# use one path whose name is arbitrary in the virtual merge base X between
 719# D and E" and then further let the rename detection to notice that that
 720# arbitrary path gets renamed between X-D to "newname" and X-E also to
 721# "newname" to resolve it as both sides renaming it to the same new
 722# name. It is akin to what we do at the content level, i.e. "B and C do not
 723# agree what the final contents should be, so we leave the conflict marker
 724# but that may cancel out at the final merge stage".
 725
 726test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
 727        test_create_repo rename-squared-squared &&
 728        (
 729                cd rename-squared-squared &&
 730
 731                printf "1\n2\n3\n4\n5\n6\n" >a &&
 732                git add a &&
 733                git commit -m A &&
 734                git tag A &&
 735
 736                git checkout -b B A &&
 737                git mv a b &&
 738                echo 7 >>b &&
 739                git add -u &&
 740                git commit -m B &&
 741
 742                git checkout -b C A &&
 743                git mv a c &&
 744                git commit -m C &&
 745
 746                git checkout -q B^0 &&
 747                git merge --no-commit -s ours C^0 &&
 748                git mv b newname &&
 749                git commit -m "Merge commit C^0 into HEAD" &&
 750                git tag D &&
 751
 752                git checkout -q C^0 &&
 753                git merge --no-commit -s ours B^0 &&
 754                git mv c newname &&
 755                printf "7\n8\n" >>newname &&
 756                git add -u &&
 757                git commit -m "Merge commit B^0 into HEAD" &&
 758                git tag E
 759        )
 760'
 761
 762test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
 763        (
 764                cd rename-squared-squared &&
 765
 766                git checkout D^0 &&
 767
 768                git merge -s recursive E^0 &&
 769
 770                git ls-files -s >out &&
 771                test_line_count = 1 out &&
 772                git ls-files -u >out &&
 773                test_line_count = 0 out &&
 774                git ls-files -o >out &&
 775                test_line_count = 1 out &&
 776
 777                test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
 778        )
 779'
 780
 781#
 782# criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify:
 783#
 784#      B   D
 785#      o---o
 786#     / \ / \
 787#  A o   X   ? F
 788#     \ / \ /
 789#      o---o
 790#      C   E
 791#
 792#   Commit A: new file: a
 793#   Commit B: rename a->b
 794#   Commit C: rename a->c, add different a
 795#   Commit D: merge B&C, keeping b&c and (new) a modified at beginning
 796#   Commit E: merge B&C, keeping b&c and (new) a modified at end
 797#
 798# Merging commits D & E should result in no conflict; doing so correctly
 799# requires getting the virtual merge base (from merging B&C) right, handling
 800# renaming carefully (both in the virtual merge base and later), and getting
 801# content merge handled.
 802
 803test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
 804        test_create_repo rename-rename-add-source &&
 805        (
 806                cd rename-rename-add-source &&
 807
 808                printf "lots\nof\nwords\nand\ncontent\n" >a &&
 809                git add a &&
 810                git commit -m A &&
 811                git tag A &&
 812
 813                git checkout -b B A &&
 814                git mv a b &&
 815                git commit -m B &&
 816
 817                git checkout -b C A &&
 818                git mv a c &&
 819                printf "2\n3\n4\n5\n6\n7\n" >a &&
 820                git add a &&
 821                git commit -m C &&
 822
 823                git checkout B^0 &&
 824                git merge --no-commit -s ours C^0 &&
 825                git checkout C -- a c &&
 826                mv a old_a &&
 827                echo 1 >a &&
 828                cat old_a >>a &&
 829                rm old_a &&
 830                git add -u &&
 831                git commit -m "Merge commit C^0 into HEAD" &&
 832                git tag D &&
 833
 834                git checkout C^0 &&
 835                git merge --no-commit -s ours B^0 &&
 836                git checkout B -- b &&
 837                echo 8 >>a &&
 838                git add -u &&
 839                git commit -m "Merge commit B^0 into HEAD" &&
 840                git tag E
 841        )
 842'
 843
 844test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
 845        (
 846                cd rename-rename-add-source &&
 847
 848                git checkout D^0 &&
 849
 850                git merge -s recursive E^0 &&
 851
 852                git ls-files -s >out &&
 853                test_line_count = 3 out &&
 854                git ls-files -u >out &&
 855                test_line_count = 0 out &&
 856                git ls-files -o >out &&
 857                test_line_count = 1 out &&
 858
 859                printf "1\n2\n3\n4\n5\n6\n7\n8\n" >correct &&
 860                git rev-parse >expect \
 861                        A:a   A:a     \
 862                        correct       &&
 863                git rev-parse   >actual  \
 864                        :0:b  :0:c       &&
 865                git hash-object >>actual \
 866                        a                &&
 867                test_cmp expect actual
 868        )
 869'
 870
 871#
 872# criss-cross with rename/rename(1to2)/add-dest + simple modify:
 873#
 874#      B   D
 875#      o---o
 876#     / \ / \
 877#  A o   X   ? F
 878#     \ / \ /
 879#      o---o
 880#      C   E
 881#
 882#   Commit A: new file: a
 883#   Commit B: rename a->b, add c
 884#   Commit C: rename a->c
 885#   Commit D: merge B&C, keeping A:a and B:c
 886#   Commit E: merge B&C, keeping A:a and slightly modified c from B
 887#
 888# Merging commits D & E should result in no conflict.  The virtual merge
 889# base of B & C needs to not delete B:c for that to work, though...
 890
 891test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
 892        test_create_repo rename-rename-add-dest &&
 893        (
 894                cd rename-rename-add-dest &&
 895
 896                >a &&
 897                git add a &&
 898                git commit -m A &&
 899                git tag A &&
 900
 901                git checkout -b B A &&
 902                git mv a b &&
 903                printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
 904                git add c &&
 905                git commit -m B &&
 906
 907                git checkout -b C A &&
 908                git mv a c &&
 909                git commit -m C &&
 910
 911                git checkout B^0 &&
 912                git merge --no-commit -s ours C^0 &&
 913                git mv b a &&
 914                git commit -m "D is like B but renames b back to a" &&
 915                git tag D &&
 916
 917                git checkout B^0 &&
 918                git merge --no-commit -s ours C^0 &&
 919                git mv b a &&
 920                echo 8 >>c &&
 921                git add c &&
 922                git commit -m "E like D but has mod in c" &&
 923                git tag E
 924        )
 925'
 926
 927test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
 928        (
 929                cd rename-rename-add-dest &&
 930
 931                git checkout D^0 &&
 932
 933                git merge -s recursive E^0 &&
 934
 935                git ls-files -s >out &&
 936                test_line_count = 2 out &&
 937                git ls-files -u >out &&
 938                test_line_count = 0 out &&
 939                git ls-files -o >out &&
 940                test_line_count = 1 out &&
 941
 942                git rev-parse >expect \
 943                        A:a   E:c     &&
 944                git rev-parse   >actual \
 945                        :0:a  :0:c      &&
 946                test_cmp expect actual
 947        )
 948'
 949
 950#
 951# criss-cross with modify/modify on a symlink:
 952#
 953#      B   D
 954#      o---o
 955#     / \ / \
 956#  A o   X   ? F
 957#     \ / \ /
 958#      o---o
 959#      C   E
 960#
 961#   Commit A: simple simlink fickle->lagoon
 962#   Commit B: redirect fickle->disneyland
 963#   Commit C: redirect fickle->home
 964#   Commit D: merge B&C, resolving in favor of B
 965#   Commit E: merge B&C, resolving in favor of C
 966#
 967# This is an obvious modify/modify conflict for the symlink 'fickle'.  Can
 968# git detect it?
 969
 970test_expect_success 'setup symlink modify/modify' '
 971        test_create_repo symlink-modify-modify &&
 972        (
 973                cd symlink-modify-modify &&
 974
 975                test_ln_s_add lagoon fickle &&
 976                git commit -m A &&
 977                git tag A &&
 978
 979                git checkout -b B A &&
 980                git rm fickle &&
 981                test_ln_s_add disneyland fickle &&
 982                git commit -m B &&
 983
 984                git checkout -b C A &&
 985                git rm fickle &&
 986                test_ln_s_add home fickle &&
 987                git add fickle &&
 988                git commit -m C &&
 989
 990                git checkout -q B^0 &&
 991                git merge -s ours -m D C^0 &&
 992                git tag D &&
 993
 994                git checkout -q C^0 &&
 995                git merge -s ours -m E B^0 &&
 996                git tag E
 997        )
 998'
 999
1000test_expect_failure 'check symlink modify/modify' '
1001        (
1002                cd symlink-modify-modify &&
1003
1004                git checkout D^0 &&
1005
1006                test_must_fail git merge -s recursive E^0 &&
1007
1008                git ls-files -s >out &&
1009                test_line_count = 3 out &&
1010                git ls-files -u >out &&
1011                test_line_count = 3 out &&
1012                git ls-files -o >out &&
1013                test_line_count = 1 out
1014        )
1015'
1016
1017#
1018# criss-cross with add/add of a symlink:
1019#
1020#      B   D
1021#      o---o
1022#     / \ / \
1023#  A o   X   ? F
1024#     \ / \ /
1025#      o---o
1026#      C   E
1027#
1028#   Commit A: No symlink or path exists yet
1029#   Commit B: set up symlink: fickle->disneyland
1030#   Commit C: set up symlink: fickle->home
1031#   Commit D: merge B&C, resolving in favor of B
1032#   Commit E: merge B&C, resolving in favor of C
1033#
1034# This is an obvious add/add conflict for the symlink 'fickle'.  Can
1035# git detect it?
1036
1037test_expect_success 'setup symlink add/add' '
1038        test_create_repo symlink-add-add &&
1039        (
1040                cd symlink-add-add &&
1041
1042                touch ignoreme &&
1043                git add ignoreme &&
1044                git commit -m A &&
1045                git tag A &&
1046
1047                git checkout -b B A &&
1048                test_ln_s_add disneyland fickle &&
1049                git commit -m B &&
1050
1051                git checkout -b C A &&
1052                test_ln_s_add home fickle &&
1053                git add fickle &&
1054                git commit -m C &&
1055
1056                git checkout -q B^0 &&
1057                git merge -s ours -m D C^0 &&
1058                git tag D &&
1059
1060                git checkout -q C^0 &&
1061                git merge -s ours -m E B^0 &&
1062                git tag E
1063        )
1064'
1065
1066test_expect_failure 'check symlink add/add' '
1067        (
1068                cd symlink-add-add &&
1069
1070                git checkout D^0 &&
1071
1072                test_must_fail git merge -s recursive E^0 &&
1073
1074                git ls-files -s >out &&
1075                test_line_count = 2 out &&
1076                git ls-files -u >out &&
1077                test_line_count = 2 out &&
1078                git ls-files -o >out &&
1079                test_line_count = 1 out
1080        )
1081'
1082
1083#
1084# criss-cross with modify/modify on a submodule:
1085#
1086#      B   D
1087#      o---o
1088#     / \ / \
1089#  A o   X   ? F
1090#     \ / \ /
1091#      o---o
1092#      C   E
1093#
1094#   Commit A: simple submodule repo
1095#   Commit B: update repo
1096#   Commit C: update repo differently
1097#   Commit D: merge B&C, resolving in favor of B
1098#   Commit E: merge B&C, resolving in favor of C
1099#
1100# This is an obvious modify/modify conflict for the submodule 'repo'.  Can
1101# git detect it?
1102
1103test_expect_success 'setup submodule modify/modify' '
1104        test_create_repo submodule-modify-modify &&
1105        (
1106                cd submodule-modify-modify &&
1107
1108                test_create_repo submod &&
1109                (
1110                        cd submod &&
1111                        touch file-A &&
1112                        git add file-A &&
1113                        git commit -m A &&
1114                        git tag A &&
1115
1116                        git checkout -b B A &&
1117                        touch file-B &&
1118                        git add file-B &&
1119                        git commit -m B &&
1120                        git tag B &&
1121
1122                        git checkout -b C A &&
1123                        touch file-C &&
1124                        git add file-C &&
1125                        git commit -m C &&
1126                        git tag C
1127                ) &&
1128
1129                git -C submod reset --hard A &&
1130                git add submod &&
1131                git commit -m A &&
1132                git tag A &&
1133
1134                git checkout -b B A &&
1135                git -C submod reset --hard B &&
1136                git add submod &&
1137                git commit -m B &&
1138
1139                git checkout -b C A &&
1140                git -C submod reset --hard C &&
1141                git add submod &&
1142                git commit -m C &&
1143
1144                git checkout -q B^0 &&
1145                git merge -s ours -m D C^0 &&
1146                git tag D &&
1147
1148                git checkout -q C^0 &&
1149                git merge -s ours -m E B^0 &&
1150                git tag E
1151        )
1152'
1153
1154test_expect_failure 'check submodule modify/modify' '
1155        (
1156                cd submodule-modify-modify &&
1157
1158                git checkout D^0 &&
1159
1160                test_must_fail git merge -s recursive E^0 &&
1161
1162                git ls-files -s >out &&
1163                test_line_count = 3 out &&
1164                git ls-files -u >out &&
1165                test_line_count = 3 out &&
1166                git ls-files -o >out &&
1167                test_line_count = 1 out
1168        )
1169'
1170
1171#
1172# criss-cross with add/add on a submodule:
1173#
1174#      B   D
1175#      o---o
1176#     / \ / \
1177#  A o   X   ? F
1178#     \ / \ /
1179#      o---o
1180#      C   E
1181#
1182#   Commit A: nothing of note
1183#   Commit B: introduce submodule repo
1184#   Commit C: introduce submodule repo at different commit
1185#   Commit D: merge B&C, resolving in favor of B
1186#   Commit E: merge B&C, resolving in favor of C
1187#
1188# This is an obvious add/add conflict for the submodule 'repo'.  Can
1189# git detect it?
1190
1191test_expect_success 'setup submodule add/add' '
1192        test_create_repo submodule-add-add &&
1193        (
1194                cd submodule-add-add &&
1195
1196                test_create_repo submod &&
1197                (
1198                        cd submod &&
1199                        touch file-A &&
1200                        git add file-A &&
1201                        git commit -m A &&
1202                        git tag A &&
1203
1204                        git checkout -b B A &&
1205                        touch file-B &&
1206                        git add file-B &&
1207                        git commit -m B &&
1208                        git tag B &&
1209
1210                        git checkout -b C A &&
1211                        touch file-C &&
1212                        git add file-C &&
1213                        git commit -m C &&
1214                        git tag C
1215                ) &&
1216
1217                touch irrelevant-file &&
1218                git add irrelevant-file &&
1219                git commit -m A &&
1220                git tag A &&
1221
1222                git checkout -b B A &&
1223                git -C submod reset --hard B &&
1224                git add submod &&
1225                git commit -m B &&
1226
1227                git checkout -b C A &&
1228                git -C submod reset --hard C &&
1229                git add submod &&
1230                git commit -m C &&
1231
1232                git checkout -q B^0 &&
1233                git merge -s ours -m D C^0 &&
1234                git tag D &&
1235
1236                git checkout -q C^0 &&
1237                git merge -s ours -m E B^0 &&
1238                git tag E
1239        )
1240'
1241
1242test_expect_failure 'check submodule add/add' '
1243        (
1244                cd submodule-add-add &&
1245
1246                git checkout D^0 &&
1247
1248                test_must_fail git merge -s recursive E^0 &&
1249
1250                git ls-files -s >out &&
1251                test_line_count = 3 out &&
1252                git ls-files -u >out &&
1253                test_line_count = 2 out &&
1254                git ls-files -o >out &&
1255                test_line_count = 1 out
1256        )
1257'
1258
1259#
1260# criss-cross with conflicting entry types:
1261#
1262#      B   D
1263#      o---o
1264#     / \ / \
1265#  A o   X   ? F
1266#     \ / \ /
1267#      o---o
1268#      C   E
1269#
1270#   Commit A: nothing of note
1271#   Commit B: introduce submodule 'path'
1272#   Commit C: introduce symlink 'path'
1273#   Commit D: merge B&C, resolving in favor of B
1274#   Commit E: merge B&C, resolving in favor of C
1275#
1276# This is an obvious add/add conflict for 'path'.  Can git detect it?
1277
1278test_expect_success 'setup conflicting entry types (submodule vs symlink)' '
1279        test_create_repo submodule-symlink-add-add &&
1280        (
1281                cd submodule-symlink-add-add &&
1282
1283                test_create_repo path &&
1284                (
1285                        cd path &&
1286                        touch file-B &&
1287                        git add file-B &&
1288                        git commit -m B &&
1289                        git tag B
1290                ) &&
1291
1292                touch irrelevant-file &&
1293                git add irrelevant-file &&
1294                git commit -m A &&
1295                git tag A &&
1296
1297                git checkout -b B A &&
1298                git -C path reset --hard B &&
1299                git add path &&
1300                git commit -m B &&
1301
1302                git checkout -b C A &&
1303                rm -rf path/ &&
1304                test_ln_s_add irrelevant-file path &&
1305                git commit -m C &&
1306
1307                git checkout -q B^0 &&
1308                git merge -s ours -m D C^0 &&
1309                git tag D &&
1310
1311                git checkout -q C^0 &&
1312                git merge -s ours -m E B^0 &&
1313                git tag E
1314        )
1315'
1316
1317test_expect_failure 'check conflicting entry types (submodule vs symlink)' '
1318        (
1319                cd submodule-symlink-add-add &&
1320
1321                git checkout D^0 &&
1322
1323                test_must_fail git merge -s recursive E^0 &&
1324
1325                git ls-files -s >out &&
1326                test_line_count = 3 out &&
1327                git ls-files -u >out &&
1328                test_line_count = 2 out &&
1329                git ls-files -o >out &&
1330                test_line_count = 1 out
1331        )
1332'
1333
1334#
1335# criss-cross with regular files that have conflicting modes:
1336#
1337#      B   D
1338#      o---o
1339#     / \ / \
1340#  A o   X   ? F
1341#     \ / \ /
1342#      o---o
1343#      C   E
1344#
1345#   Commit A: nothing of note
1346#   Commit B: introduce file source_me.bash, not executable
1347#   Commit C: introduce file source_me.bash, executable
1348#   Commit D: merge B&C, resolving in favor of B
1349#   Commit E: merge B&C, resolving in favor of C
1350#
1351# This is an obvious add/add mode conflict.  Can git detect it?
1352
1353test_expect_success 'setup conflicting modes for regular file' '
1354        test_create_repo regular-file-mode-conflict &&
1355        (
1356                cd regular-file-mode-conflict &&
1357
1358                touch irrelevant-file &&
1359                git add irrelevant-file &&
1360                git commit -m A &&
1361                git tag A &&
1362
1363                git checkout -b B A &&
1364                echo "command_to_run" >source_me.bash &&
1365                git add source_me.bash &&
1366                git commit -m B &&
1367
1368                git checkout -b C A &&
1369                echo "command_to_run" >source_me.bash &&
1370                git add source_me.bash &&
1371                test_chmod +x source_me.bash &&
1372                git commit -m C &&
1373
1374                git checkout -q B^0 &&
1375                git merge -s ours -m D C^0 &&
1376                git tag D &&
1377
1378                git checkout -q C^0 &&
1379                git merge -s ours -m E B^0 &&
1380                git tag E
1381        )
1382'
1383
1384test_expect_failure 'check conflicting modes for regular file' '
1385        (
1386                cd regular-file-mode-conflict &&
1387
1388                git checkout D^0 &&
1389
1390                test_must_fail git merge -s recursive E^0 &&
1391
1392                git ls-files -s >out &&
1393                test_line_count = 3 out &&
1394                git ls-files -u >out &&
1395                test_line_count = 2 out &&
1396                git ls-files -o >out &&
1397                test_line_count = 1 out
1398        )
1399'
1400
1401# Setup:
1402#          L1---L2
1403#         /  \ /  \
1404#   master    X    ?
1405#         \  / \  /
1406#          R1---R2
1407#
1408# Where:
1409#   master has two files, named 'b' and 'a'
1410#   branches L1 and R1 both modify each of the two files in conflicting ways
1411#
1412#   L2 is a merge of R1 into L1; more on it later.
1413#   R2 is a merge of L1 into R1; more on it later.
1414#
1415#   X is an auto-generated merge-base used when merging L2 and R2.
1416#   since X is a merge of L1 and R1, it has conflicting versions of each file
1417#
1418#   More about L2 and R2:
1419#     - both resolve the conflicts in 'b' and 'a' differently
1420#     - L2 renames 'b' to 'm'
1421#     - R2 renames 'a' to 'm'
1422#
1423#   In the end, in file 'm' we have four different conflicting files (from
1424#   two versions of 'b' and two of 'a').  In addition, if
1425#   merge.conflictstyle is diff3, then the base version also has
1426#   conflict markers of its own, leading to a total of three levels of
1427#   conflict markers.  This is a pretty weird corner case, but we just want
1428#   to ensure that we handle it as well as practical.
1429
1430test_expect_success 'setup nested conflicts' '
1431        test_create_repo nested_conflicts &&
1432        (
1433                cd nested_conflicts &&
1434
1435                # Create some related files now
1436                for i in $(test_seq 1 10)
1437                do
1438                        echo Random base content line $i
1439                done >initial &&
1440
1441                cp initial b_L1 &&
1442                cp initial b_R1 &&
1443                cp initial b_L2 &&
1444                cp initial b_R2 &&
1445                cp initial a_L1 &&
1446                cp initial a_R1 &&
1447                cp initial a_L2 &&
1448                cp initial a_R2 &&
1449
1450                test_write_lines b b_L1 >>b_L1 &&
1451                test_write_lines b b_R1 >>b_R1 &&
1452                test_write_lines b b_L2 >>b_L2 &&
1453                test_write_lines b b_R2 >>b_R2 &&
1454                test_write_lines a a_L1 >>a_L1 &&
1455                test_write_lines a a_R1 >>a_R1 &&
1456                test_write_lines a a_L2 >>a_L2 &&
1457                test_write_lines a a_R2 >>a_R2 &&
1458
1459                # Setup original commit (or merge-base), consisting of
1460                # files named "b" and "a"
1461                cp initial b &&
1462                cp initial a &&
1463                echo b >>b &&
1464                echo a >>a &&
1465                git add b a &&
1466                test_tick && git commit -m initial &&
1467
1468                git branch L &&
1469                git branch R &&
1470
1471                # Handle the left side
1472                git checkout L &&
1473                mv -f b_L1 b &&
1474                mv -f a_L1 a &&
1475                git add b a &&
1476                test_tick && git commit -m "version L1 of files" &&
1477                git tag L1 &&
1478
1479                # Handle the right side
1480                git checkout R &&
1481                mv -f b_R1 b &&
1482                mv -f a_R1 a &&
1483                git add b a &&
1484                test_tick && git commit -m "verson R1 of files" &&
1485                git tag R1 &&
1486
1487                # Create first merge on left side
1488                git checkout L &&
1489                test_must_fail git merge R1 &&
1490                mv -f b_L2 b &&
1491                mv -f a_L2 a &&
1492                git add b a &&
1493                git mv b m &&
1494                test_tick && git commit -m "left merge, rename b->m" &&
1495                git tag L2 &&
1496
1497                # Create first merge on right side
1498                git checkout R &&
1499                test_must_fail git merge L1 &&
1500                mv -f b_R2 b &&
1501                mv -f a_R2 a &&
1502                git add b a &&
1503                git mv a m &&
1504                test_tick && git commit -m "right merge, rename a->m" &&
1505                git tag R2
1506        )
1507'
1508
1509test_expect_success 'check nested conflicts' '
1510        (
1511                cd nested_conflicts &&
1512
1513                git clean -f &&
1514                git checkout L2^0 &&
1515
1516                # Merge must fail; there is a conflict
1517                test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R2^0 &&
1518
1519                # Make sure the index has the right number of entries
1520                git ls-files -s >out &&
1521                test_line_count = 2 out &&
1522                git ls-files -u >out &&
1523                test_line_count = 2 out &&
1524                # Ensure we have the correct number of untracked files
1525                git ls-files -o >out &&
1526                test_line_count = 1 out &&
1527
1528                # Create a and b from virtual merge base X
1529                git cat-file -p master:a >base &&
1530                git cat-file -p L1:a >ours &&
1531                git cat-file -p R1:a >theirs &&
1532                test_must_fail git merge-file --diff3 \
1533                        -L "Temporary merge branch 1" \
1534                        -L "merged common ancestors"  \
1535                        -L "Temporary merge branch 2" \
1536                        ours  \
1537                        base  \
1538                        theirs &&
1539                sed -e "s/^\([<|=>]\)/\1\1/" ours >vmb_a &&
1540
1541                git cat-file -p master:b >base &&
1542                git cat-file -p L1:b >ours &&
1543                git cat-file -p R1:b >theirs &&
1544                test_must_fail git merge-file --diff3 \
1545                        -L "Temporary merge branch 1" \
1546                        -L "merged common ancestors"  \
1547                        -L "Temporary merge branch 2" \
1548                        ours  \
1549                        base  \
1550                        theirs &&
1551                sed -e "s/^\([<|=>]\)/\1\1/" ours >vmb_b &&
1552
1553                # Compare :2:m to expected values
1554                git cat-file -p L2:m >ours &&
1555                git cat-file -p R2:b >theirs &&
1556                test_must_fail git merge-file --diff3  \
1557                        -L "HEAD:m"                    \
1558                        -L "merged common ancestors:b" \
1559                        -L "R2^0:b"                    \
1560                        ours                           \
1561                        vmb_b                          \
1562                        theirs                         &&
1563                sed -e "s/^\([<|=>]\)/\1\1/" ours >m_stage_2 &&
1564                git cat-file -p :2:m >actual &&
1565                test_cmp m_stage_2 actual &&
1566
1567                # Compare :3:m to expected values
1568                git cat-file -p L2:a >ours &&
1569                git cat-file -p R2:m >theirs &&
1570                test_must_fail git merge-file --diff3  \
1571                        -L "HEAD:a"                    \
1572                        -L "merged common ancestors:a" \
1573                        -L "R2^0:m"                    \
1574                        ours                           \
1575                        vmb_a                          \
1576                        theirs                         &&
1577                sed -e "s/^\([<|=>]\)/\1\1/" ours >m_stage_3 &&
1578                git cat-file -p :3:m >actual &&
1579                test_cmp m_stage_3 actual &&
1580
1581                # Compare m to expected contents
1582                >empty &&
1583                cp -a m_stage_2 expected_final_m &&
1584                test_must_fail git merge-file --diff3 \
1585                        -L "HEAD"                     \
1586                        -L "merged common ancestors"  \
1587                        -L "R2^0"                     \
1588                        expected_final_m              \
1589                        empty                         \
1590                        m_stage_3                     &&
1591                test_cmp expected_final_m m
1592        )
1593'
1594
1595# Setup:
1596#          L1---L2---L3
1597#         /  \ /  \ /  \
1598#   master    X1   X2   ?
1599#         \  / \  / \  /
1600#          R1---R2---R3
1601#
1602# Where:
1603#   master has one file named 'content'
1604#   branches L1 and R1 both modify each of the two files in conflicting ways
1605#
1606#   L<n> (n>1) is a merge of R<n-1> into L<n-1>
1607#   R<n> (n>1) is a merge of L<n-1> into R<n-1>
1608#   L<n> and R<n> resolve the conflicts differently.
1609#
1610#   X<n> is an auto-generated merge-base used when merging L<n+1> and R<n+1>.
1611#   By construction, X1 has conflict markers due to conflicting versions.
1612#   X2, due to using merge.conflictstyle=3, has nested conflict markers.
1613#
1614#   So, merging R3 into L3 using merge.conflictstyle=3 should show the
1615#   nested conflict markers from X2 in the base version -- that means we
1616#   have three levels of conflict markers.  Can we distinguish all three?
1617
1618test_expect_success 'setup virtual merge base with nested conflicts' '
1619        test_create_repo virtual_merge_base_has_nested_conflicts &&
1620        (
1621                cd virtual_merge_base_has_nested_conflicts &&
1622
1623                # Create some related files now
1624                for i in $(test_seq 1 10)
1625                do
1626                        echo Random base content line $i
1627                done >content &&
1628
1629                # Setup original commit
1630                git add content &&
1631                test_tick && git commit -m initial &&
1632
1633                git branch L &&
1634                git branch R &&
1635
1636                # Create L1
1637                git checkout L &&
1638                echo left >>content &&
1639                git add content &&
1640                test_tick && git commit -m "version L1 of content" &&
1641                git tag L1 &&
1642
1643                # Create R1
1644                git checkout R &&
1645                echo right >>content &&
1646                git add content &&
1647                test_tick && git commit -m "verson R1 of content" &&
1648                git tag R1 &&
1649
1650                # Create L2
1651                git checkout L &&
1652                test_must_fail git -c merge.conflictstyle=diff3 merge R1 &&
1653                git checkout L1 content &&
1654                test_tick && git commit -m "version L2 of content" &&
1655                git tag L2 &&
1656
1657                # Create R2
1658                git checkout R &&
1659                test_must_fail git -c merge.conflictstyle=diff3 merge L1 &&
1660                git checkout R1 content &&
1661                test_tick && git commit -m "version R2 of content" &&
1662                git tag R2 &&
1663
1664                # Create L3
1665                git checkout L &&
1666                test_must_fail git -c merge.conflictstyle=diff3 merge R2 &&
1667                git checkout L1 content &&
1668                test_tick && git commit -m "version L3 of content" &&
1669                git tag L3 &&
1670
1671                # Create R3
1672                git checkout R &&
1673                test_must_fail git -c merge.conflictstyle=diff3 merge L2 &&
1674                git checkout R1 content &&
1675                test_tick && git commit -m "version R3 of content" &&
1676                git tag R3
1677        )
1678'
1679
1680test_expect_success 'check virtual merge base with nested conflicts' '
1681        (
1682                cd virtual_merge_base_has_nested_conflicts &&
1683
1684                git checkout L3^0 &&
1685
1686                # Merge must fail; there is a conflict
1687                test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R3^0 &&
1688
1689                # Make sure the index has the right number of entries
1690                git ls-files -s >out &&
1691                test_line_count = 3 out &&
1692                git ls-files -u >out &&
1693                test_line_count = 3 out &&
1694                # Ensure we have the correct number of untracked files
1695                git ls-files -o >out &&
1696                test_line_count = 1 out &&
1697
1698                # Compare :[23]:content to expected values
1699                git rev-parse L1:content R1:content >expect &&
1700                git rev-parse :2:content :3:content >actual &&
1701                test_cmp expect actual &&
1702
1703                # Imitate X1 merge base, except without long enough conflict
1704                # markers because a subsequent sed will modify them.  Put
1705                # result into vmb.
1706                git cat-file -p master:content >base &&
1707                git cat-file -p L:content >left &&
1708                git cat-file -p R:content >right &&
1709                cp left merged-once &&
1710                test_must_fail git merge-file --diff3 \
1711                        -L "Temporary merge branch 1" \
1712                        -L "merged common ancestors"  \
1713                        -L "Temporary merge branch 2" \
1714                        merged-once \
1715                        base        \
1716                        right       &&
1717                sed -e "s/^\([<|=>]\)/\1\1\1/" merged-once >vmb &&
1718
1719                # Imitate X2 merge base, overwriting vmb.  Note that we
1720                # extend both sets of conflict markers to make them longer
1721                # with the sed command.
1722                cp left merged-twice &&
1723                test_must_fail git merge-file --diff3 \
1724                        -L "Temporary merge branch 1" \
1725                        -L "merged common ancestors"  \
1726                        -L "Temporary merge branch 2" \
1727                        merged-twice \
1728                        vmb          \
1729                        right        &&
1730                sed -e "s/^\([<|=>]\)/\1\1\1/" merged-twice >vmb &&
1731
1732                # Compare :1:content to expected value
1733                git cat-file -p :1:content >actual &&
1734                test_cmp vmb actual &&
1735
1736                # Determine expected content in final outer merge, compare to
1737                # what the merge generated.
1738                cp -f left expect &&
1739                test_must_fail git merge-file --diff3                      \
1740                        -L "HEAD"  -L "merged common ancestors"  -L "R3^0" \
1741                        expect     vmb                           right     &&
1742                test_cmp expect content
1743        )
1744'
1745
1746test_done