O que são Git Worktrees?
Git worktrees permitem que você tenha múltiplas branches checked out simultaneamente em diretórios diferentes. Em vez de fazer stash ou commit de trabalho em progresso para trocar de branch, você simplesmente usa cd para outro diretório.
Com um workflow git normal, seu repositório pode ter apenas uma branch checked out por vez. Com worktrees, cada branch vive em seu próprio diretório, mas todos compartilham o mesmo histórico git. Commits, stashes e remotes são compartilhados entre todos os worktrees.
Duas Abordagens para Worktrees
Abordagem A: Adicionar Worktrees a um Repo Existente
Você mantém seu clone atual e adiciona worktrees ao lado. Rápido para começar, mas o worktree principal é um clone regular com uma pasta .git, tornando-o estruturalmente diferente dos outros.
Abordagem B: Bare Clone + Worktrees (Recomendado)
Você cria um bare clone (sem diretório de trabalho) e depois adiciona worktrees para cada branch, incluindo a principal. Este é o workflow adequado: todas as branches são iguais, e o bare repo é apenas um banco de dados git compartilhado.
Usamos a Abordagem B porque ela oferece uma estrutura limpa e simétrica onde nenhuma branch é especial.
Configurando um Bare Clone
Passo 1: Criar o Bare Clone
A pasta wrapper substitui seu clone regular, então use o mesmo nome do projeto que você sempre usou:
cd ~/Work/my-org
mv my-project my-project-old
mkdir my-project
cd my-project
git clone --bare <repo-url> .bareVocê pode ficar tentado a adicionar um sufixo como -wt ou .git para distinguir de um clone regular. Não faça isso. Uma vez que você adota worktrees, este é seu repo. Um sufixo é apenas ruído que você vai digitar todos os dias.
Passo 2: Configurar o Ponteiro .git
echo "gitdir: ./.bare" > .gitIsso cria um arquivo .git (não diretório) que diz ao git que o repo real está em .bare. Comandos git agora funcionam a partir da pasta wrapper.
Passo 3: Configurar Refs de Fetch Remoto
Por padrão, um bare clone não busca refs de branches remotas corretamente. Corrija isso:
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"Passo 4: Buscar Tudo
git fetch originAgora você tem o histórico completo do repo e pode criar worktrees.
Criando e Gerenciando Worktrees
Crie seu primeiro worktree para a branch principal:
git worktree add mainIsso cria um diretório main/ com a branch checked out. Para branches de feature:
# Branch existente
git worktree add feature-branch
# Nova branch
git worktree add -b my-new-feature my-new-featurePara listar, remover e limpar:
# Listar todos os worktrees
git worktree list
# Remover um worktree (mantém a branch)
git worktree remove feature-branch
# Deletar a branch também
git branch -d feature-branch
# Limpar referências obsoletas
git worktree pruneA Estrutura de Diretórios
Após a configuração, seu projeto fica assim:
my-project/ # Wrapper (você não trabalha aqui diretamente)
├── .bare/ # Banco de dados git (compartilhado por todos)
├── .git # Arquivo apontando para .bare
├── .shared/ # Arquivos gitignored com symlinks nos worktrees
│ └── .env
├── new-worktree.sh # Script auxiliar
├── main/ # Worktree: branch principal (manter limpo)
│ ├── .env -> ../.shared/.env
│ ├── src/
│ └── package.json
└── feature-branch/ # Worktree: branch de feature (trabalhar aqui)
├── .env -> ../.shared/.env
├── src/
└── package.jsonCada worktree tem seus próprios node_modules, saída de build e estado de trabalho. São diretórios completamente independentes. Você precisa executar npm install em cada um.
Integração com Lazygit
Lazygit tem suporte nativo a worktrees. Abra-o de qualquer diretório de worktree e ele detecta a configuração automaticamente.
Encontrando o Painel de Worktrees
A aba Worktrees fica dentro do painel de Branches:
- Pressione
3para abrir o painel de Branches - Alterne sub-abas com
](próximo) e[(anterior): Branches Locais → Remotes → Tags → Worktrees
Observe que a tecla w é dependente do contexto: no painel Files ela faz commit de arquivos staged, no painel Branches ela cria um worktree da branch selecionada. Ela não abre um painel de worktrees.
Ações de Worktree
Na aba Worktrees:
Enter— Mudar para o worktree selecionadon— Criar novo worktreed— Remover o worktree selecionado?— Mostrar todos os atalhos (funciona em qualquer painel)
Diffs Lado a Lado com Delta
Lazygit não tem uma visualização de diff dividida nativa, mas você pode obter diffs lado a lado usando o delta como pager personalizado:
brew install git-deltaEntão crie a configuração do lazygit (macOS: ~/Library/Application Support/lazygit/config.yml, Linux: ~/.config/lazygit/config.yml):
git:
paging:
colorArg: always
pager: delta --dark --paging=never --side-by-sideTodos os diffs no lazygit agora serão exibidos lado a lado com destaque de sintaxe. Pressione e em qualquer visualização de diff para abrir o arquivo no seu $EDITOR.
Layouts de Teclado Não-US
As teclas [ e ] para alternar abas podem ser difíceis de acessar em teclados não-US:
- Alemão Mac:
[=Option+5,]=Option+6 - Alemão Windows/Linux:
[=AltGr+8,]=AltGr+9
Integração com Yazi
Yazi é um gerenciador de arquivos de terminal que combina bem com worktrees. Como todas as branches são diretórios irmãos, você pode navegar entre eles visualmente.
# Abrir yazi na pasta wrapper
cd my-project
yaziWorkflow Baseado em Abas
A configuração recomendada é uma aba do yazi por worktree ativo:
- Abra o yazi na pasta wrapper
- Entre em
main/→ pressionetpara abrir em nova aba - Volte com
h, entre emfeature-x/→ pressionetnovamente - Alterne entre abas com
1e2 - Feche uma aba com
Ctrl+c
Executando Comandos pelo Yazi
;— Executar comando em segundo plano (não bloqueante, ideal paranpm start):— Executar comando em primeiro plano (bloqueante, ideal paranpm install)w— Abrir gerenciador de tarefas (ver tarefas em segundo plano)Enter(no gerenciador) — Ver logs da tarefax(no gerenciador) — Cancelar uma tarefaq(no gerenciador) — Voltar ao yazi
Isso significa que você pode iniciar um servidor de desenvolvimento com ;, digitar npm start, e continuar navegando pelos arquivos enquanto ele roda.
Gerenciando Arquivos Gitignored entre Worktrees
Um detalhe crítico que pega muitos de surpresa: Arquivos gitignored não são compartilhados entre worktrees. Cada worktree é seu próprio diretório no disco. Arquivos como .env, configurações de IDE ou configurações de ferramentas existem apenas no worktree onde foram criados.
A Solução com Symlinks
Mantenha arquivos gitignored compartilhados em um diretório .shared/ na pasta wrapper e crie symlinks para cada worktree:
mkdir .shared
cp main/.env .shared/.env
# Criar symlinks nos worktrees existentes
ln -s "$(pwd)/.shared/.env" main/.env
ln -s "$(pwd)/.shared/.env" feature-branch/.envUma única fonte de verdade, e todos os worktrees veem o mesmo arquivo. Editar em um worktree muda em todos, pois todos apontam para o mesmo local.
Compartilhar: Configuração e ferramentas (.env, .editorconfig, configurações). Manter separado: Arquivos gerados (node_modules, dist, caches de build).
A Armadilha da Barra Final
Se seu .gitignore usa uma barra final para ignorar diretórios:
.myconfig/Isso não vai corresponder a symlinks para diretórios. O git trata symlinks como arquivos, então .myconfig/ só corresponde a um diretório real. O symlink aparece como untracked.
Solução: remova a barra final:
.myconfigIsso corresponde a diretórios, arquivos e symlinks. Revise seu gitignore global e remova barras finais de padrões que possam ser symlinked.
Automatização com um Script Auxiliar
Criar symlinks manualmente fica tedioso. Coloque um script auxiliar na pasta wrapper:
#!/usr/bin/env bash
set -euo pipefail
if [ $# -eq 0 ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
echo "Usage: ./new-worktree.sh <branch-name> [--new]"
echo ""
echo " <branch-name> Checkout de branch existente"
echo " <branch-name> --new Criar nova branch e worktree"
echo ""
echo "Todos os arquivos em .shared/ são symlinked nos novos worktrees."
exit 0
fi
BRANCH_NAME="$1"
CREATE_NEW="${2:-}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SHARED_DIR="$SCRIPT_DIR/.shared"
WORKTREE_DIR="$SCRIPT_DIR/$BRANCH_NAME"
if [ "$CREATE_NEW" = "--new" ]; then
git worktree add -b "$BRANCH_NAME" "$WORKTREE_DIR"
else
git worktree add "$WORKTREE_DIR" "$BRANCH_NAME"
fi
if [ -d "$SHARED_DIR" ]; then
for item in "$SHARED_DIR"/.[!.]* "$SHARED_DIR"/*; do
[ -e "$item" ] || continue
name="$(basename "$item")"
target="$WORKTREE_DIR/$name"
if [ ! -e "$target" ]; then
ln -s "$item" "$target"
echo " Linked: $name"
else
echo " Skipped (exists): $name"
fi
done
fi
echo "Worktree ready: $WORKTREE_DIR"Agora cada novo worktree automaticamente recebe todos os arquivos compartilhados via symlink.
Workflow Diário
Mantenha seu worktree principal limpo como baseline estável. Trabalhe nos worktrees de feature:
main/ ← manter limpo, pull aqui, criar branches daqui
feature/something/ ← trabalhar aqui de verdade
feature/other/ ← outro trabalho aquiIniciando uma Nova Feature
./new-worktree.sh feature/my-feature --new
cd feature/my-feature
npm install
npm startTrocando de Contexto
Sem stash, sem commit de WIP. Apenas:
# Terminal
cd ../main
# Lazygit: Painel Branches (3) → Aba Worktrees (]) → Enter
# Yazi: navegar para o diretório irmãoCode Review
git fetch origin
git worktree add pr-review origin/someones-branch
cd pr-review
npm install && npm start
# Revisar, testar, pronto
git worktree remove pr-reviewDicas e Armadilhas
- Regra de mesma branch: Você não pode ter dois worktrees na mesma branch.
- Estado git compartilhado: Commits e stashes são compartilhados. Um commit em um worktree é visível de todos os outros.
- Dependências separadas: Cada worktree precisa de seu próprio
npm install. - Tratamento de IDE: Abra sua IDE em pastas de worktree individuais, não no wrapper. VS Code lida bem com o arquivo
.git; outras IDEs podem precisar de configuração. - Limpeza: Remova worktrees que você não precisa mais. Execute
git worktree pruneperiodicamente para limpar referências obsoletas.
