t / t3430-rebase-merges.shon commit rebase --abort/--quit: cleanup refs/rewritten (d559f50)
   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 'with a branch tip that was cherry-picked already' '
 168        git checkout -b already-upstream master &&
 169        base="$(git rev-parse --verify HEAD)" &&
 170
 171        test_commit A1 &&
 172        test_commit A2 &&
 173        git reset --hard $base &&
 174        test_commit B1 &&
 175        test_tick &&
 176        git merge -m "Merge branch A" A2 &&
 177
 178        git checkout -b upstream-with-a2 $base &&
 179        test_tick &&
 180        git cherry-pick A2 &&
 181
 182        git checkout already-upstream &&
 183        test_tick &&
 184        git rebase -i -r upstream-with-a2 &&
 185        test_cmp_graph upstream-with-a2.. <<-\EOF
 186        *   Merge branch A
 187        |\
 188        | * A1
 189        * | B1
 190        |/
 191        o A2
 192        EOF
 193'
 194
 195test_expect_success 'do not rebase cousins unless asked for' '
 196        git checkout -b cousins master &&
 197        before="$(git rev-parse --verify HEAD)" &&
 198        test_tick &&
 199        git rebase -r HEAD^ &&
 200        test_cmp_rev HEAD $before &&
 201        test_tick &&
 202        git rebase --rebase-merges=rebase-cousins HEAD^ &&
 203        test_cmp_graph HEAD^.. <<-\EOF
 204        *   Merge the topic branch '\''onebranch'\''
 205        |\
 206        | * D
 207        | * G
 208        |/
 209        o H
 210        EOF
 211'
 212
 213test_expect_success 'refs/rewritten/* is worktree-local' '
 214        git worktree add wt &&
 215        cat >wt/script-from-scratch <<-\EOF &&
 216        label xyz
 217        exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
 218        exec git rev-parse --verify refs/rewritten/xyz >b
 219        EOF
 220
 221        test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
 222        git -C wt rebase -i HEAD &&
 223        test_must_be_empty wt/a &&
 224        test_cmp_rev HEAD "$(cat wt/b)"
 225'
 226
 227test_expect_success '--abort cleans up refs/rewritten' '
 228        git checkout -b abort-cleans-refs-rewritten H &&
 229        GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
 230        git rev-parse --verify refs/rewritten/onto &&
 231        git rebase --abort &&
 232        test_must_fail git rev-parse --verify refs/rewritten/onto
 233'
 234
 235test_expect_success '--quit cleans up refs/rewritten' '
 236        git checkout -b quit-cleans-refs-rewritten H &&
 237        GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
 238        git rev-parse --verify refs/rewritten/onto &&
 239        git rebase --quit &&
 240        test_must_fail git rev-parse --verify refs/rewritten/onto
 241'
 242
 243test_expect_success 'post-rewrite hook and fixups work for merges' '
 244        git checkout -b post-rewrite H &&
 245        test_commit same1 &&
 246        git reset --hard HEAD^ &&
 247        test_commit same2 &&
 248        git merge -m "to fix up" same1 &&
 249        echo same old same old >same2.t &&
 250        test_tick &&
 251        git commit --fixup HEAD same2.t &&
 252        fixup="$(git rev-parse HEAD)" &&
 253
 254        mkdir -p .git/hooks &&
 255        test_when_finished "rm .git/hooks/post-rewrite" &&
 256        echo "cat >actual" | write_script .git/hooks/post-rewrite &&
 257
 258        test_tick &&
 259        git rebase -i --autosquash -r HEAD^^^ &&
 260        printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
 261                $fixup^^2 HEAD^2 \
 262                $fixup^^ HEAD^ \
 263                $fixup^ HEAD \
 264                $fixup HEAD) &&
 265        test_cmp expect actual
 266'
 267
 268test_expect_success 'refuse to merge ancestors of HEAD' '
 269        echo "merge HEAD^" >script-from-scratch &&
 270        test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
 271        before="$(git rev-parse HEAD)" &&
 272        git rebase -i HEAD &&
 273        test_cmp_rev HEAD $before
 274'
 275
 276test_expect_success 'root commits' '
 277        git checkout --orphan unrelated &&
 278        (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
 279         test_commit second-root) &&
 280        test_commit third-root &&
 281        cat >script-from-scratch <<-\EOF &&
 282        pick third-root
 283        label first-branch
 284        reset [new root]
 285        pick second-root
 286        merge first-branch # Merge the 3rd root
 287        EOF
 288        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 289        test_tick &&
 290        git rebase -i --force --root -r &&
 291        test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
 292        test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
 293        test $(git rev-parse second-root:second-root.t) = \
 294                $(git rev-parse HEAD^:second-root.t) &&
 295        test_cmp_graph HEAD <<-\EOF &&
 296        *   Merge the 3rd root
 297        |\
 298        | * third-root
 299        * second-root
 300        EOF
 301
 302        : fast forward if possible &&
 303        before="$(git rev-parse --verify HEAD)" &&
 304        test_might_fail git config --unset sequence.editor &&
 305        test_tick &&
 306        git rebase -i --root -r &&
 307        test_cmp_rev HEAD $before
 308'
 309
 310test_expect_success 'a "merge" into a root commit is a fast-forward' '
 311        head=$(git rev-parse HEAD) &&
 312        cat >script-from-scratch <<-EOF &&
 313        reset [new root]
 314        merge $head
 315        EOF
 316        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 317        test_tick &&
 318        git rebase -i -r HEAD^ &&
 319        test_cmp_rev HEAD $head
 320'
 321
 322test_expect_success 'A root commit can be a cousin, treat it that way' '
 323        git checkout --orphan khnum &&
 324        test_commit yama &&
 325        git checkout -b asherah master &&
 326        test_commit shamkat &&
 327        git merge --allow-unrelated-histories khnum &&
 328        test_tick &&
 329        git rebase -f -r HEAD^ &&
 330        ! test_cmp_rev HEAD^2 khnum &&
 331        test_cmp_graph HEAD^.. <<-\EOF &&
 332        *   Merge branch '\''khnum'\'' into asherah
 333        |\
 334        | * yama
 335        o shamkat
 336        EOF
 337        test_tick &&
 338        git rebase --rebase-merges=rebase-cousins HEAD^ &&
 339        test_cmp_graph HEAD^.. <<-\EOF
 340        *   Merge branch '\''khnum'\'' into asherah
 341        |\
 342        | * yama
 343        |/
 344        o shamkat
 345        EOF
 346'
 347
 348test_expect_success 'labels that are object IDs are rewritten' '
 349        git checkout -b third B &&
 350        test_commit I &&
 351        third=$(git rev-parse HEAD) &&
 352        git checkout -b labels master &&
 353        git merge --no-commit third &&
 354        test_tick &&
 355        git commit -m "Merge commit '\''$third'\'' into labels" &&
 356        echo noop >script-from-scratch &&
 357        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 358        test_tick &&
 359        git rebase -i -r A &&
 360        grep "^label $third-" .git/ORIGINAL-TODO &&
 361        ! grep "^label $third$" .git/ORIGINAL-TODO
 362'
 363
 364test_expect_success 'octopus merges' '
 365        git checkout -b three &&
 366        test_commit before-octopus &&
 367        test_commit three &&
 368        git checkout -b two HEAD^ &&
 369        test_commit two &&
 370        git checkout -b one HEAD^ &&
 371        test_commit one &&
 372        test_tick &&
 373        (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
 374         git merge -m "Tüntenfüsch" two three) &&
 375
 376        : fast forward if possible &&
 377        before="$(git rev-parse --verify HEAD)" &&
 378        test_tick &&
 379        git rebase -i -r HEAD^^ &&
 380        test_cmp_rev HEAD $before &&
 381
 382        test_tick &&
 383        git rebase -i --force -r HEAD^^ &&
 384        test "Hank" = "$(git show -s --format=%an HEAD)" &&
 385        test "$before" != $(git rev-parse HEAD) &&
 386        test_cmp_graph HEAD^^.. <<-\EOF
 387        *-.   Tüntenfüsch
 388        |\ \
 389        | | * three
 390        | * | two
 391        | |/
 392        * | one
 393        |/
 394        o before-octopus
 395        EOF
 396'
 397
 398test_expect_success 'with --autosquash and --exec' '
 399        git checkout -b with-exec H &&
 400        echo Booh >B.t &&
 401        test_tick &&
 402        git commit --fixup B B.t &&
 403        write_script show.sh <<-\EOF &&
 404        subject="$(git show -s --format=%s HEAD)"
 405        content="$(git diff HEAD^! | tail -n 1)"
 406        echo "$subject: $content"
 407        EOF
 408        test_tick &&
 409        git rebase -ir --autosquash --exec ./show.sh A >actual &&
 410        grep "B: +Booh" actual &&
 411        grep "E: +Booh" actual &&
 412        grep "G: +G" actual
 413'
 414
 415test_expect_success '--continue after resolving conflicts after a merge' '
 416        git checkout -b already-has-g E &&
 417        git cherry-pick E..G &&
 418        test_commit H2 &&
 419
 420        git checkout -b conflicts-in-merge H &&
 421        test_commit H2 H2.t conflicts H2-conflict &&
 422        test_must_fail git rebase -r already-has-g &&
 423        grep conflicts H2.t &&
 424        echo resolved >H2.t &&
 425        git add -u &&
 426        git rebase --continue &&
 427        test_must_fail git rev-parse --verify HEAD^2 &&
 428        test_path_is_missing .git/MERGE_HEAD
 429'
 430
 431test_done