Assumptions
- All repos live in
~/GitHub/- You always start inside a repo:
pwd = ~/GitHub/my-repo- Create worktrees as siblings (e.g.,
../my-repo--my-feature) — never nested insidemy-repo/
- A worktree = an extra working copy of the same Git object store.
- One branch ↔ one worktree (a branch can’t be checked out twice).
- Every worktree has its own working dir & index; objects are shared → fast & space-light.
git worktree list
git worktree list --porcelain # script-friendlyA) New branch from main
# from: ~/GitHub/my-repo
git fetch origin
git worktree add ../my-repo--my-feature -b my-feature origin/main
cd ../my-repo--my-featureB) Branch exists on remote (origin/my-feature)
git fetch origin my-feature
git worktree add ../my-repo--my-feature -b my-feature origin/my-feature
cd ../my-repo--my-featureC) Branch already exists locally (and not checked out elsewhere)
git fetch origin
git worktree add ../my-repo--my-feature my-feature
cd ../my-repo--my-feature
# (optional) set upstream if missing
git branch --set-upstream-to=origin/my-feature my-featuregit status
git add <files>
git commit -m "Implement X"
git push -u origin my-feature# change directories
cd ../my-repo # e.g., main
cd ../my-repo--my-feature # your feature
# or run in another tree without cd:
git -C ../my-repo--my-feature statusKeep main in ~/GitHub/my-repo and your feature in ../my-repo--my-feature.
# ensure feature is pushed
git -C ../my-repo--my-feature push
# merge on main’s worktree
cd ~/GitHub/my-repo
git switch main
git pull --ff-only origin main # or just: git pull
git merge --no-ff my-feature # or: rebase feature first, then fast-forward
git push origin maingit worktree remove ../my-repo--my-feature
# optional: delete the branch when truly done
git branch -d my-feature
git push origin --delete my-feature
# if you ever deleted a worktree folder by hand:
git worktree pruneSibling (✅ recommended):
~/GitHub/my-repo
~/GitHub/my-repo--my-feature
Nested (❌ avoid):
~/GitHub/my-repo/my-feature
Nesting confuses tools and can lead to accidental commits in the wrong place.
You can’t check out the same branch twice.
- Free it in the other worktree:
git -C ../my-repo--somewhere switch main git worktree add ../my-repo--my-feature my-feature
- Detached view at the same tip:
git worktree add --detach ../my-repo--my-feature-view my-feature
- Sibling branch from the same commit:
git worktree add ../my-repo--my-feature2 -b my-feature-2 my-feature
# Scratch at current commit (disposable)
git worktree add --detach ../my-repo--scratch HEAD
# Check out a tag/sha for debugging
git fetch --tags
git worktree add --detach ../my-repo--v1.5 v1.5.0
# Hotfix starting from a release branch
git fetch origin
git worktree add ../my-repo--hotfix -b hotfix/urgent origin/release/2.xgit fetch --all --prune # refresh refs and nuke stale remotes
git worktree prune # clear stale worktree records# Path ↔ branch map (great for quick overviews)
git worktree list --porcelain | awk '
/^worktree / {p=$2}
/^branch / {b=$2; print p "\t" b}
'- “‘my-feature’ is already checked out at …”
Switch that other worktree off (git -C <path> switch main), or use--detach, or create a sibling branch. - Used
-bwith an existing branch → drop-b.
git worktree add ../my-repo--my-feature my-feature - Upstream not set
git branch --set-upstream-to=origin/my-feature my-feature - Accidentally nested
git worktree remove ./nested-dir && git worktree prunethen recreate as a sibling.
# wt-add <my-feature> [start-point] # default start = origin/main
wt-add() {
local repo="$(basename "$PWD")"
local topic="$1"
local start="${2:-origin/main}"
git fetch --all --prune
git worktree add "../${repo}--${topic}" -b "${topic}" "${start}"
}
# wt-go <my-feature> # cd into sibling worktree
wt-go() {
local repo="$(basename "$PWD")"
cd "../${repo}--$1" || return
}
# wt-rm <my-feature> # remove worktree (+ prune); uncomment branch delete if desired
wt-rm() {
local repo="$(basename "$PWD")"
local path="../${repo}--$1"
git worktree remove "$path"
# git branch -d "$1" && git push origin --delete "$1"
git worktree prune
}# From: ~/GitHub/my-repo
git fetch --all --prune
git worktree list
# Create a new feature from main
git worktree add ../my-repo--my-feature -b my-feature origin/main
cd ../my-repo--my-feature
git add .
git commit -m "Start my-feature"
git push -u origin my-feature
# Merge on main
cd ~/GitHub/my-repo
git switch main
git pull --ff-only
git merge --no-ff my-feature
git push
# Remove when done
git worktree remove ../my-repo--my-feature
git branch -d my-feature
git push origin --delete my-feature
git worktree prune