t / t3430-rebase-merges.shon commit checkout: disambiguate dwim tracking branches and local files (be4908f)
   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 -r 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 'post-rewrite hook and fixups work for merges' '
 228        git checkout -b post-rewrite &&
 229        test_commit same1 &&
 230        git reset --hard HEAD^ &&
 231        test_commit same2 &&
 232        git merge -m "to fix up" same1 &&
 233        echo same old same old >same2.t &&
 234        test_tick &&
 235        git commit --fixup HEAD same2.t &&
 236        fixup="$(git rev-parse HEAD)" &&
 237
 238        mkdir -p .git/hooks &&
 239        test_when_finished "rm .git/hooks/post-rewrite" &&
 240        echo "cat >actual" | write_script .git/hooks/post-rewrite &&
 241
 242        test_tick &&
 243        git rebase -i --autosquash -r HEAD^^^ &&
 244        printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
 245                $fixup^^2 HEAD^2 \
 246                $fixup^^ HEAD^ \
 247                $fixup^ HEAD \
 248                $fixup HEAD) &&
 249        test_cmp expect actual
 250'
 251
 252test_expect_success 'refuse to merge ancestors of HEAD' '
 253        echo "merge HEAD^" >script-from-scratch &&
 254        test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
 255        before="$(git rev-parse HEAD)" &&
 256        git rebase -i HEAD &&
 257        test_cmp_rev HEAD $before
 258'
 259
 260test_expect_success 'root commits' '
 261        git checkout --orphan unrelated &&
 262        (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
 263         test_commit second-root) &&
 264        test_commit third-root &&
 265        cat >script-from-scratch <<-\EOF &&
 266        pick third-root
 267        label first-branch
 268        reset [new root]
 269        pick second-root
 270        merge first-branch # Merge the 3rd root
 271        EOF
 272        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 273        test_tick &&
 274        git rebase -i --force --root -r &&
 275        test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
 276        test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
 277        test $(git rev-parse second-root:second-root.t) = \
 278                $(git rev-parse HEAD^:second-root.t) &&
 279        test_cmp_graph HEAD <<-\EOF &&
 280        *   Merge the 3rd root
 281        |\
 282        | * third-root
 283        * second-root
 284        EOF
 285
 286        : fast forward if possible &&
 287        before="$(git rev-parse --verify HEAD)" &&
 288        test_might_fail git config --unset sequence.editor &&
 289        test_tick &&
 290        git rebase -i --root -r &&
 291        test_cmp_rev HEAD $before
 292'
 293
 294test_expect_success 'a "merge" into a root commit is a fast-forward' '
 295        head=$(git rev-parse HEAD) &&
 296        cat >script-from-scratch <<-EOF &&
 297        reset [new root]
 298        merge $head
 299        EOF
 300        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 301        test_tick &&
 302        git rebase -i -r HEAD^ &&
 303        test_cmp_rev HEAD $head
 304'
 305
 306test_expect_success 'A root commit can be a cousin, treat it that way' '
 307        git checkout --orphan khnum &&
 308        test_commit yama &&
 309        git checkout -b asherah master &&
 310        test_commit shamkat &&
 311        git merge --allow-unrelated-histories khnum &&
 312        test_tick &&
 313        git rebase -f -r HEAD^ &&
 314        ! test_cmp_rev HEAD^2 khnum &&
 315        test_cmp_graph HEAD^.. <<-\EOF &&
 316        *   Merge branch '\''khnum'\'' into asherah
 317        |\
 318        | * yama
 319        o shamkat
 320        EOF
 321        test_tick &&
 322        git rebase --rebase-merges=rebase-cousins HEAD^ &&
 323        test_cmp_graph HEAD^.. <<-\EOF
 324        *   Merge branch '\''khnum'\'' into asherah
 325        |\
 326        | * yama
 327        |/
 328        o shamkat
 329        EOF
 330'
 331
 332test_expect_success 'labels that are object IDs are rewritten' '
 333        git checkout -b third B &&
 334        test_commit I &&
 335        third=$(git rev-parse HEAD) &&
 336        git checkout -b labels master &&
 337        git merge --no-commit third &&
 338        test_tick &&
 339        git commit -m "Merge commit '\''$third'\'' into labels" &&
 340        echo noop >script-from-scratch &&
 341        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 342        test_tick &&
 343        git rebase -i -r A &&
 344        grep "^label $third-" .git/ORIGINAL-TODO &&
 345        ! grep "^label $third$" .git/ORIGINAL-TODO
 346'
 347
 348test_expect_success 'octopus merges' '
 349        git checkout -b three &&
 350        test_commit before-octopus &&
 351        test_commit three &&
 352        git checkout -b two HEAD^ &&
 353        test_commit two &&
 354        git checkout -b one HEAD^ &&
 355        test_commit one &&
 356        test_tick &&
 357        (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
 358         git merge -m "Tüntenfüsch" two three) &&
 359
 360        : fast forward if possible &&
 361        before="$(git rev-parse --verify HEAD)" &&
 362        test_tick &&
 363        git rebase -i -r HEAD^^ &&
 364        test_cmp_rev HEAD $before &&
 365
 366        test_tick &&
 367        git rebase -i --force -r HEAD^^ &&
 368        test "Hank" = "$(git show -s --format=%an HEAD)" &&
 369        test "$before" != $(git rev-parse HEAD) &&
 370        test_cmp_graph HEAD^^.. <<-\EOF
 371        *-.   Tüntenfüsch
 372        |\ \
 373        | | * three
 374        | * | two
 375        | |/
 376        * | one
 377        |/
 378        o before-octopus
 379        EOF
 380'
 381
 382test_expect_success 'with --autosquash and --exec' '
 383        git checkout -b with-exec H &&
 384        echo Booh >B.t &&
 385        test_tick &&
 386        git commit --fixup B B.t &&
 387        write_script show.sh <<-\EOF &&
 388        subject="$(git show -s --format=%s HEAD)"
 389        content="$(git diff HEAD^! | tail -n 1)"
 390        echo "$subject: $content"
 391        EOF
 392        test_tick &&
 393        git rebase -ir --autosquash --exec ./show.sh A >actual &&
 394        grep "B: +Booh" actual &&
 395        grep "E: +Booh" actual &&
 396        grep "G: +G" actual
 397'
 398
 399test_done