Como desfazer commits sem entrar em pânico no Git

1. Entendendo o cenário: antes de desfazer, saiba o que aconteceu

Antes de qualquer ação, é crucial distinguir entre commits locais (ainda não enviados ao repositório remoto) e remotos (já compartilhados com a equipe). Reescrever histórico público pode causar caos para outros desenvolvedores.

O Git trabalha com três áreas principais:
- Working Tree: seus arquivos no disco
- Staging Area (Index): preparação para o próximo commit
- HEAD: referência ao último commit do branch atual

Quando NÃO desfazer: evite reescrever histórico em branches compartilhados (main, develop) sem comunicação prévia com a equipe. Use git revert nesses casos.

2. Desfazendo o último commit: git commit --amend

O --amend permite modificar o commit mais recente sem criar um novo.

Corrigindo a mensagem do commit:

git commit --amend -m "Mensagem corrigida"

Adicionando arquivos esquecidos:

git add arquivo-esquecido.js
git commit --amend --no-edit

Cuidados: se o commit já foi enviado ao remoto, usar --amend exige git push --force-with-lease. Prefira git revert para commits públicos.

3. Resetando commits com git reset: soft, mixed e hard

O git reset move o HEAD para um commit anterior, com três modos:

--soft: mantém alterações no staging area

git reset --soft HEAD~1
# HEAD volta 1 commit, alterações permanecem prontas para commit

--mixed (padrão): mantém alterações na working tree, remove do staging

git reset HEAD~2
# Volta 2 commits, alterações ficam como não preparadas

--hard: remove completamente as alterações (⚠️ perigoso!)

git reset --hard HEAD~3
# Volta 3 commits, perde todas as mudanças desses commits

Voltando para um commit específico por hash:

git reset --hard a1b2c3d

4. Revertendo commits com git revert: a opção segura para histórico público

Diferente do reset, git revert cria um novo commit que desfaz as alterações de um commit específico. O histórico permanece intacto.

Revertendo o último commit:

git revert HEAD

Revertendo um commit específico:

git revert a1b2c3d

Revertendo múltiplos commits em sequência:

git revert HEAD~3..HEAD
# Reverte os últimos 3 commits, gerando commits reversos

Se houver conflitos, resolva-os manualmente e finalize com:

git revert --continue

5. Resgatando commits "perdidos" com git reflog

O reflog registra todos os movimentos do HEAD, mesmo após resets. É seu salva-vidas após um git reset --hard acidental.

Visualizando o reflog:

git reflog

Saída exemplo:

a1b2c3d HEAD@{0}: reset: moving to HEAD~2
e5f6g7h HEAD@{1}: commit: Adiciona funcionalidade X
i9j0k1l HEAD@{2}: commit: Corrige bug Y

Recuperando após reset hard:

git reflog
# Identifique o hash do commit desejado (ex: e5f6g7h)
git reset --hard e5f6g7h

6. Desfazendo alterações específicas com git restore

git restore é o comando moderno para descartar alterações (substitui git checkout para esses casos).

Descartando alterações não commitadas:

git restore arquivo-modificado.js

Removendo arquivos do staging area:

git restore --staged arquivo-preparado.js

Diferença entre git restore e git checkout:

  • git restore é mais explícito e seguro
  • git checkout ainda funciona, mas é considerado legado para essas operações

7. Estratégias para desfazer commits já enviados ao remoto

Recomendado: git revert

git revert HEAD~2
git push origin main

Forçando push (apenas em casos controlados):

git reset --hard HEAD~2
git push --force-with-lease origin meu-branch

--force-with-lease é mais seguro que --force, pois verifica se ninguém mais alterou o branch.

Comunicação com a equipe: antes de reescrever histórico compartilhado, avise pelo canal de comunicação da equipe e coordene o momento.

8. Checklist de pânico: passo a passo para situações comuns

Cenário 1: "Commit com mensagem errada"

git commit --amend -m "Mensagem correta"

Cenário 2: "Commit no branch errado"

git log --oneline -1  # Anote o hash do commit
git reset HEAD~1      # Desfaz o commit local
git stash             # Guarda as alterações
git checkout branch-correto
git stash pop         # Recupera as alterações
git add .
git commit -m "Mensagem no branch correto"

Cenário 3: "Preciso desfazer um commit antigo no remoto"

git log --oneline  # Encontre o hash do commit
git revert a1b2c3d
git push origin main

Cenário 4: "Dei reset hard e perdi tudo"

git reflog
# Identifique o hash do commit antes do reset
git reset --hard a1b2c3d

Desfazer commits no Git não precisa ser motivo de pânico. Com git commit --amend para correções rápidas, git reset para voltar no tempo localmente, git revert para histórico público, e git reflog como rede de segurança, você tem todas as ferramentas para gerenciar seu histórico com confiança. Lembre-se: a chave é entender o contexto (local vs. remoto) e escolher a ferramenta certa para cada situação.

Referências