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 recursos poderosos 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 confirmações para uma nova confirmação base. O rebasing é mais útil e melhor visualizado no contexto do fluxo de trabalho de ramificação de recurso. O processo geral pode ser visualizado da seguinte forma:

Tutorial Git: Git rebase

A partir da perspectiva de conteúdo, o rebase é o processo de alterar a base da ramificação da confirmação para outra, fazendo parecer como se você criou a ramificação a partir da confirmação diferente. De um jeito intrínseco, o Git realiza isso criando novas confirmações e aplicando-as à base especificada. É muito importante entender que, mesmo que a ramificação pareça a mesma, ela é composta de confirmações novas completas.

Uso

O principal motivo para fazer o rebase é manter um histórico de projeto linear. Por exemplo, considere uma situação em que a branch principal progrediu desde que você começou a trabalhar em uma ramificação de recurso. Você quer colocar as atualizações mais recentes da branch principal na ramificação de recurso, no entanto você também quer o histórico da ramificação limpo, para que pareça que você esteve trabalhando com a branch principal mais recente. Isto te dá o benefício posterior da mesclagem limpa da ramificação de recurso de volta para a branch principal. Por que é interessante manter um "histórico limpo"? Os benefícios de ter um histórico limpo se tornam tangíveis ao realizar operações do Git para investigar a introdução da regressão. Um cenário mais realista seria:

  1. Um bug é identificado na branch principal. Um recurso que estava funcionando do jeito habitual agora está quebrado.
  2. Um desenvolvedor examina o histórico da branch principal usando o git log devido ao "histórico limpo", o desenvolvedor pode com rapidez entender o histórico do projeto.
  3. O desenvolvedor não pode identificar quando o bug foi introduzido por meio do git log, portanto ele executa um git bisect.
  4. Como o histórico do git está limpo, o git bisect tem um conjunto refinado de confirmações para comparar ao procurar pela regressão. O desenvolvedor com rapidez encontra a confirmação que introduziu o bug e pode tomar a medida adequada.

Saiba mais sobre git log e git bisect nas páginas de uso individuais.

Você tem duas opções para integrar seu recurso na branch principal: mesclar com objetividade ou fazer o rebase e mesclar depois disso. A primeira opção resulta em uma mesclagem de 3 vias e uma confirmação de mesclagem, enquanto a segunda resulta em uma mesclagem de avanço rápido e um histórico linear ideal. O seguinte diagrama demonstra como fazer o rebase na branch principal facilita uma mesclagem de avanço rápido.

Git rebase: Ramificar para dentro da branch principal

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 na confirmação de mesclagem supérflua 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 confirmações após elas serem colocadas no repositório público. O rebase faria substituição das antigas confirmações por novas confirmações e seria como se aquela parte do histórico do projeto tivesse desaparecido do jeito abrupto.

Git Rebase Standard x Git Rebase Interactive

Git rebase interactive é quando o git rebase aceita um argumento -- i. Isto quer dizer "Interativo." Sem nenhum argumento, o comando é executado no modo padrão. Em ambos os casos, vamos supor que a gente criou uma ramificação de recurso separada.

 # Create a feature branch based off of master git checkout -b feature_branch master # Edit files git commit -a -m "Adds new feature" 

O Git rebase no modo padrão pega com agilidade as confirmações na ramificação de trabalho atual e as aplica no cabeçalho da ramificação passada.

 git rebase 

Isto vai com agilidade fazer o rebase da ramificação atual para a , que pode ser qualquer tipo de referência de confirmação (por exemplo, ID, nome de ramificação, etiqueta 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 todas as confirmações para a nova base sem conhecimento, o rebasing interativo oferece a oportunidade de alterar as confirmações individuais no processo. Isto permite que você limpe o histórico removendo, dividindo e alterando uma série existente de confirmações. É como uma versão aprimorada do Git commit --amend.

 git rebase --interactive 

Isto faz o rebase da ramificação atual para , mas usa uma sessão de rebasing interativa. Isto abre um editor onde você pode inserir comandos (descritos abaixo) para cada confirmação que vai passar pelo rebasing. Estes comandos determinam como as confirmações individuais vão ser transferidas para a nova base. Também é possível reordenar a lista de confirmações para alterar a ordem das próprias confirmações. Uma vez que você especificou os comandos para cada confirmação no rebase, o Git vai começar a reproduzir as confirmações 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) # # Comandos: # p, pick = usar confirmação # r, reword = usar confirmação, mas editar a mensagem de confirmação # e edit = usar confirmação, mas parar para alterar # s, squash = usar confirmação, mas fundir com a confirmação anterior # f, fixup = igual o "squash", mas descarta a mensagem de log desta confirmação # x, exec = executar comando (o resto da linha) usando shell # d, drop = remover confirmação 

Comandos de rebase adicionais

Conforme especificado na página reescrever histórico, o rebase pode ser usado para alterar confirmações múltiplas e mais antigas, bem como arquivos confirmados 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 during a reprodução, a confirmação vai ser descartada do bloco de confirmações combinadas final.
  • git rebase -- p mantém a confirmação como está. Ele não vai modificar a mensagem ou conteúdo da confirmação, a qual vai continuar sendo uma confirmação individual no histórico das ramificações.
  • git rebase -- x durante a reprodução vai executar um script shell de linha de comando em cada confirmação marcada. Um exemplo útil seria executar a suíte de teste da base de códigos em confirmações específicas, o que poderia ajudar a identificar 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 confirmar 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 recurso antes de mesclar na base de código principal. Isto dá a eles a oportunidade de fazer squash em confirmações insignificantes, excluir confirmações obsoletas 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 todo o recurso foi desenvolvido em uma única série de confirmações bem planejadas.

O verdadeiro poder do rebasing interativo pode ser visto no histórico da branch principal resultante. Para as demais pessoas, é como se você fosse um desenvolvedor incrível, que implementou o novo recurso com a quantidade perfeita de confirmações de primeira. É assim que o rebasing 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 boolean 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 debase.
  • rebase.autoSquash: um valor booleano que alterna o comportamento do --autosquash.
  • rebase.missingCommitsCheck: Pode ser definido com diversos valores, que alteram o comportamento do rebase em relação a confirmações ausentes.
warn Produz um resultado de aviso no modo interativo, avisando sobre confirmações removidas

error

Interrompe o rebase e produz mensagens de aviso de confirmações removidas

ignore

Definido como padrão; isto vai ignorar quaisquer avisos de confirmação ausente
  • rebase.instructionFormat: Uma cadeia de formato git 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   

O comando --onto habilita uma forma mais poderosa de rebase, que permite a passagem de refs específicas para as pontas do rebase.
Vamos supor que temos um repositório de exemplo, com ramificações da seguinte forma:

     o---o---o---o---o master         \          o---o---o---o---o featureA               \                o---o---o featureB 

featureB está baseado no 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 com o branch principal.

  git rebase --onto master featureA featureB 

featureA é a . master se torna a e o featureB é a referência para o que o HEAD da vai apontar. Os resultados são:

                        o---o---o featureB                      /     o---o---o---o---o master      \       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 mesclagem podem se tornar mais frequentes durante um fluxo de trabalho de rebase. Isto ocorre caso você tenha uma ramificação antiga que se desviou da principal. Em determinadas ocasiões você vai querer fazer o rebase contra a branch principal e nesse momento ela vai poder conter muitas novas confirmações que vão conflitar com as alterações da ramificação. Isto é resolvido com facilidade fazendo o rebase da ramificação com frequência contra a branch principal e fazendo confirmações mais frequentes. Os argumentos da linha de comando --continue e --abort podem ser passados para o git rebase para avançar ou reiniciar o rebase ao lidar com conflitos.

Uma ressalva de rebase mais séria é a perda de confirmações ao reescrever o histórico interativo. Executar o rebase no modo interativo e executar subcomandos como squash ou drop vai remover as confirmações do log imediato da ramificação. À primeira vista, isso pode parecer como se as confirmações tivessem desaparecido de vez. Usando o git reflog, estas confirmações podem ser restauradas e todo o rebase pode ser desfeito. Para mais informações sobre como usar o git reflog para encontrar confirmações perdidas, visite 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 colocado na ramificação que você está confirmando, um git pull vai então substituir quaisquer confirmações baseadas naquela ramificação anterior com a ponta que foi colocada imposto. 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:

  • modos padrão x interativo do git rebase
  • opções de configuração do git rebase
  • git rebase --onto
  • confirmações perdidas do git rebase

A gente viu o uso do git rebase com outras ferramentas como git reflog, git fetch e git push. Visite as páginas correspondentes para mais informações.

Pronto para aprender sobre o Git?

Experimente este tutorial interativo.

Comece agora mesmo