Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
5 Commits
 
 
 
 

Repository files navigation

git-worktrees

A simple worktree management utility with fzf-powered navigation.

Why wt?

Starting a new feature:

Plain git worktree wt
git worktree add -b my-feature ../my-feature wt add my-feature
cd ../my-feature auto-navigates
cp ../my-repo/frontend/.env.local frontend/ ignored files auto-copied
cd frontend && yarn install node_modules symlinked — skip this
yarn dev yarn dev
work, commit, push… work, commit, push…
cd ../my-repo
git worktree remove ../my-feature wt rm
git branch -d my-feature prompts to delete branch

Reviewing a remote branch:

Plain git worktree wt
git fetch origin git fetch origin
git worktree add -b review ../review origin/feat wt add review origin/feat
cd ../review auto-navigates
cp ../my-repo/frontend/.env.local frontend/ ignored files auto-copied
cd frontend && yarn install node_modules symlinked — skip this
yarn dev yarn dev
review… review…
cd ../my-repo
git worktree remove ../review wt rm
git branch -d review prompts to delete branch

Commands

Command Description
wt init Create .worktreeinclude and .worktreesymlink config files in the main worktree (skips if they already exist)
wt add <name> [branch] Create a new worktree, apply .worktreeinclude and .worktreesymlink, and navigate to it. Without branch: creates a new local branch <name>. With branch (e.g. origin/feat): checks out that branch directly — no extra local branch created
wt ls [name] Navigate to a worktree with fzf, or print the path of <name>
wt rm [name] [-f] Delete worktree <name>; without a name: the current worktree, or pick with fzf when run from the main worktree. -f skips confirmation
wt setup Apply .worktreeinclude / .worktreesymlink in an existing worktree — only needed for worktrees not created with wt add (which runs this automatically)
wt unsymlink Remove symlinks created from .worktreesymlink
wt --help Show help

fzf is only a shorthand: wt ls / wt rm without a name use fzf to pick the argument, then run the same code path as the non-interactive form.

Configuration files

Both files live in the main worktree's root, one entry per line; lines starting with # are comments, blank lines are ignored. They are automatically applied when you run wt add (or manually with wt setup).

.worktreeinclude — Copy files and directories

Files and directories listed here are copied into each new worktree. Use for:

  • Environment files (.env.local, .env.*.local) — secrets and local config that vary per developer
  • Hook directories (e.g. .husky/_) — git hooks shared across the repo but with filesystem state
  • Any untracked files you want each worktree to have independently

Directories are copied with rsync -a, so internal symlinks survive (important for tools like husky).

Example:

# Environment files
frontend/.env.local
frontend/ikt-skip/.env.local

# Hook directories (internal symlinks preserved)
frontend/.husky/_

Missing entries are skipped without error (useful for optional files).

.worktreesymlink — Symlink directories to save disk space

Glob patterns listed here are symlinked (not copied) into each new worktree, resolved against the main worktree. Use for:

  • node_modules directories — avoid re-running yarn install in every worktree
  • Build artifacts or other large read-only directories
  • Any directory you want all worktrees to share (on the same filesystem)

⚠️ Important: Do not run yarn install (or any package manager) in a worktree with symlinked node_modules. If you need to change dependencies:

  1. Run wt unsymlink to remove the symlinks
  2. Run yarn install or your package manager
  3. When you're done, create a new worktree with wt add to get fresh symlinks

Example:

# Symlink node_modules to avoid re-running yarn
frontend/node_modules
frontend/*/node_modules

The frontend/*/node_modules glob will match frontend/app-a/node_modules, frontend/app-b/node_modules, etc.

Worktree location

By default, wt add <name> creates the worktree as a sibling of the main repo:

parent/
  my-repo/        ← main worktree
  my-feature/     ← created by wt add my-feature

Configuring a custom location

To use a different location, set WT_WORKTREE_DIR in your shell startup file:

# Inside the repo (add to .gitignore):
export WT_WORKTREE_DIR=.worktrees       # → my-repo/.worktrees/my-feature
export WT_WORKTREE_DIR=worktrees        # → my-repo/worktrees/my-feature

# Sibling directory:
export WT_WORKTREE_DIR=../worktrees     # → parent/worktrees/my-feature

# Absolute path:
export WT_WORKTREE_DIR=/tmp/worktrees   # → /tmp/worktrees/my-feature

⚠️ Important: set WT_WORKTREE_DIR in the right file

Define WT_WORKTREE_DIR in ~/.zshenv (or ~/.bashrc), NOT ~/.zshrc (or ~/.bash_profile).

If you set it only in ~/.zshrc, it works when you run wt in your terminal, but fails silently when invoked by:

  • Automation and scripts (including Claude Code's Bash tool)
  • Editors and IDEs
  • Git hooks
  • CI/CD pipelines
  • Any non-interactive shell invocation

This happens because zsh only sources ~/.zshrc for interactive shells. Non-interactive shells source ~/.zshenv (and ~/.zprofile/~/.zlogin for login shells) — but never ~/.zshrc. Similarly, bash only sources ~/.bashrc for interactive non-login shells; ensure non-interactive contexts load it too (or use ~/.bash_profile/~/.bashrc consistently).

When WT_WORKTREE_DIR is unset, wt silently falls back to the default location: a sibling directory of the main repo (e.g., ../my-feature). If worktrees appear in the wrong place, check your shell sourcing.

If you use an in-repo location (no leading ../), add it to .gitignore:

.worktrees/

Requirements

  • fzfbrew install fzf (only for the interactive picker)
  • rsync (preinstalled on macOS and most Linux distros)

Tests

./bin/git-worktrees_test.sh

Creates a throwaway git repository in a temp directory, exercises all commands through their non-interactive forms, and asserts on filesystem state and output. No fzf needed.

Installation

Note: install.sh only supports zsh (writes to ~/.zshrc). Bash users should follow the manual installation below.

git clone https://github.com/dagjomar/git-worktrees.git
cd git-worktrees
./bin/install.sh
source ~/.zshrc

Manual installation

Add the following to your ~/.zshrc:

wt() {
  local script="/path/to/git-worktrees/bin/git-worktrees.sh"
  local dir
  case "${1:-}" in
    ls)
      dir=$("$script" "$@") && [ -n "$dir" ] && cd "$dir"
      ;;
    add)
      "$script" "$@" || return $?
      dir=$("$script" ls "${2:-}" 2>/dev/null) && [ -n "$dir" ] && cd "$dir"
      ;;
    rm)
      local main
      main=$(command git worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p' | head -1)
      "$script" "$@"
      # If we just removed the worktree we were standing in, move to main
      [ -d "$PWD" ] || cd "$main"
      ;;
    *)
      "$script" "$@"
      ;;
  esac
}

About

Git worktree manager — fzf-powered navigation, auto-symlinks for node_modules, and file copying across worktrees

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.