git reset
Redefinir o HEAD atual para o estado indicado
O comando 'git reset' serve para desfazer mudanças movendo o HEAD da branch atual para um commit indicado. Ele tem três modos principais: --soft (mantém as mudanças preparadas), --mixed (padrão - mantém as mudanças no diretório de trabalho) e --hard (descarta todas as mudanças). É essencial para desfazer commits, tirar arquivos da preparação e gerenciar o histórico. Cuidado com 'git reset --hard', que descarta as mudanças para sempre.
Sinopse
git reset [-q] [<tree-ish>] [--] <pathspec>...
git reset [-q] [--pathspec-from-file=<file>] [<tree-ish>]
git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]Opções
Escolhendo a opção certa
Modos de reset: o que é mantido?
Entender os três modos principais de reset e o que acontece com as suas mudanças
| Comando | O HEAD se move | Área de preparação | Diretório de trabalho | Use quando |
|---|---|---|---|---|
--soft | Sim | Mantidas (preparadas) | Preservado | Desfazer um commit para refazê-lo com mudanças |
--mixed (default) | Sim | Esvaziada (fora da preparação) | Preservado | Desfazer um commit para editar antes de preparar |
--hard | Sim | Esvaziado | Mudanças rastreadas PERDIDAS (não rastreadas mantidas) | Descartar completamente o commit e todas as mudanças |
Reset, revert ou checkout
Escolher o comando de desfazer certo para a sua situação
| Comando | Modifica o histórico? | Seguro para commits enviados? | Ideal para |
|---|---|---|---|
git reset | Sim (move a branch) | Não - causa problemas | Desfazer commits locais ainda não enviados |
git revert | Não (adiciona um commit novo) | Sim - seguro em branches compartilhadas | Desfazer commits enviados em branches compartilhadas |
git checkout -- file | No | Sim | Descartar mudanças sem commit de arquivos específicos |
Tirar mudanças da preparação: reset ou restore
O Git moderno oferece o git restore como alternativa ao reset para tirar da preparação
| Comando | Propósito | Afeta commits? | Recomendado |
|---|---|---|---|
git reset HEAD file | Tirar um arquivo da preparação | No | Jeito tradicional |
git restore --staged file | Tirar um arquivo da preparação | No | Jeito moderno (Git 2.23+) |
git reset | Tirar todos os arquivos da preparação | No | Funciona bem |
git restore --staged . | Tirar todos os arquivos da preparação | No | Intenção mais explícita |
Modos de reset avançados
Quando usar --merge e --keep em vez de --hard
| Comando | Mantém as mudanças locais? | Para se houver conflito? | Caso de uso |
|---|---|---|---|
--hard | Não - destrói tudo | No | Reset completo, as mudanças não importam |
--merge | Só as mudanças não preparadas (as preparadas se perdem) | Sim | Cancelar um merge mantendo o trabalho sem relação |
--keep | Sim (nos arquivos que o reset não toca) | Sim | Desfazer um commit, mas manter edições locais sem relação |
Exemplos
Desfazer o último commit, mantendo as mudanças preparadas
git reset --soft HEAD~1Volta o HEAD um commit. As mudanças daquele commit continuam preparadas, prontas para um novo commit.
Desfazer o último commit, mantendo as mudanças no diretório de trabalho
git reset HEAD~1Modo padrão (--mixed). Volta o HEAD um commit. As mudanças são mantidas, mas fora da preparação.
Descartar completamente o último commit e as mudanças
git reset --hard HEAD~1Volta o HEAD um commit e descarta TODAS as mudanças sem commit em arquivos rastreados. Use com cuidado - o trabalho sem commit perdido não pode ser recuperado (o commit em si continua no git reflog).
Tirar um arquivo específico da preparação
git reset HEAD file.jsRemove file.js da área de preparação sem descartar as mudanças. O arquivo continua modificado no diretório de trabalho.
Exemplos de fluxos de trabalho
git log --oneline -3Ver os commits recentes para confirmar qual desfazer
git reset --soft HEAD~1Desfazer o commit, mantendo as mudanças preparadas
git statusConferir que as mudanças estão preparadas
git commit -m "Better message"Fazer o commit de novo com uma mensagem nova
git statusVer o que está preparado agora
git reset HEAD unwanted-file.jsTirar um ou mais arquivos da preparação
git statusConfirmar que o arquivo saiu da preparação
git commitFazer commit só dos arquivos que você quer
git statusRevisar o que vai ser perdido
git stash(Opcional) Guardar as mudanças por garantia
git reset --hard HEAD~1Descartar o último commit E todas as mudanças rastreadas (arquivos não rastreados ficam)
git statusConfirmar que o diretório de trabalho está limpo
git reflogAchar o hash do commit anterior ao reset
git reset --hard abc1234Voltar para aquele hash de commit
git log --onelineConferir que os commits foram restaurados
Solução de problemas
fatal: ambiguous argument 'HEAD~1': unknown revision
Causa: Você está no commit inicial - não há commit anterior para onde voltar
Confira o git log. Se só existe um commit, não dá para voltar mais. Use git update-ref -d HEAD para recomeçar do zero.
error: Entry 'app.js' not uptodate. Cannot merge.
Causa: Um arquivo que o --merge ou o --keep precisaria mudar tem edições locais sem commit, então o reset é cancelado
Faça commit ou stash das mudanças antes, ou use --hard se quiser descartá-las
Usei --hard sem querer e perdi mudanças
Causa: O reset hard descarta para sempre todo o trabalho sem commit
Se as mudanças tinham ido para um commit: git reflog para achar o commit, depois git reset --hard <hash>. Se nunca foram para um commit: provavelmente se perderam para sempre.
O reset funcionou, mas um colega diz que o histórico quebrou
Causa: Você deu reset em commits que já estavam em uma branch compartilhada
Em branches compartilhadas, use git revert. Para consertar: git reflog, ache o commit original, dê reset para ele e faça force push (combinando com a equipe).
fatal: ambiguous argument 'HEAD' ao rodar git reset HEAD file
Causa: O repositório ainda não tem commits, então o HEAD não existe (ou o caminho está errado)
Tire o HEAD: git reset file.js (ou git rm --cached file.js). Confira o caminho exato com git status.
Dicas e armadilhas
Boas práticas
- ✓Nunca use git reset em commits enviados para branches compartilhadas - use git revert
- ✓Sempre rode git status antes do reset para entender o que será afetado
- ✓Prefira --soft quando quiser refazer um commit com mudanças
- ✓Use git stash antes de um reset --hard se talvez queira as mudanças de volta
- ✓Lembre que o git reflog recupera a maioria dos erros de reset (para mudanças com commit)
- ✓Use git reset HEAD <file> para tirar arquivos da preparação em vez de dar reset em commits inteiros
- ✓Considere git restore --staged como alternativa moderna para tirar arquivos da preparação
Erros comuns
- ✗Usar 'git reset --hard' sem perceber que ele apaga para sempre as mudanças sem commit. Sempre confira duas vezes antes de usar --hard.
- ✗Dar reset em commits já enviados para um remoto compartilhado. Isso reescreve o histórico e causa problemas para quem colabora. Use 'git revert'.
- ✗Confundir 'git reset' com 'git revert'. O reset move o ponteiro da branch para trás; o revert cria um commit novo que desfaz as mudanças.
- ✗Não saber que dá para se recuperar de um reset ruim usando 'git reflog' para achar o hash do commit anterior.
- ✗Usar 'git reset --hard HEAD' para descartar mudanças de trabalho quando 'git checkout -- .' ou 'git restore .' seriam mais adequados.
Perguntas frequentes
O soft mantém as mudanças preparadas. O mixed (padrão) mantém as mudanças no diretório de trabalho, mas fora da preparação. O hard descarta tudo. Pense assim: soft = desfazer só o commit, mixed = desfazer o commit + tirar da preparação, hard = desfazer tudo.
Para mudanças com commit: sim, use git reflog para achar o hash do commit original e dê reset para ele. Para mudanças sem commit perdidas com --hard: em geral não, elas se foram para sempre.
HEAD~1 significa “um commit antes do HEAD”. HEAD~2 é dois commits para trás, e assim por diante. Você também pode usar hashes de commit diretamente, como git reset abc1234.
Reset para commits locais ainda não enviados - ele reescreve o histórico de forma limpa. Revert para commits enviados em branches compartilhadas - ele cria um commit novo que desfaz as mudanças sem reescrever o histórico.
Use git reset HEAD filename para tirar um arquivo específico da preparação. Isso não move o HEAD nem afeta nenhum commit, só remove o arquivo da área de preparação.
Ele apaga para sempre as mudanças sem commit. Ao contrário das mudanças com commit (recuperáveis pelo reflog), trabalho que nunca foi para um commit não pode ser recuperado depois de um reset hard.