-rw-r--r-- git-worktrees.zsh
1 case "${WAYLAND_DISPLAY:+wayland}${DISPLAY:+x11}$OSTYPE" in 2 wayland*) 3 _copy='wl-copy' 4 ;; 5 x11*) 6 _copy='xclip -selection clipboard' 7 ;; 8 darwin*) 9 _copy=pbcopy 10 ;; 11 esac 12 13 # Add a worktree - creates branch from main if it doesn't exist 14 gw() { 15 local input="$1" 16 if [[ -z "$input" ]]; then 17 git worktree list 18 return 1 19 fi 20 21 local branch="${input##origin/}" 22 local folder="${branch//\//-}" 23 echo -n "$folder" | $_copy 24 local target="../$folder" 25 26 # Check if worktree already exists 27 if [[ -d "$target" ]]; then 28 cd "$target" 29 return 30 fi 31 32 # Check if branch exists locally 33 if git show-ref --verify --quiet "refs/heads/$branch"; then 34 git worktree add "$target" "$branch" && cd "$target" 35 return 36 fi 37 38 # Try fetching from remote 39 if git fetch origin "$branch" 2>/dev/null && \ 40 git worktree add "$target" "$branch" 2>/dev/null; then 41 cd "$target" 42 return 43 fi 44 45 # Branch doesn't exist - create from main 46 echo "Branch '$branch' not found, creating from main..." 47 git fetch origin main:main 2>/dev/null || git fetch origin main 48 git worktree add -b "$branch" "$target" main && cd "$target" 49 } 50 51 # cd to top-level folder of a worktree if we're inside a subfolder, otherwise cd to main 52 gw_top_level() { 53 local root="${HOME}/git/$1" 54 55 # Check if we're inside a subfolder of the repo 56 if [[ "$PWD" == "$root"/* ]]; then 57 # Extract path relative to root, then get first component 58 local relative="${PWD#$root/}" 59 local subfolder="${relative%%/*}" 60 cd "$root/$subfolder" 61 else 62 cd "$root/main" 63 fi 64 } 65 66 # Remove a worktree 67 gwr() { 68 if [[ "$(git rev-parse --is-inside-work-tree)" != "true" ]]; then 69 echo "Not in a worktree" 70 return 1 71 fi 72 cd $(git rev-parse --show-toplevel) 73 local target="${1:-$(basename "$PWD")}" 74 75 if [[ "$target" == "main" ]]; then 76 echo "Refusing to remove main worktree" 77 return 1 78 fi 79 80 local branch 81 if [[ -z "$1" ]]; then 82 # Current directory - get branch before leaving 83 branch=$(git branch --show-current) 84 cd ../main && git worktree remove "../$target" && git branch -D "$branch" 85 else 86 # Named worktree - get its branch first 87 branch=$(git -C "../$target" branch --show-current 2>/dev/null) 88 git worktree remove "../$target" && [[ -n "$branch" ]] && git branch -D "$branch" 89 fi 90 git worktree list 91 } 92 93 # Interactive branch selection with fzf 94 gwf() { 95 local branch=$(git branch -a --sort=-committerdate | \ 96 grep -v HEAD | \ 97 sed 's/^[* ]*//' | \ 98 sed 's#remotes/origin/##' | \ 99 awk '!seen[$0]++' | \ 100 fzf --height 40% --reverse --preview 'git log --oneline -10 {}') 101 [[ -n "$branch" ]] && gwt "$branch" 102 } 103 104 # Completions 105 _gw_branches() { 106 local branches 107 branches=(${(f)"$(git branch 2>/dev/null | sed 's/^[* ]*//' | sed 's/^[+ ]*//')"}) 108 _describe 'branch' branches 109 } 110 111 _gwr_worktrees() { 112 local worktrees 113 worktrees=(${(f)"$(git worktree list --porcelain 2>/dev/null | grep '^worktree' | awk '{print $2}' | xargs -n1 basename)"}) 114 _describe 'worktree' worktrees 115 } 116 117 compdef _gw_branches gw 118 compdef _gwr_worktrees gwr 119