t / t3430-rebase-merges.shon commit Merge branch 'pw/doc-synopsis-markup-opmode-options' (0af6d5d)
   1#!/bin/sh
   2#
   3# Copyright (c) 2018 Johannes E. Schindelin
   4#
   5
   6test_description='git rebase -i --rebase-merges
   7
   8This test runs git rebase "interactively", retaining the branch structure by
   9recreating merge commits.
  10
  11Initial setup:
  12
  13    -- B --                   (first)
  14   /       \
  15 A - C - D - E - H            (master)
  16   \    \       /
  17    \    F - G                (second)
  18     \
  19      Conflicting-G
  20'
  21. ./test-lib.sh
  22. "$TEST_DIRECTORY"/lib-rebase.sh
  23
  24test_cmp_graph () {
  25        cat >expect &&
  26        git log --graph --boundary --format=%s "$@" >output &&
  27        sed "s/ *$//" <output >output.trimmed &&
  28        test_cmp expect output.trimmed
  29}
  30
  31test_expect_success 'setup' '
  32        write_script replace-editor.sh <<-\EOF &&
  33        mv "$1" "$(git rev-parse --git-path ORIGINAL-TODO)"
  34        cp script-from-scratch "$1"
  35        EOF
  36
  37        test_commit A &&
  38        git checkout -b first &&
  39        test_commit B &&
  40        git checkout master &&
  41        test_commit C &&
  42        test_commit D &&
  43        git merge --no-commit B &&
  44        test_tick &&
  45        git commit -m E &&
  46        git tag -m E E &&
  47        git checkout -b second C &&
  48        test_commit F &&
  49        test_commit G &&
  50        git checkout master &&
  51        git merge --no-commit G &&
  52        test_tick &&
  53        git commit -m H &&
  54        git tag -m H H &&
  55        git checkout A &&
  56        test_commit conflicting-G G.t
  57'
  58
  59test_expect_success 'create completely different structure' '
  60        cat >script-from-scratch <<-\EOF &&
  61        label onto
  62
  63        # onebranch
  64        pick G
  65        pick D
  66        label onebranch
  67
  68        # second
  69        reset onto
  70        pick B
  71        label second
  72
  73        reset onto
  74        merge -C H second
  75        merge onebranch # Merge the topic branch '\''onebranch'\''
  76        EOF
  77        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
  78        test_tick &&
  79        git rebase -i -r A master &&
  80        test_cmp_graph <<-\EOF
  81        *   Merge the topic branch '\''onebranch'\''
  82        |\
  83        | * D
  84        | * G
  85        * |   H
  86        |\ \
  87        | |/
  88        |/|
  89        | * B
  90        |/
  91        * A
  92        EOF
  93'
  94
  95test_expect_success 'generate correct todo list' '
  96        cat >expect <<-\EOF &&
  97        label onto
  98
  99        reset onto
 100        pick d9df450 B
 101        label E
 102
 103        reset onto
 104        pick 5dee784 C
 105        label branch-point
 106        pick ca2c861 F
 107        pick 088b00a G
 108        label H
 109
 110        reset branch-point # C
 111        pick 12bd07b D
 112        merge -C 2051b56 E # E
 113        merge -C 233d48a H # H
 114
 115        EOF
 116
 117        grep -v "^#" <.git/ORIGINAL-TODO >output &&
 118        test_cmp expect output
 119'
 120
 121test_expect_success '`reset` refuses to overwrite untracked files' '
 122        git checkout -b refuse-to-reset &&
 123        test_commit dont-overwrite-untracked &&
 124        git checkout @{-1} &&
 125        : >dont-overwrite-untracked.t &&
 126        echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
 127        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 128        test_must_fail git rebase -ir HEAD &&
 129        git rebase --abort
 130'
 131
 132test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
 133        test_when_finished "test_might_fail git rebase --abort" &&
 134        git checkout -b conflicting-merge A &&
 135
 136        : fail because of conflicting untracked file &&
 137        >G.t &&
 138        echo "merge -C H G" >script-from-scratch &&
 139        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 140        test_tick &&
 141        test_must_fail git rebase -ir HEAD &&
 142        grep "^merge -C .* G$" .git/rebase-merge/done &&
 143        grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
 144        test_path_is_file .git/rebase-merge/patch &&
 145
 146        : fail because of merge conflict &&
 147        rm G.t .git/rebase-merge/patch &&
 148        git reset --hard conflicting-G &&
 149        test_must_fail git rebase --continue &&
 150        ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
 151        test_path_is_file .git/rebase-merge/patch
 152'
 153
 154SQ="'"
 155test_expect_success 'failed `merge <branch>` does not crash' '
 156        test_when_finished "test_might_fail git rebase --abort" &&
 157        git checkout conflicting-G &&
 158
 159        echo "merge G" >script-from-scratch &&
 160        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 161        test_tick &&
 162        test_must_fail git rebase -ir HEAD &&
 163        ! grep "^merge G$" .git/rebase-merge/git-rebase-todo &&
 164        grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
 165'
 166
 167test_expect_success 'fast-forward merge -c still rewords' '
 168        git checkout -b fast-forward-merge-c H &&
 169        (
 170                set_fake_editor &&
 171                FAKE_COMMIT_MESSAGE=edited \
 172                        GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
 173                        git rebase -ir @^
 174        ) &&
 175        echo edited >expected &&
 176        git log --pretty=format:%B -1 >actual &&
 177        test_cmp expected actual
 178'
 179
 180test_expect_success 'with a branch tip that was cherry-picked already' '
 181        git checkout -b already-upstream master &&
 182        base="$(git rev-parse --verify HEAD)" &&
 183
 184        test_commit A1 &&
 185        test_commit A2 &&
 186        git reset --hard $base &&
 187        test_commit B1 &&
 188        test_tick &&
 189        git merge -m "Merge branch A" A2 &&
 190
 191        git checkout -b upstream-with-a2 $base &&
 192        test_tick &&
 193        git cherry-pick A2 &&
 194
 195        git checkout already-upstream &&
 196        test_tick &&
 197        git rebase -i -r upstream-with-a2 &&
 198        test_cmp_graph upstream-with-a2.. <<-\EOF
 199        *   Merge branch A
 200        |\
 201        | * A1
 202        * | B1
 203        |/
 204        o A2
 205        EOF
 206'
 207
 208test_expect_success 'do not rebase cousins unless asked for' '
 209        git checkout -b cousins master &&
 210        before="$(git rev-parse --verify HEAD)" &&
 211        test_tick &&
 212        git rebase -r HEAD^ &&
 213        test_cmp_rev HEAD $before &&
 214        test_tick &&
 215        git rebase --rebase-merges=rebase-cousins HEAD^ &&
 216        test_cmp_graph HEAD^.. <<-\EOF
 217        *   Merge the topic branch '\''onebranch'\''
 218        |\
 219        | * D
 220        | * G
 221        |/
 222        o H
 223        EOF
 224'
 225
 226test_expect_success 'refs/rewritten/* is worktree-local' '
 227        git worktree add wt &&
 228        cat >wt/script-from-scratch <<-\EOF &&
 229        label xyz
 230        exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
 231        exec git rev-parse --verify refs/rewritten/xyz >b
 232        EOF
 233
 234        test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
 235        git -C wt rebase -i HEAD &&
 236        test_must_be_empty wt/a &&
 237        test_cmp_rev HEAD "$(cat wt/b)"
 238'
 239
 240test_expect_success 'post-rewrite hook and fixups work for merges' '
 241        git checkout -b post-rewrite &&
 242        test_commit same1 &&
 243        git reset --hard HEAD^ &&
 244        test_commit same2 &&
 245        git merge -m "to fix up" same1 &&
 246        echo same old same old >same2.t &&
 247        test_tick &&
 248        git commit --fixup HEAD same2.t &&
 249        fixup="$(git rev-parse HEAD)" &&
 250
 251        mkdir -p .git/hooks &&
 252        test_when_finished "rm .git/hooks/post-rewrite" &&
 253        echo "cat >actual" | write_script .git/hooks/post-rewrite &&
 254
 255        test_tick &&
 256        git rebase -i --autosquash -r HEAD^^^ &&
 257        printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
 258                $fixup^^2 HEAD^2 \
 259                $fixup^^ HEAD^ \
 260                $fixup^ HEAD \
 261                $fixup HEAD) &&
 262        test_cmp expect actual
 263'
 264
 265test_expect_success 'refuse to merge ancestors of HEAD' '
 266        echo "merge HEAD^" >script-from-scratch &&
 267        test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
 268        before="$(git rev-parse HEAD)" &&
 269        git rebase -i HEAD &&
 270        test_cmp_rev HEAD $before
 271'
 272
 273test_expect_success 'root commits' '
 274        git checkout --orphan unrelated &&
 275        (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
 276         test_commit second-root) &&
 277        test_commit third-root &&
 278        cat >script-from-scratch <<-\EOF &&
 279        pick third-root
 280        label first-branch
 281        reset [new root]
 282        pick second-root
 283        merge first-branch # Merge the 3rd root
 284        EOF
 285        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 286        test_tick &&
 287        git rebase -i --force-rebase --root -r &&
 288        test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
 289        test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
 290        test $(git rev-parse second-root:second-root.t) = \
 291                $(git rev-parse HEAD^:second-root.t) &&
 292        test_cmp_graph HEAD <<-\EOF &&
 293        *   Merge the 3rd root
 294        |\
 295        | * third-root
 296        * second-root
 297        EOF
 298
 299        : fast forward if possible &&
 300        before="$(git rev-parse --verify HEAD)" &&
 301        test_might_fail git config --unset sequence.editor &&
 302        test_tick &&
 303        git rebase -i --root -r &&
 304        test_cmp_rev HEAD $before
 305'
 306
 307test_expect_success 'a "merge" into a root commit is a fast-forward' '
 308        head=$(git rev-parse HEAD) &&
 309        cat >script-from-scratch <<-EOF &&
 310        reset [new root]
 311        merge $head
 312        EOF
 313        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 314        test_tick &&
 315        git rebase -i -r HEAD^ &&
 316        test_cmp_rev HEAD $head
 317'
 318
 319test_expect_success 'A root commit can be a cousin, treat it that way' '
 320        git checkout --orphan khnum &&
 321        test_commit yama &&
 322        git checkout -b asherah master &&
 323        test_commit shamkat &&
 324        git merge --allow-unrelated-histories khnum &&
 325        test_tick &&
 326        git rebase -f -r HEAD^ &&
 327        ! test_cmp_rev HEAD^2 khnum &&
 328        test_cmp_graph HEAD^.. <<-\EOF &&
 329        *   Merge branch '\''khnum'\'' into asherah
 330        |\
 331        | * yama
 332        o shamkat
 333        EOF
 334        test_tick &&
 335        git rebase --rebase-merges=rebase-cousins HEAD^ &&
 336        test_cmp_graph HEAD^.. <<-\EOF
 337        *   Merge branch '\''khnum'\'' into asherah
 338        |\
 339        | * yama
 340        |/
 341        o shamkat
 342        EOF
 343'
 344
 345test_expect_success 'labels that are object IDs are rewritten' '
 346        git checkout -b third B &&
 347        test_commit I &&
 348        third=$(git rev-parse HEAD) &&
 349        git checkout -b labels master &&
 350        git merge --no-commit third &&
 351        test_tick &&
 352        git commit -m "Merge commit '\''$third'\'' into labels" &&
 353        echo noop >script-from-scratch &&
 354        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 355        test_tick &&
 356        git rebase -i -r A &&
 357        grep "^label $third-" .git/ORIGINAL-TODO &&
 358        ! grep "^label $third$" .git/ORIGINAL-TODO
 359'
 360
 361test_expect_success 'octopus merges' '
 362        git checkout -b three &&
 363        test_commit before-octopus &&
 364        test_commit three &&
 365        git checkout -b two HEAD^ &&
 366        test_commit two &&
 367        git checkout -b one HEAD^ &&
 368        test_commit one &&
 369        test_tick &&
 370        (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
 371         git merge -m "Tüntenfüsch" two three) &&
 372
 373        : fast forward if possible &&
 374        before="$(git rev-parse --verify HEAD)" &&
 375        test_tick &&
 376        git rebase -i -r HEAD^^ &&
 377        test_cmp_rev HEAD $before &&
 378
 379        test_tick &&
 380        git rebase -i --force-rebase -r HEAD^^ &&
 381        test "Hank" = "$(git show -s --format=%an HEAD)" &&
 382        test "$before" != $(git rev-parse HEAD) &&
 383        test_cmp_graph HEAD^^.. <<-\EOF
 384        *-.   Tüntenfüsch
 385        |\ \
 386        | | * three
 387        | * | two
 388        | |/
 389        * | one
 390        |/
 391        o before-octopus
 392        EOF
 393'
 394
 395test_expect_success 'with --autosquash and --exec' '
 396        git checkout -b with-exec H &&
 397        echo Booh >B.t &&
 398        test_tick &&
 399        git commit --fixup B B.t &&
 400        write_script show.sh <<-\EOF &&
 401        subject="$(git show -s --format=%s HEAD)"
 402        content="$(git diff HEAD^! | tail -n 1)"
 403        echo "$subject: $content"
 404        EOF
 405        test_tick &&
 406        git rebase -ir --autosquash --exec ./show.sh A >actual &&
 407        grep "B: +Booh" actual &&
 408        grep "E: +Booh" actual &&
 409        grep "G: +G" actual
 410'
 411
 412test_expect_success '--continue after resolving conflicts after a merge' '
 413        git checkout -b already-has-g E &&
 414        git cherry-pick E..G &&
 415        test_commit H2 &&
 416
 417        git checkout -b conflicts-in-merge H &&
 418        test_commit H2 H2.t conflicts H2-conflict &&
 419        test_must_fail git rebase -r already-has-g &&
 420        grep conflicts H2.t &&
 421        echo resolved >H2.t &&
 422        git add -u &&
 423        git rebase --continue &&
 424        test_must_fail git rev-parse --verify HEAD^2 &&
 425        test_path_is_missing .git/MERGE_HEAD
 426'
 427
 428test_done