Programming for fun and profit

A blog about software engineering, programming languages and technical tinkering

Mon 22 July 2019

Git worktrees: work in parallel on multiple versions of a project

Posted by Simon Larsén in Tip of the Week   

I've been AWOL for a month due to injury, sickness and conference-going. But with all that finally out of the way, I have another Tip of the Week, this time relating to Git: the git worktree command. With git worktree, you can check out multiple branches at once, which is super useful for when working on major changes where you need to view multiple versions, or maybe you're just trying a few different solutions to a single prodlem. If you've ever found yourself frantically switching branches, stashing changes to be able to switch branches, and even creating copies of the repository you're working in, then this article is for you.

An example repo

Let's first create an example repo. Here's a little terminal session where I create a repository, add a README to it on the master branch, add another line to the readme on a branch called other, and finally checking out to master.

[~] $ mkdir repo
[~] $ cd repo
[repo] $ git init
Initialized empty Git repository in /home/slarse/repo/.git/
[repo] $ echo "Hello!" > README.md
[repo] $ git add README.md && git commit -m 'Add README'
[master (root-commit) 6094baf] Add README
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
(master)[repo] $ git checkout -b other
Switched to a new branch 'other'
(other)[repo] $ echo "There!" >> README.md 
(other)[repo] $ git commit -am 'Add new line to README'
[other b779dfb] Add new line to README
 1 file changed, 1 insertion(+)
(other)[repo] $ git checkout master
Switched to branch 'master'
(master)[repo] $ 

It's not super important how you do it, just make sure to have two branches.

Adding a new worktree

First of all: what is a worktree? Usually, you only have the worktree, which is the part of a repository where you actually do your work (edit files etc). Running git worktree list on most repos will show the location of this single worktree, and what commit/branch it is checked out to.

(master)[repo] $ pwd # just checking the current working directory
/home/slarse/repo
(master)[repo] $ git worktree list
/home/slarse/repo  6094baf [master]  # points to the cwd, checked out to master

Note: When I run git worktree list after this point, it's just to show the results of commands.

With git worktree add, you can add additional worktrees checked out to different commits. The most basic usage is git worktree add <path> <commit-ish>, where path is a path to the new worktree (i.e. where you want to put it), and commit-ish is something like a commit or branch (or a few other things that are not important for every-day use). Let's check out other in a new worktree.

(master)[repo] $ git worktree add ../repo-other other
Preparing worktree (checking out 'other')
HEAD is now at b779dfb Add new line to README
(master)[repo] $ git worktree list
/home/slarse/repo        6094baf [master]
/home/slarse/repo-other  b779dfb [other]
(master)[repo] $ ls -a ../repo-other # have a look in the new working tree
.  ..  .git  README.md

As you can see, the new worktree has been created, and can be seen in the list of worktrees. .git is usually a directory, but in the case of a non-primary worktree, it's actually just a file with a path to the original .git directory.

(master)[repo] $ cat ../repo-other/.git 
gitdir: /home/slarse/repo/.git/worktrees/repo-other

Like many things in Git, it's brilliantly simple. You can start working in your new worktree like it's an entirely separate repository, with the caveat that you can't check out to a branch that is checked out in some other worktree. That includes checking out to other commits or branches, and even creating entirely new branches.

Moving a worktree

If for some reason you need to move a worktree, you should use git worktree move to make sure that all of the references are correctly changed. It's very simple, just type git worktree move <src> <dst>. For example, if I want to move ../repo-other to ../repo-work, I do:

(master)[repo] $ git worktree move ../repo-other ../repo-work
(master)[repo] $ git worktree list
/home/slarse/repo       6094baf [master]
/home/slarse/repo-work  b779dfb [other]

That's all there is to moving worktrees. Not very exciting, and I can't recall ever actually doing it, but I can see how it could be useful.

Removing a worktree

To remove a worktree, run git worktree remove <path>.

(master)[repo] $ git worktree remove ../repo-work/
(master)[repo] $ git worktree list
/home/slarse/repo  6094baf [master]

You can also just remove the directory with the worktree and the reference to it will be removed automatically (but not necessarily immediately). Run git worktree prune to trigger this removal process.

The other worktree commands

There are a few more git worktree commands that I've never felt the need to use. Have a look at them in the git-worktree documentation.

Summary

In this short article I showcased git worktree. It's super useful to work in parallel on different versions of the same project, without having to create copies of the repository and thereby having to deal with synchronizing multiple local copies (which can quickly get hard to manage). I find myself using this more and more, and if you find it useful yourself I highly recommend reading up on it more in its man-page (either with man git-worktree or online).