Skip to content

Instantly share code, notes, and snippets.

@gadenbuie
Last active January 22, 2026 14:25
Show Gist options
  • Select an option

  • Save gadenbuie/6a9d1b8088f6bc9154b6c534896bbd25 to your computer and use it in GitHub Desktop.

Select an option

Save gadenbuie/6a9d1b8088f6bc9154b6c534896bbd25 to your computer and use it in GitHub Desktop.
tiny bash script to quickly spin up a new worktree and open a new ide session
#!/usr/bin/env bash
set -e
help() {
echo "Usage: create-worktree <branch-name> [--base <base-branch>] [--open [auto|false|<cmd>]]"
echo ""
echo "Creates a git worktree with a new branch."
echo ""
echo "Options:"
echo " --base <branch> Base branch to create from (default: current branch)"
echo " --open [mode] How to open the worktree folder:"
echo " auto - detect editor from environment (default)"
echo " false - don't open"
echo " <cmd> - use specified command (e.g., 'code', 'cursor')"
echo " (bare) - use 'open .' (Finder on macOS)"
echo " --no-setup Skip running setup commands (npm, uv, make)"
echo " -h, --help Show this help message"
echo ""
echo "To delete a worktree later:"
echo " git worktree remove <path> # removes worktree and branch"
echo " git worktree list # list all worktrees"
}
auto_detect_editor() {
# Check for Positron first, since it is based on VS Code
if [[ "${POSITRON:-}" == "1" ]]; then
echo "positron"
return
fi
# Check for Cursor (VS Code fork) - has its own env vars
if [[ -n "${CURSOR_TRACE_ID:-}" || -n "${CURSOR_SESSION_ID:-}" ]]; then
echo "cursor"
return
fi
# Check for Windsurf (Codeium's VS Code fork)
if [[ -n "${WINDSURF:-}" || -n "${CODEIUM_WIND_SURF:-}" ]]; then
echo "windsurf"
return
fi
# Check for Zed
if [[ -n "${ZED_SESSION:-}" || "${TERM_PROGRAM:-}" == "zed" ]]; then
echo "zed"
return
fi
# Check TERM_PROGRAM (set by many terminal-integrated editors)
# Note: Cursor/Windsurf may report as "vscode", so check them first above
case "${TERM_PROGRAM:-}" in
vscode)
# Could be VS Code or a fork - check for Insiders
if [[ "${VSCODE_GIT_IPC_HANDLE:-}" == *"Code - Insiders"* ]]; then
echo "code-insiders"
else
echo "code"
fi
return
;;
esac
# Fallback: check for VS Code via other env vars
if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_PID:-}" ]]; then
if [[ "${VSCODE_GIT_IPC_HANDLE:-}" == *"Code - Insiders"* ]]; then
echo "code-insiders"
else
echo "code"
fi
return
fi
# No editor detected
echo ""
}
# Parse arguments
BRANCH_NAME=""
BASE_BRANCH=""
OPEN_MODE="auto"
RUN_SETUP=true
while [[ $# -gt 0 ]]; do
case $1 in
--base)
BASE_BRANCH="$2"
shift 2
;;
--no-setup)
RUN_SETUP=false
shift
;;
--open)
# Check if next arg exists and is not a flag
if [[ -z "${2:-}" || "$2" == -* ]]; then
# Bare --open flag
OPEN_MODE="finder"
shift
else
OPEN_MODE="$2"
shift 2
fi
;;
-h|--help)
help
exit 0
;;
-*)
echo "Unknown option: $1"
echo ""
help
exit 1
;;
*)
if [[ -z "$BRANCH_NAME" ]]; then
BRANCH_NAME="$1"
else
echo "Unexpected argument: $1"
echo ""
help
exit 1
fi
shift
;;
esac
done
if [[ -z "$BRANCH_NAME" ]]; then
help
exit 1
fi
# Get repo root and name
REPO_ROOT=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename "$REPO_ROOT")
# Default base branch to current branch
if [[ -z "$BASE_BRANCH" ]]; then
BASE_BRANCH=$(git branch --show-current)
fi
# Calculate worktree path
WORKTREE_DIR="$(dirname "$REPO_ROOT")/${REPO_NAME}.worktrees/${BRANCH_NAME}"
# Create the branch from base (if it doesn't already exist)
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
echo "Branch '$BRANCH_NAME' already exists, using existing branch..."
else
echo "Creating branch '$BRANCH_NAME' from '$BASE_BRANCH'..."
git branch "$BRANCH_NAME" "$BASE_BRANCH"
fi
# Create the worktree
echo "Creating worktree at '$WORKTREE_DIR'..."
mkdir -p "$(dirname "$WORKTREE_DIR")"
git worktree add "$WORKTREE_DIR" "$BRANCH_NAME"
# Symlink _dev if it exists
if [[ -d "$REPO_ROOT/_dev" ]]; then
echo "Creating symlink to _dev folder..."
ln -s "$REPO_ROOT/_dev" "$WORKTREE_DIR/_dev"
fi
# Symlink .claude if it exists in the source repo
if [[ -d "$REPO_ROOT/.claude" ]]; then
if [[ ! -d "$WORKTREE_DIR/.claude" ]]; then
# .claude folder doesn't exist in worktree, symlink the entire folder
echo "Creating symlink to .claude folder..."
ln -s "$REPO_ROOT/.claude" "$WORKTREE_DIR/.claude"
else
# .claude exists in worktree, check for settings.local.json
if [[ -f "$REPO_ROOT/.claude/settings.local.json" ]] && [[ ! -f "$WORKTREE_DIR/.claude/settings.local.json" ]]; then
echo "Creating symlink to .claude/settings.local.json..."
ln -s "$REPO_ROOT/.claude/settings.local.json" "$WORKTREE_DIR/.claude/settings.local.json"
fi
fi
fi
echo "Worktree created at: $WORKTREE_DIR"
# Change to worktree directory
cd "$WORKTREE_DIR"
# Run setup commands if applicable
if [[ "$RUN_SETUP" == true ]]; then
if [[ -f "package.json" ]] && command -v npm &> /dev/null; then
echo "Running npm install..."
npm install
fi
if [[ -f "pyproject.toml" ]] && command -v uv &> /dev/null; then
echo "Running uv sync --all-groups..."
uv sync --all-groups
fi
if [[ -f "Makefile" ]] && grep -q '^setup:' Makefile; then
echo "Running make setup..."
make setup
fi
fi
# Determine open command
OPEN_CMD=""
case "$OPEN_MODE" in
false)
# Don't open
;;
auto)
EDITOR=$(auto_detect_editor)
if [[ -n "$EDITOR" ]]; then
OPEN_CMD="$EDITOR ."
fi
;;
finder)
OPEN_CMD="open ."
;;
*)
# Custom command
OPEN_CMD="$OPEN_MODE ."
;;
esac
if [[ -n "$OPEN_CMD" ]]; then
echo "Opening with: $OPEN_CMD"
$OPEN_CMD
fi
echo ""
echo "To enter the worktree, run:"
echo " cd \"$WORKTREE_DIR\""
echo ""
echo "To delete this worktree later:"
echo " git worktree remove \"$WORKTREE_DIR\""
@gadenbuie
Copy link
Author

gadenbuie commented Jan 22, 2026

Installation

# Download the script                                                                       
curl -o ~/.local/bin/create-worktree https://gist.githubusercontent.com/gadenbuie/6a9d1b8088
f6bc9154b6c534896bbd25/raw/create-worktree

# Make it executable
chmod +x ~/.local/bin/create-worktree

Make sure ~/.local/bin is in your PATH. Add this to your shell profile if needed:

export PATH="$HOME/.local/bin:$PATH"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment