Skip to content

Instantly share code, notes, and snippets.

@fcoury
Last active September 5, 2025 15:04
Show Gist options
  • Select an option

  • Save fcoury/7925373475b5fd0034a26026944a2247 to your computer and use it in GitHub Desktop.

Select an option

Save fcoury/7925373475b5fd0034a26026944a2247 to your computer and use it in GitHub Desktop.
Manage Worktrees
# Git Worktree Creation Function
function wk --description "Create a git worktree with format <current_dir>-<branch_name>"
# Check if we're in a git repository
if not git rev-parse --git-dir >/dev/null 2>&1
echo "Error: Not in a git repository"
return 1
end
set existing_only false
set branch_name ""
# Parse arguments
for arg in $argv
switch $arg
case '--existing' '-e'
set existing_only true
case '*'
if test -z "$branch_name"
set branch_name $arg
else
echo "Error: Multiple branch names provided"
return 1
end
end
end
# Check if branch name is provided
if test -z "$branch_name"
echo "Usage: wk [--existing|-e] <branch_name>"
echo "Creates worktree: <current_dir>-<branch_name>"
echo ""
echo "Options:"
echo " --existing, -e Only use existing branches (local or remote)"
return 1
end
set current_dir (basename (pwd))
# Convert branch name to safe directory name by replacing slashes with dashes
set safe_branch_name (string replace -a '/' '-' $branch_name)
set worktree_name "$current_dir-$safe_branch_name"
# Get the parent directory (one level up from current git repo)
set parent_dir (dirname (pwd))
set worktree_path "$parent_dir/$worktree_name"
echo "Creating worktree: $worktree_name"
echo "Path: $worktree_path"
echo "Branch: $branch_name"
# Check if branch exists (locally or remotely)
set branch_exists false
set target_branch $branch_name
# Check if branch exists locally
if git show-ref --verify --quiet "refs/heads/$branch_name"
set branch_exists true
echo "🌿 Using existing local branch: $branch_name"
# Check if branch exists on remote
else if git show-ref --verify --quiet "refs/remotes/origin/$branch_name"
set branch_exists true
echo "🌿 Using existing remote branch: origin/$branch_name"
set target_branch "origin/$branch_name"
end
# Create the worktree
if test $branch_exists = true
# Branch exists, create worktree from existing branch
if git worktree add "$worktree_path" "$target_branch"
echo "βœ… Worktree '$worktree_name' created successfully"
echo "πŸ“ Location: $worktree_path"
echo "🌿 Branch: $branch_name"
cd "$worktree_path"
else
echo "❌ Failed to create worktree"
return 1
end
else
if test $existing_only = true
# --existing flag used but branch doesn't exist
echo "❌ Branch '$branch_name' not found (local or remote)"
echo "πŸ’‘ Available branches:"
git branch -a | head -10
return 1
else
# Branch doesn't exist, create new branch and worktree
echo "πŸ†• Creating new branch: $branch_name"
if git worktree add -b "$branch_name" "$worktree_path"
echo "βœ… Worktree '$worktree_name' created successfully"
echo "πŸ“ Location: $worktree_path"
echo "🌿 New branch: $branch_name"
cd "$worktree_path"
else
echo "❌ Failed to create worktree with new branch"
return 1
end
end
end
end
# Git Worktree Deletion Function
function wd --description "Delete a git worktree with format <current_dir>-<branch_name>"
# Check if we're in a git repository
if not git rev-parse --git-dir >/dev/null 2>&1
echo "Error: Not in a git repository"
return 1
end
set force_flag false
set delete_branch false
set branch_name ""
# Parse arguments
for arg in $argv
switch $arg
case '--force' '-f'
set force_flag true
case '--delete-branch' '-d'
set delete_branch true
case '*'
if test -z "$branch_name"
set branch_name $arg
else
echo "Error: Multiple branch names provided"
return 1
end
end
end
# Check if branch name is provided
if test -z "$branch_name"
echo "Usage: wd [--force|-f] [--delete-branch|-d] <branch_name>"
echo "Deletes worktree: <current_dir>-<branch_name>"
echo ""
echo "Options:"
echo " --force, -f Force deletion of worktree"
echo " --delete-branch, -d Also delete the associated branch"
return 1
end
set current_dir (basename (pwd))
# Convert branch name to safe directory name by replacing slashes with dashes
set safe_branch_name (string replace -a '/' '-' $branch_name)
set worktree_name "$current_dir-$safe_branch_name"
# Get the parent directory
set parent_dir (dirname (pwd))
set worktree_path "$parent_dir/$worktree_name"
# Check if worktree exists
if not test -d "$worktree_path"
echo "❌ Worktree '$worktree_name' not found at: $worktree_path"
echo " (Looking for branch: $branch_name)"
return 1
end
echo "Deleting worktree: $worktree_name"
echo "Path: $worktree_path"
echo "Branch: $branch_name"
# Remove the worktree
set git_cmd git worktree remove "$worktree_path"
if test $force_flag = true
set git_cmd $git_cmd --force
echo "πŸ”₯ Force deleting..."
end
if eval $git_cmd
echo "βœ… Worktree '$worktree_name' deleted successfully"
# Delete the branch if requested
if test $delete_branch = true
echo "πŸ—‘οΈ Deleting branch '$branch_name'..."
# Check if branch exists locally
if git show-ref --verify --quiet "refs/heads/$branch_name"
set branch_cmd git branch -d "$branch_name"
if test $force_flag = true
set branch_cmd git branch -D "$branch_name"
echo "πŸ”₯ Force deleting branch..."
end
if eval $branch_cmd
echo "βœ… Branch '$branch_name' deleted successfully"
else
echo "❌ Failed to delete branch '$branch_name'"
if test $force_flag = false
echo "πŸ’‘ Try with --force flag to force delete the branch"
end
return 1
end
else
echo "⚠️ Branch '$branch_name' not found locally (may be remote-only)"
end
end
else
echo "❌ Failed to delete worktree"
if test $force_flag = false
echo "πŸ’‘ Try with --force flag if there are uncommitted changes"
end
return 1
end
end
# Git Worktree List Function - helpful for seeing all worktrees with their safe names
function wl --description "List all git worktrees with their corresponding branches"
# Check if we're in a git repository
if not git rev-parse --git-dir >/dev/null 2>&1
echo "Error: Not in a git repository"
return 1
end
echo "πŸ“‹ Git Worktrees:"
git worktree list --porcelain | while read -l line
if string match -q "worktree *" $line
set worktree_path (string replace "worktree " "" $line)
set worktree_name (basename $worktree_path)
else if string match -q "branch *" $line
set branch_name (string replace "branch refs/heads/" "" $line)
set safe_branch_name (string replace -a '/' '-' $branch_name)
echo " πŸ“ $worktree_name β†’ 🌿 $branch_name"
end
end
end
# Optional: Add completions for branch names
complete -c wk -f -a "(git branch -a | string replace -r '^\s*\*?\s*' '' | string replace -r '^remotes/[^/]+/' '')"
complete -c wk -l existing -s e -d "Only use existing branches (local or remote)"
complete -c wd -f -a "(git branch -a | string replace -r '^\s*\*?\s*' '' | string replace -r '^remotes/[^/]+/' '')"
complete -c wd -l force -s f -d "Force deletion of worktree"
complete -c wd -l delete-branch -s d -d "Also delete the associated branch"
# --- Git Worktree Helpers (bash/zsh) -----------------------------------------
# Not tested -- converted from the fish version automatically by ChatGPT
# wk: Create a git worktree with format <current_dir>-<branch_name>
wk() {
# Ensure 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
local existing_only=false
local branch_name=""
# Arg parsing
while [ $# -gt 0 ]; do
case "$1" in
--existing|-e) existing_only=true ;;
--) shift; break ;;
-*) echo "Error: Unknown option: $1"; return 1 ;;
*) if [ -z "$branch_name" ]; then
branch_name="$1"
else
echo "Error: Multiple branch names provided"
return 1
fi
;;
esac
shift
done
if [ -z "$branch_name" ]; then
echo "Usage: wk [--existing|-e] <branch_name>"
echo "Creates worktree: <current_dir>-<branch_name>"
echo ""
echo "Options:"
echo " --existing, -e Only use existing branches (local or remote)"
return 1
fi
local current_dir parent_dir safe_branch_name worktree_name worktree_path
current_dir="$(basename "$PWD")"
safe_branch_name="${branch_name//\//-}"
worktree_name="${current_dir}-${safe_branch_name}"
parent_dir="$(dirname "$PWD")"
worktree_path="${parent_dir}/${worktree_name}"
echo "Creating worktree: $worktree_name"
echo "Path: $worktree_path"
echo "Branch: $branch_name"
local branch_exists=false
local target_branch="$branch_name"
if git show-ref --verify --quiet "refs/heads/$branch_name"; then
branch_exists=true
echo "🌿 Using existing local branch: $branch_name"
elif git show-ref --verify --quiet "refs/remotes/origin/$branch_name"; then
branch_exists=true
target_branch="origin/$branch_name"
echo "🌿 Using existing remote branch: origin/$branch_name"
fi
if [ "$branch_exists" = true ]; then
if git worktree add "$worktree_path" "$target_branch"; then
echo "βœ… Worktree '$worktree_name' created successfully"
echo "πŸ“ Location: $worktree_path"
echo "🌿 Branch: $branch_name"
cd "$worktree_path" || return 1
else
echo "❌ Failed to create worktree"
return 1
fi
else
if [ "$existing_only" = true ]; then
echo "❌ Branch '$branch_name' not found (local or remote)"
echo "πŸ’‘ Available branches:"
git branch -a | head -10
return 1
else
echo "πŸ†• Creating new branch: $branch_name"
if git worktree add -b "$branch_name" "$worktree_path"; then
echo "βœ… Worktree '$worktree_name' created successfully"
echo "πŸ“ Location: $worktree_path"
echo "🌿 New branch: $branch_name"
cd "$worktree_path" || return 1
else
echo "❌ Failed to create worktree with new branch"
return 1
fi
fi
fi
}
# wd: Delete a git worktree with format <current_dir>-<branch_name>
wd() {
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
return 1
fi
local force_flag=false
local delete_branch=false
local branch_name=""
# Arg parsing
while [ $# -gt 0 ]; do
case "$1" in
--force|-f) force_flag=true ;;
--delete-branch|-d) delete_branch=true ;;
--) shift; break ;;
-*) echo "Error: Unknown option: $1"; return 1 ;;
*) if [ -z "$branch_name" ]; then
branch_name="$1"
else
echo "Error: Multiple branch names provided"
return 1
fi
;;
esac
shift
done
if [ -z "$branch_name" ]; then
echo "Usage: wd [--force|-f] [--delete-branch|-d] <branch_name>"
echo "Deletes worktree: <current_dir>-<branch_name>"
echo ""
echo "Options:"
echo " --force, -f Force deletion of worktree"
echo " --delete-branch, -d Also delete the associated branch"
return 1
fi
local current_dir parent_dir safe_branch_name worktree_name worktree_path
current_dir="$(basename "$PWD")"
safe_branch_name="${branch_name//\//-}"
worktree_name="${current_dir}-${safe_branch_name}"
parent_dir="$(dirname "$PWD")"
worktree_path="${parent_dir}/${worktree_name}"
if [ ! -d "$worktree_path" ]; then
echo "❌ Worktree '$worktree_name' not found at: $worktree_path"
echo " (Looking for branch: $branch_name)"
return 1
fi
echo "Deleting worktree: $worktree_name"
echo "Path: $worktree_path"
echo "Branch: $branch_name"
if [ "$force_flag" = true ]; then
echo "πŸ”₯ Force deleting..."
if ! git worktree remove --force "$worktree_path"; then
echo "❌ Failed to delete worktree"
echo "πŸ’‘ You may need to close open files or shells in that directory"
return 1
fi
else
if ! git worktree remove "$worktree_path"; then
echo "❌ Failed to delete worktree"
echo "πŸ’‘ Try with --force flag if there are uncommitted changes"
return 1
fi
fi
echo "βœ… Worktree '$worktree_name' deleted successfully"
if [ "$delete_branch" = true ]; then
echo "πŸ—‘οΈ Deleting branch '$branch_name'..."
if git show-ref --verify --quiet "refs/heads/$branch_name"; then
if [ "$force_flag" = true ]; then
echo "πŸ”₯ Force deleting branch..."
if git branch -D "$branch_name"; then
echo "βœ… Branch '$branch_name' deleted successfully"
else
echo "❌ Failed to delete branch '$branch_name'"
return 1
fi
else
if git branch -d "$branch_name"; then
echo "βœ… Branch '$branch_name' deleted successfully"
else
echo "❌ Failed to delete branch '$branch_name'"
echo "πŸ’‘ Try with --force flag to force delete the branch"
return 1
fi
fi
else
echo "⚠️ Branch '$branch_name' not found locally (may be remote-only)"
fi
fi
}
# wl: List all git worktrees with their corresponding branches
wl() {
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
return 1
fi
echo "πŸ“‹ Git Worktrees:"
local line worktree_path worktree_name branch_name
git worktree list --porcelain | while IFS= read -r line; do
case "$line" in
worktree\ *)
worktree_path="${line#worktree }"
worktree_name="$(basename "$worktree_path")"
;;
branch\ refs/heads/*)
branch_name="${line#branch refs/heads/}"
printf " πŸ“ %s β†’ 🌿 %s\n" "$worktree_name" "$branch_name"
;;
esac
done
}
# --- Completions --------------------------------------------------------------
# Branch list helper (local + remote without the 'remotes/<remote>/' prefix)
__wk_wd_branch_list() {
# tolerate being called outside git repos
if ! git rev-parse --git-dir >/dev/null 2>&1; then
return 0
fi
# List branches and normalize names
git branch -a 2>/dev/null \
| sed -E 's/^[*[:space:]]+//; s|^remotes/[^/]+/||' \
| sort -u
}
# Bash completion
if [ -n "${BASH_VERSION:-}" ]; then
_wk_complete() {
local cur prev
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case "$prev" in
--existing|-e) ;; # next is branch
esac
case "$cur" in
--*) COMPREPLY=( $(compgen -W "--existing -e" -- "$cur") ); return 0 ;;
*) COMPREPLY=( $(compgen -W "$(__wk_wd_branch_list)" -- "$cur") ); return 0 ;;
esac
}
_wd_complete() {
local cur prev
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case "$cur" in
--*) COMPREPLY=( $(compgen -W "--force -f --delete-branch -d" -- "$cur") ); return 0 ;;
*) COMPREPLY=( $(compgen -W "$(__wk_wd_branch_list)" -- "$cur") ); return 0 ;;
esac
}
complete -F _wk_complete wk
complete -F _wd_complete wd
fi
# zsh completion
if [ -n "${ZSH_VERSION:-}" ]; then
autoload -U compinit >/dev/null 2>&1 || true
# Simple compdef using dynamic word list
_wk_zsh_complete() {
local -a opts branches
opts=('--existing' '-e')
branches=($(__wk_wd_branch_list))
_arguments \
'1:branch name:->branch' \
'*::options:->opts'
case $state in
branch) _describe -t branches 'branches' branches ;;
opts) _describe -t options 'options' opts ;;
esac
}
_wd_zsh_complete() {
local -a opts branches
opts=('--force' '-f' '--delete-branch' '-d')
branches=($(__wk_wd_branch_list))
_arguments \
'1:branch name:->branch' \
'*::options:->opts'
case $state in
branch) _describe -t branches 'branches' branches ;;
opts) _describe -t options 'options' opts ;;
esac
}
compdef _wk_zsh_complete wk
compdef _wd_zsh_complete wd
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment