Git Work Trees
Tags: git, worktree, vcs
Published: 2023-03-01 05:26:35

## Overview

The [git worktree](https://git-scm.com/docs/git-worktree) command allows you to use and/or manage multiple work trees at the same time.

So what's a work tree? You're already using one, you just might not know it. Or you might call it "working copy".

When you clone a repository the classic way (or create a new repository using `git init`), git will create what is called the "main work tree":

1. it clones the "bare" repository in the `.git` folder
1. it creates the main work tree one level above the bare repository; what you might already know as simply "the folder in which the repository was cloned"

For example:

```shell
$ git clone git@github.com:bsrz/mvvm.git
Cloning into 'mvvm'...
remote: Enumerating objects: 131, done.
remote: Counting objects: 100% (131/131), done.
remote: Compressing objects: 100% (79/79), done.
remote: Total 131 (delta 56), reused 109 (delta 38), pack-reused 0
Receiving objects: 100% (131/131), 474.84 KiB | 2.53 MiB/s, done.
Resolving deltas: 100% (56/56), done.

$ cd mvvm
$ ls -la
total 32
drwxr-xr-x  11 bsrz  staff    352 Feb 28 23:30 ./
drwxr-xr-x  25 bsrz  staff    800 Feb 28 23:30 ../
drwxr-xr-x  12 bsrz  staff    384 Feb 28 23:31 .git/
-rw-r--r--   1 bsrz  staff   2171 Feb 28 23:30 .gitignore
drwxr-xr-x   4 bsrz  staff    128 Feb 28 23:30 .img/
-rw-r--r--   1 bsrz  staff   1069 Feb 28 23:30 LICENSE
drwxr-xr-x   5 bsrz  staff    160 Feb 28 23:30 MVVM/
drwxr-xr-x   5 bsrz  staff    160 Feb 28 23:30 MVVM.xcodeproj/
drwxr-xr-x   4 bsrz  staff    128 Feb 28 23:30 MVVMTests/
drwxr-xr-x   4 bsrz  staff    128 Feb 28 23:30 MVVMUITests/
-rw-r--r--   1 bsrz  staff  22913 Feb 28 23:30 README.md
```

You can see the `.git` folder, and one level above (aka the current directory) contains all of the files committed to the repository, aka the work tree, or working copy.

## Why?

Ever had a situation where you had modified files and someone (maybe your boss) asked you to look into a bug in production code? Or maybe you have a major refactoring effort in progress with hundreds (or thousands!) of modified files, but you were asked to quickly complete another task?

If so, then you'll be familiar with the Ill-just-commit-everything-I-have-in-a-WIP-commit approach or the stash management hell.

Wouldn't it be a dream to have more than one branch checked out at the same time? This is why.

## Work trees

Let's try out the same example as before but using work trees:

```shell
$ mkdir mvvm # 1
$ cd mvvm # 2
$ git clone --bare git@github.com:bsrz/mvvm.git .bare # 3
Cloning into bare repository '.bare'...
remote: Enumerating objects: 131, done.
remote: Counting objects: 100% (131/131), done.
remote: Compressing objects: 100% (79/79), done.
remote: Total 131 (delta 56), reused 109 (delta 38), pack-reused 0
Receiving objects: 100% (131/131), 474.84 KiB | 2.43 MiB/s, done.
Resolving deltas: 100% (56/56), done.
```

1. Start by creating the directory that will contain all of your branches and the bare repository
1. Change the directory to the newly created one
1. This is the key part here, you want to clone a _bare_ version of the repository; this is more or less cloning only the `.git` folder that's normally done automatically by the classic clone method

Next, you'll create the main work tree. This is done by using the `git worktree add` method to register a new work tree with the bare repository:

```shell
$ cd .bare # 1
$ git worktree add ../main main # 2
Preparing worktree (checking out 'main')
HEAD is now at 0db2467 <commit message>
$ cd ../main # 3
$ ls -la # 4
total 36
drwxr-xr-x  11 bsrz  staff    352 Feb 28 23:53 ./
drwxr-xr-x   4 bsrz  staff    128 Feb 28 23:53 ../
-rw-r--r--   1 bsrz  staff     61 Feb 28 23:53 .git
-rw-r--r--   1 bsrz  staff   2171 Feb 28 23:53 .gitignore
drwxr-xr-x   4 bsrz  staff    128 Feb 28 23:53 .img/
-rw-r--r--   1 bsrz  staff   1069 Feb 28 23:53 LICENSE
drwxr-xr-x   5 bsrz  staff    160 Feb 28 23:53 MVVM/
drwxr-xr-x   5 bsrz  staff    160 Feb 28 23:53 MVVM.xcodeproj/
drwxr-xr-x   4 bsrz  staff    128 Feb 28 23:53 MVVMTests/
drwxr-xr-x   4 bsrz  staff    128 Feb 28 23:53 MVVMUITests/
-rw-r--r--   1 bsrz  staff  22913 Feb 28 23:53 README.md
```

1. Change the directory to the bare repository
1. Add the main work tree to the mvvm folder, one level above the bare repository; 🗒️ this structure is purely a personal choice, you can clone the repository anywhere you want and check out branches anywhere you want
1. Change the directory to your newly created work tree
1. The committed files in your repository appear in the same way as before

You can work in the main directory in the exact same way as you were before. You can checkout other branches, you can stash modified files, you can commit files, you can rebase, merge, etc...

The power of bare repositories lie in their ability to add a 2nd, a 3rd, an Nth work tree and checkout another branch inside of them.

```shell
$ cd ../.bare # 1
$ git worktree add --track ../my-awesome-branch -b my-awesome-branch # 2
Preparing worktree (new branch 'my-awesome-branch')
branch 'my-awesome-branch' set up to track 'main'.
HEAD is now at 0db2467 <commit message>
$ cd ../my-awesome-branch # 3
```

1. Go back to the bare repository
1. Add a new work tree, this time we're creating a new branch and tracking it
1. Change the directory to the new work tree

Now you can make modifications to the main work tree (using the main branch) _at the same time_ as making modifications to the my-awesome-branch work tree (using the my-awesome-branch branch). The same capabilities apply to the new work tree: you can checkout other branches, you can stash modified files, you can commit files, you can rebase, merge, etc...

The only _caveat_ here, is that a branch can only be checked out in a single work tree at a time. If you try to checkout a branch that's already checked out in a different work tree, you will receive this error:

```shell
$ git checkout main
fatal: 'main' is already checked out at '/Users/bsrz/Developer/mvvm/main'
```

## Conclusion

Although, I don't always use this method, I'm starting to use it more and more. The ability to leave my work in progress as is and start new work in a separate folder has given me a ton of flexibility and has caused a lot less "git management" work. I no longer have to constantly manage stashes or save patches for later, and it allows me to pivot onto a new problem pretty quickly.

Hope this helped you learn something!
Cheers! 🍻