Como posso desfazer o git reset --hard HEAD ~ 1?


1159

É possível desfazer as alterações causadas pelo seguinte comando? Se sim, como?

git reset --hard HEAD~1

8
Eu escrevi um guia completo para recuperar qualquer commit perdido com o git. Ele ainda tem ilustrações :-) [Check it out] [fixLink] [fixLink]: programblings.com/2008/06/07/...
webmat

12
--harddescarta alterações não confirmadas. Como eles não são rastreados pelo git, não há como restaurá-los através do git.
Zaz


1
Este é um ótimo artigo para ajudá-lo a recuperar seus arquivos.
Jan Swart

2
Este é um grande recurso em linha reta do Github: Como desfazer (quase) qualquer coisa com Git
jasonleonhard

Respostas:


1774

Pat Notz está correto. Você pode recuperar o commit desde que dentro de alguns dias. O lixo do git só é coletado após cerca de um mês, a menos que você diga explicitamente para remover os blobs mais recentes.

$ git init
Initialized empty Git repository in .git/

$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file1

$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file2

$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1

$ cat file2
cat: file2: No such file or directory

$ git reflog
1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@{1}: commit: added file2

$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2

$ cat file2
added new file

Você pode ver no exemplo que o arquivo2 foi removido como resultado da reinicialização completa, mas foi colocado novamente no local quando redefinei por meio do reflog.


222
Você pode usar "git reset --hard HEAD @ {1}", sem necessidade de usar SHA1. Na maioria dos casos, deve ser suficiente usar "git reset --hard ORIG_HEAD".
Jakub Narębski

39
git log -gpode ser uma maneira um pouco melhor de ver o reflog do que git reflog.
Dan Molding

60
Há uma ressalva muito importante com isso ... e essa é a parte "--hard". --hard afasta suas alterações locais não confirmadas. E você não pode recuperá-los assim (pois eles não foram cometidos em lugar algum). Eu acredito que não há nada que você possa fazer sobre isso :(
Michael Anderson

3
^ Só para você saber que pode ocultar suas alterações locais antes de fazer uma redefinição - difícil, e depois basta abri-las e você não perde nada! Tenho que amar idiota.
Lethjakman

5
Eu prefiro git reflogmais git log -gsimplesmente porque você obter todas as informações em uma linha com sha1, info HEAD e as mensagens de todos alinhados. Muito mais fácil de ler.
Snowcrash 11/11/11

371

O que você deseja fazer é especificar o sha1 da confirmação para a qual você deseja restaurar. Você pode obter o sha1 examinando o reflog ( git reflog) e fazendo

git reset --hard <sha1 of desired commit>

Mas não espere muito ... depois de algumas semanas, o git acabará vendo esse commit como não referenciado e excluirá todos os blobs.


2
aviso para mim mesmo alguns minutos atrás: isso redefinirá todas as modificações. embora não toque nos arquivos não rastreados.
aexl

175

A resposta está oculta na resposta detalhada acima, você pode simplesmente fazer:

$> git reset --hard HEAD@{1}

(Veja a saída do git reflog show )


17
Observe que essa não é a solução se você tiver feito outras alterações de repo desde a redefinição. Definitivamente, dê uma olhada no reflog antes de executar qualquer coisa.
forresthopkinsa

2
Redefinir acidentalmente meu repositório, pensei que meu trabalho estivesse perdido para sempre. Esta resposta salvou meu dia.
Zaiyang Li

uau! você realmente me salvar o dia =)
jfcogato

Ótimo. Isso realmente ajudou.
Prashant Biradar

117

É possível recuperá-lo se o Git ainda não tiver coletado lixo.

Obtenha uma visão geral dos commits dangling com fsck:

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

Recupere o commit dangling com rebase:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

1
Isso é incrível. Vida salva.
Mohhamad Hasham 30/08/19

Uma explicação detalhada pode ser encontrada aqui: medium.com/@CarrieGuss/… . Coisas que salvam vidas.
tuan.dinh 20/03

49

Se você tiver muita sorte, como eu, você pode voltar ao seu editor de texto e clicar em 'desfazer'.

Sei que essa não é realmente uma resposta adequada, mas me salvou meio dia de trabalho, então espero que faça o mesmo com outra pessoa!


3
Esta é realmente uma dica muito boa, me salvou um monte de vezes;) e seu modo mais simples do que fazer qualquer coisa no git ...
Severin

3
e obrigado de toda bondade graciosa neste mundo inteiro. obrigado. obrigado. obrigado.
Viagem

9
Essa é a única maneira de recuperar alterações nos estágios dos arquivos após a reinicialização total. Salvei-me também;)
Czarek Tomczak

4
Como uma dica adicional, alguns IDEs como eclipse também têm o histórico recente de arquivos salvos. Dessa forma, você poderá recuperar alterações mais antigas após o fechamento do editor. Isso fez maravilhas para mim.
martin

1
Deus te abençoe Chris
Adam Waite

45

até onde eu sei, --harddescartará alterações não confirmadas. Como esses não são rastreados pelo git. mas você pode desfazer o discarded commit.

$ git reflog

lista:

b0d059c HEAD@{0}: reset: moving to HEAD~1
4bac331 HEAD@{1}: commit: added level introduction....
....

onde 4bac331representa a discarded commit.

Agora basta mover a cabeça para esse commit ::

$ git reset --hard 4bac331

34

Exemplo de caso de IRL:

$ git fsck --lost-found

Checking object directories: 100% (256/256), done.
Checking objects: 100% (3/3), done.
dangling blob 025cab9725ccc00fbd7202da543f556c146cb119
dangling blob 84e9af799c2f5f08fb50874e5be7fb5cb7aa7c1b
dangling blob 85f4d1a289e094012819d9732f017c7805ee85b4
dangling blob 8f654d1cd425da7389d12c17dd2d88d318496d98
dangling blob 9183b84bbd292dcc238ca546dab896e073432933
dangling blob 1448ee51d0ea16f259371b32a557b60f908d15ee
dangling blob 95372cef6148d980ab1d7539ee6fbb44f5e87e22
dangling blob 9b3bf9fb1ee82c6d6d5ec9149e38fe53d4151fbd
dangling blob 2b21002ca449a9e30dbb87e535fbd4e65bac18f7
dangling blob 2fff2f8e4ea6408ac84a8560477aa00583002e66
dangling blob 333e76340b59a944456b4befd0e007c2e23ab37b
dangling blob b87163c8def315d40721e592f15c2192a33816bb
dangling blob c22aafb90358f6bf22577d1ae077ad89d9eea0a7
dangling blob c6ef78dd64c886e9c9895e2fc4556e69e4fbb133
dangling blob 4a71f9ff8262701171d42559a283c751fea6a201
dangling blob 6b762d368f44ddd441e5b8eae6a7b611335b49a2
dangling blob 724d23914b48443b19eada79c3eb1813c3c67fed
dangling blob 749ffc9a412e7584245af5106e78167b9480a27b
dangling commit f6ce1a403399772d4146d306d5763f3f5715cb5a    <- it's this one

$ git show f6ce1a403399772d4146d306d5763f3f5715cb5a

commit f6ce1a403399772d4146d306d5763f3f5715cb5a
Author: Stian Gudmundsen Høiland <stian@Stians-Mac-mini.local>
Date:   Wed Aug 15 08:41:30 2012 +0200

    *MY COMMIT MESSAGE IS DISPLAYED HERE*

diff --git a/Some.file b/Some.file
new file mode 100644
index 0000000..15baeba
--- /dev/null
+++ b/Some.file
*THE WHOLE COMMIT IS DISPLAYED HERE*

$ git rebase f6ce1a403399772d4146d306d5763f3f5715cb5a

First, rewinding head to replay your work on top of it...
Fast-forwarded master to f6ce1a403399772d4146d306d5763f3f5715cb5a.

2
Dangling blob soa como um monstro do AD&D!
sbichenko

Obrigado @Stian Bem explicado! Gostaria de acrescentar para outras pessoas que encontrarem esta resposta que, se você tiver mais de um commit "dangling", não é certo que você deseja fazer uma nova recuperação na última linha :) #
1013 JimiSweden

git show salvou alguns dos meus arquivos, muito obrigado cara!
William

32

Na maioria dos casos, sim.

Dependendo do estado em que seu repositório estava quando você executou o comando, os efeitos de git reset --hardpodem variar de trivial a desfazer, a basicamente impossível.

Abaixo, listei uma variedade de diferentes cenários possíveis e como você pode se recuperar deles.

Todas as minhas alterações foram confirmadas, mas agora as confirmações acabaram!

Essa situação geralmente ocorre quando você executa git resetum argumento, como em git reset --hard HEAD~. Não se preocupe, é fácil recuperar isso!

Se você acabou de executar git resete não fez mais nada desde então, pode voltar para onde estava com este recurso:

git reset --hard @{1}

Isso redefine sua ramificação atual em qualquer estado em que estava antes da última vez em que foi modificada (no seu caso, a modificação mais recente na ramificação seria a redefinição definitiva que você está tentando desfazer).

Se, no entanto, você tiver feito outras modificações em sua ramificação desde a redefinição, o one-liner acima não funcionará. Em vez disso, você deve executar para ver uma lista de todas as alterações recentes feitas em sua ramificação (incluindo redefinições). Essa lista será mais ou menos assim:git reflog <branchname>

7c169bd master@{0}: reset: moving to HEAD~
3ae5027 master@{1}: commit: Changed file2
7c169bd master@{2}: commit: Some change
5eb37ca master@{3}: commit (initial): Initial commit

Encontre a operação nesta lista que deseja "desfazer". No exemplo acima, seria a primeira linha, a que diz "redefinir: movendo para HEAD ~". Em seguida, copie a representação da confirmação antes (abaixo) dessa operação. No nosso caso, isso seria master@{1}(ou 3ae5027, ambos representam o mesmo commit) e executado git reset --hard <commit>para redefinir sua ramificação atual de volta para esse commit.

Organizei minhas alterações com git add, mas nunca me comprometi. Agora minhas mudanças se foram!

Isso é um pouco mais difícil de recuperar. git faz tem cópias dos arquivos que você adicionou, mas desde que essas cópias não estavam vinculados a qualquer particular, comprometer você não pode restaurar as alterações de uma só vez. Em vez disso, você precisa localizar os arquivos individuais no banco de dados do git e restaurá-los manualmente. Você pode fazer isso usando git fsck.

Para obter detalhes sobre isso, consulte Desfazer git reset --hard com arquivos não confirmados na área de preparação .

Tive alterações nos arquivos do meu diretório de trabalho com os quais nunca estageei git adde nunca confirmei. Agora minhas mudanças se foram!

Ah, oh. Eu odeio dizer isso, mas você provavelmente está sem sorte. O git não armazena alterações que você não adiciona ou confirma, e de acordo com a documentação paragit reset :

--Difícil

Redefine o índice e a árvore de trabalho. Quaisquer alterações nos arquivos rastreados na árvore de trabalho desde então <commit>são descartadas.

É possível que você pode ser capaz de recuperar as suas alterações com algum tipo de utilitário de recuperação de disco ou um serviço de recuperação de dados profissional, mas neste momento isso é provavelmente mais problemas do que vale a pena.


1
One-liner trabalhou para mim, obrigado, mas estou me perguntando o que exatamente isso "@ {1}" faz ..
Stan Bashtavenko

1
@StanB A documentação está aqui: git-scm.com/docs/git-rev-parse basicamente se refere à primeira entrada de reflog na ramificação atual.
Ajedi32

Obrigado por cobrir todos os casos. Eu não tinha cometido ou adicionado o meu.
Xdhmoore 28/02

21

Se você ainda não coletou lixo no seu repositório (por exemplo, usando git repack -dor git gc, mas observe que a coleta de lixo também pode ocorrer automaticamente), seu commit ainda está lá - ele não está mais acessível através do HEAD.

Você pode tentar encontrar seu commit olhando através da saída de git fsck --lost-found .

As versões mais recentes do Git têm algo chamado "reflog", que é um log de todas as alterações feitas nas refs (em oposição às alterações feitas no conteúdo do repositório). Então, por exemplo, toda vez que você alternar seu HEAD (ou seja, toda vez que você git checkoutalternar ramos), isso será registrado. E, é claro, você git resettambém manipulou o HEAD, por isso também foi registrado. Você pode acessar estados mais antigos de suas referências de maneira semelhante a acessar estados mais antigos de seu repositório, usando um @sinal em vez de um ~, comogit reset HEAD@{1} .

Demorei um pouco para entender qual a diferença entre HEAD @ {1} e HEAD ~ 1, então aqui está uma pequena explicação:

git init
git commit --allow-empty -mOne
git commit --allow-empty -mTwo
git checkout -b anotherbranch
git commit --allow-empty -mThree
git checkout master # This changes the HEAD, but not the repository contents
git show HEAD~1 # => One
git show HEAD@{1} # => Three
git reflog

Então, HEAD~1significa "ir para o commit antes do commit que o HEAD aponta atualmente", enquantoHEAD@{1} significa "ir para o commit que o HEAD apontou antes de apontar para onde aponta atualmente".

Isso permitirá que você encontre com facilidade o seu commit perdido e o recupere.


2
Outra explicação que eu acho que seria mais claro: Cabeça ~ 1 meios ir ao "pai da cabeça", enquanto CABEÇA @ {1} ir para "retroceder um passo na história da CABEÇA"
kizzx2

1
O problema é que o termo "histórico" está realmente sobrecarregado nos VCS. Ainda outra maneira de expressar é que ~ retrocede na história de commit , enquanto @ retrocede na história cronológica ou temporal . Mas nenhuma das três versões é particularmente boa.
Jörg W Mittag

@ kizzx2 (e Jorg), na verdade, esses 3 explicações, quando tomados em conjunto, ajuda muito - THX
Richard Le Mesurier

14

Antes de responder, vamos adicionar alguns antecedentes, explicando o que é isso HEAD.

First of all what is HEAD?

HEADé simplesmente uma referência ao commit atual (mais recente) no branch atual.
Só pode haver um single HEADa qualquer momento. (excluindogit worktree )

O conteúdo de HEADé armazenado dentro .git/HEADe contém os 40 bytes SHA-1 da confirmação atual.


detached HEAD

Se você não está no último commit, o que significa que HEADestá apontando para um commit anterior no histórico detached HEAD.

insira a descrição da imagem aqui

Na linha de comando, será semelhante a este - SHA-1 em vez do nome da ramificação, pois HEADnão está apontando para a ponta da ramificação atual

insira a descrição da imagem aqui


Algumas opções sobre como recuperar de um HEAD desanexado:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Isso fará o checkout do novo ramo apontando para o commit desejado.
Este comando fará checkout para um determinado commit.
Nesse ponto, você pode criar uma ramificação e começar a trabalhar a partir deste ponto.

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Você sempre pode usar o reflogtambém.
git reflogexibirá qualquer alteração que tenha atualizado HEADe o check-out da entrada de reflog desejada HEADretornará a esta confirmação.

Sempre que o HEAD for modificado, haverá uma nova entrada no reflog

git reflog
git checkout HEAD@{...}

Isso o levará de volta ao seu commit desejado

insira a descrição da imagem aqui


git reset HEAD --hard <commit_id>

"Mova" sua cabeça para o commit desejado.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.
  • Nota: ( Desde o Git 2.7 ),
    você também pode usá-lo git rebase --no-autostash.


git revert <sha-1>

"Desfazer" o intervalo de confirmação ou confirmação fornecido.
O comando reset irá "desfazer" quaisquer alterações feitas no commit fornecido.
Um novo commit com o patch desfazer será confirmado enquanto o commit original também permanecerá no histórico.

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Este esquema ilustra qual comando faz o quê.
Como você pode ver lá, reset && checkoutmodifique o arquivo HEAD.

insira a descrição da imagem aqui


Parece que seu git reset HEAD --hard <commit_id>exemplo foi retirado de stackoverflow.com/questions/4114095/… - Se for esse o caso, você poderia editar na atribuição?
Rob

12

git reflog

  • Encontre seu commit sha na lista, copie e cole-o neste comando:

git cherry-pick <the sha>


1
git-cherry-pick - Aplique as alterações introduzidas por alguns commits existentes. Eu acho que é simples e muito útil neste contexto
Amitesh 12/02

1
todos estão realmente procurando por isso quando pesquisam como desfazer alterações de redefinição rígida. esta resposta deve ficar mais
positiva

@ErenL Acho que imaginei que as pessoas gostam de fazer um trabalho extra. haha
ScottyBlades 27/02

1
É isso aí, você acabou de salvar meu dia 🙂
Sylvernus Akubo 01/06

11

Sei que esse é um tópico antigo ... mas, como muitas pessoas estão procurando maneiras de desfazer coisas no Git, ainda acho que pode ser uma boa ideia continuar dando dicas aqui.

Quando você faz um "git add" ou move qualquer coisa do canto superior esquerdo para o canto inferior esquerdo no guit do git, o conteúdo do arquivo é armazenado em um blob e é possível recuperar o conteúdo do arquivo.

Portanto, é possível recuperar um arquivo, mesmo que não tenha sido confirmado, mas tenha que ter sido adicionado.

git init  
echo hello >> test.txt  
git add test.txt  

Agora, o blob é criado, mas é referenciado pelo índice, portanto não será listado com o git fsck até que seja redefinida. Então, redefinimos ...

git reset --hard  
git fsck  

você receberá um blob pendente ce013625030ba8dba906f756967f9e9ca394464a

git show ce01362  

retornará o conteúdo do arquivo "olá" de volta

Para encontrar commits não referenciados, encontrei uma dica em algum lugar sugerindo isso.

gitk --all $(git log -g --pretty=format:%h)  

Eu o tenho como uma ferramenta no git gui e é muito útil.


+1. Conforme mencionado em stackoverflow.com/a/21350689/6309 , git fsck --lost-foundpode ajudar.
VonC 27/01

7

Se você estiver usando um JetBrains IDE (qualquer coisa baseada no IntelliJ), poderá recuperar até as alterações não confirmadas através do recurso "Histórico local".

Clique com o botão direito do mouse no diretório de nível superior da árvore de arquivos, encontre "Local History" no menu de contexto e escolha "Show History". Isso abrirá uma visualização onde suas edições recentes podem ser encontradas e, depois de encontrar a revisão para a qual você deseja voltar, clique com o botão direito do mouse e clique em "Reverter".



3

Criou um script minúsculo para facilitar a localização do commit que você está procurando:

git fsck --lost-found | grep commit | cut -d ' ' -f 3 | xargs -i git show \{\} | egrep '^commit |Date:'

Sim, pode ser consideravelmente mais bonito com awk ou algo parecido, mas é simples e eu só precisava. Pode salvar alguém mais 30 segundos.


0

Meu problema é quase semelhante. Tenho arquivos não confirmados antes de entrar git reset --hard.

Agradecidamente. Consegui pular todos esses recursos. Depois notei que posso apenas desfazer ( ctrl-z). 😊 Eu só quero adicionar isso a todas as respostas acima.

Nota. Não é possível ctrl-zarquivos não abertos.


0

Isso salvou minha vida: https://medium.com/@CarrieGuss/how-to-recover-from-a-git-hard-reset-b830b5e3f60c

Basicamente, você precisa executar:

for blob in $(git fsck --lost-found | awk ‘$2 == “blob” { print $3 }’); do git cat-file -p $blob > $blob.txt; done

Em seguida, passe manualmente pelo esforço de reorganizar seus arquivos na estrutura correta.

Resumo: nunca use git reset --hardse você não entender completamente como funciona, é melhor não usá-lo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.