Um guia para desfazer erros com o Git (Parte 2)

Publicados: 2022-03-10
Resumo rápido ↬ Erros. Esses vilões cruéis nem param no belo mundo do desenvolvimento de software. Mas, embora não possamos evitar cometer erros, podemos aprender a desfazê-los! Este artigo mostrará as ferramentas certas para o seu trabalho diário com o Git. Você pode querer verificar o primeiro artigo da série também.

Nesta segunda parte da nossa série sobre “Desfazendo erros com o Git”, vamos encarar o perigo com coragem novamente: preparei quatro novos cenários apocalípticos — incluindo, é claro, algumas maneiras inteligentes de salvar nossos pescoços! Mas antes de nos aprofundarmos: dê uma olhada nos artigos anteriores sobre Git para obter ainda mais métodos de auto-resgate que ajudam você a desfazer seus erros com o Git!

Vamos lá!

Recuperando uma ramificação excluída usando o Reflog

Você já excluiu um branch e, logo depois, percebeu que não deveria? No caso improvável de você não conhecer esse sentimento, posso lhe dizer que não é bom. Uma mistura de tristeza e raiva toma conta de você, enquanto você pensa em todo o trabalho duro que foi feito nos commits daquele branch, todo o código valioso que você perdeu agora.

Felizmente, existe uma maneira de trazer esse branch de volta dos mortos — com a ajuda de uma ferramenta Git chamada “Reflog”. Havíamos usado essa ferramenta na primeira parte de nossa série, mas aqui vai um pequeno lembrete: o Reflog é como um diário onde o Git anota cada movimento do ponteiro HEAD em seu repositório local. Em outras palavras, menos nerds: sempre que você faz check-out, commit, merge, rebase, cherry-pick e assim por diante, uma entrada de diário é criada. Isso torna o Reflog uma rede de segurança perfeita quando as coisas dão errado!

Vejamos um exemplo concreto:

 $ git branch * feature/login master

Podemos ver que atualmente temos nosso feature/login de ramificação verificado. Digamos que este é o branch que vamos excluir (inadvertidamente). Antes de podermos fazer isso, no entanto, precisamos mudar para uma ramificação diferente porque não podemos excluir nossa ramificação HEAD atual!

 $ git checkout master $ git branch -d feature/login

Nossa valiosa ramificação de recursos se foi - e eu lhe darei um minuto para (a) entender a gravidade do nosso erro e (b) lamentar um pouco. Depois que você enxugou as lágrimas, precisamos encontrar uma maneira de trazer esse galho de volta! Vamos abrir o Reflog (simplesmente digitando git reflog ) e ver o que ele tem reservado para nós:

O Git's Reflog protocola todas as principais ações em nosso repositório local
O Git's Reflog protocola todas as principais ações em nosso repositório local. (Visualização grande)

Aqui estão alguns comentários para ajudá-lo a entender a saída:

  • Antes de tudo, você precisa saber que o Reflog ordena suas entradas cronologicamente: os itens mais recentes estão no topo da lista.
  • O item mais alto (e, portanto, mais novo) é o comando git checkout que executamos antes de excluir a ramificação. Ele está registrado aqui no Reflog porque é um desses “movimentos do ponteiro HEAD” que o Reflog registra com tanto zelo.
  • Para desfazer nosso grave erro, podemos simplesmente retornar ao estado anterior – que também está registrado de forma limpa e clara no Reflog!

Então vamos tentar isso, criando uma nova ramificação (com o nome da nossa ramificação “perdida”) que começa neste estado “antes” do hash SHA-1:

 $ git branch feature/login 776f8ca

E voilá! Você ficará encantado ao ver que agora restauramos nosso ramo aparentemente perdido!

Se você estiver usando uma GUI de desktop Git como “Tower”, você pode usar um bom atalho: basta pressionar CMD + Z no teclado para desfazer o último comando - mesmo que você tenha excluído violentamente uma ramificação!

Uma GUI de desktop como o Tower pode facilitar o processo de desfazer erros.
Mais depois do salto! Continue lendo abaixo ↓

Movendo um commit para um branch diferente

Em muitas equipes, há um acordo para não fazer commit em branches de longa duração como main ou develop : branches como esses só devem receber novos commits por meio de integrações (por exemplo, merges ou rebases). E, no entanto, é claro que os erros são inevitáveis: às vezes nos esquecemos e cometemos nesses ramos! Então, como podemos limpar a bagunça que fizemos?

Movendo um commit para seu branch de destino correto
Nosso commit caiu no branch errado. Como podemos movê-lo para seu branch de destino correto? (Visualização grande)

Felizmente, esses tipos de problemas podem ser facilmente corrigidos. Vamos arregaçar as mangas e mãos à obra.

A primeira etapa é alternar para a ramificação de destino correta e, em seguida, mover o commit usando o comando cherry-pick :

 $ git checkout feature/login $ git cherry-pick 776f8caf

Agora você terá o commit no branch desejado, onde deveria estar em primeiro lugar. Impressionante!

Mas ainda há uma coisa a fazer: precisamos limpar o galho onde ele caiu acidentalmente ! O comando cherry-pick , por assim dizer, criou uma cópia do commit — mas o original ainda está presente em nosso branch de longa duração:

Uma cópia do commit no branch correto, mas o original ainda está no branch errado
Criamos com sucesso uma cópia do commit no branch correto, mas o original ainda está aqui — no branch errado. (Visualização grande)

Isso significa que temos que voltar para nosso branch de longa duração e usar git reset para removê-lo:

 $ git checkout main $ git reset --hard HEAD~1

Como você pode ver, estamos usando o comando git reset aqui para apagar o commit defeituoso. O parâmetro HEAD~1 diz ao Git para “voltar 1 revisão atrás do HEAD”, apagando efetivamente o commit mais alto (e no nosso caso: indesejado) do histórico daquele branch.

E pronto: o commit agora está onde deveria estar em primeiro lugar e nosso branch de longa duração está limpo - como se nosso erro nunca tivesse acontecido!

Editando a mensagem de um commit antigo

É muito fácil contrabandear um erro de digitação em uma mensagem de confirmação - e só descobrir muito mais tarde. Nesse caso, a boa e velha opção --amend do git commit não pode ser usada para corrigir esse problema, porque só funciona para o último commit. Para corrigir qualquer commit que seja mais antigo, temos que recorrer a uma ferramenta Git chamada “Interactive Rebase”.

Uma mensagem de commit que vale a pena mudar
Aqui está uma mensagem de confirmação que vale a pena mudar. (Visualização grande)

Primeiro, temos que dizer ao Interactive Rebase qual parte do histórico de commits queremos editar. Isso é feito alimentando-o com um hash de commit: o commit pai daquele que queremos manipular.

 $ git rebase -i 6bcf266b

Uma janela do editor será aberta. Ele contém uma lista de todos os commits após o que fornecemos como base para o Interactive Rebase no comando:

Mostrando o intervalo de commits que selecionamos para edição em nossa sessão Interactive Rebase
O intervalo de commits que selecionamos para edição em nossa sessão do Interactive Rebase. (Visualização grande)

Aqui, é importante que você não siga seu primeiro impulso: nesta etapa, ainda não editamos a mensagem do commit. Em vez disso, apenas informamos ao Git que tipo de manipulação queremos fazer com qual(is) commit(s). Muito convenientemente, há uma lista de palavras-chave de ação observadas nos comentários na parte inferior desta janela. Para o nosso caso, marcamos a linha #1 com reword (substituindo assim a pick padrão).

Tudo o que resta a fazer nesta etapa é salvar e fechar a janela do editor. Em troca, uma nova janela do editor será aberta contendo a mensagem atual do commit que marcamos. E agora é finalmente a hora de fazer nossas edições!

Aqui está todo o processo de relance para você:

Usando o Interactive Rebase para editar a mensagem de um commit antigo, do início ao fim.

Corrigindo um commit quebrado (de uma maneira muito elegante)

Finalmente, vamos dar uma olhada no fixup , o canivete suíço das ferramentas de desfazer. Simplificando, ele permite que você corrija um commit quebrado/incompleto/incorreto após o fato. É realmente uma ferramenta maravilhosa por dois motivos:

  1. Não importa qual é o problema.
    Você pode ter esquecido de adicionar um arquivo, deve ter excluído algo, feito uma alteração incorreta ou simplesmente um erro de digitação. fixup funciona em todas essas situações!
  2. É extremamente elegante.
    Nossa reação normal e instintiva a um bug em um commit é produzir um novo commit que corrija o problema. Essa maneira de trabalhar, por mais intuitiva que possa parecer, faz com que seu histórico de commits pareça muito caótico, muito em breve. Você tem commits “originais” e então esses pequenos commits “band-aid” que corrigem as coisas que deram errado nos commits originais. Seu histórico está cheio de pequenos commits de band-aid sem sentido, o que torna difícil entender o que aconteceu em sua base de código.
Seu histórico de commits pode ser muito difícil de ler se você corrigir constantemente pequenos erros com os chamados commits band-aid
Consertar constantemente pequenos erros com “commits de band-aid” torna seu histórico de commits muito difícil de ler. (Visualização grande)

É aqui que entra a fixup . Ela ainda permite que você faça esse commit de correção de band-aid. Mas aí vem a mágica: ele então aplica ao commit original quebrado (reparando-o dessa forma) e então descarta o commit feio do band-aid completamente!

Fixup aplica suas correções ao commit original e então descarta o commit supérfluo do band-aid
Fixup aplica suas correções ao commit original e então descarta o commit supérfluo do band-aid. (Visualização grande)

Podemos passar por um exemplo prático juntos! Digamos que o commit selecionado aqui está quebrado.

Corrigindo o commit incorreto selecionado de maneira elegante
O commit selecionado está incorreto — e vamos corrigi-lo de maneira elegante. (Visualização grande)

Digamos também que preparei alterações em um arquivo chamado error.html que resolverá o problema. Aqui está o primeiro passo que precisamos fazer:

 $ git add error.html $ git commit --fixup 2b504bee

Estamos criando um novo commit, mas estamos dizendo ao Git que este é especial: é uma correção para um commit antigo com o hash SHA-1 especificado ( 2b504bee neste caso).

A segunda etapa, agora, é iniciar uma sessão do Interactive Rebase — porque a fixup pertence ao grande conjunto de ferramentas do Interactive Rebase.

 $ git rebase -i --autosquash 0023cddd

Vale a pena explicar duas coisas sobre este comando. Primeiro, por que 0023cddd como o hash de revisão aqui? Porque precisamos iniciar nossa sessão de Rebase Interativa no commit pai de nosso colega quebrado.

Segundo, para que serve a opção --autosquash ? Tira muito trabalho dos nossos ombros! Na janela do editor que agora se abre, tudo já está preparado para nós:

A janela da sessão de rebase interativa
A janela da sessão Interactive Rebase (visualização grande)

Graças à opção --autosquash , o Git já fez o trabalho pesado para nós:

  1. Ele marcou nosso pequeno commit de band-aid com a palavra-chave de ação de fixup . Dessa forma, o Git irá combiná-lo com o commit diretamente acima e depois descartá-lo.
  2. Ele também reordenou as linhas de acordo, movendo nosso commit band-aid diretamente abaixo do commit que queremos corrigir (novamente: fixup funciona combinando o commit marcado com o acima !).

Resumindo: não há nada a fazer por nós a não ser fechar a janela!

Vamos dar uma olhada final no resultado final.

  • O commit anteriormente quebrado foi corrigido: agora contém as mudanças que preparamos em nosso commit band-aid.
  • O próprio commit do band-aid feio foi descartado: o histórico de commits é limpo e fácil de ler — como se nenhum erro tivesse ocorrido.
Um exemplo de como um histórico de commits limpo se parece
O resultado final depois de usar a ferramenta de correção: um histórico de confirmação limpo! (Visualização grande)

Saber como desfazer erros é um superpoder

Parabéns! Agora você pode salvar seu pescoço em muitas situações difíceis! Não podemos realmente evitar essas situações: não importa quão experientes sejamos como desenvolvedores, os erros são simplesmente parte do trabalho. Mas agora que você sabe como lidar com eles, você pode enfrentá-los com um ritmo cardíaco descontraído.

Se você quiser saber mais sobre como desfazer erros com o Git, posso recomendar o “Kit de primeiros socorros para Git” gratuito, uma série de vídeos curtos exatamente sobre esse tópico.

Divirta-se cometendo erros – e, claro, desfazendo-os com facilidade!