Git Worktrees

Posted on July 18, 2021
Tags: DevOps, git, IT

What is this?1

It basically solve the problem of caching your changes when you want to switch from a feature to another without committing.

Before that, I generally used to do this by stashing my changes then reapplying them afterwards, but clearly that was slow and error prone.

How to

First, this workflow makes more sense with bare repos, you can of course create worktrees out of regular repositories, but in that case make sure to place them outside the original repo.

Let’s begin by cloning a repo using the --bare option:

/tmp
 git clone --bare https://github.com/xmonad/xmonad.git

A bare repo is just the content of the .git directory:

/tmp/xmonad.git · (master)
 ll
  Permissions Size User  Date Modified  Name
  drwxr-xr-x     - david 18 juil. 12:23 branches
  .rw-r--r--   128 david 18 juil. 12:23 config
  .rw-r--r--    73 david 18 juil. 12:23 description
  .rw-r--r--    23 david 18 juil. 12:23 HEAD
  drwxr-xr-x     - david 18 juil. 12:23 hooks
  drwxr-xr-x     - david 18 juil. 12:23 info
  drwxr-xr-x     - david 18 juil. 12:23 objects
  .rw-r--r--  2,2k david 18 juil. 12:23 packed-refs
  drwxr-xr-x     - david 18 juil. 12:23 refs

Add a worktree:

/tmp/xmonad.git · (master)
 git worktree add master
  Preparing worktree (checking out 'master')
  HEAD is now at af354f7 Merge pull request #312 from mzrinsky/update-cheatsheet

We can see that a new directory "master" has appeared2:

/tmp/xmonad.git · (master)
 ll
  Permissions Size User  Date Modified  Name
  drwxr-xr-x     - david 18 juil. 12:23 branches
  .rw-r--r--   128 david 18 juil. 12:23 config
  .rw-r--r--    73 david 18 juil. 12:23 description
  .rw-r--r--    23 david 18 juil. 12:23 HEAD
  drwxr-xr-x     - david 18 juil. 12:23 hooks
  drwxr-xr-x     - david 18 juil. 12:23 info
  drwxr-xr-x     - david 18 juil. 12:24 master
  drwxr-xr-x     - david 18 juil. 12:23 objects
  .rw-r--r--  2,2k david 18 juil. 12:23 packed-refs
  drwxr-xr-x     - david 18 juil. 12:23 refs
  drwxr-xr-x     - david 18 juil. 12:24 worktrees

In it, there is indeed the content of master:

/tmp/xmonad.git/master · (master)
 ll
  Permissions Size User  Date Modified  Name
  .rw-r--r--   130 david 18 juil. 12:24 cabal.haskell-ci
  .rw-r--r--    43 david 18 juil. 12:24 cabal.project
  .rw-r--r--  8,2k david 18 juil. 12:24 CHANGES.md
  .rw-r--r--  2,3k david 18 juil. 12:24 CONFIG
  .rw-r--r--  5,9k david 18 juil. 12:24 CONTRIBUTING.md
  .rw-r--r--  5,8k david 18 juil. 12:24 INSTALL.md
  .rw-r--r--  1,5k david 18 juil. 12:24 LICENSE
  .rw-r--r--   526 david 18 juil. 12:24 Main.hs
  .rw-r--r--  3,9k david 18 juil. 12:24 MAINTAINERS.md
  drwxr-xr-x     - david 18 juil. 12:24 man
  .rw-r--r--  4,2k david 18 juil. 12:24 README.md
  .rw-r--r--    76 david 18 juil. 12:24 Setup.lhs
  drwxr-xr-x     - david 18 juil. 12:24 src
  .rw-r--r--   447 david 18 juil. 12:24 stack.yaml
  .rw-r--r--   771 david 18 juil. 12:24 STYLE
  drwxr-xr-x     - david 18 juil. 12:24 tests
  .rw-r--r--   51k david 18 juil. 12:24 TUTORIAL.md
  drwxr-xr-x     - david 18 juil. 12:24 util
  .rw-r--r--  5,5k david 18 juil. 12:24 xmonad.cabal

Let’s go back and add another worktree:

/tmp/xmonad.git · (master)
 git worktree add update_readme
  Preparing worktree (checking out 'update_readme')
  HEAD is now at af354f7 Merge pull request #312 from mzrinsky/update-cheatsheet

Use git worktree list to list the worktrees:

/tmp/xmonad.git · (master)
 git worktree list
  /tmp/xmonad.git                (bare)
  /tmp/xmonad.git/master         67b010a [master]
  /tmp/xmonad.git/update_readme  67b010a [update_readme]

Let’s go to the update_readme directory and edit the Readme, then commit the changes:

/tmp/xmonad.git/update_readme · (update_readme±)
 git commit -am 'Update readme.'

Then go to the master worktree and merge the changes:

/tmp/xmonad.git/master · (master)
 git merge update_readme
  Updating af354f7..67b010a
  Fast-forward
  README.md | 2 ++
  1 file changed, 2 insertions(+)

The obvious advantage of this method is that we can easily create as many worktrees as needed, and switch between them in a split second.

Manage worktrees from Emacs with Magit

Magit support worktrees natively, to use them just run the following command:

M-x magit-worktree

A menu will pop, and you will be able to choose between different options, to create a worktree and a branch like above, type “c”.

You will then be asked to provide:

  • the path and the name of the worktree
  • the branch or tag from which to checkout
  • the name of the branch

How to delete a worktree

git worktree remove [name]

Footnotes


  1. I discovered it with this video, go watch it if you prefer this format.↩︎

  2. If you reference an existing branch, then git will add a worktree referencing that branch. In the other case, git will create a new branch from master.↩︎