convenience shell functions for git worktrees
If you like working with git worktrees, then you may like these following functions. They allow you to create, delete and checkout worktrees (also from upstream) from anywhere within the bare repository.
The following functions assume that standard git worktree structure (as far as I’m aware):
.
├── .git
├── worktree_1
├── worktree_2
└── worktree_n
As a bonus, here’s a function to clone a repo and setup this structure (assumes there is a dev and main branch in the remote):
gwsetup() {
if [[ -z "$2" ]]; then
echo "Args required: gwsetup <url> <project-name>"
return
fi
repo=$1
project=$2
mkdir $project
cd $project
git clone --bare $repo .git
mkdir dev
mkdir main
git worktree add ./dev dev
git worktree add ./main main
}
What allows them to be executed from anywhere is the following function, which
recursively moves upwards in the directory structure until it finds the .git
directory of the bare repository. Put this in your .zshrc or wherever you’re
setting functions:
get_git_root() {
local p=$PWD
while [ $p != "/" ]; do
if [[ -d "$p/.git" ]]; then
git_root=$p
break
fi
p=${p:h}
done
echo $git_root
}
And then in the same file, place these functions. They need to be in the same
file, because they call the get_git_root() function.
The below functions can be run from anywhere at or deeper than the bare repository root (where the
.git directory is). When applicable, the functions use fzf to provide selection options.
So you need fzf installed:
# create a new worktree
gwn() {
local wt=$1
git_root=$(get_git_root)
git worktree add "$git_root/$wt" -b $wt
cd $git_root && cd "$wt"
}
# remove a git worktree
gwr() {
git_root=$(get_git_root)
local worktrees=$(git worktree list --porcelain | grep worktree | sed 's/worktree //') && wt=$(echo "$worktrees" | fzf)
for w in $wt; do
# Get the branch associated with the worktree
branch=$(git -C "$w" rev-parse --abbrev-ref HEAD)
# Remove the worktree
git worktree remove "$@" "$w"
# Delete the branch (local only)
if [[ "$branch" != "HEAD" && "$branch" != "main" && "$branch" != "master" ]]; then
git branch -D "$branch"
else
echo "Not deleting branch: $branch"
fi
done
}
# checkout a branch as a worktree
gwc() {
git_root=$(get_git_root)
local branches branch
branches=$(git branch -a) && branch=$(echo "$branches" | fzf --tac +s +m -e | sed "s/^[[:space:]]*//" | sed "s/^remotes\/[a-A]*\///")
if [[ -z $branch ]]; then
return
fi
git worktree add "$git_root/$branch" $branch
cd $git_root && cd "$branch"
}
# remove a git worktree
gwr() {
git_root=$(get_git_root)
local worktrees=$(git worktree list --porcelain | grep worktree | sed 's/worktree //') && wt=$(echo "$worktrees" | fzf)
for w in $wt; do
# Get the branch associated with the worktree
branch=$(git -C "$w" rev-parse --abbrev-ref HEAD)
# Remove the worktree
git worktree remove "$@" "$w"
# Delete the branch (local only)
if [[ "$branch" != "HEAD" && "$branch" != "main" && "$branch" != "master" ]]; then
git branch -D "$branch"
else
echo "Not deleting branch: $branch"
fi
done
}