Como Desfazer e Remover um Commit Específico do Git no Branch Atual

GitBeginner
Pratique Agora

Introdução

Neste laboratório, você aprenderá a gerenciar seu histórico de commits do Git. Você praticará desfazer alterações com git reset, reverter commits públicos com segurança com git revert e remover commits específicos do seu branch usando um rebase interativo. Você também aprenderá a usar o reflog para recuperar commits que foram removidos. Estas são habilidades essenciais para manter um histórico de projeto limpo e compreensível.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível intermediário com uma taxa de conclusão de 69%. Recebeu uma taxa de avaliações positivas de 100% dos estudantes.

Inspecionando o Histórico de Commits

Antes de modificar o histórico, você precisa saber como visualizá-lo. O comando git log é a ferramenta principal para isso. Para este laboratório, um repositório Git foi inicializado com vários commits para simular um histórico de projeto real.

Vamos começar inspecionando o histórico de commits. Usaremos a flag --oneline para mostrar cada commit em uma única linha e --graph para exibir o histórico de commits como um gráfico, o que é útil para visualizar branches e merges.

Execute o seguinte comando no seu terminal:

git log --oneline --graph

Você deverá ver uma lista dos cinco commits que foram criados para você, com o commit mais recente no topo. Cada linha mostra um hash de commit único (uma versão curta dele) e a mensagem do commit.

* 7d3d24a (HEAD -> master) chore: Add last-commit file
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Nota: Seus hashes de commit serão diferentes do exemplo acima. Isso é esperado, pois cada hash é único.

Reserve um momento para revisar o histórico. Você manipulará esses commits nas próximas etapas.

Desfazendo o Último Commit com git reset

Às vezes, você faz um commit e imediatamente percebe que cometeu um erro. O comando git reset é perfeito para este cenário. Ele pode mover o ponteiro HEAD do branch atual para um commit anterior, efetivamente "desfazendo" um ou mais commits.

Usaremos a opção --soft, que desfaz o commit, mas mantém as alterações desse commit na sua área de staging (o índice). Isso é útil se você quiser refazer o commit das alterações, talvez com uma mensagem diferente ou combinadas com outras alterações.

Vamos desfazer o commit mais recente, "chore: Add last-commit file".

git reset --soft HEAD~1

Aqui, HEAD~1 refere-se ao commit imediatamente anterior ao HEAD atual.

Agora, verifique o status do seu repositório:

git status

Você verá que last-commit.txt agora está listado em "Changes to be committed". Isso significa que o próprio commit foi desfeito, mas as alterações do arquivo ainda estão preparadas (staged).

On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   last-commit.txt

Finalmente, visualize o log novamente para confirmar que o commit foi removido do histórico:

git log --oneline --graph
* 8a9f1b3 (HEAD -> master) docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Como você pode ver, o último commit foi removido do log, mas seu trabalho está seguro na área de staging.

Revertendo um Commit com git revert

Enquanto git reset é ótimo para alterações locais, ele reescreve o histórico, o que pode ser problemático se você já compartilhou seus commits com outras pessoas. Uma maneira mais segura de desfazer alterações em um branch público é git revert. Este comando cria um novo commit que aplica o inverso das alterações de um commit especificado.

Primeiro, vamos limpar nosso diretório de trabalho refazendo o commit das alterações da etapa anterior.

git commit -m "chore: Add last-commit file again"

Agora, digamos que queremos desfazer o commit que adicionou file2.txt. Este commit está mais atrás em nosso histórico. Vamos revertê-lo. Podemos nos referir a ele por sua posição em relação ao HEAD. Olhando o log da Etapa 1, "feat: Add file2.txt" é o quarto commit de cima para baixo, então podemos referenciá-lo como HEAD~3.

Execute o comando de revert:

git revert HEAD~3

Isso abrirá seu editor de texto padrão (vim por padrão) com uma mensagem de commit pré-preenchida como "Revert 'feat: Add file2.txt'". Você pode simplesmente salvar e fechar o arquivo para aceitar a mensagem padrão.

No vim, digite :wq e pressione Enter para salvar e sair.

Agora, verifique o log novamente:

git log --oneline --graph

Você verá um novo commit no topo, "Revert 'feat: Add file2.txt'".

* 1e2d3f4 (HEAD -> master) Revert "feat: Add file2.txt"
* 7d3d24a chore: Add last-commit file again
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

O commit original ainda está no histórico, mas suas alterações foram desfeitas pelo novo commit de revert. Você pode verificar isso listando os arquivos no diretório.

ls

Você notará que file2.txt não está mais presente.

Removendo um Commit com Rebase Interativo

Para manipulações de histórico mais complexas, como remover um commit do meio de um branch, você pode usar o rebase interativo (git rebase -i). Este é um comando muito poderoso que reescreve o histórico de commits, portanto, deve ser usado com cautela, especialmente em branches que são compartilhados com outros desenvolvedores.

Nosso objetivo é remover o commit com a mensagem "fix: Add a temporary file that should be removed". Olhando o log atual, este commit é agora HEAD~3.

Inicie o processo de rebase interativo:

git rebase -i HEAD~4

Este comando abrirá seu editor de texto (vim por padrão) com uma lista dos últimos 4 commits.

pick fd5a181 fix: Add a temporary file that should be removed
pick 5f27a4d docs: Update documentation in file1
pick 284be6f chore: Add last-commit file again
pick 8a460c5 Revert "feat: Add file2.txt"

## Rebase 9403080..8a460c5 onto 9403080 (4 commands)
#
## Commands:
## p, pick <commit> = use commit
## d, drop <commit> = remove commit
## ...

Para remover o commit, altere a palavra pick para drop (ou d) na linha que contém "fix: Add a temporary file that should be removed".

Altere isto:

pick fd5a181 fix: Add a temporary file that should be removed

Para isto:

drop fd5a181 fix: Add a temporary file that should be removed

No vim, pressione i para entrar no modo de inserção, faça suas alterações, depois pressione Esc para sair do modo de inserção. Salve e saia digitando :wq e pressionando Enter. O Git irá reproduzir os commits restantes no topo do novo histórico.

Verifique o log para ver o resultado:

git log --oneline --graph

O commit "ruim" agora desapareceu do histórico.

* a5b4c3d (HEAD -> master) chore: Add last-commit file again
* f9e8d7c docs: Update documentation in file1
* 1e2d3f4 Revert "feat: Add file2.txt"
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Além disso, verifique os arquivos no diretório. O arquivo bad-commit-file.txt foi removido.

ls

Restaurando um Commit Removido com git reflog

E se você remover um commit por engano? O Git mantém um registro de todas as alterações no ponteiro HEAD em um log especial chamado reflog. Esta é sua rede de segurança. Você pode usá-lo para encontrar e restaurar commits perdidos.

Vamos visualizar o reflog para encontrar o histórico que acabamos de reescrever.

git reflog

Você verá uma lista de ações que você realizou. Procure pela linha que diz rebase (finish): returning to refs/heads/master. A entrada logo abaixo dela, provavelmente HEAD@{1}, representa o estado do seu branch antes do rebase.

a5b4c3d (HEAD -> master) HEAD@{0}: rebase (finish): returning to refs/heads/master
1e2d3f4 HEAD@{1}: rebase (start): checkout HEAD~3
...

Podemos restaurar nosso branch para este estado anterior usando git reset --hard. Este comando é destrutivo e descartará quaisquer alterações não commitadas, portanto, use-o com cuidado.

Vamos resetar nosso branch para o estado anterior ao rebase, que neste caso é HEAD@{1}.

git reset --hard HEAD@{1}

Você verá uma mensagem confirmando que HEAD agora está no commit anterior ao início do rebase.

HEAD is now at 1e2d3f4 Revert "feat: Add file2.txt"

Agora, verifique o log uma última vez:

git log --oneline --graph

Você deverá ver o histórico como estava antes do rebase:

* f461400 (HEAD -> master) Revert "feat: Add file2.txt"
* acea45c chore: Add last-commit file again
* c04b3f5 docs: Update documentation in file1
* 9403080 feat: Add file2.txt
* ee39412 feat: Add file1.txt

Note que o commit "fix: Add a temporary file that should be removed" ainda não está presente - isso está correto! O comando git reset --hard HEAD@{1} nos restaurou para o estado antes do início do rebase interativo, não antes de removermos o commit. O rebase interativo removeu com sucesso aquele commit indesejado do nosso histórico. Você pode executar ls para confirmar que bad-commit-file.txt ainda não está presente. O reflog é uma ferramenta inestimável para se recuperar de erros no Git.

Resumo

Neste laboratório, você aprendeu técnicas práticas para gerenciar o histórico de commits do seu Git. Você começou inspecionando o histórico com git log. Em seguida, praticou o desfazimento de commits usando git reset --soft, revertendo alterações com segurança com git revert e removendo commits específicos com o poderoso comando git rebase -i. Finalmente, você aprendeu a usar git reflog como uma rede de segurança para restaurar commits que foram removidos do histórico. Essas habilidades são essenciais para manter um histórico de projeto limpo e gerenciável.