trilha

Fundamentos de Programação/02 - Código que Presta/Semana 08 - Git Além do Básico5 min

Rebase vs Merge

Perguntas-guia
  • O que cada um faz com o histórico — e qual dos dois cria commits novos?
  • Por que nunca rebasear branch que outra pessoa já puxou?
  • O que --ff-only protege?
  • Histórico linear vale a perda do contexto de "quando isso foi integrado"?

Conceito

Os dois integram trabalho de uma linha de desenvolvimento em outra. A diferença é o que acontece com o histórico.

merge rebase
O que faz cria um commit com dois pais recria seus commits sobre a nova base
Commits novos? um (o merge commit) um por commit reaplicado
Hash dos seus commits preservado muda
Histórico resultante grafo linha
Registra "quando foi integrado"? sim não
A distinção de uma frase

Merge registra. Rebase reescreve.

Rebase não move commits — ele cria cópias com o mesmo conteúdo e pai diferente. Como o hash cobre o pai, a árvore e os metadados, mudar o pai muda o hash. Os commits originais continuam existindo, órfãos, recuperáveis pelo 4. Reflog até o gc.

Por que nunca rebasear branch que outra pessoa já puxou: ela tem os commits antigos; você tem cópias novas com o mesmo conteúdo. Quando ela puxar, o git não tem como saber que são a mesma coisa — ela recebe as duas versões, e o histórico duplica. Resolver isso exige que ela faça reset --hard para o seu estado, perdendo qualquer trabalho local.

A regra de ouro é simples: rebase só no que é privado. Antes de publicar, reescreva à vontade. Depois de publicar, só merge.

O que --ff-only protege: ele recusa criar um merge commit. Se as branches divergiram, ele falha em vez de fazer merge — te forçando a decidir consciente entre merge e rebase, em vez de acumular merge commits acidentais de git pull.

Histórico linear vale a perda de contexto? Depende do que você faz com o histórico:

  • linear é mais fácil de ler e faz bisect andar melhor (5. Bisect)
  • linear inventa estados que nunca existiram — seus commits reaplicados nunca foram testados naquela base. É a origem do conflito semântico: compila, não conflita, e está quebrado (6. Resolução de Conflitos)
  • merge preserva a verdade histórica e produz um grafo que ninguém lê sem ferramenta

Na prática

Verificado num repo de teste: o mesmo commit, antes e depois do rebase.

$ git log --oneline feat          # antes
5493ccb add x
$ git rebase master
$ git log --oneline feat          # depois
680076a add x                     # <- hash DIFERENTE, diff idêntico

Mesmo conteúdo (1 file changed, 1 insertion(+)), identidade nova. É isso que "reescrever o histórico" significa concretamente.

E o --ff-only recusando, com a saída real:

$ git merge --ff-only feat2
hint: Diverging branches can't be fast-forwarded, you need to either:
hint: 	git merge --no-ff
hint: or:
hint: 	git rebase
fatal: Not possible to fast-forward, aborting.

Sem o --ff-only, o mesmo comando criaria um merge commit silenciosamente.

Respostas às perguntas-guia

1. O que cada um faz com o histórico — e qual dos dois cria commits novos?

Merge cria um commit (o de merge, com dois pais) e preserva os seus. Rebase cria um commit novo por commit reaplicado e abandona os originais.

Verificado: 5493ccb virou 680076a com o mesmo diff.

2. Por que nunca rebasear branch que outra pessoa já puxou?

Porque ela tem os commits antigos e você tem cópias novas. O git não consegue reconciliar, e a resolução para ela é destrutiva (reset --hard para o seu estado). Rebase só no que é privado.

3. O que --ff-only protege?

Protege contra merge commit acidental. Ele falha quando não é possível avançar o ponteiro, em vez de criar um merge. Vale configurar como padrão:

git config --global pull.ff only

4. Histórico linear vale a perda do contexto de "quando isso foi integrado"?

Se você usa o histórico para investigar (bisect, log -S, blame), linear ganha. Se você usa para auditar integração, merge ganha. E há um custo escondido no linear: commits reaplicados nunca existiram naquela base, então "compilou em cada commit" deixa de ser verdade — o que enfraquece exatamente o bisect que motivou a linearidade.

Armadilhas

  • git pull sem configuração faz merge por padrão e enche o histórico de "Merge branch 'main' of ...". Configure pull.ff only ou pull.rebase true.
  • Rebase de branch longa te faz resolver o mesmo conflito repetidamente, uma vez por commit. git rerere grava a resolução e reaplica (6. Resolução de Conflitos).
  • --force após rebase é obrigatório e perigoso. Use --force-with-lease: ele recusa se alguém empurrou algo que você não viu.
  • Squash antes de mergear perde a granularidade que o bisect usa. Squash de 15 commits transforma 15 pontos de busca em 1.

Comandos essenciais

git merge feat                    # cria merge commit se divergiu
git merge --ff-only feat          # RECUSA se não for fast-forward
git merge --no-ff feat            # força merge commit mesmo quando poderia avançar
git rebase main                   # reaplica seus commits sobre main
git rebase --onto main feat~3 feat  # move só os 3 últimos
git rebase --abort                # desiste, volta ao estado anterior
git rebase --continue             # segue após resolver conflito
git push --force-with-lease       # empurra reescrita sem atropelar ninguém
git log --graph --oneline --all   # vê a topologia de verdade
git config --global pull.ff only  # nunca mais merge acidental no pull

git rebase -i (interativo) permite reordenar, juntar e editar commits. É a ferramenta mais útil da lista e a mais perigosa — só em branch privada.

Relacionado


Parte de Semana 08 - Git Além do Básico · 00 - MOC Fundamentos de Programação

Buscar

Busca por título, seção e texto das notas