Instant workspaces with warm build caches, powered by copy-on-write clones.
When you need multiple copies of a repository -- for parallel features, code review, or experimentation -- the standard approaches (git worktree, git clone) create fresh working trees. They share git objects, but lose all gitignored local state: build caches, compiled outputs, dependency metadata, node_modules/, .gradle/, build/ directories.
Every new working copy starts cold. For large projects, rebuilding takes minutes.
Grove maintains a golden copy of your repository -- a checkout with warm build state (compiled outputs, dependency caches, everything). When you need a new workspace, Grove creates a copy-on-write filesystem clone of the entire golden copy, including all gitignored files.
Regardless of repo size, CoW clones complete in under a second and share disk blocks with the original. Blocks duplicate only when one side writes to them. Each workspace gets its own git branch, working tree, and build state.
Golden Copy (warm build state)
|
+-- grove create --> Workspace 1 (CoW clone, own branch)
+-- grove create --> Workspace 2 (CoW clone, own branch)
+-- grove create --> Workspace 3 (CoW clone, own branch)
| Grove | git worktree | git clone --shared | Full git clone | |
|---|---|---|---|---|
| Clone speed | ~1s (any size) | ~1s | Seconds | Minutes |
| Build caches preserved | Yes | No | No | No |
| Gitignored files cloned | Yes | No | No | No |
| Disk usage | Shared (CoW) | Shared (.git) | Shared (objects) | Full copy |
| Isolation | Full | Shared .git | Partial | Full |
| Branch independence | Yes | Yes (limited) | Yes | Yes |
Grove uses git under the hood. It replaces only the step where you create a new working copy.
# Homebrew (macOS)
brew install chrisbanes/tap/grove
# Go install
go install github.com/chrisbanes/grove/cmd/grove@latest
# Or download a binary from the latest release:
# https://github.com/chrisbanes/grove/releasesStart config-less: the first grove create in a git repo auto-initializes
.grove/config.json with defaults. Run grove config afterward only if you
want to customize settings.
# 1. Create a workspace (first run auto-initializes .grove/config.json)
cd ~/dev/myproject
grove create --branch feature/new-login
# Workspace created: feature-new-login-a1b2
# Path: /Users/you/grove-workspaces/myproject/feature-new-login-a1b2
# Branch: feature/new-login
# 2. Optional: customize settings for future workspaces
grove config --warmup-command "./gradlew assemble"
# 3. Work in the workspace
cd ~/grove-workspaces/myproject/feature-new-login-a1b2
# 4. Clean up when done
grove destroy feature-new-login-a1b2Configure a git repository as a grove-managed golden copy. Creates a .grove/ directory with configuration, hooks, and a .grove/.gitignore for workspace markers (and legacy runtime paths). Defaults to the current directory.
grove config --warmup-command "npm run build" --workspace-dir ~/workspaces/myproject
# Running warmup: npm run build
# Grove configured at /Users/you/dev/myproject
# Workspace dir: /Users/you/workspaces/myproject
# Experimental image backend (macOS): creates a base sparsebundle
grove config --backend image --image-size-gb 200| Flag | Description |
|---|---|
--warmup-command |
Shell command to warm build caches (runs during config and update) |
--workspace-dir |
Directory for workspaces (default: ~/grove-workspaces/{project}) |
--backend |
Workspace backend (cp default, image experimental) |
--image-size-gb |
Base sparsebundle size in GB for --backend image |
--force |
Proceed even if the golden copy has uncommitted changes |
Create a workspace from the golden copy using the configured backend:
clone_backend: "cp"(default): APFS directory clone (cp -c -R)clone_backend: "image"(experimental): attach base sparsebundle with per-workspace shadow
Without --branch, the workspace stays on the golden copy's current branch.
With --branch, Grove creates and checks out a new git branch in the workspace.
On first run in an unconfigured git repo, grove create auto-initializes
.grove/config.json with default settings, then creates the workspace.
Use grove config to customize settings later (warmup command,
workspace location, backend, and more).
With --json, Grove writes only JSON to stdout and does not print the
auto-init note to stderr.
grove create --branch feature/auth
# Workspace created: feature-auth-f7e8
# Path: /Users/you/grove-workspaces/myproject/feature-auth-f7e8
# Branch: feature/auth
grove create
# Workspace created: main-d9c0
# Path: /Users/you/grove-workspaces/myproject/main-d9c0For machine-readable output:
grove create --branch agent/fix-bug --json{
"id": "agent-fix-bug-f7e8",
"golden_copy": "/Users/you/dev/myproject",
"golden_commit": "abc1234",
"created_at": "2026-02-17T10:30:00Z",
"branch": "agent/fix-bug",
"path": "/Users/you/grove-workspaces/myproject/agent-fix-bug-f7e8"
}For long-running clones in large repos, opt in to progress output:
grove create --progress
# [5%] clone
# [42%] clone
# [95%] post-clone hook
# [100%] done
# Workspace created: main-d9c0When combined with --json, progress is written to stderr and final JSON is
written to stdout.
| Flag | Description |
|---|---|
--branch |
Create and checkout a new git branch in the workspace (default: golden copy's current branch) |
--force |
Proceed even if the golden copy has uncommitted changes |
--json |
Output workspace info as JSON |
--progress |
Show progress output for long-running create operations (written to stderr) |
List active workspaces.
grove list
# ID BRANCH CREATED PATH
# feature-auth-f7e8 feature/auth 5m ago /Users/you/grove-workspaces/myproject/feature-auth-f7e8
# feature-new-login-a1b2 feature/new-login 2h ago /Users/you/grove-workspaces/myproject/feature-new-login-a1b2| Flag | Description |
|---|---|
--json |
Output workspace list as JSON |
Remove a workspace. Takes a workspace ID or absolute path.
cpbackend workspaces are removed by deleting the directory.imagebackend workspaces are detached first, then shadow + metadata are removed.
# Destroy a single workspace
grove destroy feature-auth-f7e8
# Destroyed: feature-auth-f7e8
# Push the branch to origin before destroying
grove destroy --push feature-auth-f7e8
# Destroy all workspaces
grove destroy --all
# Destroyed: feature-auth-f7e8
# Destroyed: feature-new-login-a1b2| Flag | Description |
|---|---|
--push |
Push the workspace branch to origin before destroying |
--all |
Destroy all workspaces |
Pull the latest changes and re-run the warmup command on the golden copy.
grove update
# Pulling latest...
# Running warmup: ./gradlew assemble
# Refreshing image backend... # when clone_backend is "image"
# Golden copy updated to abc1234If clone_backend is image, update performs an incremental base refresh.
For safety, refresh is refused while image-backed workspaces are active.
Show golden copy info and workspace summary.
grove status
# Golden copy: /Users/you/dev/myproject
# Branch: main
# Commit: abc1234
# Status: clean
#
# Workspaces: 2 / 10 (max)
# Directory: /Users/you/grove-workspaces/myprojectPrint the grove version.
grove version
# grove v0.1.0Grove stores its configuration in .grove/config.json inside the golden copy:
{
"warmup_command": "./gradlew assemble",
"workspace_dir": "~/grove-workspaces/{project}",
"max_workspaces": 10,
"exclude": ["*.lock", "__pycache__", ".gradle/configuration-cache"],
"clone_backend": "cp"
}| Field | Description | Default |
|---|---|---|
warmup_command |
Shell command to warm build caches. Runs during grove config and grove update. |
(none) |
workspace_dir |
Where workspaces are created. {project} expands to the golden copy's directory name. |
~/grove-workspaces/{project} |
max_workspaces |
Maximum concurrent workspaces. Prevents disk exhaustion. | 10 |
exclude |
Glob patterns for files/directories to skip when cloning. See Exclude Patterns. | [] |
clone_backend |
Workspace backend: cp (default) or image (experimental, macOS). |
cp |
| Characteristic | cp (default) |
image (experimental, macOS) |
|---|---|---|
| Create speed | Fast APFS clone via cp -c -R; can become metadata-bound in very large repos |
Very fast create via base image attach + per-workspace shadow |
| Disk space | CoW shared blocks with low operational overhead | Higher overhead from base image + shadows; can approach ~2x in worst case |
| Operational complexity | Simple lifecycle, no extra backend state | More complex mount/attach/detach state and metadata handling |
| Update behavior | grove update does git pull + optional warmup |
Adds incremental base image refresh during grove update |
| Safety constraints | Fewer backend-specific guards | Refresh/migration guarded while image-backed workspaces are active |
| Platform support | macOS/APFS | macOS-only and still experimental |
| Best for | Most repositories and teams prioritizing predictability | Very large repos where cp -c -R clone time is the bottleneck |
The image backend is for large repositories where raw cp -c -R clone time
is dominated by filesystem metadata work.
Model:
- runtime root:
<workspace_dir>/runtimes/<runtime-id>/ - base image:
<runtime-root>/images/base.sparsebundle - per-workspace overlay:
<runtime-root>/shadows/<id>.shadow - workspace metadata:
<runtime-root>/workspaces/<id>.json
Runtime data lives under your configured workspace_dir, not inside the repo.
The runtime ID is stored in .grove/.runtime-id (gitignored).
grove create attaches the base image with a workspace-specific shadow, which
makes create time mount-time fast.
grove update incrementally refreshes the base via rsync and is blocked while
any image-backed workspace is active.
Warmup command examples by ecosystem:
| Ecosystem | Warmup command |
|---|---|
| Gradle | ./gradlew assemble |
| npm | npm run build |
| Cargo | cargo build |
| Go | go build ./... |
Exclude patterns prevent specific files or directories from being copied during workspace creation. Unlike post-clone hooks, excluded paths are never touched at all -- they aren't copied and then deleted, they're skipped entirely. This matters for performance (large caches) and correctness (lock files, PID files, sockets).
{
"exclude": [
"*.lock",
"__pycache__",
".gradle/configuration-cache"
]
}Patterns use Go's filepath.Match syntax:
- Simple patterns (no
/) match against the basename at any depth.*.lockmatchesyarn.lock,packages/foo/yarn.lock, etc.__pycache__matches any directory with that name. - Path patterns (contain
/) match against the full relative path from the repo root..gradle/configuration-cachematches only that exact path.
The .grove directory is never excluded, regardless of patterns.
Exclude patterns vs. post-clone hooks: Use exclude patterns for files that should never be cloned (large caches, lock files). Use hooks for cleanup that requires logic (e.g., patching config files, running commands).
Grove runs executable scripts from .grove/hooks/ at specific lifecycle points.
Runs inside each new workspace after the CoW clone completes, before branch checkout. Use it to clean up non-relocatable state (lock files, caches with embedded absolute paths).
Gradle example (.grove/hooks/post-clone):
#!/bin/bash
# Clean Gradle lock files and non-relocatable caches
find . -name "*.lock" -path "*/.gradle/*" -delete
rm -rf .gradle/configuration-cachePython example (.grove/hooks/post-clone):
#!/bin/bash
# Remove bytecode caches (they embed absolute paths)
find . -type d -name "__pycache__" -exec rm -rf {} +
find . -name "*.pyc" -deleteHooks must be executable (chmod +x .grove/hooks/post-clone). Grove errors if a hook file exists but lacks execute permission. Commit your hooks to the repo so all contributors share them.
Grove targets multi-agent AI workflows where each agent needs an isolated workspace with warm build state. The --json flag on create and list provides machine-readable output for programmatic consumers.
Typical agent workflow:
# Agent creates its own workspace
grove create --branch agent/fix-login --json
# Parse JSON for workspace path
# Agent works in the isolated workspace
cd ~/grove-workspaces/myproject/agent-fix-login-a1b2
# ... make changes, run tests ...
# Push branch and clean up
grove destroy --push agent-fix-login-a1b2Multiple agents can work in parallel, each in its own workspace. Every workspace starts with the same warm build state from the golden copy.
Beyond AI agents, Grove serves any workflow that needs parallel workspaces: CI pipelines, code review, and experimentation.
Grove includes a Claude Code plugin with skills that integrate Grove into Claude Code workflows. When installed, these skills replace the built-in worktree-based skills with Grove-aware equivalents.
Claude Code:
# Add the Grove marketplace
/plugin marketplace add chrisbanes/grove
# Install the plugin
/plugin install grove@chrisbanes-groveOther agents (Codex, Cursor, etc.):
npx skills add chrisbanes/groveThis copies the skill files to your agent's skills directory (e.g., ~/.claude/skills/ or ~/.agents/skills/).
| Skill | Description |
|---|---|
grove:using-grove |
Creates a warm workspace before feature work. Replaces superpowers:using-git-worktrees. |
grove:finishing-grove-workspace |
Guides completion and cleanup when work is done. Replaces superpowers:finishing-a-development-branch. |
grove:grove-config |
Opinionated first-time setup with build system detection. Also available as /grove-config. |
grove:grove-doctor |
Diagnoses Grove setup issues (APFS, CLI, hooks, disk space). |
grove:grove-multi-agent |
Orchestrates parallel agents across isolated workspaces. |
A SessionStart hook runs at the beginning of each conversation. It detects whether you're in a Grove golden copy or workspace and tells Claude which skills are relevant. In a golden copy, Claude will use grove:using-grove to create workspaces. In a workspace, it knows to use grove:finishing-grove-workspace when done.
The skills integrate with the superpowers workflow -- brainstorming, subagent-driven-development, and executing-plans all invoke grove:using-grove instead of using-git-worktrees when Grove is detected.
| Platform | Filesystem | Status |
|---|---|---|
| macOS | APFS | Supported |
| Linux | Btrfs / XFS (reflink) | Planned |
| Linux | ext4 | Not supported (no CoW) |
| Windows | NTFS / ReFS | Not supported |
Grove requires a filesystem with copy-on-write support. All modern Macs (macOS High Sierra / 2017 and later) use APFS. Linux support for Btrfs and XFS reflink is planned -- the Cloner interface and filesystem detection scaffolding are already in place.
Grove errors with a clear message on unsupported filesystems. It never silently falls back to a regular copy.
On macOS, Grove uses cp -c -R to create an APFS clone. This filesystem-level operation shares disk blocks between the clone and the original; blocks duplicate only when one side writes to them.
Before cloning, Grove verifies APFS support by querying diskutil info at runtime.
All state lives in .grove/ within the repo -- no daemon, no global config, no database. Each workspace contains a .grove/workspace.json marker file, which grove list discovers by scanning the workspace directory.
Contributions are welcome. See CONTRIBUTING.md for development setup, testing, and PR guidelines.