O congestionamento do checkout único

Estou desenvolvendo um app de menu bar no macOS. Tenho três features no backlog: um sparkline de consumo, notificações nativas, e um widget de desktop. As três são independentes. Vou fazer as três com Claude Code.

O problema: Claude Code trabalha em um diretório. Um diretório tem uma branch. E git checkout é como uma rotatória de uma só faixa: só passa um.

Se quero avançar as três ao mesmo tempo, minhas opções clássicas são:

  1. Pingue-pongue do stash: git stash, mudar branch, trabalhar, git stash pop, rezar para que não hajam conflitos. Repetir até a loucura ou a aposentadoria, o que vier primeiro.

  2. Clonar o repo três vezes: Funciona, mas agora tenho três cópias de .git/, três históricos independentes, e um git fetch para fazer em cada um. Um desperdício.

  3. Aceitar a vida serial: Uma feature atrás da outra. Seguro, previsível, e lento como um merge sort na mão.

Nenhuma é legal. Mas há uma quarta opção que está no git desde 2015 e que quase ninguém usa.

Worktrees: a solução que você já tinha instalada

Um worktree é um segundo diretório de trabalho que compartilha o mesmo repositório .git. Sem cópias, sem clones, sem magia negra.

A analogia: seu repo é uma biblioteca. Até agora você tinha uma mesa onde só podia ter um livro aberto. Um worktree é colocar mais mesas. Cada uma com um livro diferente aberto, mas todas pegando da mesma estante.

~/code/meuapp/                    ← mesa 1 (main)
     .git/                       ← a biblioteca (uma só)

~/code/meuapp-sparkline/          ← mesa 2 (feature/sparkline)
     .git  ← arquivo, não pasta (ponteiro para a biblioteca)

~/code/meuapp-notificacoes/       ← mesa 3 (feature/notifications)
     .git  ← outro ponteiro

Cada diretório é um checkout completo com todos os arquivos. Você pode compilar em um, rodar testes em outro, e ter seu agente de IA trabalhando no terceiro. Ao mesmo tempo.

Criar é uma linha

Do seu repo principal:

1
2
git worktree add ../meuapp-sparkline -b feature/sparkline
git worktree add ../meuapp-notificacoes -b feature/notifications

Pronto. Dois diretórios novos, cada um na sua branch, compartilhando todo o banco de dados git. Nada de clonar, nada de configurar remotes, nada de duplicar histórico.

O que compartilham e o que não

Isso é importante. Os worktrees compartilham todo o repo: commits, branches, tags, remotes, hooks, configuração. Se você faz um commit no worktree do sparkline, pode vê-lo imediatamente do de notificações sem fazer fetch nem nada, porque é o mesmo banco de dados.

O que não compartilham:

  • Os arquivos no disco (cada mesa tem sua cópia de trabalho)
  • A área de staging (cada um tem seu próprio git add)
  • O HEAD (cada um aponta para sua branch)

Dito em linguagem simples: o estado de “no que estou trabalhando” é privado de cada worktree. Todo o resto é comum.

O fluxo de trabalho com agentes de codificação

Aqui é onde fica interessante. Com worktrees, você pode ter literalmente vários agentes trabalhando em paralelo no mesmo projeto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Terminal 1: Claude Code no sparkline
cd ~/code/meuapp-sparkline
claude

# Terminal 2: Claude Code nas notificações
cd ~/code/meuapp-notificacoes
claude

# Terminal 3: main intacto, o app rodando
cd ~/code/meuapp
make run

Cada instância do Claude tem seu próprio diretório, sua própria branch, seu próprio .build/. Não se atropelam. Não competem pelo index. Não precisam fazer stash de nada.

E como compartilham o banco de dados git, quando um dos agentes termina e faz push, os demais já veem essa branch.

Fazer merge: exatamente igual que sempre

Os worktrees não mudam nada do fluxo de merge. São branches normais em diretórios separados:

1
2
3
4
5
6
7
8
9
# Opção A: merge local
cd ~/code/meuapp
git merge feature/sparkline
git merge feature/notifications

# Opção B: PRs (o habitual)
cd ~/code/meuapp-sparkline
git push -u origin feature/sparkline
# Criar PR no GitHub/Gitea, review, merge

Quando termina, você limpa:

1
2
git worktree remove ../meuapp-sparkline
git branch -d feature/sparkline  # se já foi mergeada

As pegadinhas que ninguém te conta

1. Uma branch, um worktree

Você não pode ter main checada em dois worktrees ao mesmo tempo. É por design: evita que dois diretórios modifiquem o mesmo HEAD e se corrompam. Se precisa de um segundo checkout de main, crie uma branch temporária.

2. O primeiro build é do zero

Cada worktree tem seu próprio diretório de build. A primeira compilação vai ser lenta. Depois, cada worktree mantém seu cache independente, que é justamente a vantagem sobre o git checkout clássico (que invalida o cache toda vez que você muda de branch).

3. Arquivos locais não rastreados

Seu .env.local, configurações de editor, arquivos que não estão no git… não são copiados para o worktree novo. Você terá que recriá-los ou fazer symlinks.

4. Apps com estado compartilhado no disco

Se seu app escreve dados em ~/Library/Application Support/ ou similar, duas instâncias do app de diferentes worktrees vão competir pelo mesmo arquivo. Não é um problema do worktree, é um problema de rodar duas instâncias do mesmo app. A solução: não rodar dois ao mesmo tempo, ou parametrizar o diretório de dados por build.

5. Não apague o diretório na mão

Se você faz rm -rf do worktree em vez de usar git worktree remove, o git continua achando que a branch está ocupada. Execute git worktree prune para limpar as referências órfãs.

6. O remote não sabe de nada

Os worktrees são 100% locais. Gitea, GitHub, GitLab… nenhum remote sabe que existem. Só veem git push normais com branches normais. É como perguntar se seu servidor tem problemas com você usar Vim ou VS Code: não sabe, não afeta.

Boas práticas

Convenção de nomes: coloque os worktrees como irmãos do repo original, com um sufixo descritivo:

~/code/meuapp/                    ← main
~/code/meuapp-sparkline/          ← feature
~/code/meuapp-notificacoes/       ← feature
~/code/meuapp-hotfix-login/       ← hotfix

Assim um ls ~/code/meuapp* te mostra tudo de uma só olhada.

Um worktree por feature, não por capricho: crie worktrees para trabalho que realmente vai ser paralelo. Se você vai fazer uma coisa atrás da outra, uma branch normal com checkout é suficiente.

Limpe quando terminar: os worktrees abandonados são como as branches que ninguém apaga — se acumulam e confundem. git worktree list é seu amigo.

Não edite o mesmo arquivo de dois worktrees: tecnicamente você pode, cada um tem sua cópia. Mas se ambos modificarem o mesmo arquivo, você terá conflitos ao fazer merge. Tente fazer com que as features toquem áreas diferentes do código.

Proposta de fluxo de trabalho completo

Para quem quiser um fluxo de trabalho organizado, aqui vai o que eu uso:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 1. Criar worktrees para as features da sprint
cd ~/code/meuapp
git worktree add ../meuapp-feat-a -b feature/feat-a
git worktree add ../meuapp-feat-b -b feature/feat-b

# 2. Lançar um agente em cada um
cd ~/code/meuapp-feat-a && claude    # terminal 1
cd ~/code/meuapp-feat-b && claude    # terminal 2

# 3. Ir fazendo merge conforme terminam
cd ~/code/meuapp-feat-a
git push -u origin feature/feat-a   # criar PR

# 4. Limpar o que já foi mergeado
git worktree remove ../meuapp-feat-a
git branch -d feature/feat-a

# 5. Ver o que resta vivo
git worktree list

O ciclo é: criar → trabalhar em paralelo → push/PR → merge → limpar. Cada worktree vive o que vive a feature, nem mais nem menos.

Para fechar

Os worktrees estão no git desde a versão 2.5 (julho de 2015). Mais de dez anos. E a maioria das pessoas continua fazendo git stash como se estivéssemos em 2010.

Com a chegada dos agentes de codificação, o gargalo não é mais a velocidade com que você escreve código — é a velocidade com que consegue mudar de contexto. E os worktrees eliminam essa mudança de contexto completamente: você não muda de branch, muda de diretório. cd em vez de checkout.

Que é, no fim das contas, o que sempre deveríamos ter feito.


TL;DR: git worktree add ../nome -b branch cria um segundo diretório de trabalho no mesmo repo. Sem cópias, sem stash, sem invalidar caches. Perfeito para ter vários agentes de codificação trabalhando em paralelo. Limpe com git worktree remove quando terminar.