In questo articolo

🔍 Cos'è un git worktree (e perché il vibe coding ne ha bisogno)

Un git worktree è una working directory aggiuntiva agganciata allo stesso repository: ha i suoi file e il suo branch, ma condivide la directory .git — cronologia, remote e oggetti — con il checkout principale. Niente secondo clone, niente remote da riconfigurare: un commit fatto nel worktree è subito visibile da qualunque altro checkout dello stesso repo.

Per il lavoro con gli agenti è la primitiva perfetta: ogni sessione di Claude Code riceve una copia dei file tutta sua, così una sessione costruisce una feature mentre un'altra sistema un bug, senza che le modifiche dell'una compaiano nel diff dell'altra. È un livello di isolamento diverso da quello dei subagent e degli agent team, che coordinano il lavoro: i worktree isolano i file, e le due cose si combinano.

Prima del supporto nativo il giro era manuale: git worktree add, cd, claude. Funziona ancora — e serve ancora in un caso preciso che tratto più avanti — ma per la sessione parallela quotidiana oggi basta un flag.

Un repo, una .git, più working directory
Schema dei git worktrees: il checkout principale sul branch main e due worktree sotto .claude/worktrees, ognuno sul proprio branch, condividono tutti la stessa directory .git del repository.

I worktree condividono cronologia e remote col checkout principale: cambiano solo i file su disco e il branch.

🚀 Il flag --worktree: la sessione nasce già isolata

Con claude --worktree <nome> (abbreviato -w) Claude Code chiama git worktree add per me: crea la directory sotto .claude/worktrees/<nome>/ alla radice del repo, su un branch nuovo chiamato worktree-<nome>, e apre la sessione lì dentro. Un secondo terminale con un nome diverso è la seconda sessione parallela; se il nome lo ometto, Claude ne genera uno tipo bright-running-fox.

Due dettagli operativi che ho imparato a rispettare: la prima volta una sessione interattiva richiede il workspace trust — se in quella directory non ho mai lanciato claude, claude --worktree esce con un errore finché non accetto il dialog; claude -p --worktree salta invece il trust check e procede anche senza. Conviene inoltre aggiungere .claude/worktrees/ al .gitignore, altrimenti il checkout principale si riempie di file untracked fantasma.

C'è anche la variante senza terminale: chiedere a Claude di "lavorare in un worktree" durante una sessione. Lo crea da sé con il tool EnterWorktree e ci si sposta; uscire da .claude/worktrees/ verso path arbitrari richiede invece la mia approvazione esplicita, perché il working directory della sessione — e con lui CLAUDE.md e settings — migrerebbe altrove.

Due sessioni parallele in due worktree
# terminale 1: la feature
$ claude --worktree feature-auth
#   -> .claude/worktrees/feature-auth/  branch worktree-feature-auth

# terminale 2: il bugfix, in parallelo
$ claude -w bugfix-tags
#   -> .claude/worktrees/bugfix-tags/   branch worktree-bugfix-tags

# da un pull request: fetcha pull/1234/head da origin
$ claude --worktree "#1234"   # le virgolette proteggono il #

# nel .gitignore del progetto
.claude/worktrees/

Worktrees · Claude Code Docs

Un flag per sessione, zero collisioni. Codice completo nel repo: https://github.com/fscamuzzi/claude-code-worktrees-demo

📁 .worktreeinclude: i file gitignorati che seguono la sessione

Un worktree è un checkout fresco: i file tracciati ci sono tutti, ma .env, .env.local e qualsiasi altro file gitignorato restano nel checkout principale. La prima sessione parallela che ho lanciato è morta subito per questo — l'app non partiva perché mancava la configurazione locale.

La soluzione nativa è il file .worktreeinclude alla radice del progetto: usa la sintassi di .gitignore e ogni volta che Claude Code crea un worktree copia dentro i file che fanno match. La regola di sicurezza è elegante: viene copiato solo ciò che è sia nel .worktreeinclude sia gitignorato, quindi un file tracciato non viene mai duplicato per sbaglio.

Vale per ogni worktree creato da Claude Code con git — quelli di --worktree, quelli dei subagent isolati e le sessioni parallele dell'app desktop. Il resto dell'ambiente (dipendenze, build) va comunque reinizializzato: la prima richiesta che faccio a una sessione appena nata in un worktree è installare le dipendenze.

.worktreeinclude · cosa segue la sessione nel worktree
.env
.env.local
config/secrets.json

# sintassi .gitignore; viene copiato SOLO ciò che è
# anche gitignorato: i file tracciati non si duplicano mai

.worktreeinclude · Claude Code Docs

I .env seguono ogni nuovo worktree in automatico. Codice completo nel repo: https://github.com/fscamuzzi/claude-code-worktrees-demo

🔒 L'isolamento non è un consiglio: i quattro check

La parte che distingue il supporto nativo dal worktree fatto a mano è l'enforcement: mentre una sessione è isolata, Claude Code blocca attivamente ogni tool call che proverebbe a toccare il checkout principale. Non è un prompt che chiede gentilezza al modello: è un controllo sul tool use, e vale anche per tutti i subagent spawnati dalla sessione.

I check sono quattro. File edit: un Edit, Write o NotebookEdit che punta a un path del checkout principale viene rifiutato. Working directory: un comando Bash, PowerShell o Monitor la cui directory di lavoro risolve nel checkout principale — o non è verificabile — non parte. Redirect git: nei comandi Bash e Monitor, git -C, --git-dir, le variabili GIT_DIR e GIT_WORK_TREE o un cd prima del comando non permettono di aggirare il confine. Forma del comando: Bash e Monitor rifiutano i costrutti shell che Claude Code non riesce a tracciare senza eseguirli, come le brace expansion o gli heredoc con delimitatori non quotati — e questo check non si può spegnere. PowerShell applica solo il check sulla working directory; Bash e Monitor applicano tutti e tre i check sui comandi.

Il rifiuto non è un vicolo cieco: Claude lo vede come un errore di tool che nomina il worktree e spiega come riscrivere il comando — tipicamente spezzandolo in comandi semplici. In pratica l'agente si corregge da solo al primo rimbalzo.

I quattro check tra la sessione e il checkout principale
I quattro check di isolamento dei worktree in Claude Code: file edit, working directory, redirect git e forma del comando vengono bloccati prima di raggiungere il checkout principale.

Edit, Write e NotebookEdit coprono i file; PowerShell usa solo la working directory, Bash e Monitor tutti e tre i check sui comandi.

🤖 Subagent isolati: isolation worktree nel frontmatter

I worktree e i subagent si compongono: un subagent può girare nel suo worktree temporaneo, così più agenti che modificano file in parallelo non si scontrano mai. Si chiede al volo ("usa worktree per i tuoi agent") oppure si rende permanente nel frontmatter della subagent definition con isolation: worktree.

Il ciclo di vita è gestito: il worktree del subagent viene rimosso in automatico se l'agente finisce senza modifiche; se invece contiene lavoro, resta su disco finché lo sweep periodico non riesce a toglierlo senza perdere nulla — e mentre l'agente gira, un git worktree lock impedisce a qualunque cleanup concorrente di strapparglielo da sotto i piedi.

È il pattern che uso per i refactor meccanici su tanti file: il refactorer lavora nel suo worktree, i test girano lì, e il diff arriva pulito senza aver mai toccato la mia copia. Con la mappa un modulo → un agente dell'articolo sugli agent team, i due strumenti si incastrano: il team coordina, i worktree separano.

.claude/agents/refactorer.md · sempre nel suo worktree
---
name: refactorer
description: Applies mechanical refactors across many files,
  then runs the test suite and reports the results.
isolation: worktree
---

Apply the requested refactor across every affected file.
Run the full test suite (yarn test) inside YOUR worktree
and report the diff summary and the test output.

Subagent isolation · Claude Code Docs

Una riga di frontmatter e il subagent nasce isolato. Codice completo nel repo: https://github.com/fscamuzzi/claude-code-worktrees-demo

⚙️ baseRef: da dove parte il branch del worktree

Di default ogni worktree usa "fresh" per partire dal branch di default del repository sul remote, quando disponibile. Claude Code tiene aggiornato origin/HEAD da sé: se il repo non è stato fetchato nelle ultime 24 ore, fetcha il branch di default con un tetto di cinque secondi; se il fetch fallisce ma il ref remoto in cache è utilizzabile, usa quello. Se non c'è un remote configurato, oppure origin/HEAD non è disponibile né recuperabile, ripiega sul mio HEAD locale. Di norma ottengo quindi un albero pulito allineato al remote, con il commit locale corrente come fallback.

L'alternativa è "head": il worktree parte dal mio HEAD locale, commit non pushati compresi. È il valore giusto quando isolo subagent che devono lavorare sul mio work-in-progress — un refactorer che partisse da main non vedrebbe la feature a metà su cui deve intervenire.

Quello che baseRef non fa è puntare a un branch arbitrario: per aprire un worktree su un branch esistente specifico la strada resta git worktree add manuale, e poi claude dentro la directory. È l'unico caso in cui il giro vecchio serve ancora.

.claude/settings.json · worktree dal mio HEAD locale
{
  "worktree": {
    "baseRef": "head"
  }
}

Base branch · Claude Code Docs

"fresh" usa il default branch remoto quando disponibile e ripiega sul mio HEAD locale; "head" usa sempre il mio HEAD. Codice completo nel repo: https://github.com/fscamuzzi/claude-code-worktrees-demo

🧹 Cleanup e resume: cosa succede quando chiudo

All'uscita di una sessione interattiva Claude Code controlla cosa perderebbe rimuovendo il worktree: file modificati, untracked, commit nuovi. Worktree pulito e sessione senza nome: rimozione automatica, branch compreso. Sessione con nome o worktree con lavoro dentro: mi chiede se tenere o rimuovere — tenere preserva directory e branch per tornarci dopo.

Le run non interattive con -p sono l'eccezione da conoscere: niente prompt di uscita, quindi niente cleanup — e il lock preso alla creazione resta lì finché lo sweep di una sessione successiva non lo rilascia. Per toglierli a mano: git worktree remove, preceduto da git worktree unlock se git si rifiuta.

Il resume chiude il cerchio: riprendere una sessione che viveva in un worktree la riporta in quel worktree — vale per il resume interattivo, per --continue e --resume con -p, e per l'Agent SDK. Prima di rientrare, Claude Code verifica che la directory sia ancora un checkout separato e sano; se il worktree non esiste più, la sessione riparte dalla directory di lancio e il binding viene azzerato.

Gestire i worktree rimasti su disco
# cosa c'è in giro
$ git worktree list
/repo                                  abc123 [main]
/repo/.claude/worktrees/feature-auth   def456 [worktree-feature-auth]

# rimozione (con --force se ha modifiche non committate)
$ git worktree remove .claude/worktrees/feature-auth

# se git rifiuta per un lock lasciato da una run -p
$ git worktree unlock .claude/worktrees/feature-auth
$ git worktree remove .claude/worktrees/feature-auth

Cleanup · Claude Code Docs

Le run -p non puliscono mai da sole. Codice completo nel repo: https://github.com/fscamuzzi/claude-code-worktrees-demo

🧭 E con Codex? Worktree a mano e thread isolati

Il pattern non è esclusiva di Claude Code. Nel composer di un nuovo thread dell'app Codex scelgo Worktree e il branch di partenza. Codex crea il worktree dal branch selezionato, ma per default lo avvia in detached HEAD: non nasce subito alcun branch isolato. Il branch esiste solo quando scelgo Create branch here nell'header della chat. La retention predefinita conserva i 15 worktree gestiti da Codex più recenti; posso cambiare il limite o disattivare l'eliminazione automatica.

Con la CLI di Codex il supporto è manuale ma il flusso è identico a quello pre-flag di Claude Code: git worktree add con un branch nuovo, cd nella directory, codex. Un vincolo di git da ricordare: lo stesso branch non può essere checkout in due worktree contemporaneamente, quindi ogni istanza vuole il suo.

Quello che manca rispetto a Claude Code è l'enforcement: nel worktree manuale l'isolamento è una convenzione, non un vincolo — niente check sui path, niente blocco dei redirect git. Le regole condivise in AGENTS.md e una CI severa restano gli unici guardrail, ed è il motivo per cui sul lavoro parallelo pesante preferisco il supporto nativo.

Codex CLI in un worktree manuale
# un worktree per istanza, un branch per worktree
$ git worktree add ../demo-feature-auth -b feature-auth
$ cd ../demo-feature-auth && codex

# in parallelo, in un altro terminale
$ git worktree add ../demo-bugfix-tags -b bugfix-tags
$ cd ../demo-bugfix-tags && codex

# a fine lavoro
$ git worktree remove ../demo-feature-auth

Worktrees · OpenAI Codex Docs

Stesso pattern, isolamento per convenzione. Codice completo nel repo: https://github.com/fscamuzzi/claude-code-worktrees-demo

📦 GitHub repo

Tutto il materiale dell'articolo è in un repository pubblico: WorktreeNotes, una piccola API Node a due moduli verticali (Notes e Tags) con la suite di test su node --test — abbastanza vera da dare a due sessioni parallele qualcosa di reale su cui lavorare senza conflitti. Intorno all'app ci sono i pezzi del flusso: il .worktreeinclude con l'esempio di .env, il .claude/settings.json con worktree.baseRef, la subagent definition refactorer con isolation: worktree, i prompt pronti in prompts/ e due script di servizio per lo stato e la pulizia dei worktree.

Per provarla mi servono Node 20+, Git e Yarn; su macOS/Linux serve Bash. Claude Code serve solo per i flussi worktree, ma è opzionale se voglio soltanto avviare l'API senza dipendenze. Non servono servizi esterni: niente database, niente Docker. Copio .env.example in .env per mostrare il flusso dei worktree, ma l'app non carica automaticamente .env a runtime; i test non hanno dipendenze.

Clona e prova il repo
# prerequisiti: Node.js 20+, Git, Yarn; Bash su macOS/Linux
# Claude Code: solo flussi worktree; opzionale per avviare l'API senza dipendenze
$ git clone https://github.com/fscamuzzi/claude-code-worktrees-demo.git
$ cd claude-code-worktrees-demo
$ cp .env.example .env   # .env non viene caricato automaticamente
$ yarn test    # node --test: 9 test superati
$ yarn start   # l'API su http://localhost:3000

# poi le due sessioni parallele:
$ claude --worktree feature-auth   # terminale 1
$ claude -w bugfix-tags            # terminale 2

# stato e pulizia dei worktree
$ bash scripts/wt-status.sh
$ bash scripts/wt-clean.sh

claude-code-worktrees-demo · GitHub

Codice completo nel repo: https://github.com/fscamuzzi/claude-code-worktrees-demo

✅ Checklist finale: il flusso worktree in otto mosse

Ricapitolo il percorso. La sintesi dopo settimane con il flag: i worktree non sostituiscono subagent e agent team — sono lo strato sotto, quello che separa i file mentre gli altri coordinano il lavoro. La sessione parallela quotidiana parte con -w e muore col prompt di cleanup; il worktree manuale resta solo per i branch esistenti e per Codex.

E la lezione trasversale è la stessa dei miei hook: l'isolamento che funziona è quello che il tool fa rispettare, non quello che chiedo al modello per favore.

Sessioni parallele con i worktree in 8 mosse
  1. 01
    .gitignore.claude/worktrees/ fuori dal checkout principale
  2. 02
    Trust una tantumserve per l'interattivo; claude -p --worktree lo salta
  3. 03
    claude -w <nome>una sessione per worktree, branch automatico
  4. 04
    .worktreeinclude.env e config locali seguono ogni worktree
  5. 05
    Setup ambientedipendenze installate nel worktree appena nato
  6. 06
    baseRef giusto"fresh" dal remote o fallback HEAD locale; "head" dal mio WIP
  7. 07
    Subagent isolatiisolation: worktree nel frontmatter
  8. 08
    Cleanup consapevole-p non pulisce: git worktree remove/unlock

File separati per costruzione, coordinamento libero sopra.

Domande frequenti su git worktrees con Claude Code

Che differenza c'è tra un git worktree e un semplice branch?

Un branch è un puntatore nella cronologia: per cambiarlo devo fare checkout e i file sul disco cambiano sotto i piedi di chiunque stia lavorando. Un worktree è una working directory in più, con il suo branch, che condivide la stessa .git: due sessioni lavorano su due branch contemporaneamente, ognuna con la sua copia dei file. È questa copia separata che rende sicuro il parallelismo tra agenti.

Dove crea i worktree il flag --worktree e come li pulisco?

Sotto .claude/worktrees/<nome>/ alla radice del repo, su un branch worktree-<nome>. All'uscita di una sessione interattiva Claude Code rimuove da sé un worktree pulito senza nome e chiede cosa fare negli altri casi. Le run non interattive con -p non puliscono mai: per quelle servono git worktree remove ed eventualmente git worktree unlock prima.

Come porto i file .env dentro un nuovo worktree?

Con un file .worktreeinclude alla radice del progetto, in sintassi .gitignore. A ogni creazione di worktree Claude Code copia i file che fanno match, ma solo se sono anche gitignorati: un file tracciato non viene mai duplicato. Vale per i worktree di --worktree, per quelli dei subagent isolati e per le sessioni parallele dell'app desktop.

Posso far partire il worktree da un branch esistente?

Non con il flag: worktree.baseRef accetta solo "fresh" (default branch del remote quando disponibile, con fallback sul mio HEAD locale) o "head" (sempre il mio HEAD locale, commit non pushati compresi), non un nome di branch. Per un branch esistente il giro è manuale: git worktree add con il branch, cd nella directory e claude da lì. In alternativa, --worktree "#1234" crea un worktree dal head di una pull request.

Quando uso i worktree e quando un agent team?

Sono livelli diversi che si combinano. I worktree isolano i file: servono quando due o più sessioni modificano lo stesso repo in parallelo, o quando un lavoro deve sopravvivere alla sessione con branch e commit propri. Gli agent team coordinano il lavoro: task list condivisa, messaggi tra teammate. Per il parallelismo pesante uso entrambi: team sopra, worktree sotto — anche i subagent possono girare ognuno nel suo con isolation: worktree.

Parliamone

Se questo tema ti riguarda, scrivimi: confrontarsi su codice e AI è sempre tempo speso bene.