¿Cómo recuperar un alijo abandonado en Git?

Con frecuencia uso git stash y git stash pop para save y restaurar cambios en mi tree de trabajo. Ayer tuve algunos cambios en mi tree de trabajo que había escondido y reventado, y luego hice más cambios en mi tree de trabajo. Me gustaría volver y revisar los cambios ocultos de ayer, pero git stash pop parece eliminar todas las references a la confirmación asociada.

Sé que si uso git stash entonces .git / refs / stash contiene la reference de la confirmación utilizada para crear el alijo. Y .git / logs / refs / stash contiene todo el alijo. Pero esas references desaparecieron después de que git stash pop . Sé que el compromiso todavía está en mi repository en alguna parte, pero no sé qué era.

¿Hay alguna manera fácil de recuperar la reference de confirmación de stash de ayer?

Tenga en count que esto no es crítico para mí hoy porque tengo copys de security diarias y puedo volver al tree de trabajo de ayer para get mis cambios. ¡Lo estoy preguntando porque debe haber una manera más fácil!

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

De lo contrario, puede encontrarlo usando esto para Linux y Unix:

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

y 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 compromiso, que ya no se referencen desde ninguna twig o label: cada compromiso perdido, incluyendo cada compromiso de escondite que haya creado alguna vez, estará en algún lugar de ese gráfico.

La forma más fácil de encontrar el alijo que desea es, probablemente, pasar esa list a gitk :

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

Esto abrirá un browser de repository que le mostrará cada compromiso en el repository , independientemente de si es alcanzable o no.

Puede replace gitk allí con algo como git log --graph --oneline --decorate si prefiere un buen gráfico en la console a través de una aplicación GUI separada.

Para detectar confusiones ocultas, busca los posts de confirmación de este formulario:

WIP en somebranch : commithash Algún post de confirmación anterior

Nota : El post de confirmación solo estará en este formulario (comenzando con "WIP activado") si no proporcionó un post cuando lo hizo.

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

 git stash apply $stash_hash 

O puede usar el menu contextual en gitk para crear twigs 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, solo sopla esas twigs de nuevo.

Si no cerró la terminal, solo mire la salida de git stash pop y tendrá la ID del object del package oculto. Por lo general, se ve así:

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

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

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

 git stash apply tmp git stash 

Tenerlo como una twig 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 usa "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.

Acabo de build un command que me ayudó a encontrar mi commit de stash perdido:

 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 enumera todos los objects en el tree .git / objects, localiza los que son de tipo commit, luego muestra un resumen de cada uno. A partir de este punto, solo fue cuestión de revisar los compromisos para encontrar un "WIP en trabajo apropiado: 6a9bb2" ("trabajo" es mi twig, 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 uso "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 

Para get la list de depósitos que todavía están en su repository, pero que ya no se pueden alcanzar:

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

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

El command está reemplazando por "WIP" porque el post de confirmación pnetworkingeterminado para un alijo está en el formulario WIP on mybranch: [previous-commit-hash] Message of the previous commit.

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

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

Si quiere restash un escondite perdido, primero necesita encontrar el hash de su escondite perdido.

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

Personalmente utilizo mi alias log-all que me muestra cada confirmación (confirmaciones recuperables) para tener una mejor visión 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 busca posts de "WIP encendido".

Una vez que conozca su sha1, simplemente cambie su reflog de escondite para agregar el viejo escondite:

 git update-ref refs/stash ed6721d 

Probablemente prefieras tener un post asociado por lo que -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 

Me gustó el enfoque de Aristóteles, pero no me gustó usar GITK … ya que estoy acostumbrado a usar GIT desde la command-line.

En cambio, tomé los commits colgantes y envié el código a un file 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 file diff / txt resultante (está en su carpeta de inicio) en su editor de text y ver el código real y el SHA resultante.

Entonces solo usa

 git stash apply ad38abbf76e26c803b27a6079348192d32f52219 

En OSX con git v2.6.4, acabo de ejecutar git stash drop accidentalmente, luego lo encontré yendo a través de los pasos siguientes

Si conoce el nombre del alijo entonces use:

$ 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 identificación del resultado de forma manual con:

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

Luego, cuando encuentres la identificación de compromiso, solo presiona el alijo de git aplica {commit-id}

Espero que esto ayude a alguien rápidamente

Quiero agregar a la solución aceptada otra buena forma de pasar por todos los cambios, cuando o no tienes gitk disponible o no hay 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 todos los diffs para esos hash mostrados uno tras otro. Presione 'q' para llegar a la siguiente diferencia.

Equivalente de Windows PowerShell con gitk:

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

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

¿Por qué la gente hace esta pregunta? Porque todavía no conocen o no comprenden el reflog.

La mayoría de las respuestas a esta pregunta ofrecen commands largos con opciones que casi nadie recordará. Entonces las personas entran en esta pregunta y copyn y pegan lo que creen que necesitan y lo olvidan casi inmediatamente después.

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 list de todas las confirmaciones, hay cientos de maneras de averiguar qué compromiso está buscando y seleccionarlo o crear una sucursal a partir de él. En el process, habrá aprendido sobre el reflog y las opciones útiles para varios commands básicos de git.

La respuesta aceptada por Aristóteles mostrará todas las confirmaciones alcanzables, incluidas las confirmaciones que no sean escondidas. 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 includeá confirmaciones que tengan exactamente 3 confirmaciones principales (que tendrá un alijo) y cuyo post incluya "WIP activado".

Tenga en count que si guardó su alijo con un post (por ej., Guardado de git stash save "My newly created stash" ), esto anulará el post "WIP en …" pnetworkingeterminado.

Puede mostrar más información sobre cada confirmación, por ejemplo, mostrar el post de confirmación o pasarlo a la presentación de 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 

No pude get ninguna de las respuestas para trabajar en Windows en una window de command simple (Windows 7 en mi caso). awk , grep y Select-string no fueron reconocidos como commands. Así que probé con un enfoque diferente:

  • primera ejecución: git fsck --unreachable | findstr "commit" git fsck --unreachable | findstr "commit"
  • copyr la salida al bloc de notas
  • search replace "commit 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

  • save como un file .bat y ejecutarlo
  • la secuencia de commands abrirá un montón de windows de command, mostrando cada compromiso
  • si encontraste el que estás buscando, ejecuta: git stash apply (your hash)

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

Lo que vine a search es cómo recuperar el dinero, independientemente de lo que haya prestado. En particular, había escondido algo, luego había revisado una versión anterior, y luego la había preparado, pero el alijo no funcionaba en ese momento, así que el alijo desapareció; No podría hacer un git stash para empujarlo a la stack. 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/[email protected]{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 not git stash pop . Estaba haciendo una bisect y tenía un pequeño parche que quería aplicar en cada paso de la 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. 

Lo recuperó usando los siguientes pasos:

  1. Identifique el código hash escondido eliminado:

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

  2. Cherry Elija el Stash:

    git cherry-pick -m 1 $ stash_hash_code

  3. Resuelva conflictos si alguno usando:

    git mergetool

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

  1. Use el restablecimiento completo a la confirmación previa y vuelva a comprometer este cambio.
  2. También puede esconder el cambio, volver a establecer la base y volver a comprometerse.