stash recuperar eliminado borrados borrado archivos archivo git recovery git-stash

recuperar - git stash pop



¿Cómo recuperar un alijo caído en Git? (19)

Con frecuencia utilizo git stash y git stash pop para guardar y restaurar cambios en mi árbol de trabajo. Ayer tuve algunos cambios en mi árbol de trabajo que había guardado y reventado, y luego hice más cambios en mi árbol de trabajo. Me gustaría volver atrás y revisar los cambios ocultos de ayer, pero parece que git stash pop elimina todas las referencias al compromiso asociado.

Sé que si uso git stash , .git / refs / stash contiene la referencia del commit usado para crear el stash. Y .git / logs / refs / stash contiene todo el alijo. Pero esas referencias se han ido después de git stash pop . Sé que el compromiso todavía está en mi repositorio en alguna parte, pero no sé qué fue.

¿Hay una manera fácil de recuperar la referencia de confirmación de almacenamiento de ayer?

Tenga en cuenta que esto no es crítico para mí hoy porque tengo copias de seguridad diarias y puedo volver al árbol de trabajo de ayer para obtener mis cambios. ¡Lo pregunto porque debe haber una manera más fácil!


¿Por qué la gente hace esta pregunta? Porque aún no conocen ni entienden el reflog.

La mayoría de las respuestas a esta pregunta dan comandos largos con opciones que casi nadie recordará. Así que la gente entra en esta pregunta y copia, pega lo que crea que necesita y lo olvida casi inmediatamente.

Aconsejaría a todos con esta pregunta que simplemente revisen el reflog (git reflog), no mucho más que eso. Una vez que vea la lista de todos los compromisos, hay cientos de formas de averiguar qué compromiso está buscando y seleccionarlo o crear una rama a partir de él. En el proceso, habrá aprendido acerca de las opciones útiles y de repetición para varios comandos básicos de git.


Acabo de construir un comando que me ayudó a encontrar mi comilla perdida:

for ref in `find .git/objects | sed -e ''s#.git/objects/##'' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less

Esto lista todos los objetos en el árbol .git / objects, localiza los que son de tipo commit, luego muestra un resumen de cada uno. A partir de este punto, fue solo una cuestión de buscar en el "WIP en el trabajo: 6a9bb2" (el "trabajo" es mi sucursal, 619bb2 es un compromiso reciente).

Observo que si uso "git stash apply" en lugar de "git stash pop" no tendría este problema, y ​​si utilizo "git stash save message ", la confirmación podría haber sido más fácil de encontrar.

Actualización: Con la idea de Nathan, esto se vuelve más corto:

for ref in `git fsck --unreachable | grep commit | cut -d'' '' -f3`; do git show --summary $ref; done | less


Accidentalmente eliminé el alijo en la aplicación GitUP. Solo presiona Ctrl + Z para deshacerlo.

Tal vez ayude a alguien;)


En OSX con git v2.6.4, simplemente ejecuté Git Stash Drop accidentalmente, luego lo encontré siguiendo los pasos a continuación.

Si conoces el nombre del alijo entonces usa:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>

De lo contrario, encontrará la ID del resultado manualmente con:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

Luego, cuando encuentre el ID de confirmación, simplemente pulse el alijo de git aplique {ID de compromiso}

Espero que esto ayude a alguien rápidamente


Equivalente de Windows PowerShell usando gitk:

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split('' '')[2] })

Probablemente haya una forma más eficiente de hacer esto en una tubería, pero esto hace el trabajo.


La respuesta aceptada por Aristóteles mostrará todas las confirmaciones alcanzables, incluidas las confirmaciones que no son de tipo escondido. Para filtrar el ruido:

git fsck --no-reflog | / awk ''/dangling commit/ {print $3}'' | / xargs git log --no-walk --format="%H" / --grep="WIP on" --min-parents=3 --max-parents=3

Esto solo incluirá confirmaciones que tienen exactamente 3 confirmaciones principales (que tendrá un alijo), y cuyo mensaje incluye "WIP activado".

Tenga en cuenta que si guardó su alijo con un mensaje (por ejemplo, git stash save "My newly created stash" ), esto anulará el mensaje predeterminado "WIP on ...".

Puede mostrar más información sobre cada confirmación, por ejemplo, mostrar el mensaje de confirmación o pasarlo a git stash show :

git fsck --no-reflog | / awk ''/dangling commit/ {print $3}'' | / xargs git log --no-walk --format="%H" / --grep="WIP on" --min-parents=3 --max-parents=3 | / xargs -n1 -I ''{}'' bash -c "/ git log -1 --format=medium --color=always ''{}''; echo; / git stash show --color=always ''{}''; echo; echo" | / less -R


Lo que vine aquí buscando es cómo recuperar realmente el alijo, independientemente de lo que haya comprobado. En particular, había escondido algo, luego revisé una versión anterior, luego la abrí, pero el alijo no era operativo en ese momento anterior, por lo que el alijo desapareció; No podía simplemente hacer git stash para empujarlo de nuevo en la pila. Esto funcionó para mí:

$ git checkout somethingOld $ git stash pop ... nothing added to commit but untracked files present (use "git add" to track) Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179) $ git checkout 27f6bd8ba3c $ git reset HEAD^ # Make the working tree differ from the parent. $ git stash # Put the stash back in the stack. Saved working directory and index state WIP on (no branch): c2be516 Some message. HEAD is now at c2be516 Some message. $ git checkout somethingOld # Now we are back where we were.

En retrospectiva, debería haber estado usando git stash apply no git stash pop . Estaba haciendo un bisect y tenía un pequeño parche que quería aplicar en cada paso del bisect . Ahora estoy haciendo esto:

$ git reset --hard; git bisect good; git stash apply $ # Run tests $ git reset --hard; git bisect bad; git stash apply etc.


Me gustó el enfoque de Aristóteles, pero no me gustó usar GITK ... ya que estoy acostumbrado a usar GIT desde la línea de comandos.

En su lugar, tomé las confirmaciones pendientes y envié el código a un archivo DIFF para su revisión en mi editor de código.

git show $( git fsck --no-reflog | awk ''/dangling commit/ {print $3}'' ) > ~/stash_recovery.diff

Ahora puede cargar el archivo diff / txt resultante (está en su carpeta de inicio) en su editor de txt y ver el código real y el SHA resultante.

Entonces solo usa

git stash apply ad38abbf76e26c803b27a6079348192d32f52219


No pude obtener ninguna de las respuestas para trabajar en Windows en una simple ventana de comandos (Windows 7 en mi caso). awk , grep y Select-string no fueron reconocidos como comandos. Así que intenté un enfoque diferente:

  • primera ejecución: git fsck --unreachable | findstr "commit" git fsck --unreachable | findstr "commit"
  • Copia la salida al bloc de notas
  • encontrar reemplazar "compromiso inalcanzable" con start cmd /k git show

se verá algo como esto:

start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4 start cmd /k git show 44078733e1b36962571019126243782421fcd8ae start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1 start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e

  • guardar como un archivo .bat y ejecutarlo
  • El script abrirá un montón de ventanas de comando, mostrando cada confirmación
  • si encontró el que está buscando, ejecute: git stash apply (your hash)

Puede que no sea la mejor solución, pero funcionó para mí


Otro caso de uso común: ¿Intentaste aparecer en la rama incorrecta y tuviste conflictos?

Todo lo que desea es deshacer el pop, pero aún así mantenerlo en la lista de escondites para que pueda sacarlo en la rama correcta.

Para arreglarlo, haga lo siguiente:

git reset HEAD --hard git checkout my_correct_branch git stash pop

¡Hecho!


Para obtener la lista de escondites que aún están en su repositorio, pero que ya no están disponibles:

git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP

Si le dio un título a su alijo, reemplace "WIP" en -grep=WIP al final del comando con una parte de su mensaje, por ejemplo, -grep=Tesselation .

El comando es grepping para "WIP" porque el mensaje de confirmación predeterminado para un alijo está en la forma WIP on mybranch: [previous-commit-hash] Message of the previous commit.


Puede enumerar todas las confirmaciones inalcanzables escribiendo este comando en la terminal -

git fsck --unreachable

Compruebe el hash de confirmación inalcanzable

git show hash

Finalmente aplícalo si encuentras el objeto escondido -

git stash apply hash


Quiero agregar a la solución aceptada otra buena manera de pasar por todos los cambios, cuando no tiene gitk disponible o no tiene una X para la salida.

git fsck --no-reflog | awk ''/dangling commit/ {print $3}'' > tmp_commits for h in `cat tmp_commits`; do git show $h | less; done

Luego obtienes todas las diferencias para esos hashes mostrados uno tras otro. Presiona ''q'' para llegar al siguiente diff.


Se recuperó mediante los siguientes pasos:

  1. Identifique el código hash de hash eliminado:

    gitk --todos $ (git fsck --no-reflog | awk ''/ dangling commit / {print $ 3}'')

  2. Cherry Pick the Stash:

    git cherry-pick -m 1 $ stash_hash_code

  3. Resolver conflictos en su caso utilizando:

    git mergetool

Además, es posible que tenga problemas con el mensaje de confirmación si está utilizando gerrit. Por favor, guarde sus cambios antes de seguir las siguientes alternativas:

  1. Utilice el reinicio por hardware para confirmar previamente y luego vuelva a confirmar este cambio.
  2. También puede guardar el cambio, volver a hacer la prueba y volver a comprometerse.

Si acaba de abrirlo y el terminal todavía está abierto, aún tendrá el valor de hash impreso en la pantalla de git stash pop (gracias, Dolda).

De lo contrario, puede encontrarlo usando esto para Linux, Unix o Git Bash para Windows:

git fsck --no-reflog | awk ''/dangling commit/ {print $3}''

... o utilizando Powershell para Windows:

git fsck --no-reflog | select-string ''dangling commit'' | foreach { $bits = $_ -split '' ''; echo $bits[2];}

Esto le mostrará todas las confirmaciones en las puntas de su gráfico de confirmación que ya no están referenciadas desde ninguna rama o etiqueta: cada confirmación perdida, incluida toda confirmación de alijo que haya creado, estará en algún lugar de esa gráfica.

La forma más fácil de encontrar la confirmación de alijo que desea es probablemente pasar esa lista a gitk :

gitk --all $( git fsck --no-reflog | awk ''/dangling commit/ {print $3}'' )

... o vea la respuesta de emragins si usa Powershell para Windows.

Esto abrirá un navegador de repositorio que le mostrará cada confirmación individual en el repositorio , independientemente de si es accesible o no.

Puede reemplazar gitk allí con algo como git log --graph --oneline --decorate si prefiere un buen gráfico en la consola en lugar de una aplicación GUI separada.

Para detectar las confirmaciones ocultas, busque los mensajes de confirmación de este formulario:

WIP on somebranch : commithash Algunos mensajes de confirmación antiguos

Nota : El mensaje de confirmación solo estará en este formulario (comenzando con "WIP activado") si no proporcionó un mensaje cuando realizó el git stash .

Una vez que sepa el hash de la confirmación que desea, puede aplicarlo como un alijo:

git stash apply $stash_hash

O puede usar el menú contextual en gitk para crear sucursales para cualquier compromiso inalcanzable que le interese. Después de eso, puede hacer lo que quiera con ellas con todas las herramientas normales. Cuando hayas terminado, simplemente sopla esas ramas de nuevo.


Si desea restaurar un alijo perdido, primero debe encontrar el hash de su alijo perdido.

Como Aristóteles Pagaltzis sugirió que un git fsck debería ayudarlo.

Personalmente, uso mi alias log-all que me muestra todos los confirmaciones (confirmaciones recuperables) para tener una mejor vista de la situación:

git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d'' '' -f3)

Puede hacer una búsqueda aún más rápida si solo está buscando mensajes "WIP on".

Una vez que sepas tu sha1, simplemente cambias el reflog de tu alijo para agregar el viejo alijo:

git update-ref refs/stash ed6721d

Probablemente prefieras tener un mensaje asociado, así que un -m

git update-ref -m "$(git log -1 --pretty=format:''%s'' ed6721d)" refs/stash ed6721d

E incluso querrás usar esto como un alias:

restash = !git update-ref -m $(git log -1 --pretty=format:''%s'' $1) refs/stash $1


Si no cerró el terminal, solo mire la salida de git stash pop y tendrá el ID de objeto del alijo eliminado. Normalmente se ve así:

$ git stash pop [...] Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

(Tenga en cuenta que git stash drop también produce la misma línea.)

Para recuperar ese alijo, simplemente ejecuta git branch tmp 2cae03e , y lo obtendrás como una rama. Para convertir esto a un alijo, ejecute:

git stash apply tmp git stash

Tenerlo como una rama también te permite manipularlo libremente; por ejemplo, para elegirlo o fusionarlo.


Solo quería mencionar esta adición a la solución aceptada. No fue inmediatamente obvio para mí la primera vez que probé este método (quizás debería haberlo sido), pero para aplicar el alijo del valor de hash, simplemente use "git stash apply":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Cuando era nuevo en git, esto no estaba claro para mí, y estaba probando diferentes combinaciones de "git show", "git apply", "patch", etc.


git fsck --unreachable | grep commit git fsck --unreachable | grep commit debería mostrar el sha1, aunque la lista que devuelve puede ser bastante grande. git show <sha1> mostrará si es la confirmación que desea.

git cherry-pick -m 1 <sha1> fusionará la confirmación en la rama actual.