t / t2025-worktree-add.shon commit Merge branch 'master' of https://github.com/Softcatala/git-po (87af1bb)
   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 'add --quiet' '
 256        git worktree add --quiet another-worktree master 2>actual &&
 257        test_must_be_empty actual
 258'
 259
 260test_expect_success 'local clone from linked checkout' '
 261        git clone --local here here-clone &&
 262        ( cd here-clone && git fsck )
 263'
 264
 265test_expect_success 'local clone --shared from linked checkout' '
 266        git -C bare worktree add --detach ../baretree &&
 267        git clone --local --shared baretree bare-clone &&
 268        grep /bare/ bare-clone/.git/objects/info/alternates
 269'
 270
 271test_expect_success '"add" worktree with --no-checkout' '
 272        git worktree add --no-checkout -b swamp swamp &&
 273        ! test -e swamp/init.t &&
 274        git -C swamp reset --hard &&
 275        test_cmp init.t swamp/init.t
 276'
 277
 278test_expect_success '"add" worktree with --checkout' '
 279        git worktree add --checkout -b swmap2 swamp2 &&
 280        test_cmp init.t swamp2/init.t
 281'
 282
 283test_expect_success 'put a worktree under rebase' '
 284        git worktree add under-rebase &&
 285        (
 286                cd under-rebase &&
 287                set_fake_editor &&
 288                FAKE_LINES="edit 1" git rebase -i HEAD^ &&
 289                git worktree list | grep "under-rebase.*detached HEAD"
 290        )
 291'
 292
 293test_expect_success 'add a worktree, checking out a rebased branch' '
 294        test_must_fail git worktree add new-rebase under-rebase &&
 295        ! test -d new-rebase
 296'
 297
 298test_expect_success 'checking out a rebased branch from another worktree' '
 299        git worktree add new-place &&
 300        test_must_fail git -C new-place checkout under-rebase
 301'
 302
 303test_expect_success 'not allow to delete a branch under rebase' '
 304        (
 305                cd under-rebase &&
 306                test_must_fail git branch -D under-rebase
 307        )
 308'
 309
 310test_expect_success 'rename a branch under rebase not allowed' '
 311        test_must_fail git branch -M under-rebase rebase-with-new-name
 312'
 313
 314test_expect_success 'check out from current worktree branch ok' '
 315        (
 316                cd under-rebase &&
 317                git checkout under-rebase &&
 318                git checkout - &&
 319                git rebase --abort
 320        )
 321'
 322
 323test_expect_success 'checkout a branch under bisect' '
 324        git worktree add under-bisect &&
 325        (
 326                cd under-bisect &&
 327                git bisect start &&
 328                git bisect bad &&
 329                git bisect good HEAD~2 &&
 330                git worktree list | grep "under-bisect.*detached HEAD" &&
 331                test_must_fail git worktree add new-bisect under-bisect &&
 332                ! test -d new-bisect
 333        )
 334'
 335
 336test_expect_success 'rename a branch under bisect not allowed' '
 337        test_must_fail git branch -M under-bisect bisect-with-new-name
 338'
 339# Is branch "refs/heads/$1" set to pull from "$2/$3"?
 340test_branch_upstream () {
 341        printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
 342        {
 343                git config "branch.$1.remote" &&
 344                git config "branch.$1.merge"
 345        } >actual.upstream &&
 346        test_cmp expect.upstream actual.upstream
 347}
 348
 349test_expect_success '--track sets up tracking' '
 350        test_when_finished rm -rf track &&
 351        git worktree add --track -b track track master &&
 352        test_branch_upstream track . master
 353'
 354
 355# setup remote repository $1 and repository $2 with $1 set up as
 356# remote.  The remote has two branches, master and foo.
 357setup_remote_repo () {
 358        git init $1 &&
 359        (
 360                cd $1 &&
 361                test_commit $1_master &&
 362                git checkout -b foo &&
 363                test_commit upstream_foo
 364        ) &&
 365        git init $2 &&
 366        (
 367                cd $2 &&
 368                test_commit $2_master &&
 369                git remote add $1 ../$1 &&
 370                git config remote.$1.fetch \
 371                        "refs/heads/*:refs/remotes/$1/*" &&
 372                git fetch --all
 373        )
 374}
 375
 376test_expect_success '--no-track avoids setting up tracking' '
 377        test_when_finished rm -rf repo_upstream repo_local foo &&
 378        setup_remote_repo repo_upstream repo_local &&
 379        (
 380                cd repo_local &&
 381                git worktree add --no-track -b foo ../foo repo_upstream/foo
 382        ) &&
 383        (
 384                cd foo &&
 385                test_must_fail git config "branch.foo.remote" &&
 386                test_must_fail git config "branch.foo.merge" &&
 387                test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
 388        )
 389'
 390
 391test_expect_success '"add" <path> <non-existent-branch> fails' '
 392        test_must_fail git worktree add foo non-existent
 393'
 394
 395test_expect_success '"add" <path> <branch> dwims' '
 396        test_when_finished rm -rf repo_upstream repo_dwim foo &&
 397        setup_remote_repo repo_upstream repo_dwim &&
 398        git init repo_dwim &&
 399        (
 400                cd repo_dwim &&
 401                git worktree add ../foo foo
 402        ) &&
 403        (
 404                cd foo &&
 405                test_branch_upstream foo repo_upstream foo &&
 406                test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
 407        )
 408'
 409
 410test_expect_success '"add" <path> <branch> dwims with checkout.defaultRemote' '
 411        test_when_finished rm -rf repo_upstream repo_dwim foo &&
 412        setup_remote_repo repo_upstream repo_dwim &&
 413        git init repo_dwim &&
 414        (
 415                cd repo_dwim &&
 416                git remote add repo_upstream2 ../repo_upstream &&
 417                git fetch repo_upstream2 &&
 418                test_must_fail git worktree add ../foo foo &&
 419                git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&
 420                git status -uno --porcelain >status.actual &&
 421                test_must_be_empty status.actual
 422        ) &&
 423        (
 424                cd foo &&
 425                test_branch_upstream foo repo_upstream foo &&
 426                test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
 427        )
 428'
 429
 430test_expect_success 'git worktree add does not match remote' '
 431        test_when_finished rm -rf repo_a repo_b foo &&
 432        setup_remote_repo repo_a repo_b &&
 433        (
 434                cd repo_b &&
 435                git worktree add ../foo
 436        ) &&
 437        (
 438                cd foo &&
 439                test_must_fail git config "branch.foo.remote" &&
 440                test_must_fail git config "branch.foo.merge" &&
 441                ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 442        )
 443'
 444
 445test_expect_success 'git worktree add --guess-remote sets up tracking' '
 446        test_when_finished rm -rf repo_a repo_b foo &&
 447        setup_remote_repo repo_a repo_b &&
 448        (
 449                cd repo_b &&
 450                git worktree add --guess-remote ../foo
 451        ) &&
 452        (
 453                cd foo &&
 454                test_branch_upstream foo repo_a foo &&
 455                test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 456        )
 457'
 458
 459test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
 460        test_when_finished rm -rf repo_a repo_b foo &&
 461        setup_remote_repo repo_a repo_b &&
 462        (
 463                cd repo_b &&
 464                git config worktree.guessRemote true &&
 465                git worktree add ../foo
 466        ) &&
 467        (
 468                cd foo &&
 469                test_branch_upstream foo repo_a foo &&
 470                test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 471        )
 472'
 473
 474test_expect_success 'git worktree --no-guess-remote option overrides config' '
 475        test_when_finished rm -rf repo_a repo_b foo &&
 476        setup_remote_repo repo_a repo_b &&
 477        (
 478                cd repo_b &&
 479                git config worktree.guessRemote true &&
 480                git worktree add --no-guess-remote ../foo
 481        ) &&
 482        (
 483                cd foo &&
 484                test_must_fail git config "branch.foo.remote" &&
 485                test_must_fail git config "branch.foo.merge" &&
 486                ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 487        )
 488'
 489
 490post_checkout_hook () {
 491        gitdir=${1:-.git}
 492        test_when_finished "rm -f $gitdir/hooks/post-checkout" &&
 493        mkdir -p $gitdir/hooks &&
 494        write_script $gitdir/hooks/post-checkout <<-\EOF
 495        {
 496                echo $*
 497                git rev-parse --git-dir --show-toplevel
 498        } >hook.actual
 499        EOF
 500}
 501
 502test_expect_success '"add" invokes post-checkout hook (branch)' '
 503        post_checkout_hook &&
 504        {
 505                echo $ZERO_OID $(git rev-parse HEAD) 1 &&
 506                echo $(pwd)/.git/worktrees/gumby &&
 507                echo $(pwd)/gumby
 508        } >hook.expect &&
 509        git worktree add gumby &&
 510        test_cmp hook.expect gumby/hook.actual
 511'
 512
 513test_expect_success '"add" invokes post-checkout hook (detached)' '
 514        post_checkout_hook &&
 515        {
 516                echo $ZERO_OID $(git rev-parse HEAD) 1 &&
 517                echo $(pwd)/.git/worktrees/grumpy &&
 518                echo $(pwd)/grumpy
 519        } >hook.expect &&
 520        git worktree add --detach grumpy &&
 521        test_cmp hook.expect grumpy/hook.actual
 522'
 523
 524test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
 525        post_checkout_hook &&
 526        rm -f hook.actual &&
 527        git worktree add --no-checkout gloopy &&
 528        test_path_is_missing gloopy/hook.actual
 529'
 530
 531test_expect_success '"add" in other worktree invokes post-checkout hook' '
 532        post_checkout_hook &&
 533        {
 534                echo $ZERO_OID $(git rev-parse HEAD) 1 &&
 535                echo $(pwd)/.git/worktrees/guppy &&
 536                echo $(pwd)/guppy
 537        } >hook.expect &&
 538        git -C gloopy worktree add --detach ../guppy &&
 539        test_cmp hook.expect guppy/hook.actual
 540'
 541
 542test_expect_success '"add" in bare repo invokes post-checkout hook' '
 543        rm -rf bare &&
 544        git clone --bare . bare &&
 545        {
 546                echo $ZERO_OID $(git --git-dir=bare rev-parse HEAD) 1 &&
 547                echo $(pwd)/bare/worktrees/goozy &&
 548                echo $(pwd)/goozy
 549        } >hook.expect &&
 550        post_checkout_hook bare &&
 551        git -C bare worktree add --detach ../goozy &&
 552        test_cmp hook.expect goozy/hook.actual
 553'
 554
 555test_done