Todos os artigos
118 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Tutoriais

Como desfazer alterações no Git com segurança: reset, revert, restore e reflog

Qual comando usar para desfazer alterações no Git em cada situação — reset, revert, restore, stash e reflog — sem perder trabalho.

Como desfazer alterações no Git com segurança: reset, revert, restore e reflog
COVER · Tutoriais

Você editou um arquivo errado, commitou algo pela metade ou fez git add em coisa que não devia — e agora o estômago aperta na hora de digitar o próximo comando. O medo é justo: alguns comandos de "desfazer" no Git apagam trabalho de forma silenciosa e irreversível, enquanto outros são perfeitamente seguros. A diferença entre git checkout, git restore, git reset e git revert não é estética: é a diferença entre recuperar a calma e perder uma tarde de código. Este guia mostra qual comando usar em cada situação, sempre priorizando os caminhos que não destroem nada.

Antes de qualquer coisa: descubra em que estado você está

Quase todo erro de "desfazer" vem de aplicar o comando certo no estado errado. O Git tem três áreas, e o comando correto depende de onde sua alteração está:

  • Diretório de trabalho (working directory): você editou o arquivo, mas não rodou git add.
  • Staging (index): você já rodou git add, mas ainda não commitou.
  • Histórico (commits): você já rodou git commit.

O comando que diz a verdade sobre isso é git status. Rode-o antes de desfazer qualquer coisa:

git status

Ele literalmente te diz qual comando usar ("use git restore to discard changes", "use git restore --staged to unstage"). Se você está começando agora e esses três estados ainda soam abstratos, vale a pena passar antes pelo nosso guia de Git para iniciantes, que explica o ciclo add → commit → push com calma. Aqui o foco é o oposto: como voltar atrás.

Desfazer alterações que ainda não foram commitadas

Tirar um arquivo do staging (depois do git add)

Você adicionou ao stage um arquivo por engano, mas quer manter as edições. Isso é seguro — nada é apagado:

git restore --staged arquivo.js     # Git 2.23+
git reset HEAD arquivo.js           # forma clássica, mesmo efeito

O arquivo volta para "modificado, mas não staged". Seu trabalho continua intacto.

Descartar edições no arquivo (perigo: isso apaga)

Aqui mora a primeira armadilha real. Se você quer jogar fora as alterações de um arquivo e voltar ao último commit:

git restore arquivo.js              # Git 2.23+
git checkout -- arquivo.js          # forma antiga

Esse comando descarta permanentemente o que não foi commitado. Não há reflog para edições não commitadas. Antes de rodar, pergunte-se: "tenho certeza de que não quero nada disso?". Se a dúvida bater, faça um git stash em vez de descartar — ele guarda tudo numa gaveta recuperável:

git stash          # guarda as mudanças e limpa o working directory
git stash pop      # traz de volta quando quiser

git stash é o "desfazer com rede de segurança" mais subestimado do Git.

Desfazer commits: reset vs. revert

Aqui está a decisão mais importante do guia. A regra de ouro:

Commit que ainda NÃO foi enviado (push)git reset. Commit que JÁ foi enviado e compartilhadogit revert.

git reset — para reescrever histórico local

git reset move o ponteiro do branch para um commit anterior. Tem três modos, e entender a diferença evita perda de trabalho:

git reset --soft HEAD~1    # desfaz o commit, MANTÉM tudo no staging
git reset --mixed HEAD~1   # (padrão) desfaz o commit, mantém edições no working dir
git reset --hard HEAD~1    # desfaz o commit E APAGA as alterações
  • --soft: o caso mais comum. "Commitei cedo demais / mensagem ruim." O commit some, mas seus arquivos ficam prontos para um novo commit. Seguro.
  • --mixed: o commit some e as mudanças voltam para o working directory, fora do stage. Bom para reorganizar o que vai em cada commit. Seguro.
  • --hard: destrutivo. Apaga as alterações do working directory. Só use quando tem 100% de certeza de que não quer nada daquilo.

Para corrigir só a mensagem do último commit, nem precisa de reset:

git commit --amend -m "mensagem correta"

git revert — para desfazer sem reescrever histórico

git revert não apaga nada: ele cria um novo commit que desfaz as mudanças de um commit anterior. É a escolha obrigatória quando o commit já está no remoto e outras pessoas podem tê-lo puxado.

git revert abc1234           # cria um commit que inverte abc1234

Por que não usar reset num branch compartilhado? Porque reset reescreve a linha do tempo. Se você fizer reset + push --force num branch que o colega já puxou, o histórico dele e o seu divergem, e o próximo pull vira um campo minado. revert mantém todo o histórico — quem está auditando enxerga tanto o erro quanto a correção, o que costuma ser desejável.

A rede de segurança que quase ninguém usa: git reflog

Se você fez um reset --hard e o sangue gelou, calma. O Git mantém um registro de para onde o HEAD apontou nas últimas ações: o reflog. Commits "perdidos" por reset ainda existem por semanas antes do garbage collector limpá-los.

git reflog                          # lista o histórico de movimentos do HEAD
git reset --hard HEAD@{1}           # volta para onde você estava antes do erro

O reflog recupera commits que sumiram do git log. Ele não recupera, porém, edições que nunca foram commitadas nem stashadas — por isso git restore e reset --hard em arquivos não commitados são os comandos mais perigosos do conjunto.

Tabela rápida de decisão

Situação Comando seguro
Tirar arquivo do stage git restore --staged arquivo
Descartar edição não commitada git stash (recuperável) ou git restore arquivo (apaga)
Corrigir mensagem do último commit git commit --amend
Desfazer último commit, manter código git reset --soft HEAD~1
Desfazer commit já no remoto git revert <hash>
Recuperar de um reset --hard git reflog + git reset --hard HEAD@{n}

Perguntas frequentes

Como desfazer o último commit sem perder as alterações?

Use git reset --soft HEAD~1. O commit é removido do histórico, mas todas as suas mudanças permanecem no staging, prontas para um novo commit. Se quiser que as mudanças voltem para o working directory (fora do stage), use git reset --mixed HEAD~1. Em nenhum dos dois casos seu código é apagado — só o --hard faz isso.

Qual a diferença entre git reset e git revert?

git reset reescreve o histórico: ele move o ponteiro do branch para trás e os commits desfeitos somem da linha do tempo. É ideal para erros locais que ninguém viu ainda. git revert não reescreve nada — cria um novo commit que anula um commit anterior, preservando todo o histórico. Use revert sempre que o commit já tiver sido enviado para um branch compartilhado.

git checkout e git restore fazem a mesma coisa?

Em parte. O antigo git checkout era sobrecarregado: servia tanto para trocar de branch quanto para descartar alterações de arquivos, o que confundia muita gente. A partir do Git 2.23, essa segunda função virou git restore (descartar/restaurar arquivos) e a troca de branch virou git switch. Funcionalmente git restore arquivo e git checkout -- arquivo são equivalentes, mas git restore é mais explícito e menos arriscado.

Dá para recuperar alterações depois de um git reset --hard?

Depende do estado em que estavam. Se as mudanças chegaram a virar commit, sim: git reflog mostra o ponto anterior e você volta com git reset --hard HEAD@{1}. Se eram edições nunca commitadas nem stashadas, não — elas não deixam rastro recuperável. Por isso a recomendação é stashar antes de qualquer comando destrutivo.

O que levar deste guia

A segurança no Git não vem de decorar comandos, e sim de duas perguntas antes de digitar: "minha alteração está no working dir, no stage ou já commitada?" e "esse commit já foi para o remoto?". Com a primeira você escolhe entre restore e reset; com a segunda, entre reset e revert. Reserve --hard e push --force para quando tiver certeza absoluta, use git stash como rede sempre que houver dúvida, e lembre que o git reflog é seu seguro contra commits perdidos. Desfazer no Git deixa de ser assustador quando você sabe que, na grande maioria dos casos, nada é realmente irreversível.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil