How to create, use and remove worktrees, as an alternative to constantly stashing and popping when you need to change contexts.

From man git-worktree:

A git repository can support multiple working trees, allowing
you to check out more than one branch at a time. With git
worktree add a new working tree is associated with the
repository. This new working tree is called a "linked working
tree" as opposed to the "main working tree" prepared by "git
init" or "git clone". A repository has one main working tree
(if it’s not a bare repository) and zero or more linked working
trees. When you are done with a linked working tree, remove it
with git worktree remove.

Also from man git-worktree:

git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
git worktree list [--porcelain]
git worktree lock [--reason <string>] <worktree>
git worktree move <worktree> <new-path>
git worktree prune [-n] [-v] [--expire <expire>]
git worktree remove [-f] <worktree>
git worktree unlock <worktree>

Drawbacks of git stash:

  • Constant stashing and unstashing to move between different working directory states.
  • git stash doesn't work with unmerged paths, after a merge or rebase with conflicts.
  • If you work with a compiled language, stashing means you'll have to recompile everything upon unstashing.

Applications of worktrees:

  • Emergency fixes.
  • Having multiple released versions of the software open at a time, for maintenance.
  • Testing that takes a long time that would normally tie up your working directory.
  • Comparing the behaviour of different versions of the software.

Weaknesses of worktrees:

  • Can't (normally) have the same branch checked out in two working trees.
  • Incomplete support for submodules.

From within your current working tree, create a new worktree outside the working tree, with a new branch called emergency off of the current master branch:

$ git worktree add -b emergency ../temp master

cd to that new worktree, work, commit your work on the emergency branch, and return to your original working tree:

$ pushd ../temp
... hack hack hack hack hack hack hack hack hack fix ...
$ git commit -a -m "commit emergency fix"
$ popd

where you can see the additional branch you created in the worktree:

$ git branch
  emergency
* master
$

If you have no further use for the worktree, remove it, which has no effect on the branch that was created:

$ git worktree remove ../temp
$ git branch
  emergency
* master
$

Using the “Pro Git” book as a test repo, start with some staged and unstaged changes on the master branch:

$ git status
On branch master
Your branch is up to date with 'rpjday/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   README.asc

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   LICENSE.asc

$

The original working tree is referred to as the “main working tree”, while subsequent worktrees are “linked working trees,” so you always have at least one worktree:

$ git worktree list
/home/rpjday/ebooks/progit/progit2  dd124f7 [master]
$

Create a working tree off of an earlier tag (2.1.100):

$ git worktree add -b emergency ../temp 2.1.100
Preparing worktree (new branch 'emergency')
HEAD is now at f2d0827 Merge pull request #1145 from rpjday/topic/rpjday/maintaining
$

List the working trees:

$ git worktree list
/home/rpjday/ebooks/progit/progit2  dd124f7 [master]
/home/rpjday/ebooks/progit/temp     90bdf0a [emergency]
$

Note that nothing has changed in the current working tree:

$ git status
On branch master
Your branch is up to date with 'rpjday/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   README.asc

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   LICENSE.asc

$
$ pushd ../temp

Check the status:

$ git status
On branch emergency
nothing to commit, working tree clean
$

List all working trees:

$ git worktree list
/home/rpjday/ebooks/progit/progit2  dd124f7 [master]
/home/rpjday/ebooks/progit/temp     90bdf0a [emergency]
$

Verify current branch, and current position in history:

$ git show
commit f2d082747b0a9474f9d2d08c6945482b55691e4a (HEAD -> emergency, tag: 2.1.100)
Merge: 7a13f5a 5c0663e
Author: Ben Straub <ben@straub.cc>
Date:   Mon Dec 10 10:06:11 2018 -0800

    Merge pull request #1145 from rpjday/topic/rpjday/maintaining
    
    maintaining: explain how "git describe" works with lightweight tags

$

Examine the .git file in this linked working tree – this is what identifies a linked working tree:

$ cat .git
gitdir: /home/rpjday/ebooks/progit/progit2/.git/worktrees/temp
$

Make a change, and stage it:

$ git diff --cached
diff --git a/README.asc b/README.asc
index fa40bad..9d69d12 100644
--- a/README.asc
+++ b/README.asc
@@ -1,3 +1,5 @@
+Change to README.asc on emergency branch in worktree
+
 = Pro Git, Second Edition
 
 Welcome to the second edition of the Pro Git book.
$

Commit:

$ git commit -m "Change README.asc in worktree"
[emergency 8b31c7b] Change README.asc in worktree
 1 file changed, 2 insertions(+)
$

Verify:

$ git show
commit 8b31c7b18e22fddca50eb9af706b6927f2c14ddc (HEAD -> emergency)
Author: Robert P. J. Day <rpjday@crashcourse.ca>
Date:   Sun Feb 24 08:36:12 2019 -0500

    Change README.asc in worktree

diff --git a/README.asc b/README.asc
index fa40bad..9d69d12 100644
--- a/README.asc
+++ b/README.asc
@@ -1,3 +1,5 @@
+Change to README.asc on emergency branch in worktree
+
 = Pro Git, Second Edition
 
 Welcome to the second edition of the Pro Git book.
$
$ popd
~/ebooks/progit/progit2
$

Everything here is just where we left it:

$ git status
On branch master
Your branch is up to date with 'rpjday/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   README.asc

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   LICENSE.asc

$

Linked working tree and emergency branch are still there:

$ git worktree list
/home/rpjday/ebooks/progit/progit2  5df317d [master]
/home/rpjday/ebooks/progit/temp     8b31c7b [emergency]
$
$ git branch
  emergency
* master
$

What you choose to do with the work committed to the emergency branch is up to you.

Once you're done with the worktree, remove it and “prune” it:

$ git worktree remove ../temp
[rpjday@localhost progit2]$ git worktree list
/home/rpjday/ebooks/progit/progit2  5df317d [master]
$

Note that the emergency branch still exists.

$ git branch
  emergency
* master
$
  • locking and unlocking working trees
  • moving working trees
  • git config --worktree (man git-config)
  • config var extensions.worktreeConfig
  • config var gc.worktreePruneExpire
  • config var worktree.guessRemote
  • setting up tracking branches
  • detached HEAD state
$GIT_DIR/config.worktree
    This is optional and is only searched when
    extensions.worktreeConfig is present in $GIT_DIR/config.
  • git_worktree.txt
  • Last modified: 2019/03/06 08:24
  • by rpjday