BlogHome

How git worktree changed my workflow

2025-06-03

While working on a distributed database at Oxla I've become more and more frustrated with constant stashing, checkouting and checkouting back while changing branches and tasks I was working on. Adding to the frustration was also a constant need to rebuild the project after changing branches, wasting my precious time. For these reasons I've adopted a git worktree workflow.

What is a git worktree?

It's a git feature that allows you to checkout and work on multiple git branches simultaneously.

From git docs:

Allows to manage multiple working trees attached to the same repository. A git repository can support multiple working trees, allowing you to check out more than one branch at a time.

Why would you need it?

Let's imagine a scenario when you've got a person needing your urgent help at work. To do that you would need to check out their branch, and possibly help them with some of the changes. Also let's imagine you are in the middle of working on a high priority task, with changes in quite a lot of files. The ordinary way would be to either commit and push work-in-progress changes, or git stash them. But there is a much better way.

How does it work?

1. Create a bare git repository.

When creating "ordinary" git repository, under the hood it has exactly one worktree checked out. It's the branch you are working on. And at all times you are operating on this one worktree -> one branch.

It can be easily verified by running:

$ git worktree list
/home/mateusz/Projects/mgrzonka.me  0aea21e [master]

To start actually using the git worktree magic you have to first create a bare git repository. It's basically a contents of your "ordinary" .git folder without any worktree checked out.

For the needs of this article let's assume you already have some repository laying around. Let's just clone it with a --bare flag:

$ git clone --bare git@github.com:alpinus4/mgrzonka.me.git mgrzonkame-worktree

Listing worktrees shows there's none, what a surprise :D

$ git worktree list
/home/mateusz/Repos/mgrzonkame-worktree  (bare)

But there're usual contents of a .git directory:

$ ll
total 60K
drwxr-xr-x. 1 mateusz mateusz   0 Jun  5 00:14 branches/
-rw-r--r--. 1 mateusz mateusz 123 Jun  5 00:14 config
-rw-r--r--. 1 mateusz mateusz  73 Jun  5 00:14 description
-rw-r--r--. 1 mateusz mateusz  23 Jun  5 00:15 HEAD
drwxr-xr-x. 1 mateusz mateusz 556 Jun  5 00:14 hooks/
drwxr-xr-x. 1 mateusz mateusz  14 Jun  5 00:14 info/
drwxr-xr-x. 1 mateusz mateusz  16 Jun  5 00:14 objects/
-rw-r--r--. 1 mateusz mateusz 48K Jun  5 00:15 packed-refs
drwxr-xr-x. 1 mateusz mateusz  18 Jun  5 00:14 refs/

2. Create a new worktree

$ git worktree add _master master

It creates a directory _master with contents of the master branch.

Note

I start the names of worktree directories with an underscore _, so that they are more clearly separated from internal git directories.

One important thing to note is that one branch can only be checked out on one worktree simultaneously. It's not possible to create two master worktrees. This restriction enforces, so that when making new changes you also have to make a new branch for it, which I think is a good practice overall.

Checked out worktree behaves virtually the same as “normally” cloned repository. So all your usual git commands and workflows will work.

3. Creating new branch

Similarly to git checkout -b, -b flag can also be used with worktree command:

$ git worktree add _TASK-2137-feature-a -b TASK-2137-feature-a 

It creates a new branch TASK-2137-feature-a that's inside a directory _TASK-2137-feature-a.

4. Removing worktree

To remove worktree simply run:

$ git worktree remove _TASK-2137-feature-a

It will remove the worktree directory, but the branch will stay unaffected.

Next steps

To make the workflow more streamlined I've created some bash functions

gitworktreeb() {
    if [[ -n $1 && -n $2 ]]; then
        git worktree add "_$1" "$2" -b "$1"
    else
        git worktree add "_$1" master -b "$1"
    fi
}

gitworktreerb() {
    git fetch
    git worktree add --track -b "$1" "_$1" "origin/$1"
}                                                                                             

alias gitpushb='git push -u origin $(git branch --show-current)'   

Simply put them into your .bashrc.

Managing branches becomes much easier:

# creating new branch
$ gitworktreeb my-new-branch

# creating new branch from branch other than master
$ gitworktreeb my-new-branch from-other-branch

# grabbing a branch from origin remote
$ gitworktreerb my-remote-branch

For pushing the branch for the first time I'm using gitpushb alias. It's much more convenient than writing the whole thing.