git rebase
Este documento vai servir como uma discussão aprofundada do comando git rebase
. O comando rebase também foi abordado nas páginas configurar um repositório e reescrever o histórico. Esta página vai ter uma abordagem mais aprofundada sobre a configuração e a execução do git rebase
. Casos de uso e armadilhas comuns do rebase vão ser tratados aqui.
Rebase é um dos dois utilitários do Git que se especializam em integrar alterações da ramificação para outra. O outro utilitário de integração de alterações é o git merge
. A mesclagem (merge) é uma alteração de registro de avanço. Como outra opção, o rebase tem funções poderosas para reescrever o histórico. Para saber mais sobre as diferenças entre mesclagem e rebase, acesse o guia mesclagem x rebase. O rebase tem 2 modos principais: os modos "manual" e "interativo". Vamos falar sobre os diferentes modos de rebase com mais informações abaixo.
O que é o git rebase?
Rebasing é o processo de mover ou combinar uma sequência de commits para um novo commit base. O rebasing é mais útil e melhor visualizado no contexto do fluxo de trabalho de ramificação de funções. O processo geral pode ser visualizado da seguinte forma:
A partir da perspectiva de conteúdo, o rebase é o processo de alterar a base da ramificação do commit para outra, fazendo parecer como se você criou a ramificação a partir de um commit diferente. De um jeito intrínseco, o Git realiza isso criando novos commits e aplicando-os à base especificada. É muito importante entender que, mesmo que a ramificação pareça a mesma, ela é composta de commits novos completos.
Material relacionado
Folha de consulta do Git
VER SOLUÇÃO
Aprenda a usar o Git com o Bitbucket Cloud
Uso
O principal motivo para fazer o rebase é manter um histórico de projeto linear. Por exemplo, considere uma situação em que a ramificação principal progrediu desde que você começou a trabalhar em uma ramificação de funções. Você quer colocar as atualizações mais recentes da ramificação principal na ramificação de funções, mas também quer o histórico da ramificação limpo, para que pareça que você esteve trabalhando com a ramificação principal mais recente. Assim você tem o benefício posterior do merge limpo da ramificação de funções de volta para a ramificação principal. Por que é interessante manter um "histórico limpo"? Os benefícios de ter um histórico limpo ficam tangíveis ao realizar operações do Git para investigar a introdução de uma regressão. Um cenário mais realista seria:
1. Um bug foi identificado na ramificação principal. Uma função que estava funcionando bem agora está com falha.
2. A developer examines the history of the main branch using git log
because of the "clean history" the developer is quickly able to reason about the history of the project.
3. The developer can not identify when the bug was introduced using git log
so the developer executes a git bisect
.
4. Because the git history is clean, git bisect
has a refined set of commits to compare when looking for the regression. The developer quickly finds the commit that introduced the bug and is able to act accordingly.
Saiba mais sobre git log e git bisect nas páginas de uso individuais.
Você tem duas opções para integrar sua função na ramificação principal: fazer o merge direto ou fazer o rebase e depois o merge. A primeira opção resulta em um merge de 3 vias e um commit de merge, enquanto a segunda resulta em um merge de avanço rápido e um histórico linear perfeito. O diagrama a seguir demonstra como fazer o rebase na ramificação principal facilita um merge de avanço rápido.
O rebase é um método comum para integrar alterações de upstream no repositório local. Colocar alterações de upstream com o Git merge resulta no commit de mesclagem supérfluo toda vez que você queira ver como o projeto progrediu. Por outro lado, o rebase é como dizer "Eu quero que minhas alterações sejam baseadas no que todo mundo já fez."
Não faça o rebase no histórico público
Conforme já discutido em reescrever histórico, você nunca deve fazer o rebase em commits após eles serem colocados no repositório público. O rebase faria substituição dos antigos commits por novos commits e seria como se aquela parte do histórico do projeto tivesse desaparecido do nada.
Git rebase standard vs git rebase interactive
Git rebase interactive é quando o git rebase aceita um argumento -- i
. O "i" representa "Interactive." Sem nenhum argumento, o comando é executado no modo padrão. Em ambos os casos, vamos supor que a gente criou uma ramificação de funções separada.
# Create a feature branch based off of main
git checkout -b feature_branch main
# Edit files
git commit -a -m "Adds new feature"
O Git rebase no modo padrão pega com agilidade os commits na ramificação de trabalho atual e os aplica no cabeçalho da ramificação passada.
git rebase <base>
Assim vai ser feito o rebase automático da ramificação atual para a <base>
, que pode ser qualquer tipo de referência de commit (por exemplo, ID, nome de ramificação, marcador ou referência relativa ao HEAD
).
Executar o git rebase
com a bandeira -i
dá início a uma sessão de rebasing interativa. Em vez de mover todos os commits para a nova base sem conhecimento, o rebasing interativo oferece a oportunidade de alterar os commits individuais no processo. Assim você pode limpar o histórico removendo, dividindo e alterando uma série existente de commits. É como uma versão aprimorada do Git commit --amend
.
git rebase --interactive <base>
Assim vai ser feito o rebase da ramificação atual para <base>
, mas usando a sessão de rebasing interativa. Essa ação abre um editor onde você pode inserir comandos (descritos abaixo) para cada commit que vai passar pelo rebasing. Estes comandos determinam como os commits individuais vão ser transferidos para a nova base. Também é possível reordenar a lista de commits para alterar a ordem dos próprios commits. Uma vez que você especificou os comandos para cada commit no rebase, o Git vai começar a reproduzir os commits aplicando os comandos de rebase. Os comandos de edição de rebasing são os seguintes:
pick 2231360 some old commit
pick ee2adc2 Adds new feature
# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands)
#
# 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 discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
Comandos de rebase adicionais
Conforme especificado na página reescrever histórico, o rebase pode ser usado para alterar commits múltiplos e mais antigos, bem como arquivos com commit e múltiplas mensagens. Embora estas sejam as aplicações mais comuns, o git rebase
também tem mais opções de comando que podem ser úteis em aplicações mais complexas.
git rebase -- d
significa que durante a reprodução o commit vai ser descartado do bloco de commit combinado final.git rebase -- p
deixa o commit como está. Ele não vai modificar a mensagem ou conteúdo do commit e ainda vai ser um commit individual no histórico de ramificações.git rebase -- x
durante a reprodução, executa um script do shell da linha de comandos em cada commit marcado. Um exemplo útil seria executar o conjunto de teste da base de código em commits específicos, o que poderia ajudar a identificar as regressões durante um rebase.
Recapitulando
O rebasing interativo lhe oferece controle completo sobre como o histórico do projeto se parece. Isto dá muita liberdade aos desenvolvedores, pois permite a eles fazer o commit de um histórico "bagunçado" enquanto eles estão focados em escrever código e, então, voltar e limpar mais tarde.
A maioria dos desenvolvedores gosta de utilizar um rebase interativo para polir uma ramificação de funções antes de mesclar na base de código principal. Isto dá a eles a oportunidade de fazer squash em commits insignificantes, excluir commits obsoletos e se certificar de que todo o restante está em ordem antes de confirmar no histórico "oficial" do projeto. Para as demais pessoas, vai parecer que toda a função foi desenvolvida em uma única série de commits bem planejados.
O verdadeiro poder do rebase interativo pode ser visto no histórico da ramificação principal resultante. Para as demais pessoas, é como se você fosse um desenvolvedor incrível, que implementou a nova função com a quantidade perfeita de commits de primeira. É assim que o rebase interativo pode manter o histórico do projeto limpo e significativo.
Opções de configuração
Existem algumas propriedades de rebase que podem ser definidas usando o git config
. Estas opções vão alterar a aparência do resultado do git rebase
.
rebase.stat
: Um booleano que é definido como falso por padrão. A opção alterna a exibição do conteúdo diffstat visual que mostra o que mudou desde o último rebase.rebase.autoSquash:
um valor booleano que alterna o comportamento do--autosquash
.rebase.missingCommitsCheck:
Pode ser definido para diversos valores que mudam o comportamento do rebase em relação a commits ausentes.
| Imprime uma saída de aviso no modo interativo que informa sobre commits removidos |
| Interrompe o rebase e imprime mensagens de aviso de commits removidos |
| Configurado por padrão, ignora quaisquer avisos de commits ausentes |
rebase.instructionFormat:
Uma cadeia de formatogit log
que vai ser usada para formatar a exibição do rebase interativo
Aplicação avançada de rebase
O argumento da linha de comandos --onto
pode ser passado ao git rebase
. No modo --onto
do git rebase, o comando é expandido para:
git rebase --onto <newbase> <oldbase>
The --onto
command enables a more powerful form or rebase that allows passing specific refs to be the tips of a rebase. Let’s say we have an example repo with branches like:
o---o---o---o---o main
\
o---o---o---o---o featureA
\
o---o---o featureB
featureB tem como base o featureA; no entanto, é possível notar que o featureB não depende de nenhuma das alterações no featureA e pode ser ramificado com facilidade da ramificação principal.
git rebase --onto main featureA featureB
featureA é a <oldbase>
. A principal
se torna a <newbase>
e a featureB é a referência para o que o HEAD
da <newbase>
vai apontar. Os resultados são:
o---o---o featureB
/
o---o---o---o---o main
\
o---o---o---o---o featureA
Entender os perigos do rebase
Uma ressalva a ser considerada ao trabalhar com o Git Rebase é que os conflitos de merge podem ficar mais frequentes durante um fluxo de trabalho de rebase. É o que ocorre caso você tenha uma ramificação antiga que se desviou da ramificação principal. Em algum momento você vai querer fazer o rebase da ramificação principal e, então, ele vai poder conter muitos novos commits que vão entrar em conflito com as alterações da ramificação. Esses casos podem ser resolvidos com facilidade fazendo o rebase da ramificação com frequência na ramificação principal e fazendo commits mais frequentes. Os argumentos da linha de comando --continue
e --abort
podem ser passados para o git rebase
para avançar ou redefinir o rebase ao lidar com conflitos.
Uma ressalva de rebase mais séria é a perda de commits ao reescrever o histórico interativo. Executar o rebase no modo interativo e executar subcomandos como squash ou drop vai remover os commits do log imediato da ramificação. À primeira vista, pode parecer que os commits desapareceram de vez. Usando o git reflog
, esses commits podem ser restaurados e todo o rebase pode ser desfeito. Para mais informações sobre como usar o git reflog
para encontrar commits perdidos, acesse a página de documentação do Git reflog.
O Git Rebase em si não é tão perigoso. Os casos de verdadeiro perigo surgem ao executar rebases interativos com reescrita de histórico e colocar os resultados em uma ramificação remota que é compartilhada por outros usuários. Este padrão deve ser evitado, pois ele tem o potencial de substituir o trabalho remoto de outros usuários quando eles fizerem o pull.
Recuperação a partir do rebase upstream
Caso outro usuário tenha feito o rebase e forçou o envio para a ramificação que você está fazendo o commit, um git pull
vai então substituir quaisquer commits baseados naquela ramificação anterior com a ponta que foi forçada. Por sorte, usando o git reflog
você pode obter o reflog da ramificação remota. No reflog da ramificação remota, você pode encontrar uma ref anterior ao rebase. Você pode então fazer o rebase da ramificação contra esta ref remota, usando a opção --onto
, conforme discutido acima na seção Aplicação avançada de rebase.
Resumo
Neste artigo, foi abordado o uso do git rebase
. Casos de uso básicos e avançados, bem como exemplos mais avançados foram abordados. Alguns dos pontos principais de discussão são:
- git rebase padrão vs. modos interativos
- opções de configuração do git rebase
- git rebase --onto
- commits perdidos do git rebase
We looked at git rebase
usage with other tools like git reflog, git fetch, and git push. Visit their corresponding pages for further information.
Compartilhar este artigo
Próximo tópico
Leitura recomendada
Marque esses recursos para aprender sobre os tipos de equipes de DevOps ou para obter atualizações contínuas sobre DevOps na Atlassian.