Estratégias de merge vs rebase: quando usar cada um

1. Fundamentos: merge e rebase no Git

No ecossistema Git, merge e rebase são duas operações fundamentais para integrar mudanças entre branches. Embora ambas atinjam o mesmo objetivo final — unir alterações —, elas diferem radicalmente na forma como manipulam o histórico.

Merge cria um commit de merge explícito, preservando a estrutura exata de quando cada branch divergiu. Quando você executa git merge feature, o Git gera um novo commit que conecta as duas linhas de desenvolvimento, mantendo intactos todos os commits originais e seus pais.

git checkout main
git merge feature
# Cria um commit de merge com dois pais

Rebase, por outro lado, reescreve o histórico. O comando git rebase main pega os commits da branch atual e os reaplica um a um sobre o topo da branch main, criando novos commits com novos hashes.

git checkout feature
git rebase main
# Reaplica os commits da feature sobre o topo da main

A diferença central é clara: merge preserva o histórico exato de quando e como as mudanças ocorreram; rebase cria um histórico linear e limpo, mas perde o contexto temporal original.

2. Quando usar merge: cenários recomendados

O merge é a operação mais segura para ambientes colaborativos, especialmente quando múltiplos desenvolvedores trabalham nas mesmas branches.

Trabalho em equipe com branches públicas e compartilhadas é o caso clássico. Se você está em uma branch develop que todos os membros da equipe utilizam, o merge garante que ninguém precise forçar push ou lidar com históricos divergentes.

# Cenário: integrar feature concluída na develop
git checkout develop
git merge feature-x
git push origin develop

Integração de branches de longa duração, como develop para main em releases, também favorece o merge. O commit de merge serve como um marcador claro no histórico: "aqui foi feita a release 2.3.0".

Necessidade de manter o histórico original intacto para auditoria é outro motivo forte. Em projetos regulados ou com requisitos de rastreabilidade, cada commit original precisa ser preservado com seu timestamp e autor exatos. O merge não altera commits existentes, apenas adiciona o commit de merge.

3. Quando usar rebase: cenários recomendados

O rebase brilha em cenários onde a clareza e a linearidade do histórico são mais importantes que a precisão cronológica.

Manutenção de histórico linear em branches de feature individuais é o uso mais comum. Em vez de ter múltiplos commits de merge poluindo o gráfico, você mantém uma linha reta de desenvolvimento.

# Antes do rebase: histórico com forks
git log --graph --oneline
* a1b2c3 (HEAD -> feature) Adiciona validação
* d4e5f6 Adiciona endpoint
* g7h8i9 (main) Corrige bug crítico

# Após git rebase main
git log --graph --oneline
* j1k2l3 (HEAD -> feature) Adiciona validação
* m4n5o6 Adiciona endpoint
* g7h8i9 (main) Corrige bug crítico

Atualização de uma branch de feature com a branch base antes do merge evita conflitos posteriores. Ao fazer rebase, você resolve os conflitos gradualmente, commit por commit, em vez de enfrentar um conflito gigante no merge final.

Limpeza de commits locais antes de abrir um Pull Request é outro uso poderoso. Com git rebase --interactive, você pode combinar, reordenar ou editar commits para apresentar um histórico coeso aos revisores.

4. Riscos e boas práticas do rebase

A regra de ouro do rebase é absoluta: nunca faça rebase em branches públicas ou compartilhadas. Se você reescrever commits que outros desenvolvedores já baixaram, criará divergências que exigirão git push --force e causarão dor de cabeça generalizada.

Conflitos durante o rebase são resolvidos commit a commit, o que pode ser tedioso em branches com muitos commits. Cada conflito interrompe o processo, exigindo resolução manual e git rebase --continue.

git rebase main
# Conflito no commit "Adiciona validação"
# Resolver conflitos manualmente
git add arquivo-resolvido.txt
git rebase --continue

Uso de git rebase --interactive é uma ferramenta poderosa para organizar o histórico. Você pode usar pick, squash, reword e edit para moldar os commits.

git rebase -i HEAD~5
# Editor abre com lista de commits
# pick abc123 Adiciona validação
# squash def456 Corrige typo na validação
# pick ghi789 Adiciona testes

5. Estratégias híbridas: combinando merge e rebase

A abordagem mais madura combina o melhor dos dois mundos: rebase nas branches de feature locais, merge nas branches compartilhadas.

Fluxo de trabalho recomendado: desenvolva em branches de feature, faça rebase com a branch base para manter o histórico linear, depois use merge (com --no-ff) para integrar na branch principal.

# Desenvolvedor na branch feature
git checkout feature
git rebase main
# Resolve conflitos se houver
git push origin feature --force-with-lease

# Mantenedor integrando na main
git checkout main
git merge --no-ff feature
git push origin main

O merge --no-ff força a criação de um commit de merge mesmo quando um fast-forward seria possível, preservando o contexto de que aquela integração veio de uma branch específica.

Exemplo prático completo:

# Início: ambas as branches na mesma base
git checkout feature-login
# Trabalha por 3 dias, faz 5 commits
git log --oneline
# a1 b2 c3 d4 e5 (feature-login)

# Main avançou com correções
git fetch origin
git rebase origin/main
# Reaplica a1 b2 c3 d4 e5 sobre a nova main

# Abre PR, mantenedor faz merge
git checkout main
git merge --no-ff feature-login
# Histórico: main -> commit de merge -> feature-login (linear)

6. Ferramentas e comandos úteis para decisão

git log --graph é seu melhor amigo para visualizar o histórico e decidir a estratégia. Mostra forks, merges e a estrutura geral do repositório.

git log --graph --oneline --all
*   f1g2h3 (HEAD -> main) Merge branch 'feature'
|\
| * i2j3k4 Adiciona testes
| * l5m6n7 Implementa lógica
* | o8p9q0 Corrige bug urgente
|/
* r1s2t3 Versão inicial

git merge --squash é uma alternativa ao rebase quando você quer combinar todos os commits de uma feature em um único commit, sem reescrever o histórico.

git checkout main
git merge --squash feature
git commit -m "Implementa feature de login"

Comparação de comandos:

Comando Efeito no histórico Uso recomendado
git merge Preserva forks e cria commit de merge Branches compartilhadas
git rebase Lineariza histórico, reescreve commits Branches locais
git pull --rebase Atualiza branch local sem commits de merge Atualização diária

7. Casos especiais e armadilhas comuns

Rebase de branches com commits já enviados para repositório remoto é a armadilha mais perigosa. Se você já fez push de uma branch e outros desenvolvedores a usam, nunca faça rebase. Se precisar, use --force-with-lease (mais seguro que --force) e comunique a equipe.

# Apenas se for branch pessoal e ninguém mais usar
git push origin feature --force-with-lease

Recuperação de commits perdidos após rebase é possível com git reflog. O Git mantém um registro de todos os movimentos da HEAD por aproximadamente 90 dias.

git reflog
# Encontre o commit perdido: abc123 HEAD@{2}: checkout: moving from feature to main
git checkout -b feature-recuperada abc123

Conflitos frequentes podem ser minimizados com comunicação da equipe: alocar responsabilidades claras por módulos, fazer pulls frequentes e manter branches de feature curtas (dias, não semanas).

Referências