Git Worktree: o novo hello world para DEVs

O papel de um desenvolvedor sempre envolveu alternar entre diferentes contextos.

Você está trabalhando em uma nova feature, mas surge um bug crítico que precisa de hotfix. Enquanto isso, um colega pede ajuda para revisar um Pull Request. Pouco depois, aparece um conflito entre a branch de homologação e a master que precisa ser investigado.

Em poucos minutos, você deixou de trabalhar em apenas uma tarefa e passou a lidar com três ou quatro frentes diferentes, cada uma em uma branch, com dependências e estados próprios.

Há muito tempo, em uma galáxia muito distante, costumávamos resolver esse problema com git stash.

Era simples: tudo o que você estava fazendo era armazenado em um lugar mágico. Você trocava de branch, resolvia o outro problema e, quando quisesse voltar, bastava executar:

git stash pop

E pronto.

Como em um passe de mágica, tudo estava de volta.

Pelo menos essa era a teoria.

Na prática, conforme a quantidade de código, branches e tarefas aumentou, esse processo começou a ficar cada vez mais difícil de administrar. Nos últimos meses, perdi a conta de quantas vezes já não sabia exatamente o que estava armazenado em cada stash.

Era a alteração da feature?

A correção do hotfix?

Um experimento que ainda não deveria ser commitado?

A resolução parcial de um conflito?

Quando executava git stash list, encontrava algo parecido com isto:

stash@{0}: WIP on feat/payment-flow
stash@{1}: WIP on develop
stash@{2}: WIP on feat/payment-flow
stash@{3}: WIP on hotfix/customer-validation

Tecnicamente, todas as alterações ainda estavam lá.

O problema era descobrir o que cada uma representava.

Com mais features sendo desenvolvidas simultaneamente, Pull Requests menores e incrementais, agentes de IA gerando código e interrupções frequentes durante o dia, depender do stash passou a exigir uma capacidade de organização que eu simplesmente não queria gastar.

Foi nesse contexto que comecei a utilizar Git Worktrees com mais frequência.

E encontrei nelas uma maneira muito mais simples de lidar com múltiplas branches ao mesmo tempo.

O que é uma Git Worktree?

Normalmente, quando clonamos um repositório Git, trabalhamos com uma única pasta:

meu-projeto/

Dentro dela, utilizamos git switch ou git checkout para alternar entre branches:

git switch feat/payment-flow
git switch hotfix/customer-validation

O problema é que essa pasta só consegue representar uma branch por vez.

Ao trocar de branch, todos os arquivos do diretório de trabalho precisam acompanhar a mudança. Por isso, alterações não commitadas podem impedir a troca ou precisam ser temporariamente armazenadas com stash.

Uma Worktree permite criar outros diretórios de trabalho vinculados ao mesmo repositório Git.

Em vez de alternar constantemente entre branches dentro da mesma pasta, podemos ter algo como:

meu-projeto/
├── .bare/
├── master/
├── feature-payment-flow/
└── hotfix-customer-validation/

Cada diretório está associado a uma branch diferente:

master/                         → master
feature-payment-flow/           → feat/payment-flow
hotfix-customer-validation/     → hotfix/customer-validation

Todos compartilham o histórico, os objetos, as referências e os remotes do mesmo repositório. Ao mesmo tempo, cada worktree possui seus próprios arquivos, HEAD e índice — a área que o Git usa para preparar um commit.

Essa distinção ajuda a entender o que está acontecendo: não são vários clones completos e independentes, mas também não são várias branches disputando a mesma pasta. É um único repositório Git com várias áreas de trabalho.

Na prática, isso significa que posso deixar minha feature exatamente como está, abrir outra pasta e trabalhar no hotfix sem guardar alterações nem trocar de branch. Também posso delegar uma tarefa para um agente de IA sem permitir que ele escreva nos mesmos arquivos em que estou trabalhando.

Por que começar com um repositório bare?

É possível usar Worktrees a partir de um clone comum, e para experimentar esse é o caminho mais rápido:

git worktree add -b hotfix/customer-validation ../meu-projeto-hotfix master

Para um uso pontual, isso funciona muito bem.

Quando Worktrees passam a ser o fluxo padrão, porém, eu prefiro uma organização em que nenhuma pasta tenha o status especial de “repositório principal”. Todas as branches ficam como diretórios irmãos, enquanto os dados compartilhados do Git ficam escondidos em .bare.

Um repositório bare é justamente um repositório sem uma área de trabalho própria. Ele armazena os commits, branches, tags e configurações que serão compartilhados pelas Worktrees, mas não faz checkout dos arquivos da aplicação.

Começo criando a pasta que vai agrupar o projeto:

mkdir meu-projeto
cd meu-projeto
git clone --bare git@github.com:empresa/meu-projeto.git .bare

Depois crio, na raiz, um arquivo chamado .git apontando para esse repositório.

No Bash:

echo "gitdir: ./.bare" > .git

O clone --bare traz as branches remotas como referências locais. Eu também configuro o fetch para manter referências como origin/master, porque elas deixam explícita a base usada ao criar novas branches:

git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch origin

Por fim, crio a primeira Worktree com a branch principal:

git worktree add master master

Se o seu projeto usa main, basta substituir master nos exemplos:

git worktree add main main

O resultado é esta estrutura:

meu-projeto/
├── .bare/      # dados compartilhados do Git
├── .git        # ponteiro para .bare
└── master/     # arquivos da aplicação na branch master

A partir daqui, entro em master/ e trabalho como em qualquer outro repositório:

cd master
git status
git pull

Se for o primeiro push dessa branch a partir do clone bare, posso definir o upstream:

git push -u origin master

Criando uma nova branch com Worktree

Também é possível criar uma branch nova ao mesmo tempo em que criamos a Worktree.

Na raiz meu-projeto/, executo:

git fetch origin
git worktree add -b hotfix/customer-validation hotfix-customer-validation origin/master

Nesse comando:

-b hotfix/customer-validation

cria a nova branch.

hotfix-customer-validation

define o diretório em que ela será aberta.

origin/master

define o commit de partida.

Agora posso entrar na pasta e trabalhar normalmente:

cd hotfix-customer-validation
git status
git add .
git commit -m "fix: corrige validação do cliente"
git push -u origin hotfix/customer-validation

Para uma branch local que já existe, não uso -b:

git worktree add feature-payment-flow feat/payment-flow

Caso a branch exista apenas no remoto, crio uma branch local que a acompanha:

git worktree add --track -b feat/payment-flow feature-payment-flow origin/feat/payment-flow

Isso é especialmente útil para hotfixes, experimentos e tarefas rápidas que surgem enquanto outra implementação ainda está em andamento.

Onde Worktrees ficam ainda melhores com Codex e Claude Code

Foi com a popularização dos agentes de código que Worktrees deixaram de ser apenas uma conveniência e passaram a resolver um problema de concorrência.

Se eu e um agente trabalhamos na mesma pasta, compartilhamos arquivos modificados, dependências, processos e o índice do Git. Mesmo quando estamos em partes diferentes da aplicação, basta uma formatação automática, uma migration ou uma alteração em um arquivo central para os trabalhos se atropelarem.

Com Worktrees, cada tarefa recebe um espaço físico próprio.


Enquanto implemento feat/payment-flow na minha pasta, posso deixar:

  • um agente corrigindo hotfix/customer-validation;
  • outro escrevendo testes para um módulo legado;
  • uma terceira sessão investigando por que um teste fica instável no CI.

Não estou pedindo apenas três respostas em paralelo. Estou dando a cada execução um filesystem, um índice e, de preferência, uma branch próprios.

Paralelizar não é simplesmente abrir mais agentes

Worktrees removem a colisão de arquivos, mas não removem dependências entre tarefas.

Se dois agentes precisam alterar o mesmo contrato, a mesma migration ou a mesma função central, eles provavelmente não deveriam trabalhar sem coordenação. O resultado pode ser dois diffs corretos isoladamente e caros de integrar.

As melhores tarefas paralelas têm fronteiras claras:

  • “escreva testes para este comportamento”;
  • “investigue a causa e produza um diagnóstico”;
  • “corrija o endpoint X sem alterar o contrato público”;
  • “atualize a documentação desta pasta”;
  • “revise este diff e liste riscos, sem editar arquivos”.

Quanto mais independente for a definição da tarefa, mais a Worktree se parece com uma pequena unidade de execução.

Meu fluxo de trabalho

Hoje gosto de abrir o VS Code na pasta meu-projeto/, um nível acima das Worktrees. Assim consigo enxergar master/, features e hotfixes no mesmo Explorer e abrir um terminal já apontando para a pasta da tarefa.

Normalmente mantenho master/ limpa e próxima do remoto. Quando surge uma nova demanda, atualizo essa base e crio uma Worktree específica:

cd master
git pull --ff-only
cd ..
git worktree add -b feat/payment-flow feature-payment-flow master

Meu diretório passa a funcionar quase como um quadro de tarefas:

meu-projeto/
├── master/
├── feature-payment-flow/
├── hotfix-customer-validation/
└── review-checkout-timeout/

As pastas que existem representam, de forma muito concreta, os contextos que ainda estão abertos.

Arquivos .env

O primeiro detalhe prático são os arquivos ignorados pelo Git. Uma Worktree recebe os arquivos versionados, mas não recebe automaticamente o .env, certificados locais ou outras configurações que só existem na sua máquina.

No meu fluxo, mantenho um .env na raiz meu-projeto/ e copio o arquivo para a Worktree correspondente quando preciso:

No Bash:

cp ../.env .env

É importante que esse arquivo continue fora do versionamento e que cada Worktree possa ajustar valores que precisam ser únicos, como porta da aplicação, nome do banco ou fila utilizada.

Tanto o Codex quanto o Claude Code também entendem um arquivo .worktreeinclude para Worktrees criadas por eles. Nele, posso listar arquivos ignorados que devem ser copiados:

.env
.env.local
config/secrets.json

Essa automação vale para as Worktrees gerenciadas pelas ferramentas. Nas Worktrees que crio manualmente com git worktree add, continuo copiando os arquivos por conta própria ou uso um pequeno script de setup.

Gerenciador de pacotes e Dependências

Outro ponto importante é o gerenciador de pacotes.

Eu gosto bastante do Poetry e prefiro que cada Worktree tenha seu próprio ambiente virtual dentro da pasta:

poetry config virtualenvs.in-project true
poetry install

Isso cria uma .venv independente por Worktree. A instalação ocupa mais espaço, mas evita que uma atualização de dependência em uma feature afete o hotfix que estou validando em outra pasta.

Em projetos Node.js, o mesmo raciocínio vale para node_modules. Em projetos grandes, várias Worktrees podem consumir bastante disco; por isso, não crio uma para qualquer tarefa de cinco minutos e removo as que já terminaram.

Também presto atenção aos recursos que não são isolados pelo Git:

  • duas aplicações não podem subir na mesma porta;
  • duas suítes podem disputar o mesmo banco local;
  • migrations concorrentes podem alterar o mesmo schema;
  • containers podem receber nomes iguais;
  • caches externos e filas continuam compartilhados.

A Worktree isola o código no disco. Ela não cria, sozinha, uma nova máquina.

Conclusão

Durante muito tempo, alternar branches, empilhar stashes e reconstruir mentalmente o estado de cada tarefa parecia apenas parte natural do desenvolvimento.

Worktrees não eliminam a complexidade das tarefas, mas retiram uma parte desnecessária dela: a obrigação de fazer vários contextos caberem na mesma pasta.

Com agentes como Codex e Claude Code, esse benefício fica ainda mais evidente. Cada tarefa pode ter seu diretório, sua branch, suas dependências e sua conversa. Eu continuo no meu fluxo principal enquanto outras frentes avançam sem editar o chão em que estou pisando.

No fim, talvez Git Worktree esteja se tornando um novo “Hello World” não porque seja o primeiro comando que alguém aprende, mas porque é uma das primeiras ideias que precisamos dominar para trabalhar com múltiplos agentes de forma organizada.

O stash ainda tem seu lugar.

Só não precisa mais ser o lugar onde guardamos a nossa sanidade.