My Git Worktrees Workflow
tldr;
git worktrees rock! Use git bare repos along with git worktrees to maintain a clean worktree structure. Jump to the bottom of this article to find details on my exact setup.
what are git worktrees?
Git worktrees, put simply, allow you to check out multiple branches within a git repo at the same time into separate directories. This can be useful when you need to jump between branch to branch quickly & frequently.
preamble
A normal work day for me usually includes fixing a bug or adding a feature on a git branch. Another normal occurrence for me is performing code reviews for peers. I usually like to check out the code Iām reviewing so I can test locally to verify behavior correctness. While Iām reviewing I may notice a bug/unexpected behavior Iāll want to verify doesnāt exist in the trunk (e.g. develop/main) branch to determine if this behavior was introduced in this patch or not.
Already in the scenario above Iāve had to hop branches twice! In a traditional non git worktrees workflow I would have had to stashed/committed/reverted any uncommitted changes on each branch.
What Iāve come to realize is the additional overhead of having to think of what to do with my uncommitted code when needing to switch branches was yet another slow-down/barrier when needing to switch branches.
Additionally, when I would need to switch to reviewing a branch (or several) after working on my own feature branch, there was always that moment when needing to return to my own work and remember which git branch I was working on.
what problems am i trying to solve?
Here are my two pain-points:
- Switching branches requires mental overhead of having to deal with committed changes.
- Switching back and forth between branches requires the additional step of having to remember what branch you were working on and checking it out again.
a better use of worktrees
So how does my workflow with git worktrees help with these two points?
Well, since each worktree is just a git branch checked out into its own directory we can have uncommitted changes in each worktree and easily just switch directories in our editor/IDE/terminal. Using worktrees resolves my pain-point #1.
In theory we could create a worktree per git branch, but that approach still leaves me with pain-point #2. My preference is not to automatically create a new worktree per branch, but rather create permanent worktrees that I can use to checkout branches in.
The permanent worktrees I create for most projects are:
- main (always checked out on my trunk branch (e.g. main/develop))
- work (will checkout whatever branch Iām currently working on here)
- review (reserved for whatever PR branch Iām currently reviewing)
This setup allows me to checkout a branch on my work worktree, switch to my review worktree to do PR reviews, and quickly switch back to my work worktree without having to remember what branch I was last working on.
This is my highly specific example of how I use worktrees, but hopefully it illustrates the usefulness of them. Iāve had times were Iāve needed to write up a proof of concept where Iāll work on it throughout the day between my feature work and meetings, but I donāt have anything meaningful to commit. In a case like this, I will create a temporary worktree named something like x-feature-poc so that I can easily jump to that worktree throughout the day.
tip: use git worktrees in a bare repo
My preferred approach to managing worktrees is to clone the git repo Iām working on as a bare repo so that my git files are clearly separated from my working files. I plan on writing an article at some point to outline the different approaches to using worktrees and why I prefer this one. Iāll be sure to update this article once I do that write-up!
So without further ado, hereās my setup.
- Create a folder for your project. For this example Iāll use the folder name
myApp - Inside your project folder (
myAppin our case), clone your git repo as a bare repo:git clone --bare https://yourgitserver/myApp.git - Verify that git has cloned a folder named
myApp.gitinto yourmyAppfolder that contains all of your git files with no working files. - Create a new directory in
myAppnamedworktrees. Weāll use this directory to store all of our worktrees. Your directory structure should now look something like this:
- Now within
myApp.git, rungit worktree add ../worktrees/main.
Thatās it! Youāve just created a worktree named main in your worktrees folder that checked out a branch named main!
I mentioned before that most of the projects have 3 permanent worktrees. This is an example of what my folder structure looks like:
The one thing to note is anytime you want to manage your worktrees (delete/add new ones) youāll need to do so from within the myApp.git directory.
issues with using git bare repos
Unfortunately the bare repo approach leaves us with two issues weāll need to address:
- bare repos do not automatically setup tracking of remote branches on checkout.
remote.origin.fetchis not set when cloning a bare repo so weāll need to set this ourselves.
Below are my solutions to both of these problems.
bare repo branch tracking
To fix the tracking issue Iāve created git alias to make updating the tracking on your git worktrees easier!
$ git config --global alias.track '!git branch --set-upstream-to=origin/$(git branch --show-current)'
Now you should be able to run git track anytime you need to update the tracking on your git branches!
remote.origin.fetch not set
To fix the issue with the remote.origin.fetch not being set just run the following command in your git files directory:
$ git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"