Guia para Iniciantes em Rebase Interativa
@StrideStride
Information Technology and Services
No início deste ano fiz um rebase interativo pela primeira vez e fiquei impressionado com o que se pode fazer com ele. Também achei um pouco complexo no início. Espero que este guia ajude a remover alguma incerteza em torno dele.
Também, porque ele é tão poderoso e você pode essencialmente reescrever a história, um pequeno aviso antes de começarmos: Há muitas escolas de pensamento sobre Git e se rebasear é uma boa ideia ou não. Este post não vai mergulhar nessas discussões e é puramente para caminhar pelo básico do uso do rebasing interativo.
TL;DR
- O rebasing interativo pode ser usado para mudar os commits de muitas maneiras, como editar, deletar e esmagar.
- Para dizer a Git onde iniciar o rebase interativo, use o SHA-1 ou índice do commit que imediatamente precede o commit que você quer modificar.
- Durante um rebase interativo, quando Git pausa em um commit que você marcou para editar, o fluxo de trabalho não é diferente de um processo de commit normal – você encena os arquivos e então os submete. A única diferença é que você usa o comando
git commit --amend
em vez degit commit
. - Base interativo irá criar novos SHA-1’s, portanto é melhor usar o rebase interativo em commits que você não tenha empurrado para um ramo remoto.
The Problem
Neste exemplo, vamos trabalhar em uma situação em que estivemos trabalhando em um ramo de funcionalidade e temos alguns commits que gostaríamos de alterar ou excluir. Aqui está como nosso git log se parece:
Do git log acima, aqui estão os dois commits que queremos mudar: 4a4d705
– Neste commit nós acidentalmente cometemos um conflito de fusão6c01350
– Neste commit nós removemos o conflito de fusão
O que nós gostaríamos de fazer é voltar no tempo para 4a4d705
, remover o conflito de fusão no commit, então delete 6c01350
já que o conflito de fusão está resolvido e nós não precisamos mais deste commit. Isto será uma melhoria no nosso histórico de commit por duas razões:
- Não teremos commits quebrados (conflitos de merge)
- Temos apenas commits significativos, sem commits relacionados apenas à correção de um conflito de merge perdido
A solução
Esta situação é um bom candidato para o rebase interativo. Scott Chacon, em seu livro Pro Git, descreve o rebasing interativo da seguinte forma: “Às vezes a coisa corrigida… não pode ser emendada para a não-quitetura perfeita que corrige, porque essa correção está profundamente enterrada numa série de remendos. É exatamente para isso que serve o rebase interativo: use-o depois de muito, reordenando e editando commits, e esmagando múltiplos commits em um”
Que commits queremos modificar?
Para iniciar um rebase interativo, precisamos dizer a Git quais commits queremos modificar. Fazemos isso referenciando o commit imediatamente antes do commit mais antigo que queremos modificar. Ou, colocado simplesmente, referenciamos “a última commit quer manter o “como está”, de acordo com Scott Chacon.
Vejamos o nosso exemplo para ter uma melhor compreensão. Há duas maneiras de referenciar este commit:
Por SHA-1 – O último commit que queremos reter as-is tem um SHA-1 de 528f82e
para que possamos passar isto para o nosso comando de rebase interativo.
Por Índice – A última commit que queremos reter as-is tem uma posição de índice 3 (Git usa indexação baseada em zero) para que possamos passar em HEAD~3
ao nosso comando rebase interativo.
Note – Se você tem apenas algumas commits para rebase interativo, usar o índice é provavelmente mais fácil para a maioria das pessoas. Entretanto, se você tem muitos commits, usar o SHA-1 é provavelmente mais fácil, então você não precisa contar até o fim do git log.
Inicie o rebase interativo
Baseado no nosso exemplo, vamos executar:
$ git rebase -i 528f82e
Or
$ git rebase -i HEAD~3
Que abre esta janela no Vim:
Nota que os commits estão na ordem oposta à do git log. No git log o commit mais recente está no topo. Nesta vista, o commit mais recente está em baixo. Note também que os comentários abaixo dão uma lista útil dos comandos válidos que podemos usar em cada commit.
Se você não conhece Vim, basta clicar em cada palavra pick
que você quer editar e então pressionar a tecla <i>
(para modo de inserção). Quando terminar de digitar, pressione a tecla <esc>
para sair do modo inserir.
No nosso exemplo, alterámos o comando para edit
para a submissão que queremos modificar e alterámos o comando para drop
para a submissão que queremos eliminar. Depois corremos :wq
para guardar e sair da janela Vim.
Alterar a submissão
Voltar no terminal vemos esta mensagem:
Isto faz sentido que estejamos parados em 4a4d705
. Este é o commit mais antigo da série de commits que queremos modificar. Começaremos com este commit e trabalharemos nosso caminho através de cada commit até o mais recente.
Como um lembrete, 4a4d705
foi o commit com o conflito de fusão que cometemos acidentalmente. Quando abrimos o nosso editor vemos o conflito de fusão lá:
Então nós corrigimos o conflito de fusão no arquivo, mas o que fazemos agora? Quando em dúvida, git status
:
Cool! Isto é realmente útil. Vemos que estamos actualmente a editar 4a4d705
, e vemos os próximos dois commits a serem actuados depois deste.
O resto da mensagem está a explicar-nos um fluxo de trabalho familiar. Git nos diz se queremos alterar a submissão que executamos git commit --amend
. Isto irá essencialmente agir como o nosso típico git commit
que usamos num fluxo de trabalho normal. No final desta mensagem vemos que nosso arquivo foi modificado refletindo as mudanças que acabamos de fazer para remover o conflito de merge. Nós precisamos encenar o arquivo antes de nos comprometermos. Isto não é diferente de um fluxo de trabalho normal.
Tudo o que fazemos é git add tempChanger.js
para encenar o ficheiro editado e depois git commit --amend
para submeter o ficheiro encenado! Isto irá agora abrir novamente uma janela Vim com a mensagem de submissão:
Podemos editar a mensagem de submissão ou deixar como está. Vamos escolher manter a mensagem de submissão igual e escreveremos :wq
para guardar e sair da janela.
Editamos agora a nossa antiga submissão. Então e agora? Run git status
:
Continuar o rebase interativo
Não temos mais nada para mudar no commit, então vamos continuar!
Executamos git rebase --continue
e vemos a seguinte mensagem:
Woah, terminamos? Mas e os outros dois compromissos? Bem, o próximo commit on a ser feito foi 6c01350
. Este commit que marcamos para apagar (drop
) quando começamos o rebase interativo. Git apagou-o automaticamente e passou para a próxima submissão, 41aa9d2
. Esta nunca foi modificada no rebase interativo inicial. Seu comando padrão era pick
, o que significa que a submissão será usada. Git aplicou essa commit e como essa foi a última commit, a rebase interativa foi completada.
Note, se tivéssemos mais commits para editar, teríamos simplesmente movido para a próxima commit e começado o processo de emenda, assim como fizemos acima. O ciclo continua até que não haja mais commits.
O botão ejectar
Vale a pena notar que se em algum ponto do nosso rebase interactivo nós estragarmos as coisas e não soubermos como consertá-las, podemos sempre abortar. Em qualquer ponto podemos executar git rebase --abort
no terminal e o rebase interativo será abortado sem que nenhuma alteração seja salva. Nós precisaríamos então iniciar a rebase interativa novamente.
Após a rebase interativa
Nosso log git agora se parece:
Você notará algumas coisas que mudaram de antes de iniciarmos a rebase interativa:
- Nem temos mais o commit
6c01350
com a mensagem de commit “Remove merge conflict”. Esta é a submissão que apagámos no nosso rebase interactivo. - A nossa submissão que editamos,
4a4d705
, tem um SHA-1 diferente,2b7443d
. - A nossa mais recente submissão do nosso log git original,
41aa9d2
, também tem um novo SHA-1,2b95e92
. Este commit não foi alterado, mas foi simplesmente aplicado ao commit antes dele2b7443d
.
Efeitos colaterais do rebasing interativo
Para os dois commits mais recentes em nosso log de git, porque eles têm novos SHA-1, Git os vê como commits totalmente novos. Isto é mesmo verdade para o nosso último commit, 2b95e92
, onde nem a mensagem de commit nem os arquivos mudaram em absoluto. Isto traz à tona um ponto importante com o rebasing interativo: Se você modificar um commit, esse commit e todos os commits sucessivos terão novos SHA-1’s.
Isso não afetará nada se os commits que você modificou não tiverem sido empurrados para uma ramificação remota. Entretanto, se você de fato completou um rebase interativo em commits que já foram empurrados para um branch remoto e então empurrou seu branch novamente você verá:
Tecnicamente, você poderia contornar isso usando git push --force
mas isso é muito perigoso. Se a filial remota tem commits de outras pessoas mas a sua filial local ainda não tem esses commits, você irá efetivamente apagar os commits deles.
Outra solução é usar git push --force-with-lease
que só irá modificar os seus commits mas não os commits pertencentes a outras pessoas, embora isso também possa ser problemático. Por exemplo, se outro desenvolvedor já tem aquelas commits que foram dadas novas SHA-1’s na sua filial local, quando eles puxarem a filial remota, eles terão conflitos de fusão com cada uma dessas commits.
Quando usar --force-with-lease
está além do escopo deste post no blog, mas seria melhor consultar outros membros da sua equipe antes de fazer isso. Você pode ler mais sobre git push --force-with-lease
aqui.
A chave de retirada desta seção é que é muito mais fácil e seguro usar rebase interativo em commits que ainda não foram empurrados para um ramo remoto.
Escrito por Blake DeBoer. Originalmente colocado em Dev.to
Senior, Lead, ou Desenvolvedor Principal em NYC? Stride está contratando! Quer elevar o nível de sua equipe técnica? Veja como nós fazemos isso! www.stridenyc.com
Tags
Crie sua conta gratuita para desbloquear sua experiência de leitura personalizada.
Leave a Reply