Git Worktrees

I’m using Git every day. I’m using it so much, that I can type some commands without even thinking. For example git status, git pull, git add. But there was always one limitation that I quite disliked: only one branch checked out at a time.

It became even more apparent when there’s a new release on the horizon, when it’s sometimes required to make the same change on the develop and release-x.x.x branches at the same time.

When the staging area is empty, it’s not a problem, but when there are some changes on a branch that I don’t want to commit, switching between branches becomes problematic.

And at first, I was utilizing git stash to save my uncommitted work before switching to another branch. Actually, I’ve already used it before, when doing git rebase, because Git can’t continue rebasing when there are some files in a “dirty state”.

But managing multiple stashes in a repository became too cumbersome, and I can’t tell you how many times I just straight up lost my work because I just overwrote a stash.

Worktree

I happened to stumble upon this article, and, without a joke, Git worktrees straight up changed my life!

Let me briefly walk you through what worktree is and how I’m utilizing them.

When you clone a repository to your local machine, you already get a primary working tree that points at whatever branch you checked out (often main, but it can be any branch).

Git also lets you create additional working trees linked to the same repository, which in turn allows multiple branches to be checked out at the same time.

The command to create an additional worktree is simple:

git worktree add <path> <branch>

For example, I have multiple releases checked out on my local filesystem:

git worktree add ../project-release-x.x.x release-x.x.x

And by doing it this way, I’m able to quickly open release that I need in my editor or IDE and apply some fixes, or just look around. It’s really useful to have a copy of the source code for a particular release, especially when I’m working on the legacy code.

Worktrees are not limited to the release branches for me, though. Sometimes, albeit rarely, I can also create a worktree for a couple of different features/fixes that I work on in parallel. For example:

git worktree add ../project-awesome-feature feature/project-awesome-feature

To not lose track of all of the different worktrees that I have, there’s a command available to list all of them:

git worktree list

And it looks something like this:

/path/to/the/project                06e50f4bd [feature/awesome-new-feature]
/path/to/the/project-release-7.3.0  3a295b9dd (detached HEAD)
/path/to/the/project-release-7.5.5  859984b85 [release-7.5.5]
/path/to/the/project-release-8.4.0  2afbe785a [fix/some-release-hotfix]

As you can see, each and every worktree has some different state, while on the main worktree I continue to work on new features/fixes.

Deleting a worktree is really simple:

git worktree remove <name>

Where <name> is the name of the folder where worktree was placed to. Usually I do that after the release, or when I stop working on the support ticket that requires me to take a look into the legacy system at some older release.

Sometimes though, when I have some uncommitted files in the additional worktree, it is required to use --force flag when removing it. Also, the main worktree cannot be removed.

There are additional commands available, for example lock, move, repair, and unlock. But in my day-to-day work, I’ve never needed them. You can read about them in the documentation.

Conclusion

Since I discovered worktrees, I can’t imagine going back.

They make it effortless to juggle multiple branches, fix hot issues without stashing, and keep different releases side by side.

If you ever find yourself fighting with stashes or switching branches too often, give git worktree a try. It might just change your workflow as much as it did mine.

Want to receive updates straight in your inbox?

Subscribe to the newsletter

Comments