Tasktree is a task-first workspace manager for local development across one or more Git repositories.
It creates a workspace directory with a Tasktree.yml file plus one or more normal Git checkouts. Repeated clones stay fast because tasktree keeps deterministic local bare-repo caches under the user cache directory.
- Create a dedicated workspace for a task, bug fix, or feature branch
- Add one or many Git repositories to the same tasktree
- Clone from a repo's default branch, an existing branch, or a new local branch
- Reuse cached bare clones for faster subsequent checkouts
- Run tasktree commands from the workspace root or any nested repo subdirectory
- Inspect all workspace repositories with
reposandstatus - List all known tasktrees on this machine with
list - Remove stale registry entries with
prune - Remove a checkout without touching the shared bare cache
- Manage repository aliases in
~/.config/tasktree/repos.yml - Auto-register useful aliases when a repo is added
- Migrate from the legacy
.tasktree.tomlformat withtasktree migrate - Reproduce a workspace on any machine with
tasktree apply
If you are building from source:
go install github.com/tinmancoding/tasktree/cmd/tasktree@latestOr in this repository:
go build ./cmd/tasktreeMake sure the tasktree binary is on your PATH.
mkdir -p ~/ws/feature-payments
cd ~/ws/feature-payments
tasktree init
tasktree add git@github.com:myorg/api.git --branch feature/payments
tasktree add git@github.com:myorg/web.git --branch feature/payments
tasktree repos
tasktree statusThis walkthrough shows the typical flow for starting work on a feature that touches multiple repositories.
mkdir -p ~/ws/feature-checkout
cd ~/ws/feature-checkout
tasktree initThis creates the tasktree root and writes Tasktree.yml.
Add a repo from its default branch:
tasktree add git@github.com:myorg/api.gitExample output:
Added api at api
Registered alias api -> git@github.com:myorg/api.git
Registered alias myorg-api -> git@github.com:myorg/api.git
Add a repo and check out or create a branch:
tasktree add git@github.com:myorg/web.git --branch feature/checkout--branch reuses a local branch if it exists, tracks the remote branch if only origin/<branch> exists, or creates a new branch from --from (or the default branch) if neither exists.
Add a repo from a tag or commit (headless checkout):
tasktree add git@github.com:myorg/payments.git --from v1.2.0
tasktree add git@github.com:myorg/payments.git --from 8f3e2abCreate a new branch from an explicit base ref:
tasktree add git@github.com:myorg/payments.git --branch feature/checkout --from mainAdd a repo with a custom checkout directory name:
tasktree add git@github.com:myorg/api.git --name api-v2tasktree repos
tasktree statusExample tasktree repos output:
NAME PATH REF BRANCH
api api feature/checkout feature/checkout
web web feature/checkout feature/checkout
Example tasktree status output:
Tasktree: feature-checkout
Root: /Users/alex/ws/feature-checkout
REPO PATH HEAD STATE
api api feature/checkout clean
web web feature/checkout modified
reposshows configured repositories in the current tasktree, including checkout ref and branchstatusshows the tasktree name, root path, current HEAD, and whether each repo is clean or modified
When you add a repo by URL, tasktree automatically tries to register two aliases in ~/.config/tasktree/repos.yml:
repo-nameowner-repo-name
For git@github.com:myorg/api.git, tasktree will try to register:
apimyorg-api
If an alias is already used by another repository, tasktree skips it and prints a message explaining why.
Example conflict output:
Added app at app
Skipped alias app; already used by git@github.com:someone-else/app.git
Registered alias myorg-app -> git@github.com:myorg/app.git
After aliases exist, you can add by alias instead of full URL:
tasktree add api
tasktree add myorg-web --branch feature/checkout```
You can also manage aliases explicitly:
```bash
tasktree repo add-alias payments git@github.com:myorg/payments.git
tasktree repo aliases
tasktree repo remove-alias paymentstasktree remove webThis removes the checkout from the current tasktree and updates metadata. It does not remove the shared bare cache.
Tasktree commands resolve the workspace root by walking upward from the current directory, so this works from nested repo paths too:
cd ~/ws/feature-checkout/api/internal/server
tasktree root
tasktree statusTasktree.yml is pure desired state — no resolved commit SHAs, no machine-specific paths. It is safe to commit to version control or share with teammates.
To reproduce a workspace on a new machine, copy Tasktree.yml to a directory and run tasktree apply:
mkdir ~/ws/feature-checkout-copy
cp ~/ws/feature-checkout/Tasktree.yml ~/ws/feature-checkout-copy/
cd ~/ws/feature-checkout-copy
tasktree applyapply clones every source in the spec that is not yet present on disk. Sources already present are skipped, so apply is safe to run repeatedly.
-v, --verbose: print underlying git commands to stderr
Initialize a tasktree in the current directory or the provided path. Writes Tasktree.yml.
Examples:
tasktree init
tasktree init ~/ws/feature-checkoutAdd a repository to the current tasktree.
Accepted input:
- a clone URL such as
git@github.com:myorg/api.git - a configured alias such as
api
Flags:
--branch <branch>: the branch to use. Reuses the branch if it already exists locally, creates a local tracking branch if onlyorigin/<branch>exists, or creates a new local branch from--from(falling back to the repo default branch) if neither exists.--from <ref>: base ref for branch creation when--branchis provided but the branch does not yet exist. When--branchis omitted,--fromperforms a direct checkout of the given branch, tag, commit, or ref without creating a new branch.--name <name>: use a custom checkout directory name
Examples:
# Check out default branch
tasktree add git@github.com:myorg/api.git
# Use an existing local or remote branch
tasktree add git@github.com:myorg/api.git --branch feature/payments
# Create a new branch from an explicit base
tasktree add git@github.com:myorg/api.git --branch feature/payments --from main
# Headless checkout of a tag, commit, or ref
tasktree add git@github.com:myorg/api.git --from v1.2.0
tasktree add git@github.com:myorg/api.git --from 8f3e2ab
# Custom checkout directory name
tasktree add git@github.com:myorg/api.git --name api2
# Use an alias
tasktree add apiWhat --branch does:
- If the branch already exists locally — check it out; ignore
--from. - Else if
origin/<branch>exists — create a local tracking branch; ignore--from. - Else — resolve
--from(or the repo default branch) and create the branch from there.
The command prints which path it took so ignored --from values are visible:
Using existing local branch "feature/x".
Using existing remote branch "feature/x" from origin; ignoring --from "main".
Creating new branch "feature/x" from "main".
Checking out "v1.2.0" without creating a branch.
What add does:
- resolves the current tasktree root
- resolves the input as an alias if one exists
- uses the shared local bare-repo cache when cloning
- creates a normal checkout inside the tasktree
- records the source in
Tasktree.ymlunderspec.sources - attempts to register derived aliases in
repos.yml - prints what aliases were added, already existed, or were skipped due to conflicts
Materialize all sources declared in Tasktree.yml that are not yet present on disk.
For each source in spec.sources:
- If the destination path already exists — skip it without error
- If the source type is
git— populate the bare-clone cache and clone the repo, then apply the declared branch or ref - If the source type is not yet supported — skip with a warning
Flags:
--dry-run: preview what would be done without creating any directories
Examples:
# Reproduce a workspace from Tasktree.yml
tasktree apply
# See what would be cloned without doing anything
tasktree apply --dry-runapply is idempotent: running it when all sources are already present prints "All sources are already present." and exits cleanly.
List repositories configured in the current tasktree.
Output columns:
NAMEPATHREFBRANCH
List all tasktrees known to this machine.
Output columns:
NAMEPATHSTATUS(omitted when OK; showsmissingorinvalidfor stale entries)
Remove stale entries from the global tasktree registry.
An entry is considered stale when:
- its path no longer exists on disk (
missing) - its path exists but no longer contains a
Tasktree.yml(invalid)
Flags:
--dry-run: preview what would be removed without modifying the registry
Examples:
tasktree prune
tasktree prune --dry-runShow live status for repositories in the current tasktree.
Output includes:
- tasktree name
- tasktree root
- one row per repo with
REPO,PATH,HEAD, andSTATE
Remove a repository checkout from the current tasktree.
This removes the working checkout and updates Tasktree.yml. It does not remove shared cached clones.
Convert a legacy .tasktree.toml to Tasktree.yml.
Run this once in any workspace created with an older version of tasktree:
tasktree migrateWhat it does:
- Reads
.tasktree.tomlin the current directory (orpathif provided) - Maps each
[[repos]]entry to aspec.sourcesentry of typegit - Writes
Tasktree.yml - Renames
.tasktree.tomlto.tasktree.toml.bak
Resolved state fields (resolved_ref, commit) are intentionally discarded — live state is always queried from the Git checkouts directly.
Example output:
Found .tasktree.toml in current directory.
Converting to Tasktree.yml...
api git git@github.com:myorg/api.git (branch: feature/checkout)
web git git@github.com:myorg/web.git (branch: feature/checkout)
Note: resolved_ref and commit fields are not carried over.
Live state is always queried from the Git checkouts directly.
Written: Tasktree.yml
Renamed: .tasktree.toml → .tasktree.toml.bak
Migration complete. Review Tasktree.yml and commit it to version control.
If tasktree detects a .tasktree.toml without a Tasktree.yml, all commands (except migrate) will prompt you to run tasktree migrate first.
Print the resolved root path for the current tasktree.
Add a manual alias for a repository URL.
Rules:
- adding the same alias for the same repo is a no-op
- adding an alias already used by a different repo fails
Example:
tasktree repo add-alias api git@github.com:myorg/api.gitRemove an alias from the global alias catalog.
Example:
tasktree repo remove-alias apiList all configured aliases and their clone URLs.
Example:
tasktree repo aliasesGenerate shell completion scripts.
Example:
tasktree completion bash
tasktree completion zshEach workspace stores its desired state in a Tasktree.yml file at the workspace root. It uses a Kubernetes-style structure:
apiVersion: tasktree.dev/v1
kind: Tasktree
metadata:
name: feature-checkout
createdAt: "2026-03-25T12:00:00Z"
spec:
sources:
- name: api
type: git
path: api
git:
url: "git@github.com:myorg/api.git"
branch: feature/checkout
- name: web
type: git
git:
url: "git@github.com:myorg/web.git"
branch: feature/checkoutTasktree.yml is pure desired state — it contains no resolved commit SHAs, no timestamps written by the tool beyond createdAt, and no machine-specific paths. It is safe to commit to version control and share with teammates.
The tool appends or removes entries in spec.sources on tasktree add / tasktree remove. It never writes resolved state back into the file.
A JSON Schema for editor validation and autocompletion is available at schema/tasktree.schema.json in the tasktree repository.
Workspaces created with older versions of tasktree use .tasktree.toml. Run tasktree migrate once to convert:
cd ~/ws/my-old-workspace
tasktree migrateSee tasktree migrate for full details.
Tasktree keeps a global registry of all initialized tasktrees at:
~/.local/state/tasktree/registry.toml
This registry is updated automatically on tasktree init and is used by tasktree list to show all known workspaces. Stale entries (paths that no longer exist or have lost their Tasktree.yml) are reported in the STATUS column and can be cleaned up with tasktree prune.
Tasktree stores repository aliases in the default config directory, usually:
~/.config/tasktree/repos.yml
Structure:
repos:
- url: git@github.com:myorg/api.git
aliases:
- api
- myorg-api
- url: git@github.com:myorg/web.git
aliases:
- web
- myorg-webNotes:
- aliases are global across tasktrees
- an alias can only point to one repository URL
- a repo entry may exist even if it currently has no aliases
Tasktree keeps a deterministic local bare clone cache under the user cache directory, typically something like:
~/.cache/tasktree/repos
When you add the same repository again in another tasktree, tasktree refreshes the bare cache and clones from it instead of cloning from the network every time.
go test ./...
go run ./cmd/tasktree --helpGitHub Actions uses Release Please and GoReleaser to manage version tags, GitHub releases, and downloadable binaries.
- Merge changes using Conventional Commits such as
feat:,fix:, orchore: - Release Please opens or updates a release PR with the next version and changelog
- Merging that PR creates the tag and GitHub release
- GoReleaser publishes cross-platform archives and
checksums.txt
- Git operations use the system
gitbinary - Metadata is written atomically
- Removing a repo checkout does not remove its local bare cache