31fe4268d53084f840dad6e6558cea762d176696
   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` 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
 154test_expect_success 'with a branch tip that was cherry-picked already' '
 155        git checkout -b already-upstream master &&
 156        base="$(git rev-parse --verify HEAD)" &&
 157
 158        test_commit A1 &&
 159        test_commit A2 &&
 160        git reset --hard $base &&
 161        test_commit B1 &&
 162        test_tick &&
 163        git merge -m "Merge branch A" A2 &&
 164
 165        git checkout -b upstream-with-a2 $base &&
 166        test_tick &&
 167        git cherry-pick A2 &&
 168
 169        git checkout already-upstream &&
 170        test_tick &&
 171        git rebase -i -r upstream-with-a2 &&
 172        test_cmp_graph upstream-with-a2.. <<-\EOF
 173        *   Merge branch A
 174        |\
 175        | * A1
 176        * | B1
 177        |/
 178        o A2
 179        EOF
 180'
 181
 182test_expect_success 'do not rebase cousins unless asked for' '
 183        git checkout -b cousins master &&
 184        before="$(git rev-parse --verify HEAD)" &&
 185        test_tick &&
 186        git rebase -r HEAD^ &&
 187        test_cmp_rev HEAD $before &&
 188        test_tick &&
 189        git rebase --rebase-merges=rebase-cousins HEAD^ &&
 190        test_cmp_graph HEAD^.. <<-\EOF
 191        *   Merge the topic branch '\''onebranch'\''
 192        |\
 193        | * D
 194        | * G
 195        |/
 196        o H
 197        EOF
 198'
 199
 200test_expect_success 'refs/rewritten/* is worktree-local' '
 201        git worktree add wt &&
 202        cat >wt/script-from-scratch <<-\EOF &&
 203        label xyz
 204        exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
 205        exec git rev-parse --verify refs/rewritten/xyz >b
 206        EOF
 207
 208        test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
 209        git -C wt rebase -i HEAD &&
 210        test_must_be_empty wt/a &&
 211        test_cmp_rev HEAD "$(cat wt/b)"
 212'
 213
 214test_expect_success 'post-rewrite hook and fixups work for merges' '
 215        git checkout -b post-rewrite &&
 216        test_commit same1 &&
 217        git reset --hard HEAD^ &&
 218        test_commit same2 &&
 219        git merge -m "to fix up" same1 &&
 220        echo same old same old >same2.t &&
 221        test_tick &&
 222        git commit --fixup HEAD same2.t &&
 223        fixup="$(git rev-parse HEAD)" &&
 224
 225        mkdir -p .git/hooks &&
 226        test_when_finished "rm .git/hooks/post-rewrite" &&
 227        echo "cat >actual" | write_script .git/hooks/post-rewrite &&
 228
 229        test_tick &&
 230        git rebase -i --autosquash -r HEAD^^^ &&
 231        printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
 232                $fixup^^2 HEAD^2 \
 233                $fixup^^ HEAD^ \
 234                $fixup^ HEAD \
 235                $fixup HEAD) &&
 236        test_cmp expect actual
 237'
 238
 239test_expect_success 'refuse to merge ancestors of HEAD' '
 240        echo "merge HEAD^" >script-from-scratch &&
 241        test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
 242        before="$(git rev-parse HEAD)" &&
 243        git rebase -i HEAD &&
 244        test_cmp_rev HEAD $before
 245'
 246
 247test_expect_success 'root commits' '
 248        git checkout --orphan unrelated &&
 249        (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
 250         test_commit second-root) &&
 251        test_commit third-root &&
 252        cat >script-from-scratch <<-\EOF &&
 253        pick third-root
 254        label first-branch
 255        reset [new root]
 256        pick second-root
 257        merge first-branch # Merge the 3rd root
 258        EOF
 259        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 260        test_tick &&
 261        git rebase -i --force --root -r &&
 262        test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
 263        test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
 264        test $(git rev-parse second-root:second-root.t) = \
 265                $(git rev-parse HEAD^:second-root.t) &&
 266        test_cmp_graph HEAD <<-\EOF &&
 267        *   Merge the 3rd root
 268        |\
 269        | * third-root
 270        * second-root
 271        EOF
 272
 273        : fast forward if possible &&
 274        before="$(git rev-parse --verify HEAD)" &&
 275        test_might_fail git config --unset sequence.editor &&
 276        test_tick &&
 277        git rebase -i --root -r &&
 278        test_cmp_rev HEAD $before
 279'
 280
 281test_expect_success 'a "merge" into a root commit is a fast-forward' '
 282        head=$(git rev-parse HEAD) &&
 283        cat >script-from-scratch <<-EOF &&
 284        reset [new root]
 285        merge $head
 286        EOF
 287        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 288        test_tick &&
 289        git rebase -i -r HEAD^ &&
 290        test_cmp_rev HEAD $head
 291'
 292
 293test_expect_success 'A root commit can be a cousin, treat it that way' '
 294        git checkout --orphan khnum &&
 295        test_commit yama &&
 296        git checkout -b asherah master &&
 297        test_commit shamkat &&
 298        git merge --allow-unrelated-histories khnum &&
 299        test_tick &&
 300        git rebase -f -r HEAD^ &&
 301        ! test_cmp_rev HEAD^2 khnum &&
 302        test_cmp_graph HEAD^.. <<-\EOF &&
 303        *   Merge branch '\''khnum'\'' into asherah
 304        |\
 305        | * yama
 306        o shamkat
 307        EOF
 308        test_tick &&
 309        git rebase --rebase-merges=rebase-cousins HEAD^ &&
 310        test_cmp_graph HEAD^.. <<-\EOF
 311        *   Merge branch '\''khnum'\'' into asherah
 312        |\
 313        | * yama
 314        |/
 315        o shamkat
 316        EOF
 317'
 318
 319test_expect_success 'labels that are object IDs are rewritten' '
 320        git checkout -b third B &&
 321        test_commit I &&
 322        third=$(git rev-parse HEAD) &&
 323        git checkout -b labels master &&
 324        git merge --no-commit third &&
 325        test_tick &&
 326        git commit -m "Merge commit '\''$third'\'' into labels" &&
 327        echo noop >script-from-scratch &&
 328        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 329        test_tick &&
 330        git rebase -i -r A &&
 331        grep "^label $third-" .git/ORIGINAL-TODO &&
 332        ! grep "^label $third$" .git/ORIGINAL-TODO
 333'
 334
 335test_done