Posted on novembro 13, 2022
Um estudo sobre o Git Rebase
Faz um tempo já que eu andava querendo pesquisar e escrever sobre a diferença entre o git push --force
e o git push --force-with-lease
. Comecei a pesquisar sobre esses dois hoje e também pensei: hmm, por que não fazer uma introdução sobre isso falando também sobre o Git Rebase? Afinal de contas, é um bichinho tão temido, tem tantas lendas sobre ele e eu, pessoalmente, não sabia muita coisa sobre, só sei que tenho que fazer. Por isso decidi juntar a fome com a vontade de comer e fazer um estudo sobre o Git Rebase e trazer aqui, pra compartilhar com todo mundo!
ℹ️ This post is available in English at my dev.to 💫
Conceitualmente, o que é o Git Rebase?
De antemão, já adianto que o rebase
é um dos métodos de atualização de branch disponíveis no Git, junto com o merge
(se tem algum outro, me conta, porque eu não me lembro 😅). Quando você cria uma feature branch, esse lugarzinho especial onde você vai começar a desenvolver uma funcionalidade nova para um sistema, você o faz a partir do último commit de uma branch principal. A característica do rebase
, diferente do merge, é que ele vai mudar a base da sua branch de um commit para o outro, fazendo parecer com que você tenha criado a sua feature branch à partir de um outro ponto de partida, mais recente, diferente daquele onde você iniciou o seu trabalho.
Meio confuso, né? Pérai que vou tentar desenhar.
Um Git Rebase em 3 atos
A pessoa que vos fala foi lá e criou um novo branch a partir da branch principal de um projeto. O commit inicial da minha branch, que aqui foi chamada de branch-oli
, é o commit wap662
, que contém tudo que está na branch principal até agora.
No dia seguinte, o coleguinha veio e fez merge da feature branch dele, a branch-do-colega
, na main. Com isso, a main foi atualizada e seu commit mais recente, que servirá de base para as próximas feature branches a serem criadas, é o commit sus0sz
. Porém, a minha branch, branch-oli
, ficou desatualizada e eu preciso que ela fique em dia com o que está na branch principal. Eu posso fazer isso de duas formas: com o git merge ou com o git rebase. E hoje, eu vou de rebase: ou seja, vou fazer com que a branch-oli tenha o sus0sz
como commit base.
Xablau! Nosso código está devidamente atualizado.
Ok, mas quais são as vantagens do Git Rebase?
A vantagem mais prática do Rebase é: você terá um merge mais limpo da sua feature branch na branch principal. Agora, a vantagem poética é a seguinte: os commits da branch principal são majoritariamente compostos de merges de outras branches. E quando esses merges contém títulos descritivos e feitos com amor e carinho, os commits da main branch vão nos contando a história de um sistema. O Rebase ajuda com que essa história fique mais linear e, com isso, faça mais sentido.
E na hora de procurar qual feature branch meteu um bug lá pra dentro do código-base de produção, fica muito mais fácil achar se tivermos um histórico limpinho e organizado de commits.
E como isso é feito?
O Git organiza essa “história” através da criação de novos commits à partir dos que já foram feitos e os aplicando à base (que, no caso, seria a sua feature branch). Por mais que a branch pareça inalterada para nós, ela é composta por commits completamentes diferentes e novos depois do Rebase.
Tipos de Git Rebase
Existem dois tipos de Git Rebase disponíveis para utilizarmos, cada um contendo suas vantagens e desvantagens.
O Git Rebase Standard, que é quando não passamos nenhuma flag junto com o nosso comando de rebase, vai simplesmente pegar todos os novos commits criados em uma branch e vai adicioná-los automaticamente à frente do novo commit base da nossa feature branch. Sem pensar muito, sem preocupações.
Já o Git Rebase Interactive (ou Interativo) acontece quando, junto ao comando de rebase, passamos também a flag -i
para o console/terminal. Esse tipo de rebase nos dá a possibilidade de alterar os commits já feitos durante o processo de desenvolvimento, além de também podermos dividí-los, removê-los ou reordená-los.
Com o rebase interativo, podemos, por exemplo, focar no desenvolvimento da feature sem nos preocuparmos com os commits que estamos fazendo: se suas mensagens estão descritivas o suficiente, se estão contando uma história como deve ser… Vamos só fazendo as alterações necessárias e, antes de fazer o merge para a main, podemos arrumar a casa através da flag -i
.
Principais diferenças entre o Git Merge e o Git Rebase
Antes de mais nada, é importante ressaltar que ambos os comandos resolvem o mesmo problema: a integração de mudanças de código entre branches.
A principal diferença é: o Git Merge vai trazer o novo código de forma independente e integrá-lo através de um único commit dentro da sua feature branch. Ele é uma operação não-destrutiva, logo: não há grande perigo que você faça alguma mudança não desejada na branch em que você está trabalhando.
Porém, o Merge também vai fazer com que a sua branch tenha commits extras, além daqueles que você criou. Caso a main branch seja muito movimentada e você precise fazer vários merges ao longo do desenvolvimento da sua feature, esses commits extras podem acabar poluindo a “história” da sua branch.
Enquanto isso, o Git Rebase vai reescrevendo a história do seu projeto, como já foi explicado lá em cima. O que é bom em termos de clareza e limpeza de histórico, porém também pode ter um ponto negativo: não vai ser possível ver com clareza quando novas mudanças foram adicionadas na feature, o que faz com que não haja tanto contexto dentro da branch como teria caso as atualizações fossem feitas através do Git Merge.
Sobre os forces pushes durante o Git Rebase
Por que precisamos do —force push durante um rebase?
O Git contém uma regra chamada Fast-Forward Rule: basicamente, ele vai rejeitar qualquer commit se o que estiver na branch remota não for um “ancestral” do que está no local. E o Rebase quebra essa regra, visto que ele cria novos commits e muda a base da branch local, por isso precisamos do force push para burlar isso. A flag --force
vai ignorar completamente o histórico já existente da feature branch e vai substituí-lo com o que está na sua máquina.
Diferença entre o —force e o —force-with-lease
O --force
é força bruta e pronto. Se por um acaso alguém tiver feito algum commit enquanto você fazia o Git Rebase localmente, essa alteração estará perdida para sempre. Porém, isso pode ser ultrapassado com o --force-with-lease
, que eu traduzi livremente para forçado com jeitinho: o Git vai recusar a atualização da branch caso seu estado esteja diferente do esperado.
Ou seja, quando usamos o --force-with-lease
, o Git vai, primeiro, verificar se a versão remota da sua feature branch é a mesma que você fez o rebase ou se há alguma alteração além dos commits “re-criados”. Caso haja algo além, como alterações commitadas por outro dev, por exemplo, o push será rejeitado. Se não houver nenhuma alteração adicional, será possível burlar a Fast-Forward Rule sem grandes problemas.
Principais comandos relacionados ao Git Rebase
Para fazer o Rebase:
git rebase [-i] nome-da-branch
Observação: a flag -i
é opcional e serve para fazer rebases interativos, conforme expliquei a definição lá em cima.
Para continuar com as alterações feitas:
git rebase --continue
Para pular alterações:
git rebase --skip
Force pushes:
git push --force
git push --force-with-lease
📖 Links consultados
📗git rebase – Atlassian Git Tutorial
📘Atualizando um branch com git rebase – Jessica Temporal
📙Git rebase: integrando alterações de uma branch em outra, por Daniela Araújo – BeTrybe Blog
📕Merging vs. Rebasing – Atlassian Git Tutorial
📗Git Force vs Force with Lease, por Mohammad-Ali A’Râbi
📘Essa resposta no Stack Overflow
📜 Posts mais recentes
🎯 A maneira SMART de escrever objetivos
🫂 Terapia em grupo – sobre criação de conteúdo
💌 Recadinhos
Gostou do texto? Tem algo a adicionar? Alguma crítica construtiva? Feedbacks? Sugestões? Pedidos? Fique à vontade para me contatar via email (oli.pmatt@gmail.com), Twitter (@oliviamattiazzo), LinkedIn (/oliviamattiazzo) ou pela caixa de comentários aqui embaixo! Vai ser um prazer conversar contigo! ✨
Olivia, se houverem commits na branch originada do commit wap822, eles se perdem quando é feito o rebase? Eu sempre evito usar o rebase por receio de perder commits, não sei se é um excesso de preciosismo ou medo…
Na
branch-oli
, você diz, né? Não, eles não se perdem com o rebase; porém seus “ids” irão mudar, porque o rebase vai “apagar” os commits antigos e vai criar novos, com o mesmíssimo conteúdo (caso seja o rebase default, claro).Ajudou? 😀