Using git worktree instead of switching branches
I’d been meaning to learn git worktree. It lets you check out more than one branch at the same time, each in its own folder. All the folders share one repo, so a commit you make in one is already there in the others. No second clone, and nothing to push or pull between them.
So you don’t have to switch branches anymore. Normally, to work on another branch, you stash or commit what you have, check out the other branch, maybe rebuild, and then do it all again to come back. With a worktree you just cd to another folder.
The most common case is fixing a bug on main while you’re in the middle of a feature:
$ git fetch origin
$ git worktree add -b fix-login ../hotfix origin/main
$ cd ../hotfix
# fix the bug and commit
$ git push -u origin fix-login
$ cd -
$ git worktree remove ../hotfix
git worktree add makes a new folder, ../hotfix, with a new branch fix-login that starts from the latest main. When you’re done, git worktree remove deletes the folder. Your feature branch and its uncommitted changes are never touched.
Reviewing someone else’s branch works the same way:
$ git worktree add --detach ../review origin/their-branch
--detach checks out their branch without making a local branch, since you’re only looking. If you want to push changes to it, use git worktree add ../review their-branch instead. Git then makes a local their-branch that tracks origin/their-branch.
It’s also an easy way to see if your change broke something. Check out main in another folder and run the same test there:
$ git worktree add --detach ../main origin/main
$ cd ../main
# run the same test
If the test fails on main too, it wasn’t your change. Same for benchmarks, or anything else you want to compare.
For projects with release branches, keep a worktree for each one. Then copying a fix to an older release (a backport) is quick:
$ git worktree add ../release-2.4 release/2.4
$ cd ../release-2.4
$ git cherry-pick -x 1a2b3c4
You only need the add once. 1a2b3c4 is a fix you just committed on your branch. You don’t need to push it first, since every worktree sees the same commits. -x adds “(cherry picked from commit …)” to the message, so later you can tell where it came from.
git bisect finds the commit that broke something by testing commits between a good one and a bad one. That can take a while, so run it in its own worktree and keep working in your main folder:
$ git worktree add --detach ../bisect HEAD
$ cd ../bisect
$ git bisect start HEAD v2.3.0
$ git bisect run ~/test.sh
HEADhas the bug, andv2.3.0is the last version you know worked.test.shis your test. Bisect runs it on each commit, and it should exit 0 if the commit is good and 1 if it’s bad.- Keep the script outside the repo, so the old commits bisect checks out can’t change or delete it.
A few things that aren’t obvious:
- A branch can only be checked out in one worktree at a time. Adding a worktree for a branch that’s already checked out fails with “already used by worktree at …”. Use
--detach, or make a new branch with-b. - A new worktree only has the files git tracks. Ignored files like
node_modules,.envor build output aren’t there, so you may need to install or build again. Submodules aren’t set up either (git submodule update --init --recursive). git worktree removedeletes the folder but keeps the branch. If yourm -rfthe folder instead, git still thinks the branch is checked out there until you rungit worktree prune.- The stash is shared. Something you stash in one worktree shows up in
git stash listin all of them. - Put worktrees next to the repo (
../hotfix), not inside it, orgit statusshows them as untracked files. git worktree listshows every worktree and which branch each one has.
Short version: if you’re about to git stash just to look at another branch, add a worktree instead.