Deshacer git reset --hard con archivos no confirmados en el área de preparación
Estoy intentando recuperar mi trabajo. Lo hice estúpidamente git reset --hard
, pero antes de eso sólo lo hice get add .
y no lo hice git commit
. ¡Por favor ayuda! Aquí está mi registro:
MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# modified: .gitignore
...
MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api
¿Es posible deshacer git reset --hard
en esta situación?
Debería poder recuperar cualquier archivo que haya agregado al índice (por ejemplo, como en su situación, con git add .
), aunque podría requerir un poco de trabajo. Para agregar un archivo al índice, git lo agrega a la base de datos de objetos, lo que significa que se puede recuperar siempre que la recolección de basura aún no haya ocurrido. Hay un ejemplo de cómo hacer esto en la respuesta de Jakub Narębski aquí:
- Recuperando el archivo agregado después de hacer git reset --hard HEAD^
Sin embargo, lo probé en un repositorio de prueba y hubo un par de problemas: --cached
debería ser --cache
y descubrí que en realidad no creaba el .git/lost-found
directorio. Sin embargo, los siguientes pasos funcionaron para mí:
git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")
Eso debería generar todos los objetos en la base de datos de objetos a los que no puede acceder ninguna referencia, en el índice o mediante el reflog. La salida se verá así:
unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03
... y para cada uno de esos blobs, puedes hacer:
git show 907b308
Para generar el contenido del archivo.
¿Demasiada producción?
Actualizacion en respuesta al comentario de sehe a continuacion:
Si descubre que tiene muchas confirmaciones y árboles enumerados en la salida de ese comando, es posible que desee eliminar de la salida cualquier objeto al que se haga referencia desde confirmaciones no referenciadas. (Por lo general, puede volver a estas confirmaciones a través del reflog de todos modos; solo nos interesan los objetos que se agregaron al índice pero que nunca se pueden encontrar mediante una confirmación).
Primero, guarde el resultado del comando, con:
git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all
Ahora los nombres de los objetos de esas confirmaciones inalcanzables se pueden encontrar con:
egrep commit all | cut -d ' ' -f 3
Por lo tanto, puede encontrar solo los árboles y objetos que se agregaron al índice, pero que no se confirmaron en ningún momento, con:
git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
$(egrep commit all | cut -d ' ' -f 3)
Eso reduce enormemente la cantidad de objetos que tendrás que considerar.
Actualización: Philip Oakley a continuación sugiere otra forma de reducir la cantidad de objetos a considerar, que es considerar solo los archivos modificados más recientemente en .git/objects
. Puedes encontrarlos con:
find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort
(Encontré esa find
invocación aquí ). El final de esa lista podría verse así:
2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a
En cuyo caso puedes ver esos objetos con:
git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a
(Tenga en cuenta que debe eliminar /
al final de la ruta para obtener el nombre del objeto).
Acabo de hacer un git reset --hard
compromiso y perdí uno. Pero conocía el hash de confirmación, así que pude git cherry-pick COMMIT_HASH
restaurarlo.
Hice esto unos minutos después de perder el compromiso, por lo que puede funcionar para algunos de ustedes.