Um guia para desfazer erros com o Git (Parte 2)
Publicados: 2022-03-10Nesta 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:

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!
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?

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:

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”.


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:

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ê:
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:
- 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! - É 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.

É 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!

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

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:

Graças à opção --autosquash
, o Git já fez o trabalho pesado para nós:
- 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. - 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.

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!