Multi-repo task workspaces backed by git worktrees.
If your work routinely touches a handful of repos at once — services and the
libraries they share — tsk automates the bootstrap: one task directory, one
worktree per repo, all on a fresh branch off each repo's default upstream.
go install github.com/invopop/tsk# 1. Create a task in the current directory.
tsk create app-489 multi-payment-methods
# → /path/to/cwd/app-489-multi-payment-methods
cd app-489-multi-payment-methods
# 2. Add as many repos as the task needs.
# Each becomes a worktree on a new branch named after the slug.
tsk add ../../gobl.html ../../pdf.go ../../at-pt
# 3. Look across worktrees at any time.
tsk status
# gobl.html [multi-payment-methods] M 3 ↑2
# pdf.go [multi-payment-methods] clean
# at-pt [multi-payment-methods] clean
# 4. Drop a repo if it turns out you don't need it.
tsk rm pdf.go
# 5. When the work is shipped (and pushed!) close the task.
cd ..
tsk close app-489-multi-payment-methodsThere is no global config and no central tasks directory. A task is just a
directory containing a .tsk.yaml marker:
ref: app-489
slug: multi-payment-methodsRun tsk create wherever you want the task to live. The task directory's name is
<ref>-<slug> (or just <slug> if you skip the ref). The branch created on each
source repo by tsk add defaults to <slug> — the ref is not part of the
branch name.
The base branch defaults to the first remote's default branch — e.g. on a
typical clone, origin/main, but tsk follows whatever the repo is actually
configured with (upstream/master works just the same). Override it with
--base <remote>/<branch> on tsk add (or tsk create when using -a):
# Base the new worktrees off origin/develop instead of the default.
tsk add --base origin/develop ../../gobl.html
The full `<remote>/<branch>` form is required so it's never ambiguous whether
you mean a local branch or a remote-tracking one.
When `--base` helps:
- **Stacking on an unreleased feature branch.** Your task depends on a
colleague's change that is approved but not yet merged to `main`. Branching
off their feature branch keeps your diff focused on your own work instead
of dragging in theirs, and avoids the "merge their branch into mine, then
rebase later" dance.
- **Long-lived integration branches.** When several tasks land into a shared
`develop` (or similar) before promotion, base new worktrees there so each
task starts from the state the integration branch is actually in.
## Adding the same repo twice
By default each worktree's directory is named after the repo (`gov-fr`), so
adding the same repo again would collide. Use `--as <name>` to give the second
worktree its own directory name, and `-b` to put it on its own branch (git can't
check out one branch in two worktrees):
```sh
tsk add ../../gov-fr # → gov-fr [<slug>]
tsk add --as gov-fr-hotfix -b hotfix ../../gov-fr # → gov-fr-hotfix [hotfix]--as names a single directory, so it can't be combined with multiple repo
paths in one tsk add.
Closing a task removes each worktree and deletes the task directory. Before doing
that, close refuses to touch a worktree if either:
- the working tree is dirty, or
- the branch was never pushed, or has unpushed commits ahead of its upstream.
This is the whole point: it is easy to forget that a worktree had local-only
work. --force is the explicit escape hatch for the cases where you really do
want to discard.
tsk create [--base <remote>/<branch>] [<ref>] <slug> [-a <repo>...]
Create a task directory in cwd
tsk add [--base <remote>/<branch>] [-b <branch>] [--as <name>] <repo-path> [...]
Add worktrees to the current task
tsk status git status summary across all worktrees
tsk rm [-f] <repo-path> Remove one worktree from the current task
tsk close [-f] <task-path> Decommission a task: clean worktrees + delete dir
add, status and rm walk up from cwd looking for .tsk.yaml, so they work
from any subdirectory inside a task. close takes the path explicitly so you
can run it from outside.
Apache-2.0 — see LICENSE.