musteresel's blog


Gotcha with git worktree and removing the worktree directory

2018-01-31

tagged: git

git worktree is a very usefull feature of git which allows for easy hotfixes and can even be used for deploying. I just stumbled over a gotcha, though.

The man page says (emphasis mine):

When you are done with a linked working tree you can simply delete it. The working tree’s administrative files in the repository [..] will eventually be removed automatically [..], or you can run git worktree prune in the main or any linked working tree to clean up any stale administrative files.

So, my expectation was that I can add a worktree, remove the containing directory, and if it’s then still in git worktree list then just recreate the directory and continue my work:

git worktree add feature
git worktree list
# /tmp/repo          <COMMIT> [master]
# /tmp/repo/feature  <COMMIT> [feature]
rm -r feature
git worktree list
# /tmp/repo          <COMMIT> [master]
# /tmp/repo/feature  <COMMIT> [feature]
mkdir feature
touch feature/file
git worktree list
# /tmp/repo          <COMMIT> [master]
# /tmp/repo/feature  <COMMIT> [feature]

And indeed, the above output shows that git hasn’t pruned (removed) the feature worktree until now. Changing into the feature directory though reveals the truth: git commands just act on the main worktree. And further commands…

git worktree prune
git worktree list
# /tmp/repo          <COMMIT> [master]

…show that the worktree is really gone.

Why is that? Because the man page is a bit unclear: git doesn’t really care about the directory, but rather about a hidden file it creates within said directory:

git worktree add new_feature
ls -a1 new_feature
# ...
# .git
# ...

Removing this file is what actually kills the worktree even before the prune. That file is though only mentioned shortly in the man page:

These settings are made in a .git file located at the top directory of the linked working tree.

A quick look at the contents of the .git shows which settings are in there (note: there can be more than shown here):

gitdir: /tmp/repo/.git/worktrees/new_feature

So it’s really the combination of keeping the path to the worktree and the .git file within the worktree to keep the worktree alive.

You may ask how I could stumble over this? Or rather why I would want to remove the worktree directory. The answer is: My make clean command did just that … rm -r build/ (where build/ is a git worktree) … because it was the simplest solution.