Tutorial

git rebase: como funciona, modo interativo e squash de commits

Atualizado: · Skillaria.Top

O git rebase move os commits da branch atual para uma nova base: o Git tira temporariamente seus commits, posiciona a branch no fim da branch indicada e os reaplica como commits novos. Assim você obtém um histórico linear sem commits de merge, e com git rebase -i dá para juntar (squash), renomear ou remover commits; só reescreva assim commits que ainda não foram publicados para outras pessoas.

O que o git rebase faz

O git rebase "transplanta" sua branch para outro ponto do histórico. Imagine que você criou a feature a partir da main há uma semana, e a main andou desde então. Depois do rebase, sua branch parece ter sido começada agora — a partir do commit mais recente da main.

git switch feature
git fetch origin
git rebase origin/main

Por baixo dos panos acontecem três passos. O Git encontra o ancestral comum das branches, guarda uma a uma as alterações de cada commit seu feito depois dele, move a branch para origin/main e reaplica as alterações guardadas, uma após a outra. O resultado são commits novos com o mesmo conteúdo e as mesmas mensagens, mas com outros hashes.

Antes:   A---B---C---F---G   main
                  \
                   D---E     feature

Depois:  A---B---C---F---G   main
                          \
                           D'---E'   feature

O apóstrofo em D' e E' não é enfeite. São outros objetos, com outros hashes e outro pai. Daí vem a regra principal do rebase, logo abaixo.

Regra de ouro: não faça rebase do que já foi publicado

O rebase reescreve o histórico. Enquanto os commits só existem na sua máquina, é seguro. Mas se os colegas já baixaram a branch, depois do rebase eles continuam com os antigos D e E, você fica com os novos D' e E', e no próximo pull o histórico duplica e os conflitos precisam ser resolvidos de novo.

Regra prática: use rebase nas suas branches locais e nas feature branches pessoais em que só uma pessoa trabalha. Branches compartilhadas — main, develop, de release — nunca passam por rebase.

Como enviar a branch depois do rebase

Se a branch já estava no servidor, o git push normal será rejeitado: o histórico local não continua mais o remoto. É preciso um push forçado, na versão segura:

git push --force-with-lease

Diferente do --force, o --force-with-lease se recusa a sobrescrever a branch se surgiram no servidor commits que você não viu. Assim você não apaga o trabalho de alguém por acidente.

Conflitos no rebase

Como os commits são aplicados um por um, o conflito pode surgir em qualquer um deles — e até várias vezes seguidas. O Git para e informa qual commit não conseguiu aplicar.

$ git rebase origin/main
Auto-merging src/api.js
CONFLICT (content): Merge conflict in src/api.js
error: could not apply 4e1f9a2... Adicionar cache de requisições
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

O que fazer:

  1. Abra o arquivo em conflito, escolha a versão correta e remova os marcadores <<<<<<<, =======, >>>>>>>.
  2. Marque o arquivo como resolvido: git add src/api.js. Não é preciso commitar.
  3. Continue: git rebase --continue. O Git aplica o próximo commit.

Os demais comandos para controlar o processo:

ComandoO que faz
git rebase --continueContinuar depois de resolver o conflito
git rebase --skipPular o commit atual inteiro (as alterações dele se perdem)
git rebase --abortInterromper o rebase e voltar a branch ao estado original

Atenção à confusão entre o lado "nosso" e o "deles". Durante o rebase, o HEAD é a base para onde você está movendo a branch, e as alterações "que chegam" são os seus próprios commits. Por isso, no rebase o git checkout --ours pega a versão da main, e o --theirs, a sua. No merge é exatamente o contrário.

Conflitos repetidos: rerere

Se você faz rebase com frequência de uma branch longa e resolve o mesmo conflito várias vezes, ative git config --global rerere.enabled true. O Git memoriza como você resolveu o conflito e, na próxima vez, aplica a solução sozinho.

Rebase interativo: arrumando o histórico antes de publicar

O mais valioso do rebase é o modo interativo. Com ele você organiza seus commits antes de mostrá-los no review: junta "fix", "fix typo", "mais um fix" em um commit que faça sentido, reescreve mensagens, muda a ordem ou descarta o que sobra.

git rebase -i HEAD~4        # os últimos 4 commits
git rebase -i origin/main   # todos os commits da branch que não estão na main

O Git abre o editor com a lista de commits — dos mais antigos para os mais novos (ao contrário do git log):

pick 1a2b3c4 Adicionar formulário de login
pick 5d6e7f8 fix
pick 9a0b1c2 Validação de e-mail
pick 3d4e5f6 typo na validação

# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash" but keep only the previous commit's log message
# d, drop = remove commit

Você troca a palavra no início da linha, salva e fecha o editor — o Git executa o plano de cima para baixo.

ComandoEfeito
pickManter o commit como está
rewordManter as alterações, mas reescrever a mensagem
editParar no commit para alterar o conteúdo (git commit --amend) ou dividi-lo em vários
squashFundir no commit anterior, juntando e editando as mensagens
fixupFundir no commit anterior, descartando a mensagem deste commit
dropRemover o commit junto com as alterações

Reordenar as linhas muda a ordem dos commits; apagar uma linha equivale a drop.

Git squash: como juntar commits em um só

Não existe um comando git squash no Git. Dá para fazer squash de commits de três formas, e o rebase interativo é a mais flexível.

Forma 1. Rebase interativo

No exemplo acima, vamos transformar quatro commits em dois bem organizados:

pick 1a2b3c4 Adicionar formulário de login
fixup 5d6e7f8 fix
pick 9a0b1c2 Validação de e-mail
fixup 3d4e5f6 typo na validação

O primeiro commit do grupo precisa continuar como pick: squash e fixup fundem o commit no anterior, e o primeiro não tem onde ser fundido.

Forma 2. Commits fixup e autosquash

Se você já sabe qual commit está corrigindo, crie a correção já marcada:

git commit --fixup=1a2b3c4
git rebase -i --autosquash origin/main

O Git posiciona o commit fixup logo abaixo do commit certo e o marca como fixup — só falta salvar o plano. Para tornar isso o padrão, use git config --global rebase.autoSquash true.

Forma 3. Condensar a branch inteira em um commit

git reset --soft $(git merge-base HEAD origin/main)
git commit -m "Login por e-mail com validação"

O reset --soft volta o ponteiro da branch ao ponto em que ela foi criada, mas mantém todas as alterações no índice — só falta fazer um commit. Outra opção é o git merge --squash ao integrar na branch principal, ou o botão "Squash and merge" no pull request.

Variações úteis do rebase

git pull --rebase

O pull normal faz merge da branch remota na sua e cria um commit de merge se você commitou em paralelo com os colegas. O git pull --rebase, em vez disso, reaplica seus commits locais sobre os remotos mais recentes. Para tornar o padrão: git config --global pull.rebase true. Essa é justamente a zona segura do rebase — ninguém viu seus commits locais ainda.

--onto: mover parte da branch

Você criou a feature-b a partir da feature-a e agora quer que a feature-b parta direto da main, sem os commits da feature-a:

git rebase --onto main feature-a feature-b

Leia assim: pegue os commits da feature-b que não estão na feature-a e transplante-os para a main.

--exec: testar cada commit

git rebase -i --exec "npm test" origin/main

Depois de cada commit aplicado, os testes rodam; se falharem, o rebase para no commit problemático. Útil quando é importante que todo commit do histórico compile.

--update-refs: pilha de branches dependentes

Se você mantém uma cadeia de branches que dependem umas das outras, a flag --update-refs (Git 2.38+) move também as intermediárias ao fazer rebase da branch do topo.

Como desfazer um rebase

Enquanto o processo está em andamento — git rebase --abort. Se o rebase já terminou e você não gostou do resultado, os commits antigos não sumiram: o log de referências se lembra deles.

$ git reflog
e5f6a7b HEAD@{0}: rebase (finish): returning to refs/heads/feature
e5f6a7b HEAD@{1}: rebase (pick): Validação de e-mail
...
c3d4e5f HEAD@{5}: commit: typo na validação

$ git reset --hard HEAD@{5}

Encontre no git reflog a entrada anterior ao início do rebase e resete a branch para ela. Logo depois do rebase também funciona o atalho git reset --hard ORIG_HEAD. O reflog fica só na sua máquina e, por padrão, guarda as entradas por cerca de 90 dias, então não precisa entrar em pânico depois de um rebase que deu errado.

Erros comuns

  • Fizeram rebase de uma branch compartilhada e um force push — o histórico dos colegas se desencontrou. Correção: alinhar com o time, restaurar a branch a partir do reflog de quem tem a versão correta e proibir force push nas branches protegidas do servidor.
  • Usam --force em vez de --force-with-lease e apagam um commit que o colega acabou de enviar para a mesma branch.
  • Squash na primeira linha do plano interativo — o Git responde com o erro "cannot 'squash' without a previous commit".
  • Começaram o rebase com a árvore de trabalho suja. O Git se recusa a rodar. Commite as alterações, guarde-as com git stash ou use git rebase --autostash.
  • Resolveram o conflito e rodaram git commit em vez de git rebase --continue. Sobra um commit extra; depois do --continue, dá para removê-lo com outro rebase interativo.

Conclusão

O git rebase move seus commits para uma nova base e os recria, por isso o histórico fica linear e limpo. O modo interativo transforma commits de rascunho em um histórico organizado: squash e fixup juntam as correções, reword corrige mensagens, drop elimina o que sobra. A regra principal é uma só — faça rebase apenas do que ainda não publicou para outras pessoas e, depois do rebase da sua branch remota, envie com --force-with-lease. O uso do rebase e a limpeza do histórico de commits são praticados no curso de Git.

Perguntas frequentes

O git rebase altera o conteúdo do código?

Sem conflitos, o conteúdo final dos arquivos é o mesmo de um merge, mas os commits são recriados com novos hashes e novos pais. Se num conflito você escolher outra versão, os commits intermediários vão diferir dos originais.

Como fazer rebase a partir do primeiríssimo commit do repositório?

Use git rebase -i --root: o plano inclui todos os commits, inclusive o raiz. Isso raramente é necessário, por exemplo para reescrever a mensagem do primeiro commit.

O rebase preserva o autor e a data dos commits?

O autor e a data de autoria são preservados; o committer e a data do commit passam a ser os atuais. A flag --committer-date-is-author-date mantém a data do commit igual à de autoria.

Dá para fazer rebase de commits de merge?

Por padrão, o rebase lineariza o histórico e descarta os commits de merge. Para preservar a estrutura de merges, use git rebase --rebase-merges.

Como saber se há um rebase em andamento?

O git status mostra a linha interactive rebase in progress ou You are currently rebasing e indica quais comandos estão disponíveis. Muitos shells e IDEs também exibem isso no prompt.

Aprenda este tema em vídeo

Este tema é abordado em profundidade, com prática, no curso “Git do zero ao desenvolvedor confiante”.

Ir para o curso