worktree¶
Create and manage parallel working copies as git worktrees.
Every project that consumes this collection gets the same predictable, reusable
way to spin up an isolated checkout for a feature branch, without ever switching
the primary checkout off develop.
See References → Common contract for the
USER_WORKING_DIR, pin-strategy, and override-syntax conventions that
apply across every module.
Prerequisites¶
giton thePATH, run from inside a repository that has anoriginremote. The module derives<repo>fromorigin, never from the current working directory.- Optionally, the
NOLTE_WORKTREE_ROOTenvironment variable to choose where worktrees land. It wins over theWORKTREE_ROOT_DEFAULTvariable at runtime; both default to~/repos/.worktrees.
Layout¶
Every worktree lands in the same predictable place:
${NOLTE_WORKTREE_ROOT:-~/repos/.worktrees}/<repo>/<slug>/
<repo> comes from the origin remote and <slug> is a single
kebab-case path segment (the branch name with its prefix stripped, unless
you pass one explicitly).
Tasks¶
| Task | Description |
|---|---|
worktree:add -- <branch> [slug] |
Fetch the base ref, create <branch> off {{.WORKTREE_BASE_REF}} in a new worktree, and seed a .resume/<slug>/plan.md plan stub. |
worktree:remove -- <slug> [force] |
Remove the worktree for <slug>. The branch remains intact. Pass force to discard a worktree that still holds uncommitted or untracked work (including the seeded plan stub). |
worktree:list |
Run git worktree list for the current repository. |
worktree:root |
Print the resolved worktree root for this machine. |
worktree:add validates the branch prefix against
WORKTREE_ALLOWED_PREFIXES (the branching-model rule): the path slug may
drop the prefix, but the branch itself must carry one. The branch is always
cut from a freshly fetched {{.WORKTREE_BASE_REF}}, so it starts from the
remote tip regardless of the local checkout's state.
The seeded .resume/<slug>/plan.md is a plan-before-work gate. Fill it in
before starting substantive work, so a fresh resumable session started in the
worktree can pick the work up from a known starting point. It lives under
.resume/, which the consumer repository typically keeps out of version
control.
Variables¶
| Variable | Default | Purpose |
|---|---|---|
WORKTREE_BASE_REF |
origin/develop |
Ref new branches are cut from. |
WORKTREE_FETCH_REMOTE |
origin |
Remote fetched before the worktree is created. |
WORKTREE_FETCH_BRANCH |
develop |
Branch fetched before the worktree is created. |
WORKTREE_ALLOWED_PREFIXES |
feat fix chore docs exp |
Space-separated branch prefixes accepted by add. |
WORKTREE_ROOT_DEFAULT |
~/repos/.worktrees |
Fallback root when NOLTE_WORKTREE_ROOT is unset. |
Example¶
version: '3'
vars:
TASK_COLLECTION_BASE: https://raw.githubusercontent.com/nolte/taskfiles/main/src
includes:
worktree:
taskfile: "{{.TASK_COLLECTION_BASE}}/taskfile-include-worktree.yaml"
vars:
WORKTREE_BASE_REF: "origin/main"
WORKTREE_ALLOWED_PREFIXES: "feat fix chore"
The includes: key is the task namespace, and the consumer chooses it. If
"worktree" is not the vocabulary you want, mount the same module under a
different key — for example workingcopy — and every task answers under that
prefix instead, with identical behaviour:
includes:
workingcopy: "{{.TASK_COLLECTION_BASE}}/taskfile-include-worktree.yaml"
task workingcopy:add -- feat/parser-fix
task workingcopy:remove -- parser-fix
Then, from the consumer's working directory:
# Create a worktree for a feature branch off the base ref.
task worktree:add -- feat/parser-fix
# Pass an explicit short slug for the directory name.
task worktree:add -- chore/ci-tidy ci
# Inspect and clean up.
task worktree:list
task worktree:remove -- ci
Troubleshooting¶
worktree:addrejects the branch. The branch must carry one of the prefixes inWORKTREE_ALLOWED_PREFIXES(defaultfeat fix chore docs exp). The slug may drop the prefix, but the branch must not.worktree:removefails withoutforce.git worktree removerefuses to drop a worktree with uncommitted or untracked files, and the seeded.resume/<slug>/plan.mdcounts as untracked unless the consumer gitignores.resume/. Re-run withtask worktree:remove -- <slug> forceto discard it, or commit/move the work first.- The worktree landed in the wrong place. The root is
NOLTE_WORKTREE_ROOTif set, otherwiseWORKTREE_ROOT_DEFAULT. Runtask worktree:rootto see the resolved value for the current machine. originnot found. The module derives<repo>from theoriginremote. Add one (git remote add origin …) or run inside a clone that already has it.