Rebase: reescrevendo o histórico

1. Fundamentos do Rebase

O git rebase é uma ferramenta poderosa que permite reescrever o histórico de commits de um repositório Git. Diferentemente do merge, que cria um commit de fusão explícito, o rebase reaplica commits de uma branch sobre outra base, resultando em um histórico linear e mais limpo.

O mecanismo interno do rebase funciona da seguinte forma: ele identifica os commits que estão na branch atual mas não estão na branch alvo, "desfaz" temporariamente esses commits, move o ponteiro da branch para o commit mais recente da branch alvo e, em seguida, reaplica cada commit um a um sobre essa nova base.

Casos de uso típicos incluem:
- Rebase: quando você quer manter um histórico linear e limpo, especialmente em branches de feature antes de integrá-las
- Merge: quando você precisa preservar o contexto de que uma branch foi integrada em outra, ou em branches compartilhadas com múltiplos colaboradores

2. Rebase Básico: Linearizando o Histórico

A sintaxe básica do rebase é simples. Suponha que você tenha uma branch feature com dois commits e deseja integrá-la na main:

# Na branch feature
git checkout feature
git rebase main

Exemplo prático completo:

# Criando cenário de exemplo
git init rebase-exemplo
cd rebase-exemplo
echo "versão 1" > arquivo.txt
git add arquivo.txt
git commit -m "Commit inicial na main"

git checkout -b feature
echo "feature trabalho 1" >> arquivo.txt
git commit -am "Adiciona primeira funcionalidade"
echo "feature trabalho 2" >> arquivo.txt
git commit -am "Adiciona segunda funcionalidade"

git checkout main
echo "correção urgente" >> arquivo.txt
git commit -am "Correção na main"

# Rebase da feature sobre a main
git checkout feature
git rebase main

# Verificando o resultado
git log --graph --oneline --all

O resultado será um histórico linear onde os commits da feature aparecem após o commit mais recente da main, como se tivessem sido feitos sequencialmente.

3. Rebase Interativo: Controle Total sobre os Commits

O rebase interativo oferece controle granular sobre cada commit. Inicie-o com:

git rebase -i HEAD~3

Isso abrirá um editor com uma lista de commits e comandos disponíveis:

pick a1b2c3d Primeiro commit
pick e4f5g6h Segundo commit
pick i7j8k9l Terceiro commit

Os principais comandos são:
- pick: mantém o commit como está
- reword: altera apenas a mensagem do commit
- edit: pausa o rebase para modificar o conteúdo do commit
- squash: mescla o commit com o anterior e permite editar a mensagem combinada
- fixup: mescla o commit com o anterior descartando sua mensagem
- drop: remove o commit do histórico

4. Squash e Fixup: Limpeza de Commits

Suponha que você tenha uma série de commits "sujos" que deseja limpar:

git log --oneline
# Saída:
# d4e5f6g Correção final
# c3d4e5f Ajuste no código
# b2c3d4e WIP: implementação inicial
# a1b2c3d Commit inicial

Para transformar esses commits em um único commit limpo:

git rebase -i a1b2c3d

No editor, reorganize para:

pick a1b2c3d Commit inicial
pick b2c3d4e WIP: implementação inicial
squash c3d4e5f Ajuste no código
fixup d4e5f6g Correção final

Com squash, você poderá editar a mensagem combinada. Com fixup, a mensagem do commit é descartada automaticamente.

5. Reordenando e Editando Commits

Para reordenar commits, basta alterar a ordem das linhas no editor do rebase interativo:

pick c3d4e5f Ajuste no código
pick b2c3d4e WIP: implementação inicial
pick d4e5f6g Correção final

Para editar uma mensagem de commit, use reword:

reword b2c3d4e WIP: implementação inicial

O Git abrirá o editor para que você modifique a mensagem.

Para modificar o conteúdo de um commit, use edit:

edit b2c3d4e WIP: implementação inicial

Quando o rebase pausar, faça as alterações necessárias, adicione os arquivos e execute:

git commit --amend
git rebase --continue

6. Conflitos Durante o Rebase

Durante o rebase, cada commit é reaplicado individualmente, o que significa que conflitos podem surgir em qualquer um deles. O Git pausa o rebase no commit problemático:

git rebase main
# Conflito ao reaplicar commit "Adiciona primeira funcionalidade"

Para resolver:
1. Edite os arquivos conflitantes
2. Use git mergetool para uma interface visual (opcional)
3. Adicione os arquivos resolvidos: git add arquivo.txt
4. Continue o rebase: git rebase --continue

Se necessário:
- git rebase --abort: retorna ao estado anterior ao rebase
- git rebase --skip: pula o commit problemático (remove-o do histórico)

7. Cuidados e Boas Práticas

A regra de ouro do rebase: nunca faça rebase em commits que já foram enviados para um repositório remoto compartilhado. Reescrever o histórico público causa confusão e problemas de sincronização para outros desenvolvedores.

Diferenças importantes:
- Rebase: ideal para branches locais e de feature antes do merge
- Merge: recomendado para branches compartilhadas e para preservar o contexto de integração

Antes de fazer push, verifique o histórico:

git log --oneline
git diff main..feature

Configure o pull com rebase automático:

git config --global pull.rebase true

8. Recuperação e Cenários Avançados

Se um rebase der errado, o git reflog é seu salva-vidas:

git reflog
# Encontre o commit anterior ao rebase
git reset --hard HEAD@{2}

O rebase com --onto permite mover uma sequência de commits para outra base:

# Move commits do branch feature para uma nova base
git rebase --onto main feature~3 feature

Para atualizar sua branch com a main usando rebase:

git pull --rebase origin main

Isso evita commits de merge desnecessários e mantém o histórico linear.

O rebase é uma ferramenta essencial para manter um histórico Git limpo e compreensível. Quando usado corretamente, especialmente em branches locais e de feature, ele transforma um histórico caótico de desenvolvimento em uma narrativa clara e profissional das mudanças no código.

Referências