Skip to content

Instantly share code, notes, and snippets.

@MartinSadovy
Created March 3, 2026 09:57
Show Gist options
  • Select an option

  • Save MartinSadovy/40852c48c9851650aacabd78237b551f to your computer and use it in GitHub Desktop.

Select an option

Save MartinSadovy/40852c48c9851650aacabd78237b551f to your computer and use it in GitHub Desktop.
git wcheckout - works like checkout with support checkouting from git worktree to working directory with stashing and unstashing

git wcheckout

Worktree-aware git checkout. Works identically to git checkout <branch>, but if the target branch is checked out in another worktree, it automatically:

  1. Stashes uncommitted changes in that worktree
  2. Removes the worktree
  3. Checks out the branch in the current working tree
  4. Restores the stashed changes

Installation

  1. Save the script to ~/bin/git-wcheckout.bash:
mkdir -p ~/bin
curl -o ~/bin/git-wcheckout.bash <SCRIPT_URL>
chmod +x ~/bin/git-wcheckout.bash
  1. Add a git alias to ~/.gitconfig:
[alias]
    wcheckout = !/Users/<USERNAME>/bin/git-wcheckout.bash

Note: git config escapes !, so edit ~/.gitconfig manually rather than using git config --global alias.wcheckout.

Usage

git wcheckout <branch>          # switch to branch, even if it's in a worktree
git wcheckout feature-x --force # supports additional checkout arguments
#!/usr/bin/env bash
#
# git-wcheckout - checkout that gracefully handles branches in worktrees
#
# If the target branch is checked out in a worktree:
# 1. Stash any uncommitted changes in that worktree
# 2. Remove the worktree
# 3. Checkout the branch in the current working tree
#
# Usage: git wcheckout <branch> [checkout-args...]
set -euo pipefail
if [ $# -eq 0 ]; then
echo "Usage: git wcheckout <branch> [checkout-args...]"
exit 1
fi
branch="$1"
shift
extra_args=("$@")
# Find if this branch is checked out in a worktree (not the main working tree)
main_worktree=""
target_worktree=""
while IFS= read -r line; do
case "$line" in
"worktree "*)
current_wt="${line#worktree }"
is_bare=false
current_branch=""
;;
"branch "*)
current_branch="${line#branch refs/heads/}"
;;
"bare")
is_bare=true
;;
"")
if [ -z "$main_worktree" ] && [ "$is_bare" = false ]; then
main_worktree="$current_wt"
elif [ "$current_branch" = "$branch" ] && [ "$current_wt" != "$main_worktree" ]; then
target_worktree="$current_wt"
fi
current_wt=""
current_branch=""
;;
esac
done < <(git worktree list --porcelain && echo "")
if [ -n "$target_worktree" ]; then
echo "Branch '$branch' is checked out in worktree: $target_worktree"
# Stash uncommitted changes in that worktree (if any)
stash_output=$(git -C "$target_worktree" stash 2>&1)
if echo "$stash_output" | grep -q "Saved working directory"; then
echo " → Stashed changes in worktree"
had_stash=true
else
echo " → No changes to stash"
had_stash=false
fi
# Remove the worktree
if git worktree remove "$target_worktree" 2>/dev/null; then
echo " → Removed worktree: $target_worktree"
else
echo " → Worktree has modifications, force-removing..."
git worktree remove --force "$target_worktree"
echo " → Force-removed worktree: $target_worktree"
fi
fi
# Checkout the branch in the current working tree
git checkout "$branch" "${extra_args[@]+"${extra_args[@]}"}"
# Pop the stash if we stashed changes from the worktree
if [ "${had_stash:-false}" = true ]; then
echo " → Restoring stashed changes..."
git stash pop
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment