t / t6036-recursive-corner-cases.shon commit Merge branch 'master' of https://github.com/Softcatala/git-po (87af1bb)
   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 = 3 out &&
  68
  69                git rev-parse >expect       \
  70                        L2:three   R2:three \
  71                        L2:three   R2:three &&
  72                git rev-parse   >actual     \
  73                        :2:three   :3:three &&
  74                git hash-object >>actual    \
  75                        three~HEAD three~R2^0 &&
  76                test_cmp expect actual
  77        )
  78'
  79
  80#
  81# Same as before, but modify L1 slightly:
  82#
  83#  L1m L2
  84#   o---o
  85#  / \ / \
  86# o   X   ?
  87#  \ / \ /
  88#   o---o
  89#  R1  R2
  90#
  91
  92test_expect_success 'setup criss-cross + rename merges with basic modification' '
  93        test_create_repo rename-modify &&
  94        (
  95                cd rename-modify &&
  96
  97                ten="0 1 2 3 4 5 6 7 8 9" &&
  98                for i in $ten
  99                do
 100                        echo line $i in a sample file
 101                done >one &&
 102                for i in $ten
 103                do
 104                        echo line $i in another sample file
 105                done >two &&
 106                git add one two &&
 107                test_tick && git commit -m initial &&
 108
 109                git branch L1 &&
 110                git checkout -b R1 &&
 111                git mv one three &&
 112                echo more >>two &&
 113                git add two &&
 114                test_tick && git commit -m R1 &&
 115
 116                git checkout L1 &&
 117                git mv two three &&
 118                test_tick && git commit -m L1 &&
 119
 120                git checkout L1^0 &&
 121                test_tick && git merge -s ours R1 &&
 122                git tag L2 &&
 123
 124                git checkout R1^0 &&
 125                test_tick && git merge -s ours L1 &&
 126                git tag R2
 127        )
 128'
 129
 130test_expect_success 'merge criss-cross + rename merges with basic modification' '
 131        (
 132                cd rename-modify &&
 133
 134                git checkout L2^0 &&
 135
 136                test_must_fail git merge -s recursive R2^0 &&
 137
 138                git ls-files -s >out &&
 139                test_line_count = 2 out &&
 140                git ls-files -u >out &&
 141                test_line_count = 2 out &&
 142                git ls-files -o >out &&
 143                test_line_count = 3 out &&
 144
 145                git rev-parse >expect       \
 146                        L2:three   R2:three \
 147                        L2:three   R2:three &&
 148                git rev-parse   >actual     \
 149                        :2:three   :3:three &&
 150                git hash-object >>actual    \
 151                        three~HEAD three~R2^0 &&
 152                test_cmp expect actual
 153        )
 154'
 155
 156#
 157# For the next test, we start with three commits in two lines of development
 158# which setup a rename/add conflict:
 159#   Commit A: File 'a' exists
 160#   Commit B: Rename 'a' -> 'new_a'
 161#   Commit C: Modify 'a', create different 'new_a'
 162# Later, two different people merge and resolve differently:
 163#   Commit D: Merge B & C, ignoring separately created 'new_a'
 164#   Commit E: Merge B & C making use of some piece of secondary 'new_a'
 165# Finally, someone goes to merge D & E.  Does git detect the conflict?
 166#
 167#      B   D
 168#      o---o
 169#     / \ / \
 170#  A o   X   ? F
 171#     \ / \ /
 172#      o---o
 173#      C   E
 174#
 175
 176test_expect_success 'setup differently handled merges of rename/add conflict' '
 177        test_create_repo rename-add &&
 178        (
 179                cd rename-add &&
 180
 181                printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
 182                git add a &&
 183                test_tick && git commit -m A &&
 184
 185                git branch B &&
 186                git checkout -b C &&
 187                echo 10 >>a &&
 188                echo "other content" >>new_a &&
 189                git add a new_a &&
 190                test_tick && git commit -m C &&
 191
 192                git checkout B &&
 193                git mv a new_a &&
 194                test_tick && git commit -m B &&
 195
 196                git checkout B^0 &&
 197                test_must_fail git merge C &&
 198                git clean -f &&
 199                test_tick && git commit -m D &&
 200                git tag D &&
 201
 202                git checkout C^0 &&
 203                test_must_fail git merge B &&
 204                rm new_a~HEAD new_a &&
 205                printf "Incorrectly merged content" >>new_a &&
 206                git add -u &&
 207                test_tick && git commit -m E &&
 208                git tag E
 209        )
 210'
 211
 212test_expect_success 'git detects differently handled merges conflict' '
 213        (
 214                cd rename-add &&
 215
 216                git checkout D^0 &&
 217
 218                test_must_fail git merge -s recursive E^0 &&
 219
 220                git ls-files -s >out &&
 221                test_line_count = 3 out &&
 222                git ls-files -u >out &&
 223                test_line_count = 3 out &&
 224                git ls-files -o >out &&
 225                test_line_count = 1 out &&
 226
 227                git rev-parse >expect       \
 228                        D:new_a  E:new_a &&
 229                git rev-parse   >actual     \
 230                        :2:new_a :3:new_a &&
 231                test_cmp expect actual &&
 232
 233                git cat-file -p B:new_a >ours &&
 234                git cat-file -p C:new_a >theirs &&
 235                >empty &&
 236                test_must_fail git merge-file \
 237                        -L "Temporary merge branch 2" \
 238                        -L "" \
 239                        -L "Temporary merge branch 1" \
 240                        ours empty theirs &&
 241                sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
 242                git cat-file -p :1:new_a >actual &&
 243                test_cmp expect actual
 244        )
 245'
 246
 247#
 248# criss-cross + modify/delete:
 249#
 250#      B   D
 251#      o---o
 252#     / \ / \
 253#  A o   X   ? F
 254#     \ / \ /
 255#      o---o
 256#      C   E
 257#
 258#   Commit A: file with contents 'A\n'
 259#   Commit B: file with contents 'B\n'
 260#   Commit C: file not present
 261#   Commit D: file with contents 'B\n'
 262#   Commit E: file not present
 263#
 264# Merging commits D & E should result in modify/delete conflict.
 265
 266test_expect_success 'setup criss-cross + modify/delete resolved differently' '
 267        test_create_repo modify-delete &&
 268        (
 269                cd modify-delete &&
 270
 271                echo A >file &&
 272                git add file &&
 273                test_tick &&
 274                git commit -m A &&
 275
 276                git branch B &&
 277                git checkout -b C &&
 278                git rm file &&
 279                test_tick &&
 280                git commit -m C &&
 281
 282                git checkout B &&
 283                echo B >file &&
 284                git add file &&
 285                test_tick &&
 286                git commit -m B &&
 287
 288                git checkout B^0 &&
 289                test_must_fail git merge C &&
 290                echo B >file &&
 291                git add file &&
 292                test_tick &&
 293                git commit -m D &&
 294                git tag D &&
 295
 296                git checkout C^0 &&
 297                test_must_fail git merge B &&
 298                git rm file &&
 299                test_tick &&
 300                git commit -m E &&
 301                git tag E
 302        )
 303'
 304
 305test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
 306        (
 307                cd modify-delete &&
 308
 309                git checkout D^0 &&
 310
 311                test_must_fail git merge -s recursive E^0 &&
 312
 313                git ls-files -s >out &&
 314                test_line_count = 2 out &&
 315                git ls-files -u >out &&
 316                test_line_count = 2 out &&
 317
 318                git rev-parse >expect       \
 319                        master:file  B:file &&
 320                git rev-parse   >actual      \
 321                        :1:file      :2:file &&
 322                test_cmp expect actual
 323        )
 324'
 325
 326test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
 327        (
 328                cd modify-delete &&
 329
 330                git reset --hard &&
 331                git checkout E^0 &&
 332
 333                test_must_fail git merge -s recursive D^0 &&
 334
 335                git ls-files -s >out &&
 336                test_line_count = 2 out &&
 337                git ls-files -u >out &&
 338                test_line_count = 2 out &&
 339
 340                git rev-parse >expect       \
 341                        master:file  B:file &&
 342                git rev-parse   >actual      \
 343                        :1:file      :3:file &&
 344                test_cmp expect actual
 345        )
 346'
 347
 348#      SORRY FOR THE SUPER LONG DESCRIPTION, BUT THIS NEXT ONE IS HAIRY
 349#
 350# criss-cross + d/f conflict via add/add:
 351#   Commit A: Neither file 'a' nor directory 'a/' exists.
 352#   Commit B: Introduce 'a'
 353#   Commit C: Introduce 'a/file'
 354#   Commit D1: Merge B & C, keeping 'a'    and deleting 'a/'
 355#   Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
 356#
 357#      B   D1 or D2
 358#      o---o
 359#     / \ / \
 360#  A o   X   ? F
 361#     \ / \ /
 362#      o---o
 363#      C   E1 or E2 or E3
 364#
 365# I'll describe D2, E2, & E3 (which are alternatives for D1 & E1) more below...
 366#
 367# Merging D1 & E1 requires we first create a virtual merge base X from
 368# merging A & B in memory.  There are several possibilities for the merge-base:
 369#   1: Keep both 'a' and 'a/file' (assuming crazy filesystem allowing a tree
 370#      with a directory and file at same path): results in merge of D1 & E1
 371#      being clean with both files deleted.  Bad (no conflict detected).
 372#   2: Keep 'a' but not 'a/file': Merging D1 & E1 is clean and matches E1.  Bad.
 373#   3: Keep 'a/file' but not 'a': Merging D1 & E1 is clean and matches D1.  Bad.
 374#   4: Keep neither file: Merging D1 & E1 reports the D/F add/add conflict.
 375#
 376# So 4 sounds good for this case, but if we were to merge D1 & E3, where E3
 377# is defined as:
 378#   Commit E3: Merge B & C, keeping modified a, and deleting a/
 379# then we'd get an add/add conflict for 'a', which seems suboptimal.  A little
 380# creativity leads us to an alternate choice:
 381#   5: Keep 'a' as 'a~$UNIQUE' and a/file; results:
 382#        Merge D1 & E1: rename/delete conflict for 'a'; a/file silently deleted
 383#        Merge D1 & E3 is clean, as expected.
 384#
 385# So choice 5 at least provides some kind of conflict for the original case,
 386# and can merge cleanly as expected with D1 and E3.  It also made things just
 387# slightly funny for merging D1 and e$, where E4 is defined as:
 388#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
 389# in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE
 390# gets renamed to 'a' in D1 and to 'a2' in E4.  But that's better than having
 391# two files (both 'a' and 'a2') sitting around without the user being notified
 392# that we could detect they were related and need to be merged.  Also, choice
 393# 5 makes the handling of 'a/file' seem suboptimal.  What if we were to merge
 394# D2 and E4, where D2 is:
 395#   Commit D2: Merge B & C, renaming 'a'->'a2', keeping 'a/file'
 396# This would result in a clean merge with 'a2' having three-way merged
 397# contents (good), and deleting 'a/' (bad) -- it doesn't detect the
 398# conflict in how the different sides treated a/file differently.
 399# Continuing down the creative route:
 400#   6: Keep 'a' as 'a~$UNIQUE1' and keep 'a/' as 'a~$UNIQUE2/'; results:
 401#        Merge D1 & E1: rename/delete conflict for 'a' and each path under 'a/'.
 402#        Merge D1 & E3: clean, as expected.
 403#        Merge D1 & E4: rename/rename(1to2) conflict on 'a' vs 'a2'.
 404#        Merge D2 & E4: clean for 'a2', rename/delete for a/file
 405#
 406# Choice 6 could cause rename detection to take longer (providing more targets
 407# that need to be searched).  Also, the conflict message for each path under
 408# 'a/' might be annoying unless we can detect it at the directory level, print
 409# it once, and then suppress it for individual filepaths underneath.
 410#
 411#
 412# As of time of writing, git uses choice 5.  Directory rename detection and
 413# rename detection performance improvements might make choice 6 a desirable
 414# improvement.  But we can at least document where we fall short for now...
 415#
 416#
 417# Historically, this testcase also used:
 418#   Commit E2: Merge B & C, deleting 'a' but keeping slightly modified 'a/file'
 419# The merge of D1 & E2 is very similar to D1 & E1 -- it has similar issues for
 420# path 'a', but should always result in a modify/delete conflict for path
 421# 'a/file'.  These tests ran the two merges
 422#   D1 & E1
 423#   D1 & E2
 424# in both directions, to check for directional issues with D/F conflict
 425# handling. Later we added
 426#   D1 & E3
 427#   D1 & E4
 428#   D2 & E4
 429# for good measure, though we only ran those one way because we had pretty
 430# good confidence in merge-recursive's directional handling of D/F issues.
 431#
 432# Just to summarize all the intermediate merge commits:
 433#   Commit D1: Merge B & C, keeping a    and deleting a/
 434#   Commit D2: Merge B & C, renaming a->a2, keeping a/file
 435#   Commit E1: Merge B & C, deleting a but keeping a/file
 436#   Commit E2: Merge B & C, deleting a but keeping slightly modified a/file
 437#   Commit E3: Merge B & C, keeping modified a, and deleting a/
 438#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
 439#
 440
 441test_expect_success 'setup differently handled merges of directory/file conflict' '
 442        test_create_repo directory-file &&
 443        (
 444                cd directory-file &&
 445
 446                >ignore-me &&
 447                git add ignore-me &&
 448                test_tick &&
 449                git commit -m A &&
 450                git tag A &&
 451
 452                git branch B &&
 453                git checkout -b C &&
 454                mkdir a &&
 455                test_write_lines a b c d e f g >a/file &&
 456                git add a/file &&
 457                test_tick &&
 458                git commit -m C &&
 459
 460                git checkout B &&
 461                test_write_lines 1 2 3 4 5 6 7 >a &&
 462                git add a &&
 463                test_tick &&
 464                git commit -m B &&
 465
 466                git checkout B^0 &&
 467                git merge -s ours -m D1 C^0 &&
 468                git tag D1 &&
 469
 470                git checkout B^0 &&
 471                test_must_fail git merge C^0 &&
 472                git clean -fd &&
 473                git rm -rf a/ &&
 474                git rm a &&
 475                git cat-file -p B:a >a2 &&
 476                git add a2 &&
 477                git commit -m D2 &&
 478                git tag D2 &&
 479
 480                git checkout C^0 &&
 481                git merge -s ours -m E1 B^0 &&
 482                git tag E1 &&
 483
 484                git checkout C^0 &&
 485                git merge -s ours -m E2 B^0 &&
 486                test_write_lines a b c d e f g h >a/file &&
 487                git add a/file &&
 488                git commit --amend -C HEAD &&
 489                git tag E2 &&
 490
 491                git checkout C^0 &&
 492                test_must_fail git merge B^0 &&
 493                git clean -fd &&
 494                git rm -rf a/ &&
 495                test_write_lines 1 2 3 4 5 6 7 8 >a &&
 496                git add a &&
 497                git commit -m E3 &&
 498                git tag E3 &&
 499
 500                git checkout C^0 &&
 501                test_must_fail git merge B^0 &&
 502                git clean -fd &&
 503                git rm -rf a/ &&
 504                git rm a &&
 505                test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
 506                git add a2 &&
 507                git commit -m E4 &&
 508                git tag E4
 509        )
 510'
 511
 512test_expect_success 'merge of D1 & E1 fails but has appropriate contents' '
 513        test_when_finished "git -C directory-file reset --hard" &&
 514        test_when_finished "git -C directory-file clean -fdqx" &&
 515        (
 516                cd directory-file &&
 517
 518                git checkout D1^0 &&
 519
 520                test_must_fail git merge -s recursive E1^0 &&
 521
 522                git ls-files -s >out &&
 523                test_line_count = 2 out &&
 524                git ls-files -u >out &&
 525                test_line_count = 1 out &&
 526                git ls-files -o >out &&
 527                test_line_count = 1 out &&
 528
 529                git rev-parse >expect    \
 530                        A:ignore-me  B:a &&
 531                git rev-parse   >actual   \
 532                        :0:ignore-me :2:a &&
 533                test_cmp expect actual
 534        )
 535'
 536
 537test_expect_success 'merge of E1 & D1 fails but has appropriate contents' '
 538        test_when_finished "git -C directory-file reset --hard" &&
 539        test_when_finished "git -C directory-file clean -fdqx" &&
 540        (
 541                cd directory-file &&
 542
 543                git checkout E1^0 &&
 544
 545                test_must_fail git merge -s recursive D1^0 &&
 546
 547                git ls-files -s >out &&
 548                test_line_count = 2 out &&
 549                git ls-files -u >out &&
 550                test_line_count = 1 out &&
 551                git ls-files -o >out &&
 552                test_line_count = 1 out &&
 553
 554                git rev-parse >expect    \
 555                        A:ignore-me  B:a &&
 556                git rev-parse   >actual   \
 557                        :0:ignore-me :3:a &&
 558                test_cmp expect actual
 559        )
 560'
 561
 562test_expect_success 'merge of D1 & E2 fails but has appropriate contents' '
 563        test_when_finished "git -C directory-file reset --hard" &&
 564        test_when_finished "git -C directory-file clean -fdqx" &&
 565        (
 566                cd directory-file &&
 567
 568                git checkout D1^0 &&
 569
 570                test_must_fail git merge -s recursive E2^0 &&
 571
 572                git ls-files -s >out &&
 573                test_line_count = 4 out &&
 574                git ls-files -u >out &&
 575                test_line_count = 3 out &&
 576                git ls-files -o >out &&
 577                test_line_count = 2 out &&
 578
 579                git rev-parse >expect    \
 580                        B:a   E2:a/file  C:a/file   A:ignore-me &&
 581                git rev-parse   >actual   \
 582                        :2:a  :3:a/file  :1:a/file  :0:ignore-me &&
 583                test_cmp expect actual &&
 584
 585                test_path_is_file a~HEAD
 586        )
 587'
 588
 589test_expect_success 'merge of E2 & D1 fails but has appropriate contents' '
 590        test_when_finished "git -C directory-file reset --hard" &&
 591        test_when_finished "git -C directory-file clean -fdqx" &&
 592        (
 593                cd directory-file &&
 594
 595                git checkout E2^0 &&
 596
 597                test_must_fail git merge -s recursive D1^0 &&
 598
 599                git ls-files -s >out &&
 600                test_line_count = 4 out &&
 601                git ls-files -u >out &&
 602                test_line_count = 3 out &&
 603                git ls-files -o >out &&
 604                test_line_count = 2 out &&
 605
 606                git rev-parse >expect    \
 607                        B:a   E2:a/file  C:a/file   A:ignore-me &&
 608                git rev-parse   >actual   \
 609                        :3:a  :2:a/file  :1:a/file  :0:ignore-me &&
 610                test_cmp expect actual &&
 611
 612                test_path_is_file a~D1^0
 613        )
 614'
 615
 616test_expect_success 'merge of D1 & E3 succeeds' '
 617        test_when_finished "git -C directory-file reset --hard" &&
 618        test_when_finished "git -C directory-file clean -fdqx" &&
 619        (
 620                cd directory-file &&
 621
 622                git checkout D1^0 &&
 623
 624                git merge -s recursive E3^0 &&
 625
 626                git ls-files -s >out &&
 627                test_line_count = 2 out &&
 628                git ls-files -u >out &&
 629                test_line_count = 0 out &&
 630                git ls-files -o >out &&
 631                test_line_count = 1 out &&
 632
 633                git rev-parse >expect    \
 634                        A:ignore-me  E3:a &&
 635                git rev-parse   >actual   \
 636                        :0:ignore-me :0:a &&
 637                test_cmp expect actual
 638        )
 639'
 640
 641test_expect_success 'merge of D1 & E4 notifies user a and a2 are related' '
 642        test_when_finished "git -C directory-file reset --hard" &&
 643        test_when_finished "git -C directory-file clean -fdqx" &&
 644        (
 645                cd directory-file &&
 646
 647                git checkout D1^0 &&
 648
 649                test_must_fail git merge -s recursive E4^0 &&
 650
 651                git ls-files -s >out &&
 652                test_line_count = 4 out &&
 653                git ls-files -u >out &&
 654                test_line_count = 3 out &&
 655                git ls-files -o >out &&
 656                test_line_count = 1 out &&
 657
 658                git rev-parse >expect                  \
 659                        A:ignore-me  B:a   D1:a  E4:a2 &&
 660                git rev-parse   >actual                \
 661                        :0:ignore-me :1:a~Temporary\ merge\ branch\ 2  :2:a  :3:a2 &&
 662                test_cmp expect actual
 663        )
 664'
 665
 666test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file' '
 667        test_when_finished "git -C directory-file reset --hard" &&
 668        test_when_finished "git -C directory-file clean -fdqx" &&
 669        (
 670                cd directory-file &&
 671
 672                git checkout D2^0 &&
 673
 674                test_must_fail git merge -s recursive E4^0 &&
 675
 676                git ls-files -s >out &&
 677                test_line_count = 3 out &&
 678                git ls-files -u >out &&
 679                test_line_count = 1 out &&
 680                git ls-files -o >out &&
 681                test_line_count = 1 out &&
 682
 683                git rev-parse >expect                 \
 684                        A:ignore-me  E4:a2  D2:a/file &&
 685                git rev-parse   >actual               \
 686                        :0:ignore-me :0:a2  :2:a/file &&
 687                test_cmp expect actual
 688        )
 689'
 690
 691#
 692# criss-cross with rename/rename(1to2)/modify followed by
 693# rename/rename(2to1)/modify:
 694#
 695#      B   D
 696#      o---o
 697#     / \ / \
 698#  A o   X   ? F
 699#     \ / \ /
 700#      o---o
 701#      C   E
 702#
 703#   Commit A: new file: a
 704#   Commit B: rename a->b, modifying by adding a line
 705#   Commit C: rename a->c
 706#   Commit D: merge B&C, resolving conflict by keeping contents in newname
 707#   Commit E: merge B&C, resolving conflict similar to D but adding another line
 708#
 709# There is a conflict merging B & C, but one of filename not of file
 710# content.  Whoever created D and E chose specific resolutions for that
 711# conflict resolution.  Now, since: (1) there is no content conflict
 712# merging B & C, (2) D does not modify that merged content further, and (3)
 713# both D & E resolve the name conflict in the same way, the modification to
 714# newname in E should not cause any conflicts when it is merged with D.
 715# (Note that this can be accomplished by having the virtual merge base have
 716# the merged contents of b and c stored in a file named a, which seems like
 717# the most logical choice anyway.)
 718#
 719# Comment from Junio: I do not necessarily agree with the choice "a", but
 720# it feels sound to say "B and C do not agree what the final pathname
 721# should be, but we know this content was derived from the common A:a so we
 722# use one path whose name is arbitrary in the virtual merge base X between
 723# D and E" and then further let the rename detection to notice that that
 724# arbitrary path gets renamed between X-D to "newname" and X-E also to
 725# "newname" to resolve it as both sides renaming it to the same new
 726# name. It is akin to what we do at the content level, i.e. "B and C do not
 727# agree what the final contents should be, so we leave the conflict marker
 728# but that may cancel out at the final merge stage".
 729
 730test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
 731        test_create_repo rename-squared-squared &&
 732        (
 733                cd rename-squared-squared &&
 734
 735                printf "1\n2\n3\n4\n5\n6\n" >a &&
 736                git add a &&
 737                git commit -m A &&
 738                git tag A &&
 739
 740                git checkout -b B A &&
 741                git mv a b &&
 742                echo 7 >>b &&
 743                git add -u &&
 744                git commit -m B &&
 745
 746                git checkout -b C A &&
 747                git mv a c &&
 748                git commit -m C &&
 749
 750                git checkout -q B^0 &&
 751                git merge --no-commit -s ours C^0 &&
 752                git mv b newname &&
 753                git commit -m "Merge commit C^0 into HEAD" &&
 754                git tag D &&
 755
 756                git checkout -q C^0 &&
 757                git merge --no-commit -s ours B^0 &&
 758                git mv c newname &&
 759                printf "7\n8\n" >>newname &&
 760                git add -u &&
 761                git commit -m "Merge commit B^0 into HEAD" &&
 762                git tag E
 763        )
 764'
 765
 766test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
 767        (
 768                cd rename-squared-squared &&
 769
 770                git checkout D^0 &&
 771
 772                git merge -s recursive E^0 &&
 773
 774                git ls-files -s >out &&
 775                test_line_count = 1 out &&
 776                git ls-files -u >out &&
 777                test_line_count = 0 out &&
 778                git ls-files -o >out &&
 779                test_line_count = 1 out &&
 780
 781                test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
 782        )
 783'
 784
 785#
 786# criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify:
 787#
 788#      B   D
 789#      o---o
 790#     / \ / \
 791#  A o   X   ? F
 792#     \ / \ /
 793#      o---o
 794#      C   E
 795#
 796#   Commit A: new file: a
 797#   Commit B: rename a->b
 798#   Commit C: rename a->c, add different a
 799#   Commit D: merge B&C, keeping b&c and (new) a modified at beginning
 800#   Commit E: merge B&C, keeping b&c and (new) a modified at end
 801#
 802# Merging commits D & E should result in no conflict; doing so correctly
 803# requires getting the virtual merge base (from merging B&C) right, handling
 804# renaming carefully (both in the virtual merge base and later), and getting
 805# content merge handled.
 806
 807test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
 808        test_create_repo rename-rename-add-source &&
 809        (
 810                cd rename-rename-add-source &&
 811
 812                printf "lots\nof\nwords\nand\ncontent\n" >a &&
 813                git add a &&
 814                git commit -m A &&
 815                git tag A &&
 816
 817                git checkout -b B A &&
 818                git mv a b &&
 819                git commit -m B &&
 820
 821                git checkout -b C A &&
 822                git mv a c &&
 823                printf "2\n3\n4\n5\n6\n7\n" >a &&
 824                git add a &&
 825                git commit -m C &&
 826
 827                git checkout B^0 &&
 828                git merge --no-commit -s ours C^0 &&
 829                git checkout C -- a c &&
 830                mv a old_a &&
 831                echo 1 >a &&
 832                cat old_a >>a &&
 833                rm old_a &&
 834                git add -u &&
 835                git commit -m "Merge commit C^0 into HEAD" &&
 836                git tag D &&
 837
 838                git checkout C^0 &&
 839                git merge --no-commit -s ours B^0 &&
 840                git checkout B -- b &&
 841                echo 8 >>a &&
 842                git add -u &&
 843                git commit -m "Merge commit B^0 into HEAD" &&
 844                git tag E
 845        )
 846'
 847
 848test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
 849        (
 850                cd rename-rename-add-source &&
 851
 852                git checkout D^0 &&
 853
 854                git merge -s recursive E^0 &&
 855
 856                git ls-files -s >out &&
 857                test_line_count = 3 out &&
 858                git ls-files -u >out &&
 859                test_line_count = 0 out &&
 860                git ls-files -o >out &&
 861                test_line_count = 1 out &&
 862
 863                printf "1\n2\n3\n4\n5\n6\n7\n8\n" >correct &&
 864                git rev-parse >expect \
 865                        A:a   A:a     \
 866                        correct       &&
 867                git rev-parse   >actual  \
 868                        :0:b  :0:c       &&
 869                git hash-object >>actual \
 870                        a                &&
 871                test_cmp expect actual
 872        )
 873'
 874
 875#
 876# criss-cross with rename/rename(1to2)/add-dest + simple modify:
 877#
 878#      B   D
 879#      o---o
 880#     / \ / \
 881#  A o   X   ? F
 882#     \ / \ /
 883#      o---o
 884#      C   E
 885#
 886#   Commit A: new file: a
 887#   Commit B: rename a->b, add c
 888#   Commit C: rename a->c
 889#   Commit D: merge B&C, keeping A:a and B:c
 890#   Commit E: merge B&C, keeping A:a and slightly modified c from B
 891#
 892# Merging commits D & E should result in no conflict.  The virtual merge
 893# base of B & C needs to not delete B:c for that to work, though...
 894
 895test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
 896        test_create_repo rename-rename-add-dest &&
 897        (
 898                cd rename-rename-add-dest &&
 899
 900                >a &&
 901                git add a &&
 902                git commit -m A &&
 903                git tag A &&
 904
 905                git checkout -b B A &&
 906                git mv a b &&
 907                printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
 908                git add c &&
 909                git commit -m B &&
 910
 911                git checkout -b C A &&
 912                git mv a c &&
 913                git commit -m C &&
 914
 915                git checkout B^0 &&
 916                git merge --no-commit -s ours C^0 &&
 917                git mv b a &&
 918                git commit -m "D is like B but renames b back to a" &&
 919                git tag D &&
 920
 921                git checkout B^0 &&
 922                git merge --no-commit -s ours C^0 &&
 923                git mv b a &&
 924                echo 8 >>c &&
 925                git add c &&
 926                git commit -m "E like D but has mod in c" &&
 927                git tag E
 928        )
 929'
 930
 931test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
 932        (
 933                cd rename-rename-add-dest &&
 934
 935                git checkout D^0 &&
 936
 937                git merge -s recursive E^0 &&
 938
 939                git ls-files -s >out &&
 940                test_line_count = 2 out &&
 941                git ls-files -u >out &&
 942                test_line_count = 0 out &&
 943                git ls-files -o >out &&
 944                test_line_count = 1 out &&
 945
 946                git rev-parse >expect \
 947                        A:a   E:c     &&
 948                git rev-parse   >actual \
 949                        :0:a  :0:c      &&
 950                test_cmp expect actual
 951        )
 952'
 953
 954#
 955# criss-cross with modify/modify on a symlink:
 956#
 957#      B   D
 958#      o---o
 959#     / \ / \
 960#  A o   X   ? F
 961#     \ / \ /
 962#      o---o
 963#      C   E
 964#
 965#   Commit A: simple simlink fickle->lagoon
 966#   Commit B: redirect fickle->disneyland
 967#   Commit C: redirect fickle->home
 968#   Commit D: merge B&C, resolving in favor of B
 969#   Commit E: merge B&C, resolving in favor of C
 970#
 971# This is an obvious modify/modify conflict for the symlink 'fickle'.  Can
 972# git detect it?
 973
 974test_expect_success 'setup symlink modify/modify' '
 975        test_create_repo symlink-modify-modify &&
 976        (
 977                cd symlink-modify-modify &&
 978
 979                test_ln_s_add lagoon fickle &&
 980                git commit -m A &&
 981                git tag A &&
 982
 983                git checkout -b B A &&
 984                git rm fickle &&
 985                test_ln_s_add disneyland fickle &&
 986                git commit -m B &&
 987
 988                git checkout -b C A &&
 989                git rm fickle &&
 990                test_ln_s_add home fickle &&
 991                git add fickle &&
 992                git commit -m C &&
 993
 994                git checkout -q B^0 &&
 995                git merge -s ours -m D C^0 &&
 996                git tag D &&
 997
 998                git checkout -q C^0 &&
 999                git merge -s ours -m E B^0 &&
1000                git tag E
1001        )
1002'
1003
1004test_expect_failure 'check symlink modify/modify' '
1005        (
1006                cd symlink-modify-modify &&
1007
1008                git checkout D^0 &&
1009
1010                test_must_fail git merge -s recursive E^0 &&
1011
1012                git ls-files -s >out &&
1013                test_line_count = 3 out &&
1014                git ls-files -u >out &&
1015                test_line_count = 3 out &&
1016                git ls-files -o >out &&
1017                test_line_count = 1 out
1018        )
1019'
1020
1021#
1022# criss-cross with add/add of a symlink:
1023#
1024#      B   D
1025#      o---o
1026#     / \ / \
1027#  A o   X   ? F
1028#     \ / \ /
1029#      o---o
1030#      C   E
1031#
1032#   Commit A: No symlink or path exists yet
1033#   Commit B: set up symlink: fickle->disneyland
1034#   Commit C: set up symlink: fickle->home
1035#   Commit D: merge B&C, resolving in favor of B
1036#   Commit E: merge B&C, resolving in favor of C
1037#
1038# This is an obvious add/add conflict for the symlink 'fickle'.  Can
1039# git detect it?
1040
1041test_expect_success 'setup symlink add/add' '
1042        test_create_repo symlink-add-add &&
1043        (
1044                cd symlink-add-add &&
1045
1046                touch ignoreme &&
1047                git add ignoreme &&
1048                git commit -m A &&
1049                git tag A &&
1050
1051                git checkout -b B A &&
1052                test_ln_s_add disneyland fickle &&
1053                git commit -m B &&
1054
1055                git checkout -b C A &&
1056                test_ln_s_add home fickle &&
1057                git add fickle &&
1058                git commit -m C &&
1059
1060                git checkout -q B^0 &&
1061                git merge -s ours -m D C^0 &&
1062                git tag D &&
1063
1064                git checkout -q C^0 &&
1065                git merge -s ours -m E B^0 &&
1066                git tag E
1067        )
1068'
1069
1070test_expect_failure 'check symlink add/add' '
1071        (
1072                cd symlink-add-add &&
1073
1074                git checkout D^0 &&
1075
1076                test_must_fail git merge -s recursive E^0 &&
1077
1078                git ls-files -s >out &&
1079                test_line_count = 2 out &&
1080                git ls-files -u >out &&
1081                test_line_count = 2 out &&
1082                git ls-files -o >out &&
1083                test_line_count = 1 out
1084        )
1085'
1086
1087#
1088# criss-cross with modify/modify on a submodule:
1089#
1090#      B   D
1091#      o---o
1092#     / \ / \
1093#  A o   X   ? F
1094#     \ / \ /
1095#      o---o
1096#      C   E
1097#
1098#   Commit A: simple submodule repo
1099#   Commit B: update repo
1100#   Commit C: update repo differently
1101#   Commit D: merge B&C, resolving in favor of B
1102#   Commit E: merge B&C, resolving in favor of C
1103#
1104# This is an obvious modify/modify conflict for the submodule 'repo'.  Can
1105# git detect it?
1106
1107test_expect_success 'setup submodule modify/modify' '
1108        test_create_repo submodule-modify-modify &&
1109        (
1110                cd submodule-modify-modify &&
1111
1112                test_create_repo submod &&
1113                (
1114                        cd submod &&
1115                        touch file-A &&
1116                        git add file-A &&
1117                        git commit -m A &&
1118                        git tag A &&
1119
1120                        git checkout -b B A &&
1121                        touch file-B &&
1122                        git add file-B &&
1123                        git commit -m B &&
1124                        git tag B &&
1125
1126                        git checkout -b C A &&
1127                        touch file-C &&
1128                        git add file-C &&
1129                        git commit -m C &&
1130                        git tag C
1131                ) &&
1132
1133                git -C submod reset --hard A &&
1134                git add submod &&
1135                git commit -m A &&
1136                git tag A &&
1137
1138                git checkout -b B A &&
1139                git -C submod reset --hard B &&
1140                git add submod &&
1141                git commit -m B &&
1142
1143                git checkout -b C A &&
1144                git -C submod reset --hard C &&
1145                git add submod &&
1146                git commit -m C &&
1147
1148                git checkout -q B^0 &&
1149                git merge -s ours -m D C^0 &&
1150                git tag D &&
1151
1152                git checkout -q C^0 &&
1153                git merge -s ours -m E B^0 &&
1154                git tag E
1155        )
1156'
1157
1158test_expect_failure 'check submodule modify/modify' '
1159        (
1160                cd submodule-modify-modify &&
1161
1162                git checkout D^0 &&
1163
1164                test_must_fail git merge -s recursive E^0 &&
1165
1166                git ls-files -s >out &&
1167                test_line_count = 3 out &&
1168                git ls-files -u >out &&
1169                test_line_count = 3 out &&
1170                git ls-files -o >out &&
1171                test_line_count = 1 out
1172        )
1173'
1174
1175#
1176# criss-cross with add/add on a submodule:
1177#
1178#      B   D
1179#      o---o
1180#     / \ / \
1181#  A o   X   ? F
1182#     \ / \ /
1183#      o---o
1184#      C   E
1185#
1186#   Commit A: nothing of note
1187#   Commit B: introduce submodule repo
1188#   Commit C: introduce submodule repo at different commit
1189#   Commit D: merge B&C, resolving in favor of B
1190#   Commit E: merge B&C, resolving in favor of C
1191#
1192# This is an obvious add/add conflict for the submodule 'repo'.  Can
1193# git detect it?
1194
1195test_expect_success 'setup submodule add/add' '
1196        test_create_repo submodule-add-add &&
1197        (
1198                cd submodule-add-add &&
1199
1200                test_create_repo submod &&
1201                (
1202                        cd submod &&
1203                        touch file-A &&
1204                        git add file-A &&
1205                        git commit -m A &&
1206                        git tag A &&
1207
1208                        git checkout -b B A &&
1209                        touch file-B &&
1210                        git add file-B &&
1211                        git commit -m B &&
1212                        git tag B &&
1213
1214                        git checkout -b C A &&
1215                        touch file-C &&
1216                        git add file-C &&
1217                        git commit -m C &&
1218                        git tag C
1219                ) &&
1220
1221                touch irrelevant-file &&
1222                git add irrelevant-file &&
1223                git commit -m A &&
1224                git tag A &&
1225
1226                git checkout -b B A &&
1227                git -C submod reset --hard B &&
1228                git add submod &&
1229                git commit -m B &&
1230
1231                git checkout -b C A &&
1232                git -C submod reset --hard C &&
1233                git add submod &&
1234                git commit -m C &&
1235
1236                git checkout -q B^0 &&
1237                git merge -s ours -m D C^0 &&
1238                git tag D &&
1239
1240                git checkout -q C^0 &&
1241                git merge -s ours -m E B^0 &&
1242                git tag E
1243        )
1244'
1245
1246test_expect_failure 'check submodule add/add' '
1247        (
1248                cd submodule-add-add &&
1249
1250                git checkout D^0 &&
1251
1252                test_must_fail git merge -s recursive E^0 &&
1253
1254                git ls-files -s >out &&
1255                test_line_count = 3 out &&
1256                git ls-files -u >out &&
1257                test_line_count = 2 out &&
1258                git ls-files -o >out &&
1259                test_line_count = 1 out
1260        )
1261'
1262
1263#
1264# criss-cross with conflicting entry types:
1265#
1266#      B   D
1267#      o---o
1268#     / \ / \
1269#  A o   X   ? F
1270#     \ / \ /
1271#      o---o
1272#      C   E
1273#
1274#   Commit A: nothing of note
1275#   Commit B: introduce submodule 'path'
1276#   Commit C: introduce symlink 'path'
1277#   Commit D: merge B&C, resolving in favor of B
1278#   Commit E: merge B&C, resolving in favor of C
1279#
1280# This is an obvious add/add conflict for 'path'.  Can git detect it?
1281
1282test_expect_success 'setup conflicting entry types (submodule vs symlink)' '
1283        test_create_repo submodule-symlink-add-add &&
1284        (
1285                cd submodule-symlink-add-add &&
1286
1287                test_create_repo path &&
1288                (
1289                        cd path &&
1290                        touch file-B &&
1291                        git add file-B &&
1292                        git commit -m B &&
1293                        git tag B
1294                ) &&
1295
1296                touch irrelevant-file &&
1297                git add irrelevant-file &&
1298                git commit -m A &&
1299                git tag A &&
1300
1301                git checkout -b B A &&
1302                git -C path reset --hard B &&
1303                git add path &&
1304                git commit -m B &&
1305
1306                git checkout -b C A &&
1307                rm -rf path/ &&
1308                test_ln_s_add irrelevant-file path &&
1309                git commit -m C &&
1310
1311                git checkout -q B^0 &&
1312                git merge -s ours -m D C^0 &&
1313                git tag D &&
1314
1315                git checkout -q C^0 &&
1316                git merge -s ours -m E B^0 &&
1317                git tag E
1318        )
1319'
1320
1321test_expect_failure 'check conflicting entry types (submodule vs symlink)' '
1322        (
1323                cd submodule-symlink-add-add &&
1324
1325                git checkout D^0 &&
1326
1327                test_must_fail git merge -s recursive E^0 &&
1328
1329                git ls-files -s >out &&
1330                test_line_count = 3 out &&
1331                git ls-files -u >out &&
1332                test_line_count = 2 out &&
1333                git ls-files -o >out &&
1334                test_line_count = 1 out
1335        )
1336'
1337
1338#
1339# criss-cross with regular files that have conflicting modes:
1340#
1341#      B   D
1342#      o---o
1343#     / \ / \
1344#  A o   X   ? F
1345#     \ / \ /
1346#      o---o
1347#      C   E
1348#
1349#   Commit A: nothing of note
1350#   Commit B: introduce file source_me.bash, not executable
1351#   Commit C: introduce file source_me.bash, executable
1352#   Commit D: merge B&C, resolving in favor of B
1353#   Commit E: merge B&C, resolving in favor of C
1354#
1355# This is an obvious add/add mode conflict.  Can git detect it?
1356
1357test_expect_success 'setup conflicting modes for regular file' '
1358        test_create_repo regular-file-mode-conflict &&
1359        (
1360                cd regular-file-mode-conflict &&
1361
1362                touch irrelevant-file &&
1363                git add irrelevant-file &&
1364                git commit -m A &&
1365                git tag A &&
1366
1367                git checkout -b B A &&
1368                echo "command_to_run" >source_me.bash &&
1369                git add source_me.bash &&
1370                git commit -m B &&
1371
1372                git checkout -b C A &&
1373                echo "command_to_run" >source_me.bash &&
1374                git add source_me.bash &&
1375                test_chmod +x source_me.bash &&
1376                git commit -m C &&
1377
1378                git checkout -q B^0 &&
1379                git merge -s ours -m D C^0 &&
1380                git tag D &&
1381
1382                git checkout -q C^0 &&
1383                git merge -s ours -m E B^0 &&
1384                git tag E
1385        )
1386'
1387
1388test_expect_failure 'check conflicting modes for regular file' '
1389        (
1390                cd regular-file-mode-conflict &&
1391
1392                git checkout D^0 &&
1393
1394                test_must_fail git merge -s recursive E^0 &&
1395
1396                git ls-files -s >out &&
1397                test_line_count = 3 out &&
1398                git ls-files -u >out &&
1399                test_line_count = 2 out &&
1400                git ls-files -o >out &&
1401                test_line_count = 1 out
1402        )
1403'
1404
1405test_done