git rebase
Reaplicar commits em cima de outra base
O git rebase move ou junta uma sequência de commits sobre um novo commit base. É muito usado para manter um histórico linear, reaplicando os commits de uma branch de funcionalidade em cima da main mais recente. Diferente do merge, o rebase reescreve o histórico criando commits novos com SHAs diferentes. O rebase interativo (-i) permite juntar, reordenar, editar ou descartar commits antes de reaplicá-los.
Sinopse
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)Opções
Escolhendo a opção certa
Comparando git rebase com git merge
Entender quando usar rebase ou merge para integrar mudanças
| Comando | Aspecto | Git Rebase | Git Merge |
|---|---|---|---|
History | Linear (reescreve os commits) | Preserva o histórico divergente | |
Commits | Cria commits NOVOS com SHAs diferentes | Preserva os commits originais + um commit de merge | |
Destructive | Sim - muda os hashes dos commits | Não - só adiciona um commit de merge | |
Conflicts | Resolvidos commit a commit durante a reaplicação | Resolvidos uma vez no ponto de merge | |
Best for | Branches locais/privadas antes do push | Branches públicas/compartilhadas | |
Traceability | Mais difícil ver os pontos de integração | Registro claro da integração das branches |
Exemplos de fluxos de trabalho
git fetch originPegar as mudanças mais recentes do remoto
git checkout featureTrocar para a sua branch de funcionalidade
git rebase origin/mainReaplicar os seus commits em cima da main mais recente
git push --force-with-leaseEnviar a branch com rebase (só se não for compartilhada)
git log --onelineContar quantos commits juntar
git rebase -i HEAD~5Rebase interativo dos últimos 5 commits
(in editor) pick, squash, squash...Manter o primeiro e juntar o resto nele
(edit combined message)Escrever uma mensagem de commit limpa
(make your fix)Corrigir o problema no código
git commit --fixup=<commit-hash>Criar um commit fixup para o alvo
git rebase --autosquash mainO autosquash organiza e junta automaticamente (desde o Git 2.44 não precisa de -i)
git rebase -i HEAD~4Rebase interativo dos últimos 4 commits
(in editor) reorder linesMover as linhas dos commits para novas posições
save and close editorO Git reaplica na nova ordem
git rebase -i HEAD~3Começar um rebase interativo
(mark commit with 'edit')Parar no commit a dividir
git reset HEAD^Desfazer o commit, mantendo as mudanças não preparadas no diretório de trabalho
git add file1 && git commit -m 'Part 1'Criar o primeiro commit menor
git add file2 && git commit -m 'Part 2'Criar o segundo commit menor
git rebase --continueContinuar com os commits restantes
git reflogAchar o commit anterior ao rebase
git reset --hard HEAD@{n}Voltar ao estado anterior ao rebase
Solução de problemas
CONFLITO durante o rebase
Causa: As mudanças do seu commit conflitam com a branch base
Edite os arquivos para resolver os conflitos, faça git add e depois git rebase --continue
git add <resolved-files> && git rebase --continueCannot rebase: You have unstaged changes
Causa: O diretório de trabalho tem modificações sem commit
Faça commit, stash ou use --autostash para lidar com as mudanças sem commit
git rebase --autostash mainUm commit fica vazio depois do rebase
Causa: As mudanças deste commit já existem na branch de destino
Um rebase comum o descarta automaticamente; o interativo para - rode --skip para descartá-lo ou 'git commit --allow-empty' para mantê-lo
git rebase --skipO rebase gera muitos conflitos
Causa: A branch divergiu muito da base
Considere usar merge, ou cancele e faça o rebase em passos menores
git rebase --abortCommits perdidos depois do rebase
Causa: Os commits originais ficam órfãos (não são apagados, só deixam de ser referenciados)
Use o reflog para achar e recuperar os commits originais
git reflog && git branch recovery HEAD@{n}Push rejeitado (non-fast-forward) depois do rebase
Causa: O rebase reescreveu os commits, então a sua branch não contém mais o histórico da cópia remota
Use --force-with-lease para um force push mais seguro (ele recusa se outra pessoa enviou algo nesse meio-tempo)
git push --force-with-leasegit rebase diz “Current branch is up to date”
Causa: Nenhum commit para reaplicar - a branch já está baseada no destino
Use -f para forçar o rebase mesmo assim, se precisar de hashes novos
git rebase -f mainA lista de tarefas do rebase interativo está vazia
Causa: Não há commits no intervalo indicado
Confira o intervalo - use HEAD~n ou indique o upstream correto
git log --oneline main..HEADDicas e armadilhas
Boas práticas
- ✓Nunca faça rebase de commits enviados a um repositório compartilhado - isso obriga todo mundo a consertar as branches à mão
- ✓Sempre use 'git rebase --abort' se os conflitos ficarem complexos demais - você pode tentar outra abordagem
- ✓Use o rebase interativo (-i) para limpar os commits antes do push ou de criar um pull request
- ✓Considere usar '--autostash' para lidar automaticamente com mudanças sem commit durante o rebase
- ✓Use 'git commit --fixup=<hash>' com '--autosquash' para um fluxo de correção mais limpo
- ✓Rode os testes depois do rebase para garantir que os commits reaplicados ainda funcionam
- ✓Use '-x npm test' para rodar os testes automaticamente depois de cada commit reaplicado
- ✓Mantenha os rebases pequenos - faça rebase com frequência em vez de deixar as branches divergirem muito
- ✓Use 'git reflog' para se recuperar se um rebase der errado - os commits originais nunca são apagados na hora
- ✓Prefira merge a rebase em branches públicas/compartilhadas para preservar o histórico
- ✓Use '--force-with-lease' em vez de '--force' ao enviar branches com rebase, por segurança
- ✓Ao juntar commits, escreva uma mensagem nova e descritiva que resuma todos os commits juntados
Erros comuns
- ✗Fazer rebase de commits já enviados a um repositório compartilhado. Isso reescreve o histórico e obriga todos os outros a consertar as branches à mão.
- ✗Esquecer de preparar os conflitos resolvidos antes do --continue. Sempre rode 'git add' nos arquivos resolvidos primeiro.
- ✗Usar --skip sem cuidado, o que tira as mudanças do commit da branch com rebase. Só use quando quiser remover um commit de propósito.
- ✗Não entender que o rebase cria commits NOVOS com hashes diferentes. Os commits originais ficam órfãos.
- ✗Fazer rebase com mudanças sem commit no diretório de trabalho. Use --autostash ou faça stash antes à mão.
Perguntas frequentes
O merge preserva o histórico criando um commit de merge e mantém intactos os históricos das duas branches. O rebase reescreve o histórico reaplicando os seus commits em cima de outra branch, criando um histórico linear. Use merge em branches compartilhadas e rebase para limpeza local.
O rebase muda os hashes dos commits. Se outras pessoas basearam o trabalho nos seus commits originais, o histórico delas vai divergir do seu. Elas vão precisar consertar as branches à mão, o que é trabalhoso e sujeito a erros.
Use 'git reflog' para achar o commit anterior ao rebase (procure a ponta antiga da branch) e depois 'git reset --hard HEAD@{n}' para restaurá-lo. Por padrão, os commits originais continuam alcançáveis pelo reflog por 30 dias. Logo depois de um rebase, 'git reset --hard ORIG_HEAD' também funciona.
No rebase interativo: 'pick' mantém o commit como está, 'squash' junta com o commit anterior (mantendo as duas mensagens), 'fixup' junta mas descarta a mensagem, 'reword' mantém o commit mas deixa você editar a mensagem.
Rode 'git rebase -i HEAD~n', em que n é o número de commits. No editor, deixe 'pick' no primeiro commit e troque os outros por 'squash' ou 'fixup'. Salve e edite a mensagem do commit combinado.
--onto permite transplantar commits para outra base. Por exemplo, “git rebase --onto main feature~3 feature” move só os 3 últimos commits de feature para cima da main, pulando os anteriores.
Edite os arquivos em conflito para resolvê-los, rode 'git add' nos arquivos resolvidos e depois 'git rebase --continue'. Se ficar bagunçado demais, 'git rebase --abort' volta ao estado anterior ao rebase.
--preserve-merges foi removido no Git 2.34 (agora o git falha com “--preserve-merges was replaced by --rebase-merges”). Use --rebase-merges (-r) para preservar os commits de merge e a estrutura das branches durante o rebase. Ele recria a topologia dos merges em cima da nova base.
Sim, use 'git rebase -i' e marque com 'pick' os commits que quer manter e com 'drop' os que quer remover. Ou use --onto para indicar exatamente o intervalo de commits a mover.
O hash de um commit é calculado a partir do conteúdo, da mensagem, do autor, da data E do commit pai. Como o rebase muda o pai, todos os commits seguintes ganham hashes novos, mesmo com conteúdo idêntico.