Last active
April 17, 2026 18:24
-
-
Save neilwashere/6aac8215f3397d5edc7d3baf8a3fd9ab to your computer and use it in GitHub Desktop.
Git worktree helpers for zsh — quick wt-new/wt-review/wt-cd/wt-rm commands with Claude Code context sharing
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 for Zsh | |
| # Quick shortcuts for working with git worktrees. | |
| # Worktrees are created under ../$repo.worktrees/$branch alongside the main repo. | |
| # Includes optional Claude Code context sharing so sessions in worktrees | |
| # pick up the same project memory as the main repo. | |
| # Usage: | |
| # wt-new <branch> [base] Create a new feature branch in a worktree (defaults to main) | |
| # wt-review <branch> Check out a remote branch in a worktree | |
| # wt-cd <branch> cd into an existing worktree | |
| # wt-ls List all worktrees | |
| # wt-rm <branch-or-path> Remove a worktree (cleans up Claude symlinks too) | |
| # Install: source this file from your .zshrc | |
| # --- Internal helpers --- | |
| # Derives a worktree path: ../$repo.worktrees/$branch | |
| _wt_dir() { | |
| local repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)") | |
| local branch="${1#origin/}" # strip remote prefix only | |
| echo "../${repo}.worktrees/${branch//\//-}" | |
| } | |
| # Symlink Claude Code project context (memory, settings) from a worktree back to the main repo | |
| # so claude sessions in the worktree share accumulated knowledge | |
| _wt_link_claude() { | |
| local main_abs=$(git rev-parse --show-toplevel 2>/dev/null) | |
| local wt_abs=$(cd "$1" && pwd) | |
| local claude_base="$HOME/.claude/projects" | |
| local main_claude="${claude_base}/$(echo "$main_abs" | tr '/' '-')" | |
| local wt_claude="${claude_base}/$(echo "$wt_abs" | tr '/' '-')" | |
| if [[ -d "$main_claude" && ! -e "$wt_claude" ]]; then | |
| ln -s "$main_claude" "$wt_claude" | |
| echo "Claude context linked to main repo" | |
| fi | |
| } | |
| # Remove the Claude context symlink for a worktree | |
| _wt_unlink_claude() { | |
| local wt_abs=$(cd "$1" 2>/dev/null && pwd) || return | |
| local claude_base="$HOME/.claude/projects" | |
| local wt_claude="${claude_base}/$(echo "$wt_abs" | tr '/' '-')" | |
| if [[ -L "$wt_claude" ]]; then | |
| rm "$wt_claude" | |
| echo "Claude context symlink removed" | |
| fi | |
| } | |
| # --- Public commands --- | |
| # Open a remote branch in a new worktree for review/work | |
| wt-review() { | |
| if [[ -z "$1" ]]; then | |
| echo "Usage: wt-review <branch> (e.g. wt-review feat/async-travel)" | |
| return 1 | |
| fi | |
| git fetch origin | |
| local dir=$(_wt_dir "$1") | |
| git worktree add "$dir" "$1" 2>/dev/null \ | |
| || git worktree add "$dir" -b "$1" "origin/$1" | |
| echo "Worktree ready at $dir" | |
| cd "$dir" | |
| } | |
| # Create a new feature branch in a worktree (with shared Claude context) | |
| wt-new() { | |
| if [[ -z "$1" ]]; then | |
| echo "Usage: wt-new <branch> [base] (e.g. wt-new feat/my-feature main)" | |
| return 1 | |
| fi | |
| local base=${2:-main} | |
| git fetch origin "$base" || return 1 | |
| local dir=$(_wt_dir "$1") | |
| git worktree add "$dir" -b "$1" "origin/$base" | |
| _wt_link_claude "$dir" | |
| echo "Worktree ready at $dir (based on origin/$base)" | |
| cd "$dir" | |
| } | |
| # List all worktrees | |
| alias wt-ls="git worktree list" | |
| # Remove a worktree (by branch name or path); extra args passed to git worktree remove | |
| wt-rm() { | |
| if [[ -z "$1" ]]; then | |
| echo "Usage: wt-rm <branch-or-path> [-- git-worktree-remove-args...]" | |
| return 1 | |
| fi | |
| local target="$1"; shift | |
| # If arg looks like a path, use it directly; otherwise derive from branch name | |
| if [[ "$target" == */* && -d "$target" ]]; then | |
| _wt_unlink_claude "$target" | |
| git worktree remove "$@" "$target" | |
| else | |
| local dir=$(_wt_dir "$target") | |
| _wt_unlink_claude "$dir" | |
| git worktree remove "$@" "$dir" | |
| fi | |
| } | |
| # cd into an existing worktree by branch name | |
| wt-cd() { | |
| if [[ -z "$1" ]]; then | |
| echo "Usage: wt-cd <branch>" | |
| return 1 | |
| fi | |
| local dir=$(_wt_dir "$1") | |
| if [[ -d "$dir" ]]; then | |
| cd "$dir" | |
| else | |
| echo "No worktree found at $dir" | |
| echo "Active worktrees:" | |
| git worktree list | |
| return 1 | |
| fi | |
| } | |
| # --- Tab completion --- | |
| # wt-review and wt-rm complete on remote branches | |
| _wt_remote_branches() { | |
| compadd ${(f)"$(git branch -r 2>/dev/null | sed 's|origin/||;s/^\[ \*\]\*//' | grep -v HEAD)"} | |
| } | |
| compdef _wt_remote_branches wt-review | |
| compdef _wt_remote_branches wt-rm | |
| # wt-cd completes on existing worktree branches | |
| _wt_existing_worktrees() { | |
| compadd ${(f)"$(git worktree list --porcelain 2>/dev/null | awk '/^branch refs\\/heads\\//{sub("refs/heads/","",$2); print $2}')"} | |
| } | |
| compdef _wt_existing_worktrees wt-cd |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment