The 2016 Git Retrospective: Worktrees
The 2016 Git Retrospective: Worktrees
The awesome Git worktree command lets you check out and work on multiple repository branches in separate directories simultaneously.
Join the DZone community and get the full member experience.Join For Free
Whatever new awaits you, begin it here. In an entirely reimagined Jira.
It's been a busy year for the Git project! There were five huge feature releases in 2016 (v2.7 through v2.11) that introduced just over 4,000 new commits and a ton of new features. This is the first part of a six-part series looking at improvements made to some of Git's popular user-facing features.
In this article, we'll look at upgrades to the
git worktree command, which first appeared in Git v2.5 but was rounded out in 2016. Two valuable new features were introduced in v2.7 (the
list subcommand, and namespaced refs for bisecting), an important bug fix landed in v2.8, and the
unlock subcommands were implemented in v2.10.
Before we begin, note that many operating systems ship with legacy versions of Git, so it's worth checking that you're on the latest and greatest. If running
git --version from your terminal returns anything less than Git 2.11.0, head on over to Atlassian's quick guide to upgrade or install Git on your platform of choice.
git worktree command lets you check out and work on multiple repository branches in separate directories simultaneously. For example, if you need to make a quick hotfix but don't want to mess with your working copy, you can check out a new branch in a new directory with:
$ git worktree add -b hotfix/BB-1234 ../hotfix/BB-1234 Preparing ../hotfix/BB-1234 (identifier BB-1234) HEAD is now at 886e0ba Merged in bedwards/BB-13430-api-merge-pr (pull request #7822)
Worktrees aren't just for branches. You can check out tags as different worktrees in order to build or test them in parallel. For example, I created worktrees from the Git v2.6 and v2.7 tags in order to examine the behavior of different versions of Git:
$ git worktree add ../git-v2.6.0 v2.6.0 Preparing ../git-v2.6.0 (identifier git-v2.6.0) HEAD is now at be08dee Git 2.6 $ git worktree add ../git-v2.7.0 v2.7.0 Preparing ../git-v2.7.0 (identifier git-v2.7.0) HEAD is now at 7548842 Git 2.7 $ git worktree list /Users/kannonboy/src/git 7548842 [master] /Users/kannonboy/src/git-v2.6.0 be08dee (detached HEAD) /Users/kannonboy/src/git-v2.7.0 7548842 (detached HEAD) $ cd ../git-v2.7.0 && make
You could use the same technique to build and run different versions of your own applications side-by-side.
Git v2.7 rounded out the
git worktree command with a few improvements. The
git worktree list subcommand displays all of the worktrees associated with a repository:
$ git worktree list /Users/kannonboy/src/bitbucket/bitbucket 37732bd [master] /Users/kannonboy/src/bitbucket/staging d5924bc [staging] /Users/kannonboy/src/bitbucket/hotfix/BB-1234 37732bd [hotfix/BB-1234]
git bisect is a neat Git command that lets you perform a binary search of your commit history. It's usually used to find out which commit introduced a particular regression. For example, if a test is failing on the tip commit of my
master branch, I can use
git bisect to traverse the history of my repository looking for the commit that first broke it:
$ git bisect start # indicate the last commit known to be passing the tests (e.g. the latest release tag) $ git bisect good v2.0.0 # indicate a known broken commit (e.g. the tip of master) $ git bisect bad master # tell git bisect a script/command to run; git bisect will find the oldest commit # between "good" and "bad" that causes this script to exit with a non-zero status $ git bisect run npm test
Under the hood, bisect uses refs to track the good and bad commits used as the upper and lower bounds of the binary search range. Unfortunately for worktree fans, these refs were stored under the generic
.git/refs/bisect namespace, meaning that
git bisect operations that are run in different worktrees could interfere with each other.
As of v2.7, the bisect refs have been moved to
.git/worktrees/$worktree_name/refs/bisect, so you can run bisect operations concurrently across multiple worktrees.
When you're finished with a worktree, you can simply delete it and then run
git worktree prune or wait for it to be garbage collected automatically. However, if you're storing a worktree on a network share or removable media, then it will be cleaned up if the worktree directory isn't accessible during pruning — whether you like it or not! Git v2.10 introduced the
git worktree lock and
unlock subcommands to prevent this from happening:
# to lock the git-v2.7 worktree on my USB drive $ git worktree lock /Volumes/Flash_Gordon/git-v2.7 --reason "In case I remove my removable media" # to unlock (and delete) the worktree when I'm finished with it $ git worktree unlock /Volumes/Flash_Gordon/git-v2.7 $ rm -rf /Volumes/Flash_Gordon/git-v2.7 $ git worktree prune
--reason flag lets you leave a note for your future self, describing why the worktree is locked.
git worktree unlock and
lock both require you to specify the path to the worktree. Alternatively, you can
cd to the worktree directory and run
git worktree lock . for the same effect.
Other Small Improvements
For completeness, Git v2.7 also allows you to create local clones of worktrees (though cloning a worktree creates an entirely independent Git repository, not another worktree).
Git v2.8 also included small fixes for
-B options. By default, you can't have multiple worktrees with the same branch checked out. Appending
--force) to a command will override this restriction. Similarly, creating a new worktree with
git worktree add -b will fail if the named branch already exists. Using
-B instead of
-b will override this restriction and overwrite the existing branch for you. Prior to v2.8, there were a couple of bugs that meant that
-B behaved incorrectly in some scenarios if the
--force flag was not specified.
git worktree is already valuable for developers who need to work with multiple clones of their repository, and I'm sure we'll see even more improvements to this relatively new command in 2017. If you have any neat use cases or tips for using
git worktree, please share them with me on Twitter (I'm @kannonboy). Stay tuned for the next article in the retrospective series where we'll be investigating some of the improvements made to Git's diff subsystem in 2016. Or, if you just can't wait for more Git, head on over to Atlassian's Git tutorials for some tips and tricks to improve your workflow.
If you stumbled on these articles out of order, you can check out the other topics covered in our Git in 2016 retrospective below:
Or, if you've read 'em all and still want more, check out Atlassian's Git tutorials (I'm a regular contributor there) for some tips and tricks to improve your workflow.
Opinions expressed by DZone contributors are their own.