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

Con frecuencia uso git stash y git stash pop para guardar y restaurar los cambios en mi árbol de trabajo. Ayer tenía algunos cambios en mi árbol de trabajo que había guardado y sacado, y luego hice más cambios en mi árbol de trabajo. Me gustaría volver a revisar los cambios almacenados ayer, pero git stash pop parece eliminar todas las referencias al commit asociado.

Sé que si uso git stash entonces .git/refs/stash contiene la referencia del commit usado para crear el stash. Y .git/logs/refs/stash contiene todo el stash. Pero esas referencias desaparecen después de git stash pop. Sé que el commit sigue en mi repositorio en algún lugar, pero no sé cuál era.

¿Hay alguna manera fácil de recuperar la referencia del commit 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.

Solución

Una vez que conozcas el hash del compromiso de alijo que has dejado caer, puedes aplicarlo como alijo:

git stash apply $stash_hash

O bien, puede crear una rama separada para ella con

git branch recovered $stash_hash

Después de eso, puedes hacer lo que quieras con todas las herramientas normales. Cuando haya terminado, sólo volar la rama.

Encontrar el hash

Si acabas de reventarlo y el terminal sigue abierto, todavía tendrás el valor del hash impreso por git stash pop en pantalla (gracias, Dolda).

Si no, puedes encontrarlo usando esto para Linux, Unix o Git Bash para Windows:

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

...o usando Powershell para Windows:

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

Esto te mostrará todos los commits en las puntas de tu gráfico de commits que ya no son referenciados desde ninguna rama o etiqueta - cada commit perdido, incluyendo cada commit de stash que hayas creado, estará en algún lugar de ese gráfico.

La forma más fácil de encontrar el commit del stash que quieres es probablemente pasar esa lista a gitk:

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

...o ver la respuesta de emragins si se utiliza Powershell para Windows.

Esto lanzará un navegador de repositorios mostrando cada una de las confirmaciones en el repositorio de todos los tiempos, independientemente de si es accesible o no.

Puedes sustituir gitk por algo como git log --graph --oneline --decorate si prefieres un bonito gráfico en la consola en lugar de una aplicación GUI separada.

Para detectar confirmaciones de alijo, busca mensajes de confirmación de esta forma:

        WIP on somebranch: commithash Algún mensaje de commit antiguo

Nota: El mensaje de confirmación sólo estará en esta forma (empezando por "WIP on") si no proporcionaste un mensaje cuando hiciste git stash.

Comentarios (23)

Acabo de construir un comando que me ayudó a encontrar mi alijo perdido cometer:

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, y luego muestra un resumen de cada uno. A partir de este punto sólo era cuestión de buscar entre los commits para encontrar un "WIP on work" apropiado: 6a9bb2" ("work" es mi rama, 619bb2 es un commit reciente).

Observo que si utilizo "git stash apply" en lugar de "git stash pop" no tendría este problema, y si utilizo "git stash save message" entonces el commit podría haber sido más fácil de encontrar.

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

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

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

git cherry-pick -m 1 fusionará el commit en la rama actual.

Comentarios (1)