I use git worktrees a lot, and I’ve been annoyed by the repo itself having effectively a privileged worktree that’s always there (and refuses to let any other worktree check out the branch it’s on). And then not having a good place to put my other worktrees. If I clone to $PROJECTS/this-project, where do I put my worktrees?
- If I put them in the directory, the worktree directories are scattered among all the files in the clone’s working tree (and I seem to remember git having some issues with worktrees inside a working tree), but
- if I put them next to the directory, then the worktree names need to be prefixed with the repo name (and if there are repos
fooandfoo-charts, it’s easy to forget thatfoo-chartsisn’t just a worktree of foo.
So, my projects tended to look like this:
$PROJECT_DIR
├── my-project # a repo
│ ├── .git
│ └── ... # but also a working tree
├── my-project-add-some-feature # a worktree
│ └── ...
├── my-project-charts # a different repo, but with a worktree-looking name
│ ├── .git
│ └── ... # which also has its own working tree
├── my-project-charts-refactor-something # a worktree in the different repo
│ └── ...
└── my-project-fix-this-bug # another worktree in the first repo
└── ...
But now I have a new script for cloning a repo (abbreviated a bit here), which does this:
CLONE_DIR=$PROJECT_DIR/$REPO/repo
git clone $DOMAIN/$ORGANIZATION/$REPO $CLONE_DIR
cd $CLONE_DIR
git checkout $(git commit-tree $(git hash-object -t tree /dev/null) < /dev/null)which is what I wish git clone --bare did, but doesn’t quite. Basically, you end up with a repo without a working tree. So there’s no branch locked by the repo, and no privileged working tree.
So then I end up with something like this:
$PROJECT_DIR
├── my-project
│ ├── add-some-feature # a worktree
│ │ └── ...
│ ├── fix-this-bug # another worktree
│ │ └── ...
│ └── repo # the “bare” repo
│ └── .git # the only thing in the “bare” repo
└── my-project-charts # a different project with a worktree-looking name
├── refactor-something
│ └── ...
└── repo
└── .git
It’s annoying to have that script for cloning, so I’ll probably try to get rid of it. But after the initial clone, I really like working like this.
FWIW, I now use a very different approach (managed by a tool of my own called
astrolabe).This still uses bare repos (actually
--barenow), but shifts my development to be task-centric, rather that repo-centric.The bare repos are all hidden away as Astrolabe’s internal state, and each task directory has worktrees that reference those bare repos.
This helps with a few things
Footnotes
Astrolabe does a bunch of stuff for me, like:
- adding removing repos from a task;
- letting me know which tasks are clean, or in what state;
- cleaning up after finished tasks;
- overriding dependencies so that all of the worktrees in a task depend on each other rather than their usual published dependencies, etc.; and
- cleaning up bare repos that don’t have any worktrees or stashes.
↩