Copy a whole working directory in seconds, for almost no disk.
A worktree copies what git tracks;gitto copies what you were working in.
Three agents on three branches means three checkouts. A worktree hands each one the tracked files and stops there, so every lane resolves its dependencies and runs its build again before any work starts.
- Keep a pool of checkouts and reset them by hand.
- Install and build again in every lane.
- Work on one branch at a time.
A pool drifts: a slot's branch changes and its state does not, and the directory name stops saying what it holds. Building again is minutes per lane, forever. Working serially is the thing you were trying to stop doing.
Nothing to rebuild
The whole directory comes along, and the filesystem shares its blocks until one side writes. What a worktree leaves you to rebuild is already there:
with git worktree
with gitto
gitto new attendance-fixcloning internkim -> internkim-attendance-fix by clonefile
repointed core.hooksPath at this clone's own hooks
branch attendance-fix on origin/main, 4 submodules
/Users/you/work/internkim-attendance-fixThat copy carried 13 GB in 21 seconds and cost 60 MB of disk.
Install
One POSIX shell script, no runtime. It goes in ~/.local/bin, and it needs nothing but git and a filesystem that can share blocks.
curl -fsSL https://gitto.13e7.co/install | shAPFS on macOS, which is every Mac since 2017. On Linux, btrfs, XFS created with reflink=1, or OpenZFS 2.2 and later. Not ext4, which cannot share blocks at all.
Plugin
The plugin adds the skill, which is what makes an agent reach for a clone when the work wants its own checkout. It also carries the reasoning behind each refusal, so a blocked command is read and answered.
claude plugin marketplace add eastriverlee/gitto
claude plugin install gitto@13e7The package follows the Agent Plugins layout around an Agent Skills skill, and .agents/skills is the directory its clients share.
Every clone is a real repository
Not a worktree, so it holds its own history and its own branch, and two of them never share an index. The canonical checkout is the one nobody works in.
gitto listcanonical internkim main
internkim-attendance-fix attendance-fix dirty=0 unpushed=0
internkim-jev-router feat/jev-router dirty=3 unpushed=2A clone starts on origin/HEAD unless you name another base. Passing HEAD gives you the canonical exactly as it stands, uncommitted work included, which is how you hand an agent the mess you are in.
What a copy breaks
Copying the files is one line. The work is that a checkout is full of references to where it lives, and a plain copy leaves every one of them pointing at the original: hook paths, worktree registrations at every submodule depth, symlinks into the directory you copied from.
gitto doctor attendance-fixinternkim-attendance-fix
nothing points at the canonicalgitto re-addresses what it can and drops what cannot survive the move, such as a worktree the canonical keeps inside itself, whose history stays behind. What it cannot decide it reports instead of rewriting:
gitto doctor jev-routerinternkim-jev-router
still pointing outside this clone:
symlink .agents -> /Users/you/work/internkim/.agents
hosts internkim-docsdoctor knows what git knows. A reference from outside git, an editor workspace or a line in ~/.ssh/config, is not on that list, so a clean report means nothing points at the canonical. It does not mean the directory is safe to delete.
Removing a clone
A clone that holds unpushed commits, uncommitted changes, or the history of a worktree living somewhere else is not removed. The refusal names what would be orphaned.
gitto remove jev-routergitto: jev-router hosts the history of worktrees that live outside it:
/Users/you/work/internkim-docs
removing it would leave each of them without a repository.The same care applies before anything is copied. gitto measures the filesystem first, so a machine that cannot share blocks is told so before it pays full price:
gitto new attendance-fixgitto: copying here costs the full size, so a clone would too.
macOS needs APFS; Linux needs btrfs, XFS with reflink=1, or OpenZFS 2.2+.
ext4 and tmpfs cannot share blocks at all.