Last active
September 5, 2025 15:04
-
-
Save fcoury/7925373475b5fd0034a26026944a2247 to your computer and use it in GitHub Desktop.
Manage Worktrees
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # --- 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