My Git Worktree Workflow

I recently made a change to how I work on repositories when I have to work with others. It has been a life saver.

There is a TLDR example at the bottom to get you interested in the rest of the article.

Pain Points

Lets start with the normal git workflow and the pain points it causes. First you clone the repo. You create your branch and you start coding away. Then your buddy hits you up on Slack with a huge PR and scrolling the diff in Github isn't enough for you to approve it. Great. Now you have to stash the code you are working on, update your base so that you have their branch, and pull it all down. Now you can finally review the PR. This is nuts. Its annoying and you completely lose track of whatever you were just working on. I didn't even cover getting back to your branch and getting going again.

Painting a Picture With a Dev Story

As a developer I want to be able to review my teammates code without having to stash my work or maintain a second copy of the entire repository. I also want to be able to pause my work and work on a different branch again without having to stash my work or maintain a second copy.

Enter Git Worktree

First off, here is a link to the git worktree documentation. I won't cover every caveat. So check it out for a lot more info.

Git is capable of maintaining multiple working trees of a single repo. You heard that right. With git worktree you can check out more than one branch at a time and sensibly maintain each of those branches.

I had to rethink my folder folder structure when I started using worktree. It was weird at first. But its been amazing after I settled in.

When I clone a repository with worktree in mind I will do it a little differently.

$ git clone [email protected]:googleapis/python-tasks.git python-tasks/main

This is where the main branch lives. I really don't touch it except to fetch/pull and create new branches with worktree. Its my clean source of truth and I like to keep it that way.

From here I will create a new branch to for whatever I'm working on But I won't use git branch ${branchName} or git checkout -b ${branchName}. I'll do it with worktree while i'm in the main branch folder.

$ git worktree add -b my-awesome-branch ../my-awesome-branch main

Pay attention to that ../ If you forget it you will end up putting the branch in the folder where main lives and that can get real ugly, real fast.

So now we have:

~/Projects
  /python-tasks
    /main
    /my-awesome-branch

You can now list all of your working trees with the list argument.

$ git worktree list
~/Projects/python-tasks/main                63df2ef [master]
~/Projects/python-tasks/my-awesome-branch   63df2ef [my-awesome-branch]

I change to that directory. I do my work. Next thing I know I get a message from a teammate and he needs me to do that PR review we've been talking about. With git worktree its too easy. I just go back to main and add his branch to my worktree.

$ git worktree add --track -b add-appengine-flexible-tasks-samples ../ppr-review origin/add-appengine-flexible-tasks-samples

Preparing worktree (new branch 'add-appengine-flexible-tasks-samples')
Branch 'add-appengine-flexible-tasks-samples' set up to track remote branch 'add-appengine-flexible-tasks-samples'  from 'origin'.
HEAD is now at e2c8eee chore: generate noxfile.py for samples

There is a lot going on in that command. So check out the doc for the full explanation, but I'll try to break it down.

git worktree add: We've seen this. Its the base command to create a branch and a working tree

--track: This sets up tracking mode. We need it to track a remote branch

-b: This will create a new branch and we give it the same name as the remote branch because its the right thing to do. But really we could call it anything.

../pr-review The path we want it checked out to. Don't forget that ../

origin/add-appengine-flexible-tasks-samples Finally the <remote>/<branch> that we want check out.

Now if we do git worktree list We'll see our friends branch in our working tree ready for review.

$ git worktree list
~/Projects/python-tasks/main                63df2ef [master]
~/Projects/python-tasks/my-awesome-branch   63df2ef [my-awesome-branch]
~/Projects/python-tasks/pr-review           e2c8eee [add-appengine-flexible-tasks-samples]

Pretty sweet.

Now I've finished my PR review and I don't need the code anymore. so all we have to do from main is

$ git worktree remove pr-review

Poof, The folder is gone and we don't have to clog our working tree up with it anymore. Sometimes we get lazy right? maybe I did rm -rf ./pr-review. This is bad. I just broke my working tree. The folders gone but git still knows about it when I do git worktree list How can we fix this? Is it time to just throw the whole repo away and clone again? What do we do?

From main as usual:

$ git worktree prune

And now our mistake has been removed from the git and we can continue on about our day.

There are a few other worktree commands out there. Checkout lock and unlock if you need to put a working tree on a thumb drive or a network share thats not always mounted. That will protect it from prune. This was a long one. If you stuck around and found it useful, feel free to hit me up on my socials to tell me about it or teach me a new trick you found thats even better.

TLDR Example

$ git worktree add -b emergency-fix ../temp main
$ pushd ../temp
# ... hack hack hack ...
$ git commit -a -m 'emergency fix for boss'
$ popd
$ git worktree remove ../temp

33