Define workspaces in YAML, then plan/apply to reconcile safely.
Worktree sprawl brings pain:
- Risky cleanup and cognitive load from too many worktrees.
- Bulk creation is powerful but tedious to set up and place.
gion makes it safe and repeatable: declare task workspaces in YAML, review diffs (including deletion risk), then create and clean them up in bulk.
You don’t have to edit YAML directly—gion manifest ... lets you add/remove workspaces interactively and updates the inventory behind the scenes.
demo.mp4
- Developers working on tasks that span multiple repositories.
- GitHub-centric, PR/issue-driven workflows (spin up local worktrees for reviews/issues and work in parallel).
- People using AI agents to create and remove many worktrees.
- Reproducible inventory:
gion.yamlis the source of truth - Bulk create: spin up many worktrees at once.
- Bulk cleanup: remove worktrees in bulk (diff + confirmation, risk surfaced for dirty/unpushed/diverged/unknown).
- Fast navigation:
giongojumps to any workspace or repo - Multi-repo tasks: group repos under a single workspace via presets
- GitHub-aware entry points: create from PRs or issues with
gh
Tip: gion manifest can be shortened to gion m or gion man.
- Plan-first: always shows a diff before applying changes.
- Deletion risk visibility: removal plans include a risk summary (e.g., dirty / unpushed / diverged / unknown).
- dirty: working tree has changes.
- unpushed: local branch is ahead of upstream.
- diverged: local and upstream have both advanced.
- unknown: status cannot be determined (e.g., git error or missing upstream).
- Confirm destructive changes: removals require an explicit confirmation;
--no-promptrefuses destructive changes. - Conservative bulk cleanup:
gion manifest gcexcludes anything uncertain and acts only when it is highly likely safe. - Clear safety boundary: gion reconciles only under
GION_ROOT/(workspaces + repo stores).
brew tap tasuku43/gion
brew install gion- Version pinning with mise (optional):
mise use -g github:tasuku43/gion@<version> - Manual install via GitHub Releases (download archive → put
gionandgiongoon your PATH) - Build from source requires Go 1.24+
- Git
ghCLI (optional; required forgion manifest add --reviewandgion manifest add --issue— GitHub only)
gion initBy default, gion uses ~/gion as the root:
~/gion/
gion.yaml
bare/ # bare repo store (shared Git objects)
workspaces/ # task workspaces (worktrees)
gion repo get git@github.com:org/backend.gitBare repo store location:
~/gion/bare/github.com/org/backend.git
Add a workspace interactively (example output, trimmed):
gion manifest addInputs
• repo: git@github.com:org/backend.git
• workspace id: PROJ-123
• repo #1 (git@github.com:org/backend.git)
└─ branch: PROJ-123-sample
Info
• manifest: updated gion.yaml
Plan
• + add workspace PROJ-123
└─ backend (branch: PROJ-123-sample)
repo: github.com/org/backend
• Apply changes? (default: No) (y/n)
└─ y
Apply
• create workspace PROJ-123
• worktree add backend
└─ $ git worktree add -b PROJ-123-sample …/workspaces/PROJ-123/backend origin/main
(git output trimmed)
Result
• applied: add=1 update=0 remove=0
• gion.yaml rewrittenResulting worktree:
~/gion/workspaces/PROJ-123/backend
# Setup (once):
eval "$(giongo init)"
giongogion manifest rmSelect a workspace, review the plan, then confirm to apply.
For a command overview, see docs/guides/COMMANDS.md (or run gion help).
Declare in gion.yaml, diff with gion plan, reconcile with gion apply.
Example plan (add + remove, trimmed):
Plan
• + add workspace PROJ-123
└─ backend PROJ-123
repo: github.com/org/backend.git
• - remove workspace PROJ-099
└─ backend PROJ-099
risk: dirty (unstaged=2)
sync: upstream=origin/main ahead=1 behind=0
Apply destructive changes? (default: No)
Interactive front-end to the inventory:
gion manifest addRun with flags to skip prompts:
gion manifest add --repo git@github.com:org/backend.git PROJ-123This path is optimized for bulk creation from PRs/issues with one apply.
Interactive bulk selection (multi-select in the picker):
gion manifest addNotes:
- Requires
gh(authenticated) to fetch metadata. - The picker supports bulk selection of PRs/issues, then a single apply.
Direct URL (single workspace):
gion manifest add --review https://github.com/owner/repo/pull/123
gion manifest add --issue https://github.com/owner/repo/issues/123Create a preset:
gion manifest preset add app --repo git@github.com:org/backend.git --repo git@github.com:org/frontend.gitpresets:
app:
repos:
- git@github.com:org/backend.git
- git@github.com:org/frontend.git
- git@github.com:org/infra.gitgion manifest add --preset app PROJ-123giongo is a small companion binary that jumps into a workspace or repo using a picker.
It does not change any state.
Example (zsh function):
giongo() {
if [[ "$1" == "init" || "$1" == "--help" || "$1" == "-h" || "$1" == "--version" || "$1" == "--print" ]]; then
command giongo "$@"
return $?
fi
local dest
dest="$(command giongo --print "$@")" || return $?
[[ -n "$dest" ]] && cd "$dest"
}Shortcut (auto-generate the function for your shell):
eval "$(giongo init)"Notes:
giongo initoutputs a bash/zsh function definition.- For a permanent setup, paste the output into
~/.zshrcor~/.bashrc.
Manual removal (explicit human judgment):
gion manifest rmAutomatic cleanup (conservative):
gion manifest gcgion manifest gc removes workspace entries from gion.yaml only when they are highly likely safe to delete, then (by default) runs gion apply to reconcile.
GC safety rules (summary):
- Excludes any workspace with dirty / unpushed / diverged / unknown state.
- Considers a workspace safe only when all repos are strictly merged into their target base.
- Uses Git data from the local repo store (no PR metadata).
If the filesystem is the source of truth, rebuild the inventory:
gion importGION_ROOT is resolved in this order:
--root <path>GION_ROOTenvironment variable- default
~/gion
- Inventory file:
<GION_ROOT>/gion.yaml - Bare repo stores (shared Git objects):
<GION_ROOT>/bare/ - Workspaces (task directories containing worktrees):
<GION_ROOT>/workspaces/
GION_ROOT/ (safety boundary: gion only touches under this directory)
├─ gion.yaml # desired state (inventory)
│
├─ bare/ # shared Git object store (bare clones)
│ └─ github.com/org/
│ ├─ backend.git # bare repo store (shared)
│ ├─ frontend.git
│ └─ infra.git
│
└─ workspaces/ # task-scoped directories (each contains worktrees)
├─ PROJ-123/ # workspace_id (task)
│ ├─ backend/ # worktree checkout (repo: backend)
│ │ ├─ .git # gitdir file -> points into .../backend.git/worktrees/...
│ │ └─ ... # working directory (your changes live here)
│ ├─ frontend/
│ └─ infra/
│
└─ PROJ-456/
└─ backend/
- Workspace: a task-scoped directory under
GION_ROOT/workspaces/<WORKSPACE_ID>/that can contain multiple repos. - Worktree: a Git worktree checkout for a repo, placed under a workspace (e.g.
.../workspaces/<id>/<alias>/). - Repo store: a shared bare clone cache under
GION_ROOT/bare/(used to create and update worktrees efficiently). - Manifest: the inventory file
gion.yamland thegion manifest ...subcommands that update it.
Invariants (short):
version: 1is the current inventory schema; future changes will be versioned.- gion only reads/writes under
GION_ROOT/(safety boundary). - Workspace IDs must be valid Git branch names (used as worktree branches).
gion.yaml is plain YAML. You can edit it directly (humans or AI), then review/apply changes:
gion plan
gion applyFor the full schema, see docs/spec/core/INVENTORY.md.
Minimal example:
version: 1
workspaces:
PROJ-123:
description: "fix login flow"
mode: repo
repos:
- alias: backend
repo_key: github.com/org/backend.git
branch: PROJ-123Notes:
gion.yamlis gion-managed and rewritten; don’t rely on ordering or comments.- You can edit
gion.yamldirectly (humans or AI). For interactive changes,gion manifest ...is convenient. - If you hand-edit, run
gion planbeforegion apply. If the filesystem is the source of truth, usegion import.
See CONTRIBUTING.md.
See SECURITY.md.
See LICENSE.
- @tasuku43