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
- Documentação oficial do Git - git merge — Referência completa sobre a operação de merge, opções e flags
- Documentação oficial do Git - git rebase — Guia detalhado sobre rebase, incluindo modo interativo e opções avançadas
- Atlassian - Merging vs. Rebasing — Tutorial prático comparando as duas estratégias com exemplos visuais
- GitHub Blog - Git merge vs rebase: the definitive guide — Guia definitivo da equipe GitHub sobre quando usar cada operação
- Git SCM Book - Git Branching - Rebasing — Capítulo do livro Pro Git explicando rebase em profundidade com cenários reais
- FreeCodeCamp - Git Rebase vs Merge: What's the Difference? — Artigo didático com exemplos práticos para iniciantes e intermediários