My thoughts on git worktrees for me (and some notes on things I tried)
I recently discovered git worktrees and did some experimentation with using them for stuff that I do. The short summary of my experience so far is that while I can see the appeal for certain sorts of usage cases, I don't think git worktrees are a good fit for my situation and I'm probably to use completely independent repositories in the future.
My usage case was building my own copies of multiple versions of some project, starting with Go. Especially in the case of a language compiler and its standard library, it's reasonably useful to have the latest development version plus a stable version or two; for example, it gives me an easy way to test if something I'm working on will build on older released versions or if I've let a dependency on some recent bit of the standard library creep in. The initial process of creating a worktree for, say, Go 1.8 is reasonably straightforward:
cd /some/where/go git worktree add -b release-branch.go1.8 ../v1.8 origin/release-branch.go1.8
What proved tricky for me is updating this
v1.8 tree when the Go
people update Go 1.8, as they do periodically.
My normal way of staying up to date on what changes are happening
in the main line of Go is to do '
git pull' in my master repo
directory, note the lines that get printed out about fetched updates,
remote: Finding sources: 100% (64/64) remote: Total 64 (delta 23), reused 64 (delta 23) Unpacking objects: 100% (64/64), done. From https://go.googlesource.com/go ffab6ab877..d64c49098c master -> origin/master
And then I use '
git log ffab6ab877..d64c49098c' to see what
changed. The problem with worktrees is that this information is
printed by '
git fetch', and normally '
git fetch' updates all
branches, both the mainline and, say, a release branch you're
following. So I actively don't want to run '
git pull' or '
fetch' in the worktree directory, because otherwise I will have
to remember to stop and look at the mainline updates it's just
fetched and reported to me.
What I wound up doing was running '
git pull' in my main
and if there was an update to origin/release-branch.go1.8 reported,
I'd go to my '
v1.8' directory and do '
git merge --ff-only'. This
mostly worked (it blew up on me once for reasons I don't understand),
but it means that dealing with a worktree is different than dealing
with a normal Git repo directory (including an independently cloned
repo). Since '
git pull' and other Git commands work 'normally' in a
worktree, I have to explicitly remember that I created something as a
worktree (or check to see if
.git is a directory to know, since '
status' doesn't helpfully tell you one way or the other).
(In my current moderate level of Git knowledge and experience, I'm
going to avoid writing about the good usage cases I think I see for
worktrees. Anyway, one of them is documented in the
manpage; I note that their
scenario uses a worktree for a one-shot branch that's never updated
As mentioned, if I want to see if a particular Git repo is a worktree
or not I need to do '
ls -ld .git'. If it's a file, I have a
worktree. If I have a directory, with how I currently use Git,
it's a full repo. '
git worktree list' will list the main repo
and worktrees, but it doesn't annotate things with a 'you are here'
marker. Obviously if I used worktrees enough I could write a status
command to tell me, but then if I was doing that I could probably
write a bunch of commands to do what I want in general.
Sidebar: Excessively clever Git configuration hacking (maybe)
Bearing in mind that I don't understand Git as much as I think I
may, as far as I can see what branches '
git fetch' fetches are
determined from the configuration for the remote for a branch, not
from the branch's configuration. There appear to be two options for
fiddling things here.
The 'obvious' option is to create a second remote (call it, say,
'v1.8-origin') with the same
origin but a
setting that only fetches the particular branch:
fetch = refs/heads/release-branch.go1.8:refs/remotes/origin/release-branch.go1.8
Then I'd switch the
remote for the release-branch.go1.8 branch to
this new remote.
Git-fetch also has a feature where you can have a per-branch
$GIT_DIR/branches/<branch>; this can be used
to name the upstream 'head' (branch) that will be fetched into the
local branch. It appears that creating such a file should do the
trick, but I can't find people writing about this on the Internet
(just many copies of the
git-fetch manpage), so I'm wary of
assuming that I understand what's going to happen here. Plus, it's
apparently a deprecated legacy approach.
(If I understand all of this correctly, either approach would
git pull' in the main repo (which is on the
branch) always fetching all branches from upstream.)