¿Cómo puedo deshacer git reset --hard HEAD ~ 1?

1159

¿Es posible deshacer los cambios causados ​​por el siguiente comando? ¿Si es así, cómo?

git reset --hard HEAD~1
Paul Wicks
fuente
8
He escrito una guía completa para recuperar cualquier commit perdido con git. Incluso tiene ilustraciones :-) [Check it out] [fixLink] [fixLink]: programblings.com/2008/06/07/...
WebMAT
12
--harddescarta los cambios no confirmados. Dado que estos no son rastreados por git, no hay forma de restaurarlos a través de git.
Zaz
1
Este es un gran artículo para ayudarlo a recuperar sus archivos.
Jan Swart
2
Este es un gran recurso directamente de Github: Cómo deshacer (casi) cualquier cosa con Git
jasonleonhard

Respuestas:

1774

Pat Notz está en lo correcto. Puede recuperar la confirmación siempre que haya sido dentro de unos días. git solo se recolecta basura después de aproximadamente un mes más o menos a menos que explícitamente le diga que elimine las gotas más nuevas.

$ 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

Puede ver en el ejemplo que el archivo2 se eliminó como resultado del restablecimiento completo, pero se volvió a colocar cuando restablecí a través del registro.

Brian Riehman
fuente
222
Puede usar "git reset --hard HEAD @ {1}", no es necesario usar SHA1. En la mayoría de los casos, debería ser suficiente usar "git reset --hard ORIG_HEAD".
Jakub Narębski
39
git log -gpuede ser una forma un poco más agradable de ver el reflog que git reflog.
Dan Molding
6060
Hay una advertencia muy importante con esto ... y esa es la parte "- dura". --hard vuela tus cambios locales no comprometidos. Y no puede recuperarlos de esta manera (ya que no se han comprometido en ningún lado). Creo que no hay nada que puedas hacer al respecto :(
Michael Anderson
3
^ Para que sepas que puedes esconder tus cambios locales antes de hacer un reinicio --duro, ¡y luego simplemente revísalos y no perderás nada! Tengo que amar a Git.
Lethjakman
55
Yo prefiero git reflogmás git log -gsimplemente porque se obtiene toda la información en una línea con sha1, información HEAD y los mensajes de confirmación todos alineados. Mucho más fácil de leer.
Snowcrash
371

Lo que desea hacer es especificar el sha1 de la confirmación a la que desea restaurar. Puede obtener el sha1 examinando el reflog ( git reflog) y luego haciendo

git reset --hard <sha1 of desired commit>

Pero no espere demasiado ... después de unas pocas semanas, git eventualmente verá ese commit como sin referencia y eliminará todos los blobs.

Pat Notz
fuente
2
advertencia para mí hace unos minutos: esto restablecerá todas las modificaciones. sin embargo, no tocará los archivos no rastreados.
aexl
175

La respuesta está oculta en la respuesta detallada anterior, simplemente puede hacer:

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

(Ver la salida de git reflog show )

markmc
fuente
17
Tenga en cuenta que esta no es la solución si ha realizado otros cambios de repositorio desde el reinicio. Definitivamente eche un vistazo al reflog antes de ejecutar cualquier cosa.
forresthopkinsa
2
Accidentalmente restablecí mi repositorio, pensé que mi trabajo se había perdido para siempre. Esta respuesta me salvó el día.
Zaiyang Li
woh! realmente me salvas el día =)
jfcogato
Excelente. Realmente ayudó.
Prashant Biradar
117

Es posible recuperarlo si Git aún no ha recolectado basura.

Obtenga una descripción general de los compromisos pendientes con fsck:

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

Recupere el commit colgante con rebase:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf
sverrejoh
fuente
1
Eso es algo increíble. La vida salvada.
Mohhamad Hasham
Puede encontrar una explicación detallada aquí: medium.com/@CarrieGuss/… . Cosas que salvan vidas.
tuan.dinh
49

Si tienes suerte, como yo, puedes volver a tu editor de texto y presionar 'deshacer'.

Sé que esa no es realmente una respuesta adecuada, pero me ahorró medio día de trabajo, ¡así que espero que haga lo mismo por otra persona!

Chris
fuente
3
Este es realmente un muy buen consejo, me salvó muchas veces;) Y es mucho más simple que hacer cualquier cosa en git ...
severin
3
y gracias por toda la bondad de este mundo entero. gracias. gracias. gracias.
Viaje el
99
Esta es la única forma de recuperar cambios no escalonados en los archivos después del restablecimiento completo. También me salvó;)
Czarek Tomczak
44
Como pista adicional, algunos IDE como eclipse también tienen guardado el historial de archivos reciente. De esa manera, incluso podría recuperar los cambios anteriores después de cerrar el editor. Eso funcionó de maravilla para mí.
Martin
1
Dios te bendiga Chris
Adam Waite
45

Que yo sepa, --harddescartará los cambios no confirmados. Dado que estos no son rastreados por git. pero puedes deshacer el discarded commit.

$ git reflog

listas de voluntad:

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

donde 4bac331está el discarded commit.

Ahora solo mueve la cabeza a ese commit ::

$ git reset --hard 4bac331
suhailvs
fuente
34

Ejemplo de caso 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 <[email protected]>
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.
Stian Høiland
fuente
2
¡Una gota colgando suena como un monstruo de AD&D!
sbichenko
Gracias @Stian Bien explicado! Quisiera agregar a otros que encuentren esta respuesta que si tiene más de un compromiso "pendiente", no es seguro que quiera hacer una nueva base en la última fila :)
JimiSweden
git show guardó algunos de mis archivos, muchas gracias amigo!
William
32

En la mayoría de los casos, sí.

Dependiendo del estado en que se encontraba su repositorio cuando ejecutó el comando, los efectos git reset --hardpueden variar desde triviales hasta deshacer, hasta básicamente imposibles.

A continuación, he enumerado una serie de diferentes escenarios posibles y cómo puede recuperarse de ellos.

¡Todos mis cambios fueron confirmados, pero ahora los compromisos se han ido!

Esta situación generalmente ocurre cuando se ejecuta git resetcon un argumento, como en git reset --hard HEAD~. ¡No se preocupe, esto es fácil de recuperar!

Si acabas de correr git resety no has hecho nada más desde entonces, puedes volver a donde estabas con esta frase:

git reset --hard @{1}

Esto restablece su rama actual en cualquier estado en el que se encontraba antes de la última vez que se modificó (en su caso, la modificación más reciente a la rama sería el restablecimiento completo que está intentando deshacer).

Sin embargo, si ha realizado otras modificaciones en su rama desde el reinicio, el unicelino anterior no funcionará. En su lugar, debe ejecutar para ver una lista de todos los cambios recientes realizados en su rama (incluidos los reinicios). Esa lista se verá más o menos así: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

Encuentre la operación en esta lista que desea "deshacer". En el ejemplo anterior, sería la primera línea, la que dice "reset: mover a HEAD ~". Luego copie la representación del commit antes (abajo) de esa operación. En nuestro caso, sería master@{1}(o 3ae5027ambos representan la misma confirmación) y se ejecutará git reset --hard <commit>para restablecer su rama actual a esa confirmación.

Organicé mis cambios con git add, pero nunca me comprometí. ¡Ahora mis cambios se han ido!

Esto es un poco más difícil de recuperar. Git hace tener copias de los archivos que ha añadido, pero dado que estos ejemplares no estaban atados a ninguna confirmación en concreto no se puede restaurar los cambios que todos a la vez. En su lugar, debe ubicar los archivos individuales en la base de datos de git y restaurarlos manualmente. Puedes hacer esto usando git fsck.

Para obtener detalles sobre esto, consulte Deshacer git reset: duro con archivos no confirmados en el área de preparación .

Tuve cambios en los archivos de mi directorio de trabajo con los que nunca me preparé git addy nunca los comprometí. ¡Ahora mis cambios se han ido!

UH oh. Odio decirte esto, pero probablemente no tengas suerte. git no almacena cambios que no agrega o confirma, y ​​de acuerdo con la documentación paragit reset :

--difícil

Restablece el índice y el árbol de trabajo. Cualquier cambio en los archivos rastreados en el árbol de trabajo desde entonces <commit>se descarta.

Es posible que pueda recuperar sus cambios con algún tipo de utilidad de recuperación de disco o con un servicio profesional de recuperación de datos, pero en este punto eso probablemente sea más problema de lo que vale.

Ajedi32
fuente
1
One-liner funcionó para mí, gracias, pero me pregunto qué hace exactamente eso "@ {1}" ...
Stan Bashtavenko
1
@StanB La documentación está aquí: git-scm.com/docs/git-rev-parse básicamente se refiere a la primera entrada de registro en la rama actual.
Ajedi32
Gracias por cubrir todos los casos. No había cometido ni agregado el mío.
xdhmoore
21

Si aún no ha recogido basura en su repositorio (por ejemplo, usando git repack -do git gc, pero tenga en cuenta que la recolección de basura también puede ocurrir automáticamente), entonces su confirmación todavía está allí, ya no es accesible a través de HEAD.

Puede intentar encontrar su confirmación mirando a través de la salida de git fsck --lost-found.

Las versiones más nuevas de Git tienen algo llamado "reflog", que es un registro de todos los cambios que se realizan en las referencias (a diferencia de los cambios que se realizan en el contenido del repositorio). Entonces, por ejemplo, cada vez que cambie su CABEZA (es decir, cada vez que haga un git checkoutcambio de rama) se registrará. Y, por supuesto, git resettambién manipulaste la CABEZA, por lo que también se registró. Puede acceder a los estados más antiguos de sus referencias de una manera similar a la que puede acceder a los estados más antiguos de su repositorio, utilizando un @signo en lugar de un ~, como git reset HEAD@{1}.

Me tomó un tiempo entender cuál es la diferencia entre HEAD @ {1} y HEAD ~ 1, así que aquí hay una pequeña explicación:

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

Entonces, HEAD~1significa "ir al commit antes del commit al que HEAD apunta actualmente", mientras que HEAD@{1}significa "ir al commit que HEAD señaló antes de apuntar a donde apunta actualmente".

Eso te permitirá encontrar fácilmente tu commit perdido y recuperarlo.

Jörg W Mittag
fuente
2
Otra explicación que creo que sería más clara: HEAD ~ 1 significa ir a "padre de HEAD", mientras que HEAD @ {1} ir a "retroceder un paso en la historia de HEAD"
kizzx2
1
El problema es que el término "historial" está realmente sobrecargado en VCS. Otra forma de expresarse es que ~ retrocede en la historia de confirmación , mientras que @ retrocede en la historia cronológica o temporal . Pero ninguna de las tres versiones es particularmente buena.
Jörg W Mittag el
@ kizzx2 (y Jorg) en realidad esas 3 explicaciones, cuando se toman juntas, ayudan mucho - gracias
Richard Le Mesurier
14

Antes de responder, agreguemos algunos antecedentes, explicando qué es esto HEAD.

First of all what is HEAD?

HEADes simplemente una referencia a la confirmación actual (más reciente) en la rama actual.
Solo puede haber una sola HEADen un momento dado. (excluyendo git worktree)

El contenido de HEADse almacena dentro .git/HEADy contiene los 40 bytes SHA-1 de la confirmación actual.


detached HEAD

Si no está en la última confirmación, lo que significa que HEADapunta a una confirmación previa en la historia se llama detached HEAD.

ingrese la descripción de la imagen aquí

En la línea de comando se verá así: SHA-1 en lugar del nombre de la rama ya HEADque no apunta a la punta de la rama actual

ingrese la descripción de la imagen aquí


Algunas opciones sobre cómo recuperarse de un HEAD separado:


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

Esto verificará que la nueva rama apunte a la confirmación deseada.
Este comando pagará a un commit dado.
En este punto, puede crear una rama y comenzar a trabajar a partir de este momento.

# 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

Siempre puedes usar el reflogtambién.
git reflogmostrará cualquier cambio que haya actualizado HEADy revisando la entrada de reflog deseada establecerá el HEADrespaldo de este commit.

Cada vez que se modifique el HEAD habrá una nueva entrada en el reflog

git reflog
git checkout HEAD@{...}

Esto lo llevará de regreso a su compromiso deseado

ingrese la descripción de la imagen aquí


git reset HEAD --hard <commit_id>

"Mueva" su cabeza hacia atrás al compromiso deseado.

# 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 Git 2.7 )
    también puedes usar el git rebase --no-autostash.


git revert <sha-1>

"Deshacer" el compromiso o rango de compromiso dado.
El comando reset "deshacerá" cualquier cambio realizado en el commit dado.
Se confirmará una nueva confirmación con el parche de deshacer, mientras que la confirmación original también permanecerá en el historial.

# 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 qué comando hace qué.
Como puede ver allí reset && checkoutmodifique el HEAD.

ingrese la descripción de la imagen aquí

CodeWizard
fuente
Parece que su git reset HEAD --hard <commit_id>ejemplo está tomado de stackoverflow.com/questions/4114095/… - Si ese es el caso, ¿podría editarlo en la atribución?
Rob
12

git reflog

  • Encuentre su commit sha en la lista, luego cópielo y péguelo en este comando:

git cherry-pick <the sha>

ScottyBlades
fuente
1
git-cherry-pick - Aplica los cambios introducidos por algunos commits existentes. Creo que es simple y muy útil en este contexto
Amitesh
1
todo el mundo realmente está buscando esto cuando buscan cómo deshacer los cambios de restablecimiento completo. esta respuesta debería recibir más
votos
@ErenL Supongo que me imaginé que a la gente le gusta hacer un trabajo extra. jaja
ScottyBlades
1
Esto es todo, acabas de salvar mi día 🙂
Sylvernus Akubo
11

Sé que este es un hilo viejo ... pero como muchas personas están buscando formas de deshacer cosas en Git, sigo pensando que puede ser una buena idea continuar dando consejos aquí.

Cuando haces un "git add" o mueves algo desde la parte superior izquierda a la parte inferior izquierda en git gui, el contenido del archivo se almacena en un blob y el contenido del archivo es posible recuperar de ese blob.

Por lo tanto, es posible recuperar un archivo incluso si no se confirmó pero debe haberse agregado.

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

Ahora se crea el blob pero el índice hace referencia a él, por lo que no aparecerá en la lista con git fsck hasta que lo reiniciemos. Entonces reiniciamos ...

git reset --hard  
git fsck  

obtendrá un blob colgante ce013625030ba8dba906f756967f9e9ca394464a

git show ce01362  

le devolverá el contenido del archivo "hola"

Para encontrar confirmaciones no referenciadas, encontré un consejo en algún lugar que sugiere esto.

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

Lo tengo como herramienta en git gui y es muy útil.

martín
fuente
+1. Como se menciona en stackoverflow.com/a/21350689/6309 , git fsck --lost-foundpuede ayudar.
VonC
7

Si está utilizando un IDE de JetBrains (cualquier cosa basada en IntelliJ), puede recuperar incluso sus cambios no confirmados a través de su función "Historial local".

Haga clic derecho en su directorio de nivel superior en su árbol de archivos, busque "Historial local" en el menú contextual y elija "Mostrar historial". Esto abrirá una vista donde se pueden encontrar sus ediciones recientes, y una vez que haya encontrado la revisión a la que desea volver, haga clic derecho sobre ella y haga clic en "Revertir".

Newbyte
fuente
3

Hice un pequeño script para que sea un poco más fácil encontrar el commit que está buscando:

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

Sí, se puede hacer mucho más bonito con awk o algo así, pero es simple y solo lo necesitaba. Podría salvar a otra persona 30 segundos.

neurona
fuente
0

Mi problema es casi similar. Tengo archivos no confirmados antes de ingresar git reset --hard.

Agradecidamente. Logré omitir todos estos recursos. Después me di cuenta de que solo puedo deshacer ( ctrl-z). 😊 Solo quiero agregar esto a todas las respuestas anteriores.

Nota. No es posible ctrl-zabrir archivos sin abrir.

Vadamadafaka
fuente
0

Esto me ha salvado la vida: https://medium.com/@CarrieGuss/how-to-recover-from-a-git-hard-reset-b830b5e3f60c

Básicamente necesitas ejecutar:

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

Luego, manualmente pasando por el dolor para reorganizar sus archivos a la estructura correcta.

Para llevar: nunca lo use git reset --hardsi no comprende completamente al 100% cómo funciona, mejor no usarlo.

tuan.dinh
fuente