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 segurogit checkoutainda 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
- Documentação oficial do Git - git-reset — Referência completa sobre o comando
git reset, incluindo os modos soft, mixed e hard. - Documentação oficial do Git - git-revert — Guia detalhado sobre como reverter commits de forma segura com
git revert. - Documentação oficial do Git - git-reflog — Explicação completa do reflog e como usá-lo para recuperar commits perdidos.
- Atlassian - Git Reset, Revert e Reflog — Tutorial prático da Atlassian comparando as diferentes formas de desfazer alterações no Git.
- GitHub Blog - Como desfazer commits no Git — Artigo do GitHub com exemplos práticos de como desfazer commits em diferentes cenários.