Git Worktrees

by Matt Johnson - Fri 16 February 2018
Tags: #git

Ever wanted to have multiple branches checked out in git?

Since July 2015 (https://github.com/blog/2042-git-2-5-including-multiple-worktrees-and-triangular-workflows), git worktrees was introduced to solve this problem. Let me show you how to use this with some examples. I will create a git repo called "foo":

$ git init ~/foo

When I do this, my git repo is now located at ~/foo/.git If I view what's under the .git folder, I have the following files:

  • HEAD
  • config
  • description

And the following folders:

  • branches
  • hooks
  • info
  • objects
  • refs

I will add one file to master and commit it:

$ touch a.txt; git add a.txt; git commit -m "First checkin"

OK, now let's create a branch named "dev":

$ git branch dev

Now, let's create a folder ~/foo/worktrees called "worktrees" and switch to it (the name of this folder doesn't matter)

$ mkdir worktrees; cd worktrees

From here, we now run the following git command:

$ git worktree add dev-branch dev

After this, you'll get some output like the following:

Preparing worktrees/dev-branch (identifier dev) HEAD is now at 9aaccf4 First checkin

The git worktree takes the following form:

git worktree add [name-of-our-worktree] [branch-name]

You'll see a new directory appear named "dev-branch". If you nagivate inside, you are now in the "dev" working directory! But you can back out, and be in the "master" working directory. So in git, we refer to the working directories created by git worktree as linked working trees. This is distinquished from our main working tree which in this case is master.

What happens if you try to create a linked working tree for master? Note that our main working tree is master. Well, we can run:

$ git worktree add master master

We get the following output:

$ fatal: 'master' is already checked out at '~/foo'

Git doesn't allow us to have two working directories that would be the same. However, if you added the -f flag, you can force git to do this anyway. I'm not aware of a use case where you would want to do this.

OK, another thing to notice, is that there is a file in our linked working tree named ".git". What is this file? If we look inside, we find:

$gitdir: ~/foo/.git/worktrees/dev-branch

If we now look back in our ~/foo/.git, we find a new folder called "worktrees". Insider this folder, there is a folder for each linked working tree. If we look inside the "dev" folder, we see the following files:

  • HEAD
  • ORIG_HEAD
  • gitdir
  • commondir
  • index

and folders:

  • logs

If we look at contents of commondir, it is "../..". It's relative path, and this relative path is relative to GIT_DIR, which in this case is ~/foo/.git/worktrees/dev-branch. Thus, GIT_COMMON_DIR points to ~/foo/.git

Viewing Worktrees

At any point, if you want to list all working trees (including the main working tree), run:

$ git worktree list

This yields (given our current setup):

~/foo/.git (bare) ~/foo/worktrees/dev-branch 9aaccf4 [dev]

Note the main working directory is included in this list

Deleting Worktrees

To delete a linked working tree, simply delete the folder. When you do this, you'll notice that in the main git directory, the worktree information is still there. If you want to clean this up, run:

git worktree prune

Else, git will clean it up automatically based on the value set in your git config for gc.worktreePruneExpire

Moving Worktrees

What happens if you move your working directory? Let's do this:

$ mv dev-branch dev-branch-move

Now, if we run git worktree list it still reports ~/foo/worktrees/dev-branch as the path

To update, I modified the following items:

  • ~/foo/worktrees/dev-branch-move/.git
  • ~/foo/.git/worktrees/dev-branch --> ~/foo/.git/worktrees/dev-branch-move
  • ~/foo/.git/worktrees/dev-branch-move/gitdir

Also, I suppose you would need to update ~/foo/.git/worktrees/dev-branch-move/gitdir if you moved the linked directory tree to a different directory

What's next?

In https://git-scm.com/docs/git-worktree, it is addressed that remove and mv commands would be good for git worktree, and I agree. Right now, it's a set of manual steps.

Comments