Gerenciando múltiplas branches ao mesmo tempo com git worktree

Rod Elias
Fretebras Tech
Published in
5 min readApr 30, 2021

Não é novidade para ninguém que o Git contém uma infinidade de comandos. Dentre eles, você já conhecia o git worktree? Pois é, eu também não. E pelo visto nem o próprio Guido van Rossum, criador da linguagem de programação Python, também não conhecia ainda, conforme ele mesmo postou no Twitter recentemente:

O interessante é que esse comando foi introduzido no Git em meados de 2015, mas aparentemente não se tornou tão popular, apesar de ter uma funcionalidade muito útil conforme veremos nesse artigo.

Antes de falar o que é o comando git worktree propriamente dito, imagine o seguinte cenário: você está trabalhando todo feliz em uma branch de uma feature quando seu chefe te pede para corrigir imediatamente um bug que está acontecendo em produção. O problema é que nesse instante você tem vários arquivos novos, outros removidos e modificados nessa branch que ainda não chegaram a ser commitados. E agora?

Para resolver essa situação, pelo menos 3 possíveis abordagens podem ser aplicadas e uma delas, como você já deve ter imaginado, é com o uso do git worktree.

Uma primeira abordagem seria simplesmente realizar um commit de tudo que foi alterado e escrever uma mensagem do tipo Work In Progress para indicar que aquele trabalho está em andamento. Embora bastante simples, essa abordagem possui algumas desvantagens: depois de corrigido o bug, você precisa lembrar de corrigir a mensagem final desse commit e ainda há o risco de fazer um push acidental de algo que não estava completamente finalizado.

Uma segunda abordagem, certamente conhecida por um usuário mais assíduo de git, seria utilizar o comando git stash. Entretanto, o seu ambiente pode se encontrar em um estado tão grande de desordem que nem mesmo git stash iria funcionar. Há também o risco de você esquecer que efetuou um stash naquela branch, sem falar que precisará lembrar qual comando git mais apropriado para voltar as suas alterações: seria git stash apply? ou git stash pop? Eu particularmente sempre fico confuso com isso.

Por fim, a terceira abordagem para esse problema é justamente utilizar o nosso novo amigo git worktree.

What?

Basicamente, entenda o comando git worktree como sendo capaz de criar uma cópia extra do seu repositório, permitindo assim gerenciar múltiplas branches ao mesmo tempo.

A ideia é ter diretórios distintos onde cada diretório representa uma cópia do repositório associada a uma determinada branch. Dessa forma, você poderia, por exemplo, ter um diretório contendo a branch main, outro contendo a branch my-awesome-feature e outro contendo a branch hotfix-redirect e navegar por eles simultaneamente sem interferir no que você está trabalhando atualmente.

How

Nessa seção vamos finalmente aplicar o git worktree de forma mais detalhada para resolver o problema do cenário mencionado anteriormente.

Suponha então o seguinte exemplo fictício onde temos uma aplicação em PHP que apenas realiza algumas operações matemáticas básicas. O conteúdo da branch main é mostrado abaixo:

rod@FBCT-TECH-0061 /tmp/demo/secret-project (main) $ tree -L 2
.
├── composer.json
├── composer.lock
├── src
│ └── Math.php
└── tests
└── MathTest.php
2 directories, 4 files

Agora suponha a existência de uma branch chamada my-awesome-feature onde a saída do comando git status produz o seguinte output:

rod@FBCT-TECH-0061 /tmp/demo/secret-project (my-awesome-feature) $ git status
On branch my-awesome-feature
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: src/Math.php
deleted: tests/MathTest.php
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/AdvancedMath.php
src/BasicMath.php
tests/AdvancedMathTest.php
tests/BasicMathTest.php

Como pode ser observado, alguns arquivos foram renomeados e outros criados.

Surge então a necessidade de resolver um bug existente em main, pois suponha que nossa aplicação não está tratando casos onde é feita uma divisão por zero.

Assim, bastaria executar o comando

git worktree add -b fix-division-by-zero ../hotfix main

para criar o diretório chamado hotfix um nível acima do diretório atual contendo a branch com o nome fix-division-by-zero a qual por sua vez é baseada na branch main:

rod@FBCT-TECH-0061 /tmp/demo/secret-project (my-awesome-feature) $ git worktree add -b fix-division-by-zero ../hotfix main
Preparing worktree (new branch 'fix-division-by-zero')
HEAD is now at 3fa7a89 initial commit

A qualquer momento o comando git worktree list pode ser usado para listar todos os diretórios dos worktrees criados juntamente com os seus respectivos nomes de branch e commit associados:

rod@FBCT-TECH-0061 /tmp/demo/secret-project (my-awesome-feature) $ git worktree list
/tmp/demo/secret-project 3fa7a89 [my-awesome-feature]
/tmp/demo/hotfix 3fa7a89 [fix-division-by-zero]

Feito isso, agora é só acessar ao diretório /tmp/demo/hotfix e efetuar o bugfix necessário. Aqui o restante do fluxo acontece normalmente, ou seja, finalizado o bugfix basta fazer o merge dessa branch fix-division-by-zero na branch main.

Note que enquanto isso o seu trabalho na feature my-awesome-feature no worktree secret-project permanece inalterado e pronto para ter continuidade.

Com a correção já em main, a etapa final consiste em remover o worktree criado e para isso pode ser usado o comando git worktree remove passando o nome do diretório/worktree como argumento:

rod@FBCT-TECH-0061 /tmp/demo/secret-project (my-awesome-feature) $ git worktree remove ../hotfixrod@FBCT-TECH-0061 /tmp/demo/secret-project (my-awesome-feature) $ git worktree list
/tmp/demo/secret-project 3fa7a89 [my-awesome-feature]

Observe que em todo repositório git, a saída do git worktree list sempre irá retornar pelo menos um worktree, que é o worktree padrão quando o repositório foi inicializado via git init.

What else?

Naturalmente que o comando git worktree pode ser empregado em muitas situações e esse artigo mostrou apenas uma delas. É importante ressaltar que o intuito aqui não é para deixar de utilizar o git stash, mas sim servir como uma alternativa nos casos onde o git stash se torna impraticável. Outras aplicações para o git worktree incluem:

  • executar testes em uma branch enquanto se trabalha em outra branch
  • comparar ou executar múltiplas versões de um projeto ao mesmo tempo

Para se aprofundar ainda mais nesse comando, não deixe de ler a sua man page obtida com git worktree --help ou visite o site da documentação oficial.

Pois pronto! E então? O que achou? Com certeza esse é um comando que tentarei usar mais no dia-a-dia.

--

--