Generally, to create a branch and switch to it, here's the command:
$ git checkout -b <branch> [<start-point>]
If you want to create <branch>
and made <remote>/<branch>
its starting point and upstream though, you can omit the -b <branch>
part:
$ git checkout -t <remote>/<branch>
This also works with branches that has /
in their names (e.g. git checkout -t origin/feature/a
).
This can be further reduced to:
$ git checkout <branch>
if the branch doesn't exist locally and there's exactly one remote where a branch with this name exists.
Under these last conditions the following commands are equivalent:
$ git checkout <branch>
$ git checkout -t <remote>/<branch>
$ git checkout -b <branch> -t <remote>/<branch>
a.bats
:
strict() { set -euo pipefail; shopt -s inherit_errexit; "$@"; }
setup() {
[ "$BATS_LIB_PATH" = /usr/lib/bats ] && BATS_LIB_PATH=$HOME/.bats/lib:$BATS_LIB_PATH
bats_load_library bats-support
bats_load_library bats-assert
strict
}
# the state of the repo (3 commits, 3 local branches ba/b, bc, bd):
# ca/b (ba/b) --- cc (bc) --- cd (HEAD -> bd)
#
# the state of the cloned repo (3 commits, 1 local branch bd):
# ca/b (origin/ba/b) --- cc (origin/bc) --- cd (HEAD -> bd, origin/bd)
@test "\`git checkout -b <branch>\` creates a branch pointing to HEAD with no upstream" {
mkrepo
git checkout -b be
assert_branch be
assert_equal_refs be bd
refute_upstream be
}
@test "\`git checkout -b <branch> <start-point>\` creates a branch pointing to <start-point>" {
mkrepo
git checkout -b be bc
assert_branch be
assert_equal_refs be bc
refute_upstream be
}
@test "\`git checkout -t <remote>/<branch>\` creates a branch <branch> pointing to <remote>/<branch> with the corresponding upstream" {
mk_cloned_repo
git checkout -t origin/bc
assert_branch bc
assert_equal_refs bc origin/bc
assert_upstream bc origin/bc
}
@test "\`git checkout -t <remote>/<branch>\` handles branch names with /" {
mk_cloned_repo
git checkout -t origin/ba/b
assert_branch ba/b
assert_equal_refs ba/b origin/ba/b
assert_upstream ba/b origin/ba/b
}
@test "\`git checkout <branch>\` fails if <branch> doesn't exist and there're no branches with the matching name in the remotes" {
mk_cloned_repo
run git checkout be
assert_equal "$status" 1
assert_output "error: pathspec 'be' did not match any file(s) known to git"
}
@test "\`git checkout <branch>\` fails if <branch> doesn't exist and there're more than one branch with the matching name in the remotes" {
mk_cloned_repo
git remote add origin2 ../a
git fetch origin2
run git checkout bc
assert_equal "$status" 128
assert_output -p "fatal: 'bc' matched multiple (2) remote tracking branches"
}
@test "\`git checkout <branch>\` creates a branch, pointing to <remote>/<branch> with the corresponding upstream" {
mk_cloned_repo
git checkout bc
assert_branch bc
assert_equal_refs bc origin/bc
assert_upstream bc origin/bc
}
assert_branch() {
run git branch --list "$1"
assert_output -p "$1"
}
assert_equal_refs() {
assert_equal "`git rev-parse "$1"`" "`git rev-parse "$2"`"
}
assert_upstream() {
run git rev-parse --abbrev-ref "$1@{upstream}"
assert_output "$2"
}
refute_upstream() {
run git rev-parse --abbrev-ref "$1@{upstream}"
if [ "${2-}" ]; then
refute_output "$2"
else
assert_output "fatal: no upstream configured for branch '$1'"
fi
}
mk_cloned_repo() {
(mkrepo)
cd "$BATS_TEST_TMPDIR"
git clone a b
cd b
}
mkrepo() {
cd "$BATS_TEST_TMPDIR"
mkdir a
cd a
git init
git branch -m ba/b
git config user.email [email protected]
git config user.name "Your Name"
git commit --allow-empty -m ca/b
git checkout -b bc
git commit --allow-empty -m cc
git checkout -b bd
git commit --allow-empty -m cd
}
$ docker run --rm -itv "$PWD":/app -w /app alpine:3.21
/ # apk add git bash ncurses
/ # git clone https://github.com/bats-core/bats-core ~/.bats
/ # git clone https://github.com/bats-core/bats-support ~/.bats/lib/bats-support
/ # git clone https://github.com/bats-core/bats-assert ~/.bats/lib/bats-assert
/ # ~/.bats/bin/bats a.bats