Complementando, pra quem quiser se aprofundar no funcionamento do git rebase
, eu recomendo este site.
É longo e no começo parece que não tem nada a ver com rebase
, mas eu sugiro que leia com atenção até o final porque vale muito a pena.
Basicamente, é explicado como um repositório do Git é estruturado, pois isso é essencial para entender o que o rebase
de fato faz.
Mas só para resumir bastante (pois ainda recomendo que leia o link indicado), vamos supor que meu repositório esteja assim:

Ou seja, tenho o branch main
, com os commits first
, A
, B
e C
.
A partir do commit A
foi criado o branch develop
, no qual foram adicionados os commits D
e E
.
Eu quero que o develop
contenha as mudanças feitas nos commits B
e C
. Para isso posso usar merge
ou rebase
.
Se eu usar merge
:
git checkout develop
git merge main
O resultado será:

Ou seja, o branch develop
agora tem um novo commit F
, que é o resultado do merge. No caso ele terá dois pais: os commits C
e E
.
Agora, se eu fizer o rebase
:
git checkout develop
git rebase main
O resultado será:

Agora o branch develop
foi "movido" para a frente do branch main
, e os commits D
e E
foram "re-aplicados" a partir do commit C
.
Isso criou dois commits novos (D'
e E'
), e isso na minha opinião é o detalhe mais importante: as alterações feitas nos commits D
e E
são aplicadas novamente a partir do commit C
, e isso cria dois commits novos. Embora as alterações feitas sejam as mesmas, não são os mesmos commits (pois possuem pais diferentes: o pai de D
é o commit A
, já o pai de D'
é o commit C
).
Os próprios hashs dos commits também não serão os mesmos, o que indica que são de fato commits diferentes. Além disso, se o rebase for feito por um usuário diferente de quem criou o branch, os autores também serão diferentes. E no caso, os commits originais (D
e E
) ficarão "perdidos" e o Git pode inclusive apagá-los automaticamente depois de um tempo.
Enfim, o rebase
é como se eu "reescrevesse a história". Para quem vê somente o resultado final, é como se as alterações feitas em D
e E
sempre tivessem sido feitas a partir de C
.
Repare que com isso o histórico fica linear, enquanto que no merge
ele se divide em dois caminhos: primeiro diverge em A
para depois convergir em F
. Mas no fim o resultado final acaba sendo essencialmente o mesmo: o branch develop
contém todas as alterações feitas por ele (D'
e E'
) ao mesmo tempo em que também tem o que foi feito em B
e C
.
O que muda é como isso foi feito. Podemos dizer que o rebase
se foca mais no resultado final, sem se importar em como isso aconteceu (tanto que ele reescreve o histórico, pois o "como" é o que menos importa). Já o merge
preserva o histórico de como tudo aconteceu ("aqui o código se dividiu, mas depois se juntou lá na frente, etc"). O "como" importa e permanece intocado.
E para mais detalhes, sugiro também este outro artigo, além do respectivo capítulo do livro Pro Git.
Como curiosidade, se vc digitar git rebase --help
no seu terminal, vai ver gráficos similares aos que eu fiz, mas em ASCII (reproduzo-os logo abaixo).
Supondo que o repositório esteja assim e o branch atual seja topic
:
A---B---C topic
/
D---E---F---G master
Ao fazer git rebase master
(ou ainda git rebase master topic
), o resultado será:
A'--B'--C' topic
/
D---E---F---G master
Pois os commits A
, B
e C
foram re-aplicados em cima do G
, gerando os novos commits A'
, B'
e C'
.
Além disso, o próprio help
explica casos mais complicados, como a opção --onto
(que também é citada no link já mencionado acima).
Da mesma forma, git merge --help
também mostra um diagrama similar:
A---B---C topic
/
D---E---F---G master
Assumindo que o branch atual seja o master
, ao fazer git merge topic
o resultado será:
A---B---C topic
/ \
D---E---F---G---H master
PS: e claro, esses mesmos gráficos também podem ser vistos na documentação oficial, aqui e aqui.