From a0cc58450a8ac81ba405f1e161599263d1678686 Mon Sep 17 00:00:00 2001 From: Thomas Gummerer Date: Thu, 20 Dec 2018 13:48:13 +0000 Subject: move worktree tests to t24* The 'git worktree' command used to be just another mode in 'git checkout', namely 'git checkout --to'. When the tests for the latter were retrofitted for the former, the test name was adjusted, but the test number was kept, even though the test is testing a different command now. t/README states: "Second digit tells the particular command we are testing.", so 'git worktree' should have a separate number just for itself. Move the worktree tests to t24* to adhere to that guideline. We're going to make use of the free'd up numbers in a subsequent commit. Signed-off-by: Thomas Gummerer Signed-off-by: Junio C Hamano --- t/t2025-worktree-add.sh | 573 --------------------------------------------- t/t2026-worktree-prune.sh | 95 -------- t/t2027-worktree-list.sh | 154 ------------ t/t2028-worktree-move.sh | 188 --------------- t/t2029-worktree-config.sh | 79 ------- t/t2400-worktree-add.sh | 573 +++++++++++++++++++++++++++++++++++++++++++++ t/t2401-worktree-prune.sh | 95 ++++++++ t/t2402-worktree-list.sh | 154 ++++++++++++ t/t2403-worktree-move.sh | 188 +++++++++++++++ t/t2404-worktree-config.sh | 79 +++++++ 10 files changed, 1089 insertions(+), 1089 deletions(-) delete mode 100755 t/t2025-worktree-add.sh delete mode 100755 t/t2026-worktree-prune.sh delete mode 100755 t/t2027-worktree-list.sh delete mode 100755 t/t2028-worktree-move.sh delete mode 100755 t/t2029-worktree-config.sh create mode 100755 t/t2400-worktree-add.sh create mode 100755 t/t2401-worktree-prune.sh create mode 100755 t/t2402-worktree-list.sh create mode 100755 t/t2403-worktree-move.sh create mode 100755 t/t2404-worktree-config.sh (limited to 't') diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh deleted file mode 100755 index 286bba35d8..0000000000 --- a/t/t2025-worktree-add.sh +++ /dev/null @@ -1,573 +0,0 @@ -#!/bin/sh - -test_description='test git worktree add' - -. ./test-lib.sh - -. "$TEST_DIRECTORY"/lib-rebase.sh - -test_expect_success 'setup' ' - test_commit init -' - -test_expect_success '"add" an existing worktree' ' - mkdir -p existing/subtree && - test_must_fail git worktree add --detach existing master -' - -test_expect_success '"add" an existing empty worktree' ' - mkdir existing_empty && - git worktree add --detach existing_empty master -' - -test_expect_success '"add" using shorthand - fails when no previous branch' ' - test_must_fail git worktree add existing_short - -' - -test_expect_success '"add" using - shorthand' ' - git checkout -b newbranch && - echo hello >myworld && - git add myworld && - git commit -m myworld && - git checkout master && - git worktree add short-hand - && - echo refs/heads/newbranch >expect && - git -C short-hand rev-parse --symbolic-full-name HEAD >actual && - test_cmp expect actual -' - -test_expect_success '"add" refuses to checkout locked branch' ' - test_must_fail git worktree add zere master && - ! test -d zere && - ! test -d .git/worktrees/zere -' - -test_expect_success 'checking out paths not complaining about linked checkouts' ' - ( - cd existing_empty && - echo dirty >>init.t && - git checkout master -- init.t - ) -' - -test_expect_success '"add" worktree' ' - git rev-parse HEAD >expect && - git worktree add --detach here master && - ( - cd here && - test_cmp ../init.t init.t && - test_must_fail git symbolic-ref HEAD && - git rev-parse HEAD >actual && - test_cmp ../expect actual && - git fsck - ) -' - -test_expect_success '"add" worktree with lock' ' - git rev-parse HEAD >expect && - git worktree add --detach --lock here-with-lock master && - test -f .git/worktrees/here-with-lock/locked -' - -test_expect_success '"add" worktree from a subdir' ' - ( - mkdir sub && - cd sub && - git worktree add --detach here master && - cd here && - test_cmp ../../init.t init.t - ) -' - -test_expect_success '"add" from a linked checkout' ' - ( - cd here && - git worktree add --detach nested-here master && - cd nested-here && - git fsck - ) -' - -test_expect_success '"add" worktree creating new branch' ' - git worktree add -b newmaster there master && - ( - cd there && - test_cmp ../init.t init.t && - git symbolic-ref HEAD >actual && - echo refs/heads/newmaster >expect && - test_cmp expect actual && - git fsck - ) -' - -test_expect_success 'die the same branch is already checked out' ' - ( - cd here && - test_must_fail git checkout newmaster - ) -' - -test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' ' - head=$(git -C there rev-parse --git-path HEAD) && - ref=$(git -C there symbolic-ref HEAD) && - rm "$head" && - ln -s "$ref" "$head" && - test_must_fail git -C here checkout newmaster -' - -test_expect_success 'not die the same branch is already checked out' ' - ( - cd here && - git worktree add --force anothernewmaster newmaster - ) -' - -test_expect_success 'not die on re-checking out current branch' ' - ( - cd there && - git checkout newmaster - ) -' - -test_expect_success '"add" from a bare repo' ' - ( - git clone --bare . bare && - cd bare && - git worktree add -b bare-master ../there2 master - ) -' - -test_expect_success 'checkout from a bare repo without "add"' ' - ( - cd bare && - test_must_fail git checkout master - ) -' - -test_expect_success '"add" default branch of a bare repo' ' - ( - git clone --bare . bare2 && - cd bare2 && - git worktree add ../there3 master - ) -' - -test_expect_success 'checkout with grafts' ' - test_when_finished rm .git/info/grafts && - test_commit abc && - SHA1=$(git rev-parse HEAD) && - test_commit def && - test_commit xyz && - echo "$(git rev-parse HEAD) $SHA1" >.git/info/grafts && - cat >expected <<-\EOF && - xyz - abc - EOF - git log --format=%s -2 >actual && - test_cmp expected actual && - git worktree add --detach grafted master && - git --git-dir=grafted/.git log --format=%s -2 >actual && - test_cmp expected actual -' - -test_expect_success '"add" from relative HEAD' ' - test_commit a && - test_commit b && - test_commit c && - git rev-parse HEAD~1 >expected && - git worktree add relhead HEAD~1 && - git -C relhead rev-parse HEAD >actual && - test_cmp expected actual -' - -test_expect_success '"add -b" with omitted' ' - git worktree add -b burble flornk && - test_cmp_rev HEAD burble -' - -test_expect_success '"add --detach" with omitted' ' - git worktree add --detach fishhook && - git rev-parse HEAD >expected && - git -C fishhook rev-parse HEAD >actual && - test_cmp expected actual && - test_must_fail git -C fishhook symbolic-ref HEAD -' - -test_expect_success '"add" with omitted' ' - git worktree add wiffle/bat && - test_cmp_rev HEAD bat -' - -test_expect_success '"add" checks out existing branch of dwimd name' ' - git branch dwim HEAD~1 && - git worktree add dwim && - test_cmp_rev HEAD~1 dwim && - ( - cd dwim && - test_cmp_rev HEAD dwim - ) -' - -test_expect_success '"add " dwim fails with checked out branch' ' - git checkout -b test-branch && - test_must_fail git worktree add test-branch && - test_path_is_missing test-branch -' - -test_expect_success '"add --force" with existing dwimd name doesnt die' ' - git checkout test-branch && - git worktree add --force test-branch -' - -test_expect_success '"add" no auto-vivify with --detach and omitted' ' - git worktree add --detach mish/mash && - test_must_fail git rev-parse mash -- && - test_must_fail git -C mish/mash symbolic-ref HEAD -' - -test_expect_success '"add" -b/-B mutually exclusive' ' - test_must_fail git worktree add -b poodle -B poodle bamboo master -' - -test_expect_success '"add" -b/--detach mutually exclusive' ' - test_must_fail git worktree add -b poodle --detach bamboo master -' - -test_expect_success '"add" -B/--detach mutually exclusive' ' - test_must_fail git worktree add -B poodle --detach bamboo master -' - -test_expect_success '"add -B" fails if the branch is checked out' ' - git rev-parse newmaster >before && - test_must_fail git worktree add -B newmaster bamboo master && - git rev-parse newmaster >after && - test_cmp before after -' - -test_expect_success 'add -B' ' - git worktree add -B poodle bamboo2 master^ && - git -C bamboo2 symbolic-ref HEAD >actual && - echo refs/heads/poodle >expected && - test_cmp expected actual && - test_cmp_rev master^ poodle -' - -test_expect_success 'add --quiet' ' - git worktree add --quiet another-worktree master 2>actual && - test_must_be_empty actual -' - -test_expect_success 'local clone from linked checkout' ' - git clone --local here here-clone && - ( cd here-clone && git fsck ) -' - -test_expect_success 'local clone --shared from linked checkout' ' - git -C bare worktree add --detach ../baretree && - git clone --local --shared baretree bare-clone && - grep /bare/ bare-clone/.git/objects/info/alternates -' - -test_expect_success '"add" worktree with --no-checkout' ' - git worktree add --no-checkout -b swamp swamp && - ! test -e swamp/init.t && - git -C swamp reset --hard && - test_cmp init.t swamp/init.t -' - -test_expect_success '"add" worktree with --checkout' ' - git worktree add --checkout -b swmap2 swamp2 && - test_cmp init.t swamp2/init.t -' - -test_expect_success 'put a worktree under rebase' ' - git worktree add under-rebase && - ( - cd under-rebase && - set_fake_editor && - FAKE_LINES="edit 1" git rebase -i HEAD^ && - git worktree list | grep "under-rebase.*detached HEAD" - ) -' - -test_expect_success 'add a worktree, checking out a rebased branch' ' - test_must_fail git worktree add new-rebase under-rebase && - ! test -d new-rebase -' - -test_expect_success 'checking out a rebased branch from another worktree' ' - git worktree add new-place && - test_must_fail git -C new-place checkout under-rebase -' - -test_expect_success 'not allow to delete a branch under rebase' ' - ( - cd under-rebase && - test_must_fail git branch -D under-rebase - ) -' - -test_expect_success 'rename a branch under rebase not allowed' ' - test_must_fail git branch -M under-rebase rebase-with-new-name -' - -test_expect_success 'check out from current worktree branch ok' ' - ( - cd under-rebase && - git checkout under-rebase && - git checkout - && - git rebase --abort - ) -' - -test_expect_success 'checkout a branch under bisect' ' - git worktree add under-bisect && - ( - cd under-bisect && - git bisect start && - git bisect bad && - git bisect good HEAD~2 && - git worktree list | grep "under-bisect.*detached HEAD" && - test_must_fail git worktree add new-bisect under-bisect && - ! test -d new-bisect - ) -' - -test_expect_success 'rename a branch under bisect not allowed' ' - test_must_fail git branch -M under-bisect bisect-with-new-name -' -# Is branch "refs/heads/$1" set to pull from "$2/$3"? -test_branch_upstream () { - printf "%s\n" "$2" "refs/heads/$3" >expect.upstream && - { - git config "branch.$1.remote" && - git config "branch.$1.merge" - } >actual.upstream && - test_cmp expect.upstream actual.upstream -} - -test_expect_success '--track sets up tracking' ' - test_when_finished rm -rf track && - git worktree add --track -b track track master && - test_branch_upstream track . master -' - -# setup remote repository $1 and repository $2 with $1 set up as -# remote. The remote has two branches, master and foo. -setup_remote_repo () { - git init $1 && - ( - cd $1 && - test_commit $1_master && - git checkout -b foo && - test_commit upstream_foo - ) && - git init $2 && - ( - cd $2 && - test_commit $2_master && - git remote add $1 ../$1 && - git config remote.$1.fetch \ - "refs/heads/*:refs/remotes/$1/*" && - git fetch --all - ) -} - -test_expect_success '--no-track avoids setting up tracking' ' - test_when_finished rm -rf repo_upstream repo_local foo && - setup_remote_repo repo_upstream repo_local && - ( - cd repo_local && - git worktree add --no-track -b foo ../foo repo_upstream/foo - ) && - ( - cd foo && - test_must_fail git config "branch.foo.remote" && - test_must_fail git config "branch.foo.merge" && - test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo - ) -' - -test_expect_success '"add" fails' ' - test_must_fail git worktree add foo non-existent -' - -test_expect_success '"add" dwims' ' - test_when_finished rm -rf repo_upstream repo_dwim foo && - setup_remote_repo repo_upstream repo_dwim && - git init repo_dwim && - ( - cd repo_dwim && - git worktree add ../foo foo - ) && - ( - cd foo && - test_branch_upstream foo repo_upstream foo && - test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo - ) -' - -test_expect_success '"add" dwims with checkout.defaultRemote' ' - test_when_finished rm -rf repo_upstream repo_dwim foo && - setup_remote_repo repo_upstream repo_dwim && - git init repo_dwim && - ( - cd repo_dwim && - git remote add repo_upstream2 ../repo_upstream && - git fetch repo_upstream2 && - test_must_fail git worktree add ../foo foo && - git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo && - git status -uno --porcelain >status.actual && - test_must_be_empty status.actual - ) && - ( - cd foo && - test_branch_upstream foo repo_upstream foo && - test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo - ) -' - -test_expect_success 'git worktree add does not match remote' ' - test_when_finished rm -rf repo_a repo_b foo && - setup_remote_repo repo_a repo_b && - ( - cd repo_b && - git worktree add ../foo - ) && - ( - cd foo && - test_must_fail git config "branch.foo.remote" && - test_must_fail git config "branch.foo.merge" && - ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo - ) -' - -test_expect_success 'git worktree add --guess-remote sets up tracking' ' - test_when_finished rm -rf repo_a repo_b foo && - setup_remote_repo repo_a repo_b && - ( - cd repo_b && - git worktree add --guess-remote ../foo - ) && - ( - cd foo && - test_branch_upstream foo repo_a foo && - test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo - ) -' - -test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' ' - test_when_finished rm -rf repo_a repo_b foo && - setup_remote_repo repo_a repo_b && - ( - cd repo_b && - git config worktree.guessRemote true && - git worktree add ../foo - ) && - ( - cd foo && - test_branch_upstream foo repo_a foo && - test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo - ) -' - -test_expect_success 'git worktree --no-guess-remote option overrides config' ' - test_when_finished rm -rf repo_a repo_b foo && - setup_remote_repo repo_a repo_b && - ( - cd repo_b && - git config worktree.guessRemote true && - git worktree add --no-guess-remote ../foo - ) && - ( - cd foo && - test_must_fail git config "branch.foo.remote" && - test_must_fail git config "branch.foo.merge" && - ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo - ) -' - -post_checkout_hook () { - gitdir=${1:-.git} - test_when_finished "rm -f $gitdir/hooks/post-checkout" && - mkdir -p $gitdir/hooks && - write_script $gitdir/hooks/post-checkout <<-\EOF - { - echo $* - git rev-parse --git-dir --show-toplevel - } >hook.actual - EOF -} - -test_expect_success '"add" invokes post-checkout hook (branch)' ' - post_checkout_hook && - { - echo $ZERO_OID $(git rev-parse HEAD) 1 && - echo $(pwd)/.git/worktrees/gumby && - echo $(pwd)/gumby - } >hook.expect && - git worktree add gumby && - test_cmp hook.expect gumby/hook.actual -' - -test_expect_success '"add" invokes post-checkout hook (detached)' ' - post_checkout_hook && - { - echo $ZERO_OID $(git rev-parse HEAD) 1 && - echo $(pwd)/.git/worktrees/grumpy && - echo $(pwd)/grumpy - } >hook.expect && - git worktree add --detach grumpy && - test_cmp hook.expect grumpy/hook.actual -' - -test_expect_success '"add --no-checkout" suppresses post-checkout hook' ' - post_checkout_hook && - rm -f hook.actual && - git worktree add --no-checkout gloopy && - test_path_is_missing gloopy/hook.actual -' - -test_expect_success '"add" in other worktree invokes post-checkout hook' ' - post_checkout_hook && - { - echo $ZERO_OID $(git rev-parse HEAD) 1 && - echo $(pwd)/.git/worktrees/guppy && - echo $(pwd)/guppy - } >hook.expect && - git -C gloopy worktree add --detach ../guppy && - test_cmp hook.expect guppy/hook.actual -' - -test_expect_success '"add" in bare repo invokes post-checkout hook' ' - rm -rf bare && - git clone --bare . bare && - { - echo $ZERO_OID $(git --git-dir=bare rev-parse HEAD) 1 && - echo $(pwd)/bare/worktrees/goozy && - echo $(pwd)/goozy - } >hook.expect && - post_checkout_hook bare && - git -C bare worktree add --detach ../goozy && - test_cmp hook.expect goozy/hook.actual -' - -test_expect_success '"add" an existing but missing worktree' ' - git worktree add --detach pneu && - test_must_fail git worktree add --detach pneu && - rm -fr pneu && - test_must_fail git worktree add --detach pneu && - git worktree add --force --detach pneu -' - -test_expect_success '"add" an existing locked but missing worktree' ' - git worktree add --detach gnoo && - git worktree lock gnoo && - test_when_finished "git worktree unlock gnoo || :" && - rm -fr gnoo && - test_must_fail git worktree add --detach gnoo && - test_must_fail git worktree add --force --detach gnoo && - git worktree add --force --force --detach gnoo -' - -test_done diff --git a/t/t2026-worktree-prune.sh b/t/t2026-worktree-prune.sh deleted file mode 100755 index b7d6d5d45a..0000000000 --- a/t/t2026-worktree-prune.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/sh - -test_description='prune $GIT_DIR/worktrees' - -. ./test-lib.sh - -test_expect_success initialize ' - git commit --allow-empty -m init -' - -test_expect_success 'worktree prune on normal repo' ' - git worktree prune && - test_must_fail git worktree prune abc -' - -test_expect_success 'prune files inside $GIT_DIR/worktrees' ' - mkdir .git/worktrees && - : >.git/worktrees/abc && - git worktree prune --verbose >actual && - cat >expect <.git/worktrees/def/def && - cat >expect <actual && - test_i18ncmp expect actual && - ! test -d .git/worktrees/def && - ! test -d .git/worktrees -' - -test_expect_success SANITY 'prune directories with unreadable gitdir' ' - mkdir -p .git/worktrees/def/abc && - : >.git/worktrees/def/def && - : >.git/worktrees/def/gitdir && - chmod u-r .git/worktrees/def/gitdir && - git worktree prune --verbose >actual && - test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual && - ! test -d .git/worktrees/def && - ! test -d .git/worktrees -' - -test_expect_success 'prune directories with invalid gitdir' ' - mkdir -p .git/worktrees/def/abc && - : >.git/worktrees/def/def && - : >.git/worktrees/def/gitdir && - git worktree prune --verbose >actual && - test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual && - ! test -d .git/worktrees/def && - ! test -d .git/worktrees -' - -test_expect_success 'prune directories with gitdir pointing to nowhere' ' - mkdir -p .git/worktrees/def/abc && - : >.git/worktrees/def/def && - echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir && - git worktree prune --verbose >actual && - test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual && - ! test -d .git/worktrees/def && - ! test -d .git/worktrees -' - -test_expect_success 'not prune locked checkout' ' - test_when_finished rm -r .git/worktrees && - mkdir -p .git/worktrees/ghi && - : >.git/worktrees/ghi/locked && - git worktree prune && - test -d .git/worktrees/ghi -' - -test_expect_success 'not prune recent checkouts' ' - test_when_finished rm -r .git/worktrees && - git worktree add jlm HEAD && - test -d .git/worktrees/jlm && - rm -rf jlm && - git worktree prune --verbose --expire=2.days.ago && - test -d .git/worktrees/jlm -' - -test_expect_success 'not prune proper checkouts' ' - test_when_finished rm -r .git/worktrees && - git worktree add --detach "$PWD/nop" master && - git worktree prune && - test -d .git/worktrees/nop -' - -test_done diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh deleted file mode 100755 index bb6fb9b12c..0000000000 --- a/t/t2027-worktree-list.sh +++ /dev/null @@ -1,154 +0,0 @@ -#!/bin/sh - -test_description='test git worktree list' - -. ./test-lib.sh - -test_expect_success 'setup' ' - test_commit init -' - -test_expect_success 'rev-parse --git-common-dir on main worktree' ' - git rev-parse --git-common-dir >actual && - echo .git >expected && - test_cmp expected actual && - mkdir sub && - git -C sub rev-parse --git-common-dir >actual2 && - echo ../.git >expected2 && - test_cmp expected2 actual2 -' - -test_expect_success 'rev-parse --git-path objects linked worktree' ' - echo "$(git rev-parse --show-toplevel)/.git/objects" >expect && - test_when_finished "rm -rf linked-tree actual expect && git worktree prune" && - git worktree add --detach linked-tree master && - git -C linked-tree rev-parse --git-path objects >actual && - test_cmp expect actual -' - -test_expect_success '"list" all worktrees from main' ' - echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect && - test_when_finished "rm -rf here out actual expect && git worktree prune" && - git worktree add --detach here master && - echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect && - git worktree list >out && - sed "s/ */ /g" actual && - test_cmp expect actual -' - -test_expect_success '"list" all worktrees from linked' ' - echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect && - test_when_finished "rm -rf here out actual expect && git worktree prune" && - git worktree add --detach here master && - echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect && - git -C here worktree list >out && - sed "s/ */ /g" actual && - test_cmp expect actual -' - -test_expect_success '"list" all worktrees --porcelain' ' - echo "worktree $(git rev-parse --show-toplevel)" >expect && - echo "HEAD $(git rev-parse HEAD)" >>expect && - echo "branch $(git symbolic-ref HEAD)" >>expect && - echo >>expect && - test_when_finished "rm -rf here actual expect && git worktree prune" && - git worktree add --detach here master && - echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect && - echo "HEAD $(git rev-parse HEAD)" >>expect && - echo "detached" >>expect && - echo >>expect && - git worktree list --porcelain >actual && - test_cmp expect actual -' - -test_expect_success 'bare repo setup' ' - git init --bare bare1 && - echo "data" >file1 && - git add file1 && - git commit -m"File1: add data" && - git push bare1 master && - git reset --hard HEAD^ -' - -test_expect_success '"list" all worktrees from bare main' ' - test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" && - git -C bare1 worktree add --detach ../there master && - echo "$(pwd)/bare1 (bare)" >expect && - echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect && - git -C bare1 worktree list >out && - sed "s/ */ /g" actual && - test_cmp expect actual -' - -test_expect_success '"list" all worktrees --porcelain from bare main' ' - test_when_finished "rm -rf there actual expect && git -C bare1 worktree prune" && - git -C bare1 worktree add --detach ../there master && - echo "worktree $(pwd)/bare1" >expect && - echo "bare" >>expect && - echo >>expect && - echo "worktree $(git -C there rev-parse --show-toplevel)" >>expect && - echo "HEAD $(git -C there rev-parse HEAD)" >>expect && - echo "detached" >>expect && - echo >>expect && - git -C bare1 worktree list --porcelain >actual && - test_cmp expect actual -' - -test_expect_success '"list" all worktrees from linked with a bare main' ' - test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" && - git -C bare1 worktree add --detach ../there master && - echo "$(pwd)/bare1 (bare)" >expect && - echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect && - git -C there worktree list >out && - sed "s/ */ /g" actual && - test_cmp expect actual -' - -test_expect_success 'bare repo cleanup' ' - rm -rf bare1 -' - -test_expect_success 'broken main worktree still at the top' ' - git init broken-main && - ( - cd broken-main && - test_commit new && - git worktree add linked && - cat >expected <<-EOF && - worktree $(pwd) - HEAD $ZERO_OID - - EOF - cd linked && - echo "worktree $(pwd)" >expected && - echo "ref: .broken" >../.git/HEAD && - git worktree list --porcelain >out && - head -n 3 out >actual && - test_cmp ../expected actual && - git worktree list >out && - head -n 1 out >actual.2 && - grep -F "(error)" actual.2 - ) -' - -test_expect_success 'linked worktrees are sorted' ' - mkdir sorted && - git init sorted/main && - ( - cd sorted/main && - test_tick && - test_commit new && - git worktree add ../first && - git worktree add ../second && - git worktree list --porcelain >out && - grep ^worktree out >actual - ) && - cat >expected <<-EOF && - worktree $(pwd)/sorted/main - worktree $(pwd)/sorted/first - worktree $(pwd)/sorted/second - EOF - test_cmp expected sorted/main/actual -' - -test_done diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh deleted file mode 100755 index 33c0337733..0000000000 --- a/t/t2028-worktree-move.sh +++ /dev/null @@ -1,188 +0,0 @@ -#!/bin/sh - -test_description='test git worktree move, remove, lock and unlock' - -. ./test-lib.sh - -test_expect_success 'setup' ' - test_commit init && - git worktree add source && - git worktree list --porcelain >out && - grep "^worktree" out >actual && - cat <<-EOF >expected && - worktree $(pwd) - worktree $(pwd)/source - EOF - test_cmp expected actual -' - -test_expect_success 'lock main worktree' ' - test_must_fail git worktree lock . -' - -test_expect_success 'lock linked worktree' ' - git worktree lock --reason hahaha source && - echo hahaha >expected && - test_cmp expected .git/worktrees/source/locked -' - -test_expect_success 'lock linked worktree from another worktree' ' - rm .git/worktrees/source/locked && - git worktree add elsewhere && - git -C elsewhere worktree lock --reason hahaha ../source && - echo hahaha >expected && - test_cmp expected .git/worktrees/source/locked -' - -test_expect_success 'lock worktree twice' ' - test_must_fail git worktree lock source && - echo hahaha >expected && - test_cmp expected .git/worktrees/source/locked -' - -test_expect_success 'lock worktree twice (from the locked worktree)' ' - test_must_fail git -C source worktree lock . && - echo hahaha >expected && - test_cmp expected .git/worktrees/source/locked -' - -test_expect_success 'unlock main worktree' ' - test_must_fail git worktree unlock . -' - -test_expect_success 'unlock linked worktree' ' - git worktree unlock source && - test_path_is_missing .git/worktrees/source/locked -' - -test_expect_success 'unlock worktree twice' ' - test_must_fail git worktree unlock source && - test_path_is_missing .git/worktrees/source/locked -' - -test_expect_success 'move non-worktree' ' - mkdir abc && - test_must_fail git worktree move abc def -' - -test_expect_success 'move locked worktree' ' - git worktree lock source && - test_when_finished "git worktree unlock source" && - test_must_fail git worktree move source destination -' - -test_expect_success 'move worktree' ' - git worktree move source destination && - test_path_is_missing source && - git worktree list --porcelain >out && - grep "^worktree.*/destination$" out && - ! grep "^worktree.*/source$" out && - git -C destination log --format=%s >actual2 && - echo init >expected2 && - test_cmp expected2 actual2 -' - -test_expect_success 'move main worktree' ' - test_must_fail git worktree move . def -' - -test_expect_success 'move worktree to another dir' ' - mkdir some-dir && - git worktree move destination some-dir && - test_when_finished "git worktree move some-dir/destination destination" && - test_path_is_missing destination && - git worktree list --porcelain >out && - grep "^worktree.*/some-dir/destination$" out && - git -C some-dir/destination log --format=%s >actual2 && - echo init >expected2 && - test_cmp expected2 actual2 -' - -test_expect_success 'move locked worktree (force)' ' - test_when_finished " - git worktree unlock flump || : - git worktree remove flump || : - git worktree unlock ploof || : - git worktree remove ploof || : - " && - git worktree add --detach flump && - git worktree lock flump && - test_must_fail git worktree move flump ploof" && - test_must_fail git worktree move --force flump ploof" && - git worktree move --force --force flump ploof -' - -test_expect_success 'remove main worktree' ' - test_must_fail git worktree remove . -' - -test_expect_success 'remove locked worktree' ' - git worktree lock destination && - test_when_finished "git worktree unlock destination" && - test_must_fail git worktree remove destination -' - -test_expect_success 'remove worktree with dirty tracked file' ' - echo dirty >>destination/init.t && - test_when_finished "git -C destination checkout init.t" && - test_must_fail git worktree remove destination -' - -test_expect_success 'remove worktree with untracked file' ' - : >destination/untracked && - test_must_fail git worktree remove destination -' - -test_expect_success 'force remove worktree with untracked file' ' - git worktree remove --force destination && - test_path_is_missing destination -' - -test_expect_success 'remove missing worktree' ' - git worktree add to-be-gone && - test -d .git/worktrees/to-be-gone && - mv to-be-gone gone && - git worktree remove to-be-gone && - test_path_is_missing .git/worktrees/to-be-gone -' - -test_expect_success 'NOT remove missing-but-locked worktree' ' - git worktree add gone-but-locked && - git worktree lock gone-but-locked && - test -d .git/worktrees/gone-but-locked && - mv gone-but-locked really-gone-now && - test_must_fail git worktree remove gone-but-locked && - test_path_is_dir .git/worktrees/gone-but-locked -' - -test_expect_success 'proper error when worktree not found' ' - for i in noodle noodle/bork - do - test_must_fail git worktree lock $i 2>err && - test_i18ngrep "not a working tree" err || return 1 - done -' - -test_expect_success 'remove locked worktree (force)' ' - git worktree add --detach gumby && - test_when_finished "git worktree remove gumby || :" && - git worktree lock gumby && - test_when_finished "git worktree unlock gumby || :" && - test_must_fail git worktree remove gumby && - test_must_fail git worktree remove --force gumby && - git worktree remove --force --force gumby -' - -test_expect_success 'remove cleans up .git/worktrees when empty' ' - git init moog && - ( - cd moog && - test_commit bim && - git worktree add --detach goom && - test_path_exists .git/worktrees && - git worktree remove goom && - test_path_is_missing .git/worktrees - ) -' - -test_done diff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh deleted file mode 100755 index 286121d8de..0000000000 --- a/t/t2029-worktree-config.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh - -test_description="config file in multi worktree" - -. ./test-lib.sh - -test_expect_success 'setup' ' - test_commit start -' - -test_expect_success 'config --worktree in single worktree' ' - git config --worktree foo.bar true && - test_cmp_config true foo.bar -' - -test_expect_success 'add worktrees' ' - git worktree add wt1 && - git worktree add wt2 -' - -test_expect_success 'config --worktree without extension' ' - test_must_fail git config --worktree foo.bar false -' - -test_expect_success 'enable worktreeConfig extension' ' - git config extensions.worktreeConfig true && - test_cmp_config true extensions.worktreeConfig -' - -test_expect_success 'config is shared as before' ' - git config this.is shared && - test_cmp_config shared this.is && - test_cmp_config -C wt1 shared this.is && - test_cmp_config -C wt2 shared this.is -' - -test_expect_success 'config is shared (set from another worktree)' ' - git -C wt1 config that.is also-shared && - test_cmp_config also-shared that.is && - test_cmp_config -C wt1 also-shared that.is && - test_cmp_config -C wt2 also-shared that.is -' - -test_expect_success 'config private to main worktree' ' - git config --worktree this.is for-main && - test_cmp_config for-main this.is && - test_cmp_config -C wt1 shared this.is && - test_cmp_config -C wt2 shared this.is -' - -test_expect_success 'config private to linked worktree' ' - git -C wt1 config --worktree this.is for-wt1 && - test_cmp_config for-main this.is && - test_cmp_config -C wt1 for-wt1 this.is && - test_cmp_config -C wt2 shared this.is -' - -test_expect_success 'core.bare no longer for main only' ' - test_config core.bare true && - test "$(git rev-parse --is-bare-repository)" = true && - test "$(git -C wt1 rev-parse --is-bare-repository)" = true && - test "$(git -C wt2 rev-parse --is-bare-repository)" = true -' - -test_expect_success 'per-worktree core.bare is picked up' ' - git -C wt1 config --worktree core.bare true && - test "$(git rev-parse --is-bare-repository)" = false && - test "$(git -C wt1 rev-parse --is-bare-repository)" = true && - test "$(git -C wt2 rev-parse --is-bare-repository)" = false -' - -test_expect_success 'config.worktree no longer read without extension' ' - git config --unset extensions.worktreeConfig && - test_cmp_config shared this.is && - test_cmp_config -C wt1 shared this.is && - test_cmp_config -C wt2 shared this.is -' - -test_done diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh new file mode 100755 index 0000000000..286bba35d8 --- /dev/null +++ b/t/t2400-worktree-add.sh @@ -0,0 +1,573 @@ +#!/bin/sh + +test_description='test git worktree add' + +. ./test-lib.sh + +. "$TEST_DIRECTORY"/lib-rebase.sh + +test_expect_success 'setup' ' + test_commit init +' + +test_expect_success '"add" an existing worktree' ' + mkdir -p existing/subtree && + test_must_fail git worktree add --detach existing master +' + +test_expect_success '"add" an existing empty worktree' ' + mkdir existing_empty && + git worktree add --detach existing_empty master +' + +test_expect_success '"add" using shorthand - fails when no previous branch' ' + test_must_fail git worktree add existing_short - +' + +test_expect_success '"add" using - shorthand' ' + git checkout -b newbranch && + echo hello >myworld && + git add myworld && + git commit -m myworld && + git checkout master && + git worktree add short-hand - && + echo refs/heads/newbranch >expect && + git -C short-hand rev-parse --symbolic-full-name HEAD >actual && + test_cmp expect actual +' + +test_expect_success '"add" refuses to checkout locked branch' ' + test_must_fail git worktree add zere master && + ! test -d zere && + ! test -d .git/worktrees/zere +' + +test_expect_success 'checking out paths not complaining about linked checkouts' ' + ( + cd existing_empty && + echo dirty >>init.t && + git checkout master -- init.t + ) +' + +test_expect_success '"add" worktree' ' + git rev-parse HEAD >expect && + git worktree add --detach here master && + ( + cd here && + test_cmp ../init.t init.t && + test_must_fail git symbolic-ref HEAD && + git rev-parse HEAD >actual && + test_cmp ../expect actual && + git fsck + ) +' + +test_expect_success '"add" worktree with lock' ' + git rev-parse HEAD >expect && + git worktree add --detach --lock here-with-lock master && + test -f .git/worktrees/here-with-lock/locked +' + +test_expect_success '"add" worktree from a subdir' ' + ( + mkdir sub && + cd sub && + git worktree add --detach here master && + cd here && + test_cmp ../../init.t init.t + ) +' + +test_expect_success '"add" from a linked checkout' ' + ( + cd here && + git worktree add --detach nested-here master && + cd nested-here && + git fsck + ) +' + +test_expect_success '"add" worktree creating new branch' ' + git worktree add -b newmaster there master && + ( + cd there && + test_cmp ../init.t init.t && + git symbolic-ref HEAD >actual && + echo refs/heads/newmaster >expect && + test_cmp expect actual && + git fsck + ) +' + +test_expect_success 'die the same branch is already checked out' ' + ( + cd here && + test_must_fail git checkout newmaster + ) +' + +test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' ' + head=$(git -C there rev-parse --git-path HEAD) && + ref=$(git -C there symbolic-ref HEAD) && + rm "$head" && + ln -s "$ref" "$head" && + test_must_fail git -C here checkout newmaster +' + +test_expect_success 'not die the same branch is already checked out' ' + ( + cd here && + git worktree add --force anothernewmaster newmaster + ) +' + +test_expect_success 'not die on re-checking out current branch' ' + ( + cd there && + git checkout newmaster + ) +' + +test_expect_success '"add" from a bare repo' ' + ( + git clone --bare . bare && + cd bare && + git worktree add -b bare-master ../there2 master + ) +' + +test_expect_success 'checkout from a bare repo without "add"' ' + ( + cd bare && + test_must_fail git checkout master + ) +' + +test_expect_success '"add" default branch of a bare repo' ' + ( + git clone --bare . bare2 && + cd bare2 && + git worktree add ../there3 master + ) +' + +test_expect_success 'checkout with grafts' ' + test_when_finished rm .git/info/grafts && + test_commit abc && + SHA1=$(git rev-parse HEAD) && + test_commit def && + test_commit xyz && + echo "$(git rev-parse HEAD) $SHA1" >.git/info/grafts && + cat >expected <<-\EOF && + xyz + abc + EOF + git log --format=%s -2 >actual && + test_cmp expected actual && + git worktree add --detach grafted master && + git --git-dir=grafted/.git log --format=%s -2 >actual && + test_cmp expected actual +' + +test_expect_success '"add" from relative HEAD' ' + test_commit a && + test_commit b && + test_commit c && + git rev-parse HEAD~1 >expected && + git worktree add relhead HEAD~1 && + git -C relhead rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '"add -b" with omitted' ' + git worktree add -b burble flornk && + test_cmp_rev HEAD burble +' + +test_expect_success '"add --detach" with omitted' ' + git worktree add --detach fishhook && + git rev-parse HEAD >expected && + git -C fishhook rev-parse HEAD >actual && + test_cmp expected actual && + test_must_fail git -C fishhook symbolic-ref HEAD +' + +test_expect_success '"add" with omitted' ' + git worktree add wiffle/bat && + test_cmp_rev HEAD bat +' + +test_expect_success '"add" checks out existing branch of dwimd name' ' + git branch dwim HEAD~1 && + git worktree add dwim && + test_cmp_rev HEAD~1 dwim && + ( + cd dwim && + test_cmp_rev HEAD dwim + ) +' + +test_expect_success '"add " dwim fails with checked out branch' ' + git checkout -b test-branch && + test_must_fail git worktree add test-branch && + test_path_is_missing test-branch +' + +test_expect_success '"add --force" with existing dwimd name doesnt die' ' + git checkout test-branch && + git worktree add --force test-branch +' + +test_expect_success '"add" no auto-vivify with --detach and omitted' ' + git worktree add --detach mish/mash && + test_must_fail git rev-parse mash -- && + test_must_fail git -C mish/mash symbolic-ref HEAD +' + +test_expect_success '"add" -b/-B mutually exclusive' ' + test_must_fail git worktree add -b poodle -B poodle bamboo master +' + +test_expect_success '"add" -b/--detach mutually exclusive' ' + test_must_fail git worktree add -b poodle --detach bamboo master +' + +test_expect_success '"add" -B/--detach mutually exclusive' ' + test_must_fail git worktree add -B poodle --detach bamboo master +' + +test_expect_success '"add -B" fails if the branch is checked out' ' + git rev-parse newmaster >before && + test_must_fail git worktree add -B newmaster bamboo master && + git rev-parse newmaster >after && + test_cmp before after +' + +test_expect_success 'add -B' ' + git worktree add -B poodle bamboo2 master^ && + git -C bamboo2 symbolic-ref HEAD >actual && + echo refs/heads/poodle >expected && + test_cmp expected actual && + test_cmp_rev master^ poodle +' + +test_expect_success 'add --quiet' ' + git worktree add --quiet another-worktree master 2>actual && + test_must_be_empty actual +' + +test_expect_success 'local clone from linked checkout' ' + git clone --local here here-clone && + ( cd here-clone && git fsck ) +' + +test_expect_success 'local clone --shared from linked checkout' ' + git -C bare worktree add --detach ../baretree && + git clone --local --shared baretree bare-clone && + grep /bare/ bare-clone/.git/objects/info/alternates +' + +test_expect_success '"add" worktree with --no-checkout' ' + git worktree add --no-checkout -b swamp swamp && + ! test -e swamp/init.t && + git -C swamp reset --hard && + test_cmp init.t swamp/init.t +' + +test_expect_success '"add" worktree with --checkout' ' + git worktree add --checkout -b swmap2 swamp2 && + test_cmp init.t swamp2/init.t +' + +test_expect_success 'put a worktree under rebase' ' + git worktree add under-rebase && + ( + cd under-rebase && + set_fake_editor && + FAKE_LINES="edit 1" git rebase -i HEAD^ && + git worktree list | grep "under-rebase.*detached HEAD" + ) +' + +test_expect_success 'add a worktree, checking out a rebased branch' ' + test_must_fail git worktree add new-rebase under-rebase && + ! test -d new-rebase +' + +test_expect_success 'checking out a rebased branch from another worktree' ' + git worktree add new-place && + test_must_fail git -C new-place checkout under-rebase +' + +test_expect_success 'not allow to delete a branch under rebase' ' + ( + cd under-rebase && + test_must_fail git branch -D under-rebase + ) +' + +test_expect_success 'rename a branch under rebase not allowed' ' + test_must_fail git branch -M under-rebase rebase-with-new-name +' + +test_expect_success 'check out from current worktree branch ok' ' + ( + cd under-rebase && + git checkout under-rebase && + git checkout - && + git rebase --abort + ) +' + +test_expect_success 'checkout a branch under bisect' ' + git worktree add under-bisect && + ( + cd under-bisect && + git bisect start && + git bisect bad && + git bisect good HEAD~2 && + git worktree list | grep "under-bisect.*detached HEAD" && + test_must_fail git worktree add new-bisect under-bisect && + ! test -d new-bisect + ) +' + +test_expect_success 'rename a branch under bisect not allowed' ' + test_must_fail git branch -M under-bisect bisect-with-new-name +' +# Is branch "refs/heads/$1" set to pull from "$2/$3"? +test_branch_upstream () { + printf "%s\n" "$2" "refs/heads/$3" >expect.upstream && + { + git config "branch.$1.remote" && + git config "branch.$1.merge" + } >actual.upstream && + test_cmp expect.upstream actual.upstream +} + +test_expect_success '--track sets up tracking' ' + test_when_finished rm -rf track && + git worktree add --track -b track track master && + test_branch_upstream track . master +' + +# setup remote repository $1 and repository $2 with $1 set up as +# remote. The remote has two branches, master and foo. +setup_remote_repo () { + git init $1 && + ( + cd $1 && + test_commit $1_master && + git checkout -b foo && + test_commit upstream_foo + ) && + git init $2 && + ( + cd $2 && + test_commit $2_master && + git remote add $1 ../$1 && + git config remote.$1.fetch \ + "refs/heads/*:refs/remotes/$1/*" && + git fetch --all + ) +} + +test_expect_success '--no-track avoids setting up tracking' ' + test_when_finished rm -rf repo_upstream repo_local foo && + setup_remote_repo repo_upstream repo_local && + ( + cd repo_local && + git worktree add --no-track -b foo ../foo repo_upstream/foo + ) && + ( + cd foo && + test_must_fail git config "branch.foo.remote" && + test_must_fail git config "branch.foo.merge" && + test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo + ) +' + +test_expect_success '"add" fails' ' + test_must_fail git worktree add foo non-existent +' + +test_expect_success '"add" dwims' ' + test_when_finished rm -rf repo_upstream repo_dwim foo && + setup_remote_repo repo_upstream repo_dwim && + git init repo_dwim && + ( + cd repo_dwim && + git worktree add ../foo foo + ) && + ( + cd foo && + test_branch_upstream foo repo_upstream foo && + test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo + ) +' + +test_expect_success '"add" dwims with checkout.defaultRemote' ' + test_when_finished rm -rf repo_upstream repo_dwim foo && + setup_remote_repo repo_upstream repo_dwim && + git init repo_dwim && + ( + cd repo_dwim && + git remote add repo_upstream2 ../repo_upstream && + git fetch repo_upstream2 && + test_must_fail git worktree add ../foo foo && + git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo && + git status -uno --porcelain >status.actual && + test_must_be_empty status.actual + ) && + ( + cd foo && + test_branch_upstream foo repo_upstream foo && + test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo + ) +' + +test_expect_success 'git worktree add does not match remote' ' + test_when_finished rm -rf repo_a repo_b foo && + setup_remote_repo repo_a repo_b && + ( + cd repo_b && + git worktree add ../foo + ) && + ( + cd foo && + test_must_fail git config "branch.foo.remote" && + test_must_fail git config "branch.foo.merge" && + ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo + ) +' + +test_expect_success 'git worktree add --guess-remote sets up tracking' ' + test_when_finished rm -rf repo_a repo_b foo && + setup_remote_repo repo_a repo_b && + ( + cd repo_b && + git worktree add --guess-remote ../foo + ) && + ( + cd foo && + test_branch_upstream foo repo_a foo && + test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo + ) +' + +test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' ' + test_when_finished rm -rf repo_a repo_b foo && + setup_remote_repo repo_a repo_b && + ( + cd repo_b && + git config worktree.guessRemote true && + git worktree add ../foo + ) && + ( + cd foo && + test_branch_upstream foo repo_a foo && + test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo + ) +' + +test_expect_success 'git worktree --no-guess-remote option overrides config' ' + test_when_finished rm -rf repo_a repo_b foo && + setup_remote_repo repo_a repo_b && + ( + cd repo_b && + git config worktree.guessRemote true && + git worktree add --no-guess-remote ../foo + ) && + ( + cd foo && + test_must_fail git config "branch.foo.remote" && + test_must_fail git config "branch.foo.merge" && + ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo + ) +' + +post_checkout_hook () { + gitdir=${1:-.git} + test_when_finished "rm -f $gitdir/hooks/post-checkout" && + mkdir -p $gitdir/hooks && + write_script $gitdir/hooks/post-checkout <<-\EOF + { + echo $* + git rev-parse --git-dir --show-toplevel + } >hook.actual + EOF +} + +test_expect_success '"add" invokes post-checkout hook (branch)' ' + post_checkout_hook && + { + echo $ZERO_OID $(git rev-parse HEAD) 1 && + echo $(pwd)/.git/worktrees/gumby && + echo $(pwd)/gumby + } >hook.expect && + git worktree add gumby && + test_cmp hook.expect gumby/hook.actual +' + +test_expect_success '"add" invokes post-checkout hook (detached)' ' + post_checkout_hook && + { + echo $ZERO_OID $(git rev-parse HEAD) 1 && + echo $(pwd)/.git/worktrees/grumpy && + echo $(pwd)/grumpy + } >hook.expect && + git worktree add --detach grumpy && + test_cmp hook.expect grumpy/hook.actual +' + +test_expect_success '"add --no-checkout" suppresses post-checkout hook' ' + post_checkout_hook && + rm -f hook.actual && + git worktree add --no-checkout gloopy && + test_path_is_missing gloopy/hook.actual +' + +test_expect_success '"add" in other worktree invokes post-checkout hook' ' + post_checkout_hook && + { + echo $ZERO_OID $(git rev-parse HEAD) 1 && + echo $(pwd)/.git/worktrees/guppy && + echo $(pwd)/guppy + } >hook.expect && + git -C gloopy worktree add --detach ../guppy && + test_cmp hook.expect guppy/hook.actual +' + +test_expect_success '"add" in bare repo invokes post-checkout hook' ' + rm -rf bare && + git clone --bare . bare && + { + echo $ZERO_OID $(git --git-dir=bare rev-parse HEAD) 1 && + echo $(pwd)/bare/worktrees/goozy && + echo $(pwd)/goozy + } >hook.expect && + post_checkout_hook bare && + git -C bare worktree add --detach ../goozy && + test_cmp hook.expect goozy/hook.actual +' + +test_expect_success '"add" an existing but missing worktree' ' + git worktree add --detach pneu && + test_must_fail git worktree add --detach pneu && + rm -fr pneu && + test_must_fail git worktree add --detach pneu && + git worktree add --force --detach pneu +' + +test_expect_success '"add" an existing locked but missing worktree' ' + git worktree add --detach gnoo && + git worktree lock gnoo && + test_when_finished "git worktree unlock gnoo || :" && + rm -fr gnoo && + test_must_fail git worktree add --detach gnoo && + test_must_fail git worktree add --force --detach gnoo && + git worktree add --force --force --detach gnoo +' + +test_done diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh new file mode 100755 index 0000000000..b7d6d5d45a --- /dev/null +++ b/t/t2401-worktree-prune.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +test_description='prune $GIT_DIR/worktrees' + +. ./test-lib.sh + +test_expect_success initialize ' + git commit --allow-empty -m init +' + +test_expect_success 'worktree prune on normal repo' ' + git worktree prune && + test_must_fail git worktree prune abc +' + +test_expect_success 'prune files inside $GIT_DIR/worktrees' ' + mkdir .git/worktrees && + : >.git/worktrees/abc && + git worktree prune --verbose >actual && + cat >expect <.git/worktrees/def/def && + cat >expect <actual && + test_i18ncmp expect actual && + ! test -d .git/worktrees/def && + ! test -d .git/worktrees +' + +test_expect_success SANITY 'prune directories with unreadable gitdir' ' + mkdir -p .git/worktrees/def/abc && + : >.git/worktrees/def/def && + : >.git/worktrees/def/gitdir && + chmod u-r .git/worktrees/def/gitdir && + git worktree prune --verbose >actual && + test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual && + ! test -d .git/worktrees/def && + ! test -d .git/worktrees +' + +test_expect_success 'prune directories with invalid gitdir' ' + mkdir -p .git/worktrees/def/abc && + : >.git/worktrees/def/def && + : >.git/worktrees/def/gitdir && + git worktree prune --verbose >actual && + test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual && + ! test -d .git/worktrees/def && + ! test -d .git/worktrees +' + +test_expect_success 'prune directories with gitdir pointing to nowhere' ' + mkdir -p .git/worktrees/def/abc && + : >.git/worktrees/def/def && + echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir && + git worktree prune --verbose >actual && + test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual && + ! test -d .git/worktrees/def && + ! test -d .git/worktrees +' + +test_expect_success 'not prune locked checkout' ' + test_when_finished rm -r .git/worktrees && + mkdir -p .git/worktrees/ghi && + : >.git/worktrees/ghi/locked && + git worktree prune && + test -d .git/worktrees/ghi +' + +test_expect_success 'not prune recent checkouts' ' + test_when_finished rm -r .git/worktrees && + git worktree add jlm HEAD && + test -d .git/worktrees/jlm && + rm -rf jlm && + git worktree prune --verbose --expire=2.days.ago && + test -d .git/worktrees/jlm +' + +test_expect_success 'not prune proper checkouts' ' + test_when_finished rm -r .git/worktrees && + git worktree add --detach "$PWD/nop" master && + git worktree prune && + test -d .git/worktrees/nop +' + +test_done diff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh new file mode 100755 index 0000000000..bb6fb9b12c --- /dev/null +++ b/t/t2402-worktree-list.sh @@ -0,0 +1,154 @@ +#!/bin/sh + +test_description='test git worktree list' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit init +' + +test_expect_success 'rev-parse --git-common-dir on main worktree' ' + git rev-parse --git-common-dir >actual && + echo .git >expected && + test_cmp expected actual && + mkdir sub && + git -C sub rev-parse --git-common-dir >actual2 && + echo ../.git >expected2 && + test_cmp expected2 actual2 +' + +test_expect_success 'rev-parse --git-path objects linked worktree' ' + echo "$(git rev-parse --show-toplevel)/.git/objects" >expect && + test_when_finished "rm -rf linked-tree actual expect && git worktree prune" && + git worktree add --detach linked-tree master && + git -C linked-tree rev-parse --git-path objects >actual && + test_cmp expect actual +' + +test_expect_success '"list" all worktrees from main' ' + echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect && + test_when_finished "rm -rf here out actual expect && git worktree prune" && + git worktree add --detach here master && + echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect && + git worktree list >out && + sed "s/ */ /g" actual && + test_cmp expect actual +' + +test_expect_success '"list" all worktrees from linked' ' + echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect && + test_when_finished "rm -rf here out actual expect && git worktree prune" && + git worktree add --detach here master && + echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect && + git -C here worktree list >out && + sed "s/ */ /g" actual && + test_cmp expect actual +' + +test_expect_success '"list" all worktrees --porcelain' ' + echo "worktree $(git rev-parse --show-toplevel)" >expect && + echo "HEAD $(git rev-parse HEAD)" >>expect && + echo "branch $(git symbolic-ref HEAD)" >>expect && + echo >>expect && + test_when_finished "rm -rf here actual expect && git worktree prune" && + git worktree add --detach here master && + echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect && + echo "HEAD $(git rev-parse HEAD)" >>expect && + echo "detached" >>expect && + echo >>expect && + git worktree list --porcelain >actual && + test_cmp expect actual +' + +test_expect_success 'bare repo setup' ' + git init --bare bare1 && + echo "data" >file1 && + git add file1 && + git commit -m"File1: add data" && + git push bare1 master && + git reset --hard HEAD^ +' + +test_expect_success '"list" all worktrees from bare main' ' + test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" && + git -C bare1 worktree add --detach ../there master && + echo "$(pwd)/bare1 (bare)" >expect && + echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect && + git -C bare1 worktree list >out && + sed "s/ */ /g" actual && + test_cmp expect actual +' + +test_expect_success '"list" all worktrees --porcelain from bare main' ' + test_when_finished "rm -rf there actual expect && git -C bare1 worktree prune" && + git -C bare1 worktree add --detach ../there master && + echo "worktree $(pwd)/bare1" >expect && + echo "bare" >>expect && + echo >>expect && + echo "worktree $(git -C there rev-parse --show-toplevel)" >>expect && + echo "HEAD $(git -C there rev-parse HEAD)" >>expect && + echo "detached" >>expect && + echo >>expect && + git -C bare1 worktree list --porcelain >actual && + test_cmp expect actual +' + +test_expect_success '"list" all worktrees from linked with a bare main' ' + test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" && + git -C bare1 worktree add --detach ../there master && + echo "$(pwd)/bare1 (bare)" >expect && + echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect && + git -C there worktree list >out && + sed "s/ */ /g" actual && + test_cmp expect actual +' + +test_expect_success 'bare repo cleanup' ' + rm -rf bare1 +' + +test_expect_success 'broken main worktree still at the top' ' + git init broken-main && + ( + cd broken-main && + test_commit new && + git worktree add linked && + cat >expected <<-EOF && + worktree $(pwd) + HEAD $ZERO_OID + + EOF + cd linked && + echo "worktree $(pwd)" >expected && + echo "ref: .broken" >../.git/HEAD && + git worktree list --porcelain >out && + head -n 3 out >actual && + test_cmp ../expected actual && + git worktree list >out && + head -n 1 out >actual.2 && + grep -F "(error)" actual.2 + ) +' + +test_expect_success 'linked worktrees are sorted' ' + mkdir sorted && + git init sorted/main && + ( + cd sorted/main && + test_tick && + test_commit new && + git worktree add ../first && + git worktree add ../second && + git worktree list --porcelain >out && + grep ^worktree out >actual + ) && + cat >expected <<-EOF && + worktree $(pwd)/sorted/main + worktree $(pwd)/sorted/first + worktree $(pwd)/sorted/second + EOF + test_cmp expected sorted/main/actual +' + +test_done diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh new file mode 100755 index 0000000000..33c0337733 --- /dev/null +++ b/t/t2403-worktree-move.sh @@ -0,0 +1,188 @@ +#!/bin/sh + +test_description='test git worktree move, remove, lock and unlock' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit init && + git worktree add source && + git worktree list --porcelain >out && + grep "^worktree" out >actual && + cat <<-EOF >expected && + worktree $(pwd) + worktree $(pwd)/source + EOF + test_cmp expected actual +' + +test_expect_success 'lock main worktree' ' + test_must_fail git worktree lock . +' + +test_expect_success 'lock linked worktree' ' + git worktree lock --reason hahaha source && + echo hahaha >expected && + test_cmp expected .git/worktrees/source/locked +' + +test_expect_success 'lock linked worktree from another worktree' ' + rm .git/worktrees/source/locked && + git worktree add elsewhere && + git -C elsewhere worktree lock --reason hahaha ../source && + echo hahaha >expected && + test_cmp expected .git/worktrees/source/locked +' + +test_expect_success 'lock worktree twice' ' + test_must_fail git worktree lock source && + echo hahaha >expected && + test_cmp expected .git/worktrees/source/locked +' + +test_expect_success 'lock worktree twice (from the locked worktree)' ' + test_must_fail git -C source worktree lock . && + echo hahaha >expected && + test_cmp expected .git/worktrees/source/locked +' + +test_expect_success 'unlock main worktree' ' + test_must_fail git worktree unlock . +' + +test_expect_success 'unlock linked worktree' ' + git worktree unlock source && + test_path_is_missing .git/worktrees/source/locked +' + +test_expect_success 'unlock worktree twice' ' + test_must_fail git worktree unlock source && + test_path_is_missing .git/worktrees/source/locked +' + +test_expect_success 'move non-worktree' ' + mkdir abc && + test_must_fail git worktree move abc def +' + +test_expect_success 'move locked worktree' ' + git worktree lock source && + test_when_finished "git worktree unlock source" && + test_must_fail git worktree move source destination +' + +test_expect_success 'move worktree' ' + git worktree move source destination && + test_path_is_missing source && + git worktree list --porcelain >out && + grep "^worktree.*/destination$" out && + ! grep "^worktree.*/source$" out && + git -C destination log --format=%s >actual2 && + echo init >expected2 && + test_cmp expected2 actual2 +' + +test_expect_success 'move main worktree' ' + test_must_fail git worktree move . def +' + +test_expect_success 'move worktree to another dir' ' + mkdir some-dir && + git worktree move destination some-dir && + test_when_finished "git worktree move some-dir/destination destination" && + test_path_is_missing destination && + git worktree list --porcelain >out && + grep "^worktree.*/some-dir/destination$" out && + git -C some-dir/destination log --format=%s >actual2 && + echo init >expected2 && + test_cmp expected2 actual2 +' + +test_expect_success 'move locked worktree (force)' ' + test_when_finished " + git worktree unlock flump || : + git worktree remove flump || : + git worktree unlock ploof || : + git worktree remove ploof || : + " && + git worktree add --detach flump && + git worktree lock flump && + test_must_fail git worktree move flump ploof" && + test_must_fail git worktree move --force flump ploof" && + git worktree move --force --force flump ploof +' + +test_expect_success 'remove main worktree' ' + test_must_fail git worktree remove . +' + +test_expect_success 'remove locked worktree' ' + git worktree lock destination && + test_when_finished "git worktree unlock destination" && + test_must_fail git worktree remove destination +' + +test_expect_success 'remove worktree with dirty tracked file' ' + echo dirty >>destination/init.t && + test_when_finished "git -C destination checkout init.t" && + test_must_fail git worktree remove destination +' + +test_expect_success 'remove worktree with untracked file' ' + : >destination/untracked && + test_must_fail git worktree remove destination +' + +test_expect_success 'force remove worktree with untracked file' ' + git worktree remove --force destination && + test_path_is_missing destination +' + +test_expect_success 'remove missing worktree' ' + git worktree add to-be-gone && + test -d .git/worktrees/to-be-gone && + mv to-be-gone gone && + git worktree remove to-be-gone && + test_path_is_missing .git/worktrees/to-be-gone +' + +test_expect_success 'NOT remove missing-but-locked worktree' ' + git worktree add gone-but-locked && + git worktree lock gone-but-locked && + test -d .git/worktrees/gone-but-locked && + mv gone-but-locked really-gone-now && + test_must_fail git worktree remove gone-but-locked && + test_path_is_dir .git/worktrees/gone-but-locked +' + +test_expect_success 'proper error when worktree not found' ' + for i in noodle noodle/bork + do + test_must_fail git worktree lock $i 2>err && + test_i18ngrep "not a working tree" err || return 1 + done +' + +test_expect_success 'remove locked worktree (force)' ' + git worktree add --detach gumby && + test_when_finished "git worktree remove gumby || :" && + git worktree lock gumby && + test_when_finished "git worktree unlock gumby || :" && + test_must_fail git worktree remove gumby && + test_must_fail git worktree remove --force gumby && + git worktree remove --force --force gumby +' + +test_expect_success 'remove cleans up .git/worktrees when empty' ' + git init moog && + ( + cd moog && + test_commit bim && + git worktree add --detach goom && + test_path_exists .git/worktrees && + git worktree remove goom && + test_path_is_missing .git/worktrees + ) +' + +test_done diff --git a/t/t2404-worktree-config.sh b/t/t2404-worktree-config.sh new file mode 100755 index 0000000000..286121d8de --- /dev/null +++ b/t/t2404-worktree-config.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +test_description="config file in multi worktree" + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit start +' + +test_expect_success 'config --worktree in single worktree' ' + git config --worktree foo.bar true && + test_cmp_config true foo.bar +' + +test_expect_success 'add worktrees' ' + git worktree add wt1 && + git worktree add wt2 +' + +test_expect_success 'config --worktree without extension' ' + test_must_fail git config --worktree foo.bar false +' + +test_expect_success 'enable worktreeConfig extension' ' + git config extensions.worktreeConfig true && + test_cmp_config true extensions.worktreeConfig +' + +test_expect_success 'config is shared as before' ' + git config this.is shared && + test_cmp_config shared this.is && + test_cmp_config -C wt1 shared this.is && + test_cmp_config -C wt2 shared this.is +' + +test_expect_success 'config is shared (set from another worktree)' ' + git -C wt1 config that.is also-shared && + test_cmp_config also-shared that.is && + test_cmp_config -C wt1 also-shared that.is && + test_cmp_config -C wt2 also-shared that.is +' + +test_expect_success 'config private to main worktree' ' + git config --worktree this.is for-main && + test_cmp_config for-main this.is && + test_cmp_config -C wt1 shared this.is && + test_cmp_config -C wt2 shared this.is +' + +test_expect_success 'config private to linked worktree' ' + git -C wt1 config --worktree this.is for-wt1 && + test_cmp_config for-main this.is && + test_cmp_config -C wt1 for-wt1 this.is && + test_cmp_config -C wt2 shared this.is +' + +test_expect_success 'core.bare no longer for main only' ' + test_config core.bare true && + test "$(git rev-parse --is-bare-repository)" = true && + test "$(git -C wt1 rev-parse --is-bare-repository)" = true && + test "$(git -C wt2 rev-parse --is-bare-repository)" = true +' + +test_expect_success 'per-worktree core.bare is picked up' ' + git -C wt1 config --worktree core.bare true && + test "$(git rev-parse --is-bare-repository)" = false && + test "$(git -C wt1 rev-parse --is-bare-repository)" = true && + test "$(git -C wt2 rev-parse --is-bare-repository)" = false +' + +test_expect_success 'config.worktree no longer read without extension' ' + git config --unset extensions.worktreeConfig && + test_cmp_config shared this.is && + test_cmp_config -C wt1 shared this.is && + test_cmp_config -C wt2 shared this.is +' + +test_done -- cgit v1.2.3