git rebase

Branches e merges

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)
[ ] opcional| ou<arg> valor a substituir... repetível--flag opção

Opções

Clique em uma opção para expandir

Iniciante (3)

Retomar o rebase depois de resolver os conflitos. Rode depois de preparar os arquivos resolvidos com git add.

RecomendadoO jeito normal de retomar depois de resolver e preparar os conflitos

Exemplo

git rebase --continue

Cancelar o rebase e voltar ao estado original, de antes do início do rebase. Use quando os conflitos forem complexos demais ou você mudar de ideia.

RecomendadoSaída de emergência segura: devolve a sua branch ao estado de antes do rebase

Exemplo

git rebase --abort

Pular o commit atual e seguir para o próximo. Use quando um commit fica vazio ou as mudanças dele não são mais necessárias.

Quando necessárioDescarta o commit atual — só quando as mudanças dele realmente não são desejadas

Exemplo

git rebase --skip

Intermediário (3)

Abrir um editor para reordenar, juntar, editar ou descartar commits antes de reaplicá-los. O jeito mais poderoso de limpar o histórico.

RecomendadoO melhor jeito de arrumar commits locais antes de compartilhá-los

Exemplo

git rebase -i HEAD~3

Guardar automaticamente as mudanças sem commit antes do rebase e restaurá-las depois. Poupa você de fazer stash à mão.

Quando necessárioÚtil com a árvore suja, mas reaplicar o stash pode dar conflito

Exemplo

git rebase --autostash main

Forçar o rebase mesmo quando a branch está atualizada. Cria commits novos com hashes novos para todos os commits do intervalo.

Quando necessárioSó quando você precisa de hashes novos para commits já atualizados

Exemplo

git rebase -f main

Avançado (5)

Transplantar commits para outra base. Permite fazer rebase dos commits de uma branch sobre outro commit específico.

Quando necessárioMove um intervalo exato de commits para uma base nova — confira bem o intervalo

Exemplo

git rebase --onto main feature~3 feature

Mover e juntar automaticamente os commits fixup!/squash! (criados com git commit --fixup ou --squash) nos seus alvos. Funciona com ou sem -i desde o Git 2.44.

RecomendadoCombina com commit --fixup para limpar o histórico sem esforço

Exemplo

git rebase -i --autosquash main

Preservar os commits de merge e a estrutura de branches durante o rebase. Recria a topologia dos merges em vez de achatar o histórico.

Quando necessárioQuando a estrutura de merges de uma branch precisa sobreviver ao rebase

Exemplo

git rebase -r main

Executar um comando de shell depois de aplicar cada commit. Útil para rodar os testes e garantir que cada commit compila.

Quando necessárioRoda testes em cada commit reaplicado; lento em branches longas

Exemplo

git rebase -i -x 'npm test' main

Fazer rebase de todos os commits alcançáveis a partir do HEAD, incluindo o commit raiz. Permite reescrever o histórico inteiro.

Não recomendadoReescreve o histórico inteiro desde o primeiro commit; raramente necessário

Exemplo

git rebase -i --root

Escolhendo a opção certa

Comparando git rebase com git merge

Entender quando usar rebase ou merge para integrar mudanças

ComandoAspectoGit RebaseGit Merge
HistoryLinear (reescreve os commits)Preserva o histórico divergente
CommitsCria commits NOVOS com SHAs diferentesPreserva os commits originais + um commit de merge
DestructiveSim - muda os hashes dos commitsNão - só adiciona um commit de merge
ConflictsResolvidos commit a commit durante a reaplicaçãoResolvidos uma vez no ponto de merge
Best forBranches locais/privadas antes do pushBranches públicas/compartilhadas
TraceabilityMais difícil ver os pontos de integraçãoRegistro claro da integração das branches

Exemplos de fluxos de trabalho

1
git fetch origin

Pegar as mudanças mais recentes do remoto

2
git checkout feature

Trocar para a sua branch de funcionalidade

3
git rebase origin/main

Reaplicar os seus commits em cima da main mais recente

4
git push --force-with-lease

Enviar a branch com rebase (só se não for compartilhada)

1
git log --oneline

Contar quantos commits juntar

2
git rebase -i HEAD~5

Rebase interativo dos últimos 5 commits

3
(in editor) pick, squash, squash...

Manter o primeiro e juntar o resto nele

4
(edit combined message)

Escrever uma mensagem de commit limpa

1
(make your fix)

Corrigir o problema no código

2
git commit --fixup=<commit-hash>

Criar um commit fixup para o alvo

3
git rebase --autosquash main

O autosquash organiza e junta automaticamente (desde o Git 2.44 não precisa de -i)

1
git rebase -i HEAD~4

Rebase interativo dos últimos 4 commits

2
(in editor) reorder lines

Mover as linhas dos commits para novas posições

3
save and close editor

O Git reaplica na nova ordem

1
git rebase -i HEAD~3

Começar um rebase interativo

2
(mark commit with 'edit')

Parar no commit a dividir

3
git reset HEAD^

Desfazer o commit, mantendo as mudanças não preparadas no diretório de trabalho

4
git add file1 && git commit -m 'Part 1'

Criar o primeiro commit menor

5
git add file2 && git commit -m 'Part 2'

Criar o segundo commit menor

6
git rebase --continue

Continuar com os commits restantes

1
git reflog

Achar o commit anterior ao rebase

2
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 --continue

Cannot 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 main

Um 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 --skip

O 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 --abort

Commits 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-lease

git 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 main

A 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..HEAD

Dicas 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

A:

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.

A:

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.

A:

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.

A:

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.

A:

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.

A:

--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.

A:

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.

A:

--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.

A:

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.

A:

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.