¿Cómo recupero un alijo caído en Git?
Utilizo frecuentemente 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 escondido y sacado, y luego hice más cambios en mi árbol de trabajo. Me gustaría volver atrás y revisar los cambios escondidos de ayer, pero git stash pop
parece que se eliminan todas las referencias a la confirmación asociada.
Sé que si lo uso git stash
, .git/refs/stash contiene la referencia de la confirmación utilizada para crear el alijo. Y .git/logs/refs/stash contiene todo el alijo. Pero esas referencias desaparecieron después git stash pop
. Sé que la confirmación todavía está en algún lugar de mi repositorio, pero no sé qué fue.
¿Existe una manera fácil de recuperar la referencia de confirmación del alijo de ayer?
Una vez que conozca el hash del compromiso de alijo que soltó, puede aplicarlo como un alijo:
git stash apply $stash_hash
O puede crear una rama separada para ello con
git branch recovered $stash_hash
Después de eso, podrás hacer lo que quieras con todas las herramientas normales. Cuando hayas terminado, simplemente quita la rama.
Encontrar el hash
Si acaba de abrirlo y el terminal aún está abierto, aún tendrá el valor hash impreso en git stash pop
la pantalla (gracias, Dolda).
De lo contrario, 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 { $_.ToString().Split(" ")[2] }
Esto le mostrará todas las confirmaciones en las puntas de su gráfico de confirmaciones a las que ya no se hace referencia desde ninguna rama o etiqueta; cada confirmación perdida, incluidas todas las confirmaciones ocultas que haya creado, estará en algún lugar de ese gráfico.
La forma más sencilla 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 iniciará un navegador de repositorio que le mostrará cada confirmación en el repositorio , independientemente de si es accesible o no.
Puede reemplazarlo gitk
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 confirmaciones de alijo, busque mensajes de confirmación de este formulario:
WIP en alguna rama : commithash Algún mensaje de confirmación antiguo
Nota : El mensaje de confirmación solo tendrá este formato (comenzando con "WIP activado") si no proporcionó un mensaje cuando lo hizo git stash
.
Si no cerró la terminal, simplemente mire el resultado git stash pop
y tendrá el ID del 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 en un alijo, ejecute:
git stash apply tmp
git stash
Tenerlo como rama también te permite manipularlo libremente; por ejemplo, para seleccionarlo 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 (tal vez debería haberlo sido), pero para aplicar el alijo del valor hash, simplemente use "git stash apply":
$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219
Cuando era nuevo en git, esto no me quedaba claro y estaba probando diferentes combinaciones de "git show", "git apply", "patch", etc.
Para obtener la lista de alijos que todavía están en su repositorio, pero que ya no son accesibles:
git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP
Si le diste un título a tu alijo, reemplaza "WIP" al -grep=WIP
final del comando con una parte de tu mensaje, por ejemplo -grep=Tesselation
.
El comando busca "WIP" porque el mensaje de confirmación predeterminado para un alijo tiene el formatoWIP on mybranch: [previous-commit-hash] Message of the previous commit.
Cuando haya encontrado el compromiso, aplíquelo congit stash apply <commit_hash>
Equivalente de Windows PowerShell usando gitk:
gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })
Probablemente exista una forma más eficiente de hacer esto en una tubería, pero funciona.