diff options
Diffstat (limited to '')
-rwxr-xr-x | t/t2018-checkout-branch.sh | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh new file mode 100755 index 0000000..771c3c3 --- /dev/null +++ b/t/t2018-checkout-branch.sh @@ -0,0 +1,288 @@ +#!/bin/sh + +test_description='checkout' + +TEST_CREATE_REPO_NO_TEMPLATE=1 +. ./test-lib.sh + +# Arguments: [!] <branch> <oid> [<checkout options>] +# +# Runs "git checkout" to switch to <branch>, testing that +# +# 1) we are on the specified branch, <branch>; +# 2) HEAD is <oid>; if <oid> is not specified, the old HEAD is used. +# +# If <checkout options> is not specified, "git checkout" is run with -b. +# +# If the first argument is `!`, "git checkout" is expected to fail when +# it is run. +do_checkout () { + should_fail= && + if test "x$1" = "x!" + then + should_fail=yes && + shift + fi && + exp_branch=$1 && + exp_ref="refs/heads/$exp_branch" && + + # if <oid> is not specified, use HEAD. + exp_oid=${2:-$(git rev-parse --verify HEAD)} && + + # default options for git checkout: -b + if test -z "$3" + then + opts="-b" + else + opts="$3" + fi + + if test -n "$should_fail" + then + test_must_fail git checkout $opts $exp_branch $exp_oid + else + git checkout $opts $exp_branch $exp_oid && + echo "$exp_ref" >ref.expect && + git rev-parse --symbolic-full-name HEAD >ref.actual && + test_cmp ref.expect ref.actual && + echo "$exp_oid" >oid.expect && + git rev-parse --verify HEAD >oid.actual && + test_cmp oid.expect oid.actual + fi +} + +test_dirty_unmergeable () { + test_expect_code 1 git diff --exit-code +} + +test_dirty_unmergeable_discards_changes () { + git diff --exit-code +} + +setup_dirty_unmergeable () { + echo >>file1 change2 +} + +test_dirty_mergeable () { + test_expect_code 1 git diff --cached --exit-code +} + +test_dirty_mergeable_discards_changes () { + git diff --cached --exit-code +} + +setup_dirty_mergeable () { + echo >file2 file2 && + git add file2 +} + +test_expect_success 'setup' ' + test_commit initial file1 && + HEAD1=$(git rev-parse --verify HEAD) && + + test_commit change1 file1 && + HEAD2=$(git rev-parse --verify HEAD) && + + git branch -m branch1 +' + +test_expect_success 'checkout a branch without refs/heads/* prefix' ' + git clone --no-tags . repo-odd-prefix && + ( + cd repo-odd-prefix && + + origin=$(git symbolic-ref refs/remotes/origin/HEAD) && + git symbolic-ref refs/heads/a-branch "$origin" && + + git checkout -f a-branch && + git checkout -f a-branch + ) +' + +test_expect_success 'checkout -b to a new branch, set to HEAD' ' + test_when_finished " + git checkout branch1 && + test_might_fail git branch -D branch2" && + do_checkout branch2 +' + +test_expect_success 'checkout -b to a merge base' ' + test_when_finished " + git checkout branch1 && + test_might_fail git branch -D branch2" && + git checkout -b branch2 branch1... +' + +test_expect_success 'checkout -b to a new branch, set to an explicit ref' ' + test_when_finished " + git checkout branch1 && + test_might_fail git branch -D branch2" && + do_checkout branch2 $HEAD1 +' + +test_expect_success 'checkout -b to a new branch with unmergeable changes fails' ' + setup_dirty_unmergeable && + do_checkout ! branch2 $HEAD1 && + test_dirty_unmergeable +' + +test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' ' + test_when_finished " + git checkout branch1 && + test_might_fail git branch -D branch2" && + + # still dirty and on branch1 + do_checkout branch2 $HEAD1 "-f -b" && + test_dirty_unmergeable_discards_changes +' + +test_expect_success 'checkout -b to a new branch preserves mergeable changes' ' + test_when_finished " + git reset --hard && + git checkout branch1 && + test_might_fail git branch -D branch2" && + + setup_dirty_mergeable && + do_checkout branch2 $HEAD1 && + test_dirty_mergeable +' + +test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' ' + test_when_finished git reset --hard HEAD && + setup_dirty_mergeable && + do_checkout branch2 $HEAD1 "-f -b" && + test_dirty_mergeable_discards_changes +' + +test_expect_success 'checkout -b to an existing branch fails' ' + test_when_finished git reset --hard HEAD && + do_checkout ! branch2 $HEAD2 +' + +test_expect_success 'checkout -b to @{-1} fails with the right branch name' ' + git checkout branch1 && + git checkout branch2 && + echo >expect "fatal: a branch named '\''branch1'\'' already exists" && + test_must_fail git checkout -b @{-1} 2>actual && + test_cmp expect actual +' + +test_expect_success 'checkout -B to an existing branch resets branch to HEAD' ' + git checkout branch1 && + + do_checkout branch2 "" -B +' + +test_expect_success 'checkout -B to a merge base' ' + git checkout branch1 && + + git checkout -B branch2 branch1... +' + +test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' ' + head=$(git rev-parse --verify HEAD) && + git checkout "$head" && + + do_checkout branch2 "" -B +' + +test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' ' + git checkout branch1 && + + do_checkout branch2 $HEAD1 -B +' + +test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' ' + git checkout branch1 && + + setup_dirty_unmergeable && + do_checkout ! branch2 $HEAD1 -B && + test_dirty_unmergeable +' + +test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' ' + # still dirty and on branch1 + do_checkout branch2 $HEAD1 "-f -B" && + test_dirty_unmergeable_discards_changes +' + +test_expect_success 'checkout -B to an existing branch preserves mergeable changes' ' + test_when_finished git reset --hard && + git checkout branch1 && + + setup_dirty_mergeable && + do_checkout branch2 $HEAD1 -B && + test_dirty_mergeable +' + +test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' ' + git checkout branch1 && + + setup_dirty_mergeable && + do_checkout branch2 $HEAD1 "-f -B" && + test_dirty_mergeable_discards_changes +' + +test_expect_success 'checkout -b <describe>' ' + git tag -f -m "First commit" initial initial && + git checkout -f change1 && + name=$(git describe) && + git checkout -b $name && + git diff --exit-code change1 && + echo "refs/heads/$name" >expect && + git symbolic-ref HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'checkout -B to the current branch works' ' + git checkout branch1 && + git checkout -B branch1-scratch && + + setup_dirty_mergeable && + git checkout -B branch1-scratch initial && + test_dirty_mergeable +' + +test_expect_success 'checkout -b after clone --no-checkout does a checkout of HEAD' ' + git init src && + test_commit -C src a && + rev="$(git -C src rev-parse HEAD)" && + git clone --no-checkout src dest && + git -C dest checkout "$rev" -b branch && + test_path_is_file dest/a.t +' + +test_expect_success 'checkout -b to a new branch preserves mergeable changes despite sparse-checkout' ' + test_when_finished " + git reset --hard && + git checkout branch1-scratch && + test_might_fail git branch -D branch3 && + git config core.sparseCheckout false && + rm -rf .git/info" && + + test_commit file2 && + + echo stuff >>file1 && + mkdir .git/info && + echo file2 >.git/info/sparse-checkout && + git config core.sparseCheckout true && + + CURHEAD=$(git rev-parse HEAD) && + do_checkout branch3 $CURHEAD && + + echo file1 >expect && + git diff --name-only >actual && + test_cmp expect actual +' + +test_expect_success 'checkout -b rejects an invalid start point' ' + test_must_fail git checkout -b branch4 file1 2>err && + test_i18ngrep "is not a commit" err +' + +test_expect_success 'checkout -b rejects an extra path argument' ' + test_must_fail git checkout -b branch5 branch1 file1 2>err && + test_i18ngrep "Cannot update paths and switch to branch" err +' + +test_done |