Sessions & worktrees
Every agent session in a git project works on its own branch, in its own checkout of your repo. Run five at once and none of them touches another’s files, or yours. Your project folder stays on whatever branch you had checked out, and each session’s work sits on a normal git branch until you bring it back.
What you get when you start a session
Section titled “What you get when you start a session”When you start a session in a git repo, spwn:
- creates a branch named
spwn/<id>, where<id>is the first eight characters of the session’s id, starting from the branch your project folder has checked out (its base); - checks that branch out into a worktree of its own, with
git worktree add; - clones your heavy build folders into it, so the agent can build straight away;
- runs the agent in that worktree.
The branch lives in your real repo. git branch lists it, and you can check it out,
diff it or merge it like any other branch:
git log main..spwn/3f9c2a1b # what the session didgit merge spwn/3f9c2a1b # bring it in by handThe strip at the top of a session shows where its code lives, its branch and the base it merges back into, how many commits it is ahead, and whether it has uncommitted changes. Click the strip to open the Inspector. Its Overview tab repeats the same facts and lists the changed files.
Switching between sessions only changes what you’re looking at. Nothing on disk moves, so a session working in the background keeps going untouched.
Where worktrees go
Section titled “Where worktrees go”Settings → Sessions → Session worktree location decides where new worktrees are
created. The setting is worktreeLocation:
| Value | Where a session’s worktree goes | Notes |
|---|---|---|
sibling (default) |
<repo-parent>/.<repo-name>-worktrees/<session> |
Outside your working tree, so builds, file watchers and IDE indexers never see it. |
internal |
<repo>/.spwn/worktrees/<session> |
spwn adds /.spwn/worktrees/ to .git/info/exclude, so git ignores it without a change to your .gitignore. Tools with explicit include globs may still pick it up. |
appData |
<app data>/worktrees/<session> |
Away from your repos entirely. The app data folder is ~/Library/Application Support/gg.spwn.spwn on macOS and ~/.local/share/gg.spwn.spwn on Linux. |
For a repo at ~/code/shop, the default puts session worktrees in
~/code/.shop-worktrees/.
The setting applies to sessions started after you change it. Existing worktrees stay where they were made.
Ready to build from the first turn
Section titled “Ready to build from the first turn”A fresh worktree only has your tracked files, so there would be no node_modules and no
target, and the agent’s first build would be a cold install. spwn copies these
gitignored folders from your project folder into the new worktree, when they exist:
node_modules target .venv venv dist build .next .svelte-kit .turboOn APFS (macOS) the copy is a copy-on-write clone (cp -c). It’s near-instant and takes
almost no disk until the agent changes something. Elsewhere it’s a plain recursive copy.
A commit every turn
Section titled “A commit every turn”After each finished turn, spwn stages everything in the worktree (git add -A, which
respects .gitignore) and commits it on the session branch:
spwn turn 7e1d0c44The commit is made as spwn session <spwn@localhost> with --no-verify, so it works in a
repo with no configured identity and an unattended run can’t trip a pre-commit hook. A
turn that changed nothing makes no commit.
The session branch always carries real history. There is nothing to reconstruct when you merge, and a fork from turn 3 can start from turn 3’s code.
rerere, on by default
Section titled “rerere, on by default”Parallel sessions start from the same base. When the base moves they tend to hit the same conflict, once each. spwn turns on git’s rerere (“reuse recorded resolution”) when it creates a worktree, so a conflict resolved in one session is replayed automatically in the others. rerere’s cache lives in the repo’s shared git directory, so every worktree sees it.
spwn only sets rerere.enabled true when you haven’t set it yourself. An explicit
rerere.enabled false is left alone.
It’s all hooks
Section titled “It’s all hooks”Worktree creation, the per-turn commit, checkpoints and worktree removal aren’t
hardcoded. They ship as default scripts in ~/.spwn/hooks/, and you can read them:
| Script | What it does |
|---|---|
session-created.d/10-worktree.sh |
Creates the branch and worktree, turns on rerere, clones heavy folders. |
session-turn.d/10-commit.sh |
Commits each turn. |
session-turn.d/20-checkpoint.sh |
Takes a file snapshot each turn. |
session-deleted.d/90-worktree.sh |
Removes the worktree and deletes the branch. |
To add a step of your own, drop a separate script into the same folder, such as
session-created.d/50-my-setup.sh. Don’t edit spwn’s own scripts, because spwn may
overwrite them when it updates. Delete one to opt out of what it does. With
10-commit.sh gone, turns stop auto-committing. See
How hooks work.
Rewind: the Timeline
Section titled “Rewind: the Timeline”Every turn also takes a file snapshot of the worktree. It’s a copy-on-write clone on APFS, so a snapshot costs nearly nothing until files change. Together with the agent’s own conversation history, snapshots let you roll a session back.
Open the Inspector’s Transcript tab (the Transcript button on the agent bar) and hover any assistant message. ↺ Return here offers two choices:
- Conversation only rolls the conversation back to this point. Your next message continues from here and the later turns drop. The files stay as they are.
- Conversation + restore files also puts the worktree’s files back to their snapshot from this turn. It’s greyed out when that turn has no snapshot.
spwn rewinds by driving the agent’s own /rewind menu. Before it confirms, it reads back
the highlighted row and checks that it matches the turn you picked. If the row doesn’t
match, spwn stops and tells you, and nothing has changed.
The Inspector’s Timeline tab lists the file snapshots on their own, newest first:
- Restore puts the files back to one snapshot without touching the conversation.
- ⟲ Undo last change restores the snapshot from the turn before the latest one, so it undoes the last turn’s edits.
Every file restore does the same things:
- It saves a safety snapshot first, so the restore itself can be undone from the same list.
- It rewrites working files and deletes files created since the snapshot.
- It never touches
.git, so commit history is kept. It also leaves the heavy folders (node_modules,targetand the rest) alone.
A restore waits for the current turn to finish. spwn keeps the newest 20 turn snapshots and the newest 6 safety snapshots per session, and prunes older ones.
Deleting a session
Section titled “Deleting a session”Delete a session with the × on its sidebar row. spwn removes:
- the worktree, with
git worktree remove --force, so uncommitted changes don’t block it; - the
spwn/<id>branch, withgit branch -D; - the session’s file snapshots;
- its entry in the land order, if it had one.
Before any of that, your repo’s own session-deleted hooks run inside the worktree, so
teardown can happen while the files still exist.
Deleting a branch with unmerged work loses that work. The confirm dialog checks first and names exactly what’s at risk:
- Unmerged commits: for example,
3 not in "main"; - Uncommitted changes:
yes.
When there’s something to lose, the dialog also offers Open to merge first, which takes you to the session so you can bring the work back before you delete it.
Projects that aren’t git repos
Section titled “Projects that aren’t git repos”If a project folder isn’t a git repository, sessions run directly in the project folder. They get no branch, no worktree, no per-turn commit, no file snapshots and nothing to merge, and the status strip says no worktree. Several sessions in the same non-git folder all edit the same files.
The same happens when your project folder is on a detached HEAD: with no branch
checked out there’s no base to start from.
To get branches and merging, run git init in the project and start a new session.