t / t2025-worktree-add.shon commit built-in rebase --autostash: leave the current branch alone if possible (176f5d9)
   1#!/bin/sh
   2
   3test_description='test git worktree add'
   4
   5. ./test-lib.sh
   6
   7. "$TEST_DIRECTORY"/lib-rebase.sh
   8
   9test_expect_success 'setup' '
  10        test_commit init
  11'
  12
  13test_expect_success '"add" an existing worktree' '
  14        mkdir -p existing/subtree &&
  15        test_must_fail git worktree add --detach existing master
  16'
  17
  18test_expect_success '"add" an existing empty worktree' '
  19        mkdir existing_empty &&
  20        git worktree add --detach existing_empty master
  21'
  22
  23test_expect_success '"add" using shorthand - fails when no previous branch' '
  24        test_must_fail git worktree add existing_short -
  25'
  26
  27test_expect_success '"add" using - shorthand' '
  28        git checkout -b newbranch &&
  29        echo hello >myworld &&
  30        git add myworld &&
  31        git commit -m myworld &&
  32        git checkout master &&
  33        git worktree add short-hand - &&
  34        echo refs/heads/newbranch >expect &&
  35        git -C short-hand rev-parse --symbolic-full-name HEAD >actual &&
  36        test_cmp expect actual
  37'
  38
  39test_expect_success '"add" refuses to checkout locked branch' '
  40        test_must_fail git worktree add zere master &&
  41        ! test -d zere &&
  42        ! test -d .git/worktrees/zere
  43'
  44
  45test_expect_success 'checking out paths not complaining about linked checkouts' '
  46        (
  47        cd existing_empty &&
  48        echo dirty >>init.t &&
  49        git checkout master -- init.t
  50        )
  51'
  52
  53test_expect_success '"add" worktree' '
  54        git rev-parse HEAD >expect &&
  55        git worktree add --detach here master &&
  56        (
  57                cd here &&
  58                test_cmp ../init.t init.t &&
  59                test_must_fail git symbolic-ref HEAD &&
  60                git rev-parse HEAD >actual &&
  61                test_cmp ../expect actual &&
  62                git fsck
  63        )
  64'
  65
  66test_expect_success '"add" worktree with lock' '
  67        git rev-parse HEAD >expect &&
  68        git worktree add --detach --lock here-with-lock master &&
  69        test -f .git/worktrees/here-with-lock/locked
  70'
  71
  72test_expect_success '"add" worktree from a subdir' '
  73        (
  74                mkdir sub &&
  75                cd sub &&
  76                git worktree add --detach here master &&
  77                cd here &&
  78                test_cmp ../../init.t init.t
  79        )
  80'
  81
  82test_expect_success '"add" from a linked checkout' '
  83        (
  84                cd here &&
  85                git worktree add --detach nested-here master &&
  86                cd nested-here &&
  87                git fsck
  88        )
  89'
  90
  91test_expect_success '"add" worktree creating new branch' '
  92        git worktree add -b newmaster there master &&
  93        (
  94                cd there &&
  95                test_cmp ../init.t init.t &&
  96                git symbolic-ref HEAD >actual &&
  97                echo refs/heads/newmaster >expect &&
  98                test_cmp expect actual &&
  99                git fsck
 100        )
 101'
 102
 103test_expect_success 'die the same branch is already checked out' '
 104        (
 105                cd here &&
 106                test_must_fail git checkout newmaster
 107        )
 108'
 109
 110test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
 111        head=$(git -C there rev-parse --git-path HEAD) &&
 112        ref=$(git -C there symbolic-ref HEAD) &&
 113        rm "$head" &&
 114        ln -s "$ref" "$head" &&
 115        test_must_fail git -C here checkout newmaster
 116'
 117
 118test_expect_success 'not die the same branch is already checked out' '
 119        (
 120                cd here &&
 121                git worktree add --force anothernewmaster newmaster
 122        )
 123'
 124
 125test_expect_success 'not die on re-checking out current branch' '
 126        (
 127                cd there &&
 128                git checkout newmaster
 129        )
 130'
 131
 132test_expect_success '"add" from a bare repo' '
 133        (
 134                git clone --bare . bare &&
 135                cd bare &&
 136                git worktree add -b bare-master ../there2 master
 137        )
 138'
 139
 140test_expect_success 'checkout from a bare repo without "add"' '
 141        (
 142                cd bare &&
 143                test_must_fail git checkout master
 144        )
 145'
 146
 147test_expect_success '"add" default branch of a bare repo' '
 148        (
 149                git clone --bare . bare2 &&
 150                cd bare2 &&
 151                git worktree add ../there3 master
 152        )
 153'
 154
 155test_expect_success 'checkout with grafts' '
 156        test_when_finished rm .git/info/grafts &&
 157        test_commit abc &&
 158        SHA1=$(git rev-parse HEAD) &&
 159        test_commit def &&
 160        test_commit xyz &&
 161        echo "$(git rev-parse HEAD) $SHA1" >.git/info/grafts &&
 162        cat >expected <<-\EOF &&
 163        xyz
 164        abc
 165        EOF
 166        git log --format=%s -2 >actual &&
 167        test_cmp expected actual &&
 168        git worktree add --detach grafted master &&
 169        git --git-dir=grafted/.git log --format=%s -2 >actual &&
 170        test_cmp expected actual
 171'
 172
 173test_expect_success '"add" from relative HEAD' '
 174        test_commit a &&
 175        test_commit b &&
 176        test_commit c &&
 177        git rev-parse HEAD~1 >expected &&
 178        git worktree add relhead HEAD~1 &&
 179        git -C relhead rev-parse HEAD >actual &&
 180        test_cmp expected actual
 181'
 182
 183test_expect_success '"add -b" with <branch> omitted' '
 184        git worktree add -b burble flornk &&
 185        test_cmp_rev HEAD burble
 186'
 187
 188test_expect_success '"add --detach" with <branch> omitted' '
 189        git worktree add --detach fishhook &&
 190        git rev-parse HEAD >expected &&
 191        git -C fishhook rev-parse HEAD >actual &&
 192        test_cmp expected actual &&
 193        test_must_fail git -C fishhook symbolic-ref HEAD
 194'
 195
 196test_expect_success '"add" with <branch> omitted' '
 197        git worktree add wiffle/bat &&
 198        test_cmp_rev HEAD bat
 199'
 200
 201test_expect_success '"add" checks out existing branch of dwimd name' '
 202        git branch dwim HEAD~1 &&
 203        git worktree add dwim &&
 204        test_cmp_rev HEAD~1 dwim &&
 205        (
 206                cd dwim &&
 207                test_cmp_rev HEAD dwim
 208        )
 209'
 210
 211test_expect_success '"add <path>" dwim fails with checked out branch' '
 212        git checkout -b test-branch &&
 213        test_must_fail git worktree add test-branch &&
 214        test_path_is_missing test-branch
 215'
 216
 217test_expect_success '"add --force" with existing dwimd name doesnt die' '
 218        git checkout test-branch &&
 219        git worktree add --force test-branch
 220'
 221
 222test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
 223        git worktree add --detach mish/mash &&
 224        test_must_fail git rev-parse mash -- &&
 225        test_must_fail git -C mish/mash symbolic-ref HEAD
 226'
 227
 228test_expect_success '"add" -b/-B mutually exclusive' '
 229        test_must_fail git worktree add -b poodle -B poodle bamboo master
 230'
 231
 232test_expect_success '"add" -b/--detach mutually exclusive' '
 233        test_must_fail git worktree add -b poodle --detach bamboo master
 234'
 235
 236test_expect_success '"add" -B/--detach mutually exclusive' '
 237        test_must_fail git worktree add -B poodle --detach bamboo master
 238'
 239
 240test_expect_success '"add -B" fails if the branch is checked out' '
 241        git rev-parse newmaster >before &&
 242        test_must_fail git worktree add -B newmaster bamboo master &&
 243        git rev-parse newmaster >after &&
 244        test_cmp before after
 245'
 246
 247test_expect_success 'add -B' '
 248        git worktree add -B poodle bamboo2 master^ &&
 249        git -C bamboo2 symbolic-ref HEAD >actual &&
 250        echo refs/heads/poodle >expected &&
 251        test_cmp expected actual &&
 252        test_cmp_rev master^ poodle
 253'
 254
 255test_expect_success 'local clone from linked checkout' '
 256        git clone --local here here-clone &&
 257        ( cd here-clone && git fsck )
 258'
 259
 260test_expect_success 'local clone --shared from linked checkout' '
 261        git -C bare worktree add --detach ../baretree &&
 262        git clone --local --shared baretree bare-clone &&
 263        grep /bare/ bare-clone/.git/objects/info/alternates
 264'
 265
 266test_expect_success '"add" worktree with --no-checkout' '
 267        git worktree add --no-checkout -b swamp swamp &&
 268        ! test -e swamp/init.t &&
 269        git -C swamp reset --hard &&
 270        test_cmp init.t swamp/init.t
 271'
 272
 273test_expect_success '"add" worktree with --checkout' '
 274        git worktree add --checkout -b swmap2 swamp2 &&
 275        test_cmp init.t swamp2/init.t
 276'
 277
 278test_expect_success 'put a worktree under rebase' '
 279        git worktree add under-rebase &&
 280        (
 281                cd under-rebase &&
 282                set_fake_editor &&
 283                FAKE_LINES="edit 1" git rebase -i HEAD^ &&
 284                git worktree list | grep "under-rebase.*detached HEAD"
 285        )
 286'
 287
 288test_expect_success 'add a worktree, checking out a rebased branch' '
 289        test_must_fail git worktree add new-rebase under-rebase &&
 290        ! test -d new-rebase
 291'
 292
 293test_expect_success 'checking out a rebased branch from another worktree' '
 294        git worktree add new-place &&
 295        test_must_fail git -C new-place checkout under-rebase
 296'
 297
 298test_expect_success 'not allow to delete a branch under rebase' '
 299        (
 300                cd under-rebase &&
 301                test_must_fail git branch -D under-rebase
 302        )
 303'
 304
 305test_expect_success 'rename a branch under rebase not allowed' '
 306        test_must_fail git branch -M under-rebase rebase-with-new-name
 307'
 308
 309test_expect_success 'check out from current worktree branch ok' '
 310        (
 311                cd under-rebase &&
 312                git checkout under-rebase &&
 313                git checkout - &&
 314                git rebase --abort
 315        )
 316'
 317
 318test_expect_success 'checkout a branch under bisect' '
 319        git worktree add under-bisect &&
 320        (
 321                cd under-bisect &&
 322                git bisect start &&
 323                git bisect bad &&
 324                git bisect good HEAD~2 &&
 325                git worktree list | grep "under-bisect.*detached HEAD" &&
 326                test_must_fail git worktree add new-bisect under-bisect &&
 327                ! test -d new-bisect
 328        )
 329'
 330
 331test_expect_success 'rename a branch under bisect not allowed' '
 332        test_must_fail git branch -M under-bisect bisect-with-new-name
 333'
 334# Is branch "refs/heads/$1" set to pull from "$2/$3"?
 335test_branch_upstream () {
 336        printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
 337        {
 338                git config "branch.$1.remote" &&
 339                git config "branch.$1.merge"
 340        } >actual.upstream &&
 341        test_cmp expect.upstream actual.upstream
 342}
 343
 344test_expect_success '--track sets up tracking' '
 345        test_when_finished rm -rf track &&
 346        git worktree add --track -b track track master &&
 347        test_branch_upstream track . master
 348'
 349
 350# setup remote repository $1 and repository $2 with $1 set up as
 351# remote.  The remote has two branches, master and foo.
 352setup_remote_repo () {
 353        git init $1 &&
 354        (
 355                cd $1 &&
 356                test_commit $1_master &&
 357                git checkout -b foo &&
 358                test_commit upstream_foo
 359        ) &&
 360        git init $2 &&
 361        (
 362                cd $2 &&
 363                test_commit $2_master &&
 364                git remote add $1 ../$1 &&
 365                git config remote.$1.fetch \
 366                        "refs/heads/*:refs/remotes/$1/*" &&
 367                git fetch --all
 368        )
 369}
 370
 371test_expect_success '--no-track avoids setting up tracking' '
 372        test_when_finished rm -rf repo_upstream repo_local foo &&
 373        setup_remote_repo repo_upstream repo_local &&
 374        (
 375                cd repo_local &&
 376                git worktree add --no-track -b foo ../foo repo_upstream/foo
 377        ) &&
 378        (
 379                cd foo &&
 380                test_must_fail git config "branch.foo.remote" &&
 381                test_must_fail git config "branch.foo.merge" &&
 382                test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
 383        )
 384'
 385
 386test_expect_success '"add" <path> <non-existent-branch> fails' '
 387        test_must_fail git worktree add foo non-existent
 388'
 389
 390test_expect_success '"add" <path> <branch> dwims' '
 391        test_when_finished rm -rf repo_upstream repo_dwim foo &&
 392        setup_remote_repo repo_upstream repo_dwim &&
 393        git init repo_dwim &&
 394        (
 395                cd repo_dwim &&
 396                git worktree add ../foo foo
 397        ) &&
 398        (
 399                cd foo &&
 400                test_branch_upstream foo repo_upstream foo &&
 401                test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
 402        )
 403'
 404
 405test_expect_success '"add" <path> <branch> dwims with checkout.defaultRemote' '
 406        test_when_finished rm -rf repo_upstream repo_dwim foo &&
 407        setup_remote_repo repo_upstream repo_dwim &&
 408        git init repo_dwim &&
 409        (
 410                cd repo_dwim &&
 411                git remote add repo_upstream2 ../repo_upstream &&
 412                git fetch repo_upstream2 &&
 413                test_must_fail git worktree add ../foo foo &&
 414                git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&
 415                >status.expect &&
 416                git status -uno --porcelain >status.actual &&
 417                test_cmp status.expect status.actual
 418        ) &&
 419        (
 420                cd foo &&
 421                test_branch_upstream foo repo_upstream foo &&
 422                test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
 423        )
 424'
 425
 426test_expect_success 'git worktree add does not match remote' '
 427        test_when_finished rm -rf repo_a repo_b foo &&
 428        setup_remote_repo repo_a repo_b &&
 429        (
 430                cd repo_b &&
 431                git worktree add ../foo
 432        ) &&
 433        (
 434                cd foo &&
 435                test_must_fail git config "branch.foo.remote" &&
 436                test_must_fail git config "branch.foo.merge" &&
 437                ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 438        )
 439'
 440
 441test_expect_success 'git worktree add --guess-remote sets up tracking' '
 442        test_when_finished rm -rf repo_a repo_b foo &&
 443        setup_remote_repo repo_a repo_b &&
 444        (
 445                cd repo_b &&
 446                git worktree add --guess-remote ../foo
 447        ) &&
 448        (
 449                cd foo &&
 450                test_branch_upstream foo repo_a foo &&
 451                test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 452        )
 453'
 454
 455test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
 456        test_when_finished rm -rf repo_a repo_b foo &&
 457        setup_remote_repo repo_a repo_b &&
 458        (
 459                cd repo_b &&
 460                git config worktree.guessRemote true &&
 461                git worktree add ../foo
 462        ) &&
 463        (
 464                cd foo &&
 465                test_branch_upstream foo repo_a foo &&
 466                test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 467        )
 468'
 469
 470test_expect_success 'git worktree --no-guess-remote option overrides config' '
 471        test_when_finished rm -rf repo_a repo_b foo &&
 472        setup_remote_repo repo_a repo_b &&
 473        (
 474                cd repo_b &&
 475                git config worktree.guessRemote true &&
 476                git worktree add --no-guess-remote ../foo
 477        ) &&
 478        (
 479                cd foo &&
 480                test_must_fail git config "branch.foo.remote" &&
 481                test_must_fail git config "branch.foo.merge" &&
 482                ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 483        )
 484'
 485
 486post_checkout_hook () {
 487        gitdir=${1:-.git}
 488        test_when_finished "rm -f $gitdir/hooks/post-checkout" &&
 489        mkdir -p $gitdir/hooks &&
 490        write_script $gitdir/hooks/post-checkout <<-\EOF
 491        {
 492                echo $*
 493                git rev-parse --git-dir --show-toplevel
 494        } >hook.actual
 495        EOF
 496}
 497
 498test_expect_success '"add" invokes post-checkout hook (branch)' '
 499        post_checkout_hook &&
 500        {
 501                echo $ZERO_OID $(git rev-parse HEAD) 1 &&
 502                echo $(pwd)/.git/worktrees/gumby &&
 503                echo $(pwd)/gumby
 504        } >hook.expect &&
 505        git worktree add gumby &&
 506        test_cmp hook.expect gumby/hook.actual
 507'
 508
 509test_expect_success '"add" invokes post-checkout hook (detached)' '
 510        post_checkout_hook &&
 511        {
 512                echo $ZERO_OID $(git rev-parse HEAD) 1 &&
 513                echo $(pwd)/.git/worktrees/grumpy &&
 514                echo $(pwd)/grumpy
 515        } >hook.expect &&
 516        git worktree add --detach grumpy &&
 517        test_cmp hook.expect grumpy/hook.actual
 518'
 519
 520test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
 521        post_checkout_hook &&
 522        rm -f hook.actual &&
 523        git worktree add --no-checkout gloopy &&
 524        test_path_is_missing gloopy/hook.actual
 525'
 526
 527test_expect_success '"add" in other worktree invokes post-checkout hook' '
 528        post_checkout_hook &&
 529        {
 530                echo $ZERO_OID $(git rev-parse HEAD) 1 &&
 531                echo $(pwd)/.git/worktrees/guppy &&
 532                echo $(pwd)/guppy
 533        } >hook.expect &&
 534        git -C gloopy worktree add --detach ../guppy &&
 535        test_cmp hook.expect guppy/hook.actual
 536'
 537
 538test_expect_success '"add" in bare repo invokes post-checkout hook' '
 539        rm -rf bare &&
 540        git clone --bare . bare &&
 541        {
 542                echo $ZERO_OID $(git --git-dir=bare rev-parse HEAD) 1 &&
 543                echo $(pwd)/bare/worktrees/goozy &&
 544                echo $(pwd)/goozy
 545        } >hook.expect &&
 546        post_checkout_hook bare &&
 547        git -C bare worktree add --detach ../goozy &&
 548        test_cmp hook.expect goozy/hook.actual
 549'
 550
 551test_done