Skip to content

Instantly share code, notes, and snippets.

@FranDepascuali
Last active January 17, 2026 05:22
Show Gist options
  • Select an option

  • Save FranDepascuali/c0b4912dc458439326dc450ebd87c4b9 to your computer and use it in GitHub Desktop.

Select an option

Save FranDepascuali/c0b4912dc458439326dc450ebd87c4b9 to your computer and use it in GitHub Desktop.
#!/bin/bash
# ============================================================================
# Git Worktree Workflow for AI Agents
# ============================================================================
#
# Commands:
# wa <branch> [base] Create worktree + branch, cd into it
# wd [-f] Delete current worktree and branch
# wm <branch> Rebase branch onto current, merge, delete worktree
# wc <branch> Switch to a worktree by branch name
# wl List all worktrees
# ws Status overview
# wp Prune stale worktree references
#
# Example workflow:
# wa claude-refactor # create worktree from current branch
# # ... AI does work, makes commits ...
# wc main # switch back to main
# wm claude-refactor # rebase, merge, cleanup
#
# Install:
# Add to ~/.zshrc or ~/.bashrc:
# source ~/path/to/agent-worktree.sh
#
# ============================================================================
# Create a new worktree and branch for an AI agent to work in
# Usage: wa <branch-name> [base-branch]
wa() {
if [[ -z "$1" ]]; then
echo "Usage: wa <branch-name> [base-branch]"
echo " branch-name: name for the new branch"
echo " base-branch: branch to base off of (default: current branch)"
return 1
fi
local branch="$1"
local base_branch="${2:-HEAD}"
local repo_name="$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")"
local worktree_path="../${repo_name}--${branch}"
# Check if we're in a git repo
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "Error: Not in a git repository"
return 1
fi
# Check if branch already exists
if git show-ref --verify --quiet "refs/heads/$branch"; then
echo "Error: Branch '$branch' already exists"
return 1
fi
# Create the worktree with a new branch
git worktree add -b "$branch" "$worktree_path" "$base_branch"
# Trust mise/asdf/direnv if available
command -v mise > /dev/null && mise trust "$worktree_path" 2>/dev/null
command -v direnv > /dev/null && direnv allow "$worktree_path" 2>/dev/null
# Copy over any local env files that might be needed
[[ -f .env ]] && cp .env "$worktree_path/"
[[ -f .env.local ]] && cp .env.local "$worktree_path/"
echo ""
echo "✓ Worktree created at: $worktree_path"
echo "✓ Branch: $branch"
echo ""
echo "To enter: cd $worktree_path"
echo "To delete: wd (from within the worktree)"
# Optionally cd into it
cd "$worktree_path"
}
# Delete current worktree and its branch
# Usage: wd [--force]
wd() {
local force=false
[[ "$1" == "--force" || "$1" == "-f" ]] && force=true
local cwd="$(pwd)"
local worktree_dir="$(basename "$cwd")"
# Check if this looks like a worktree (contains --)
if [[ "$worktree_dir" != *"--"* ]]; then
echo "Error: This doesn't look like a worktree directory"
echo "Expected format: repo-name--branch-name"
return 1
fi
# Parse the directory name
local repo_name="${worktree_dir%%--*}"
local branch="${worktree_dir#*--}"
local main_repo="../$repo_name"
# Verify the main repo exists
if [[ ! -d "$main_repo/.git" && ! -f "$main_repo/.git" ]]; then
echo "Error: Cannot find main repository at $main_repo"
return 1
fi
# Confirm deletion unless --force
if [[ "$force" != true ]]; then
echo "This will remove:"
echo " • Worktree: $cwd"
echo " • Branch: $branch"
echo ""
echo -n "Continue? [y/N] "
read -r REPLY
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled"
return 0
fi
fi
# Go to main repo and clean up
cd "$main_repo"
git worktree remove "$worktree_dir" --force
git branch -D "$branch"
echo ""
echo "✓ Removed worktree and branch: $branch"
}
# List all worktrees
# Usage: wl
wl() {
git worktree list --porcelain | awk '/^worktree /{path=$2} /^branch /{gsub("refs/heads/","",$2); print " " $2 "\t" path}'
}
# Quick status of all worktrees
# Usage: ws
ws() {
echo "=== Worktrees ==="
git worktree list
echo ""
echo "=== Branches with worktrees ==="
git worktree list --porcelain | grep "^branch" | sed 's/branch refs\/heads\// /'
}
# Prune stale worktree references
# Usage: wp
wp() {
git worktree prune -v
}
# Finish worktree: rebase onto current branch, merge, and delete worktree
# Usage: wm <branch>
wm() {
if [[ -z "$1" ]]; then
echo "Usage: wm <branch>"
echo ""
echo "Available worktrees:"
git worktree list --porcelain | awk '/^worktree /{path=$2} /^branch /{gsub("refs/heads/","",$2); print " " $2 "\t" path}'
return 1
fi
local branch="$1"
local target="$(git branch --show-current)"
# Find the worktree path for this branch
local worktree_path
worktree_path=$(git worktree list --porcelain | grep -B2 "branch refs/heads/$branch$" | grep "^worktree " | sed 's/^worktree //')
if [[ -z "$worktree_path" ]]; then
echo "Error: No worktree found for branch '$branch'"
return 1
fi
# Check for uncommitted changes in the worktree
if ! git -C "$worktree_path" diff-index --quiet HEAD -- 2>/dev/null; then
echo "Error: Worktree has uncommitted changes. Commit or stash them first."
return 1
fi
echo "This will:"
echo " 1. Rebase '$branch' onto '$target'"
echo " 2. Fast-forward '$target' with commits from '$branch'"
echo " 3. Delete worktree and branch"
echo ""
echo -n "Continue? [y/N] "
read -r REPLY
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled"
return 0
fi
# Rebase the worktree branch onto current branch
git -C "$worktree_path" rebase "$target"
if [[ $? -ne 0 ]]; then
echo "Error: Rebase failed. Resolve conflicts in $worktree_path and try again."
return 1
fi
# Fast-forward merge
git merge --ff-only "$branch"
# Clean up worktree and branch
local worktree_dir="$(basename "$worktree_path")"
git worktree remove "$worktree_dir" --force
git branch -D "$branch"
echo ""
echo "✓ Merged '$branch' onto '$target' and cleaned up"
}
# Switch to a worktree by branch name
# Usage: wc <branch-name>
wc() {
if [[ -z "$1" ]]; then
echo "Usage: wc <branch-name>"
echo ""
echo "Available worktrees:"
git worktree list --porcelain | awk '/^worktree /{path=$2} /^branch /{gsub("refs/heads/","",$2); print " " $2 "\t" path}'
return 1
fi
local branch="$1"
local worktree_path
# Find the worktree path for this branch
worktree_path=$(git worktree list --porcelain | grep -B2 "branch refs/heads/$branch$" | grep "^worktree " | sed 's/^worktree //')
if [[ -z "$worktree_path" ]]; then
echo "Error: No worktree found for branch '$branch'"
echo ""
echo "Available worktrees:"
git worktree list --porcelain | awk '/^worktree /{path=$2} /^branch /{gsub("refs/heads/","",$2); print " " $2 "\t" path}'
return 1
fi
cd "$worktree_path"
}
@FranDepascuali
Copy link
Author

Install it with:

curl -o ~/.agent-worktree.sh https://gist.githubusercontent.com/FranDepascuali/c0b4912dc458439326dc450ebd87c4b9/raw/agent-worktree.sh

# Add to shell config
echo 'source ~/.agent-worktree.sh' >> ~/.zshrc  # or ~/.bashrc

# Reload
source ~/.zshrc

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