Como una pequeña explicación adicional, tenga en cuenta que git stashhace dos confirmaciones o tres confirmaciones. El valor predeterminado es dos; obtienes tres si usas cualquier ortografía de las opciones --allo --include-untracked.
Estas dos o tres confirmaciones son especiales en una forma importante: no están en ninguna rama. Git los ubica a través del nombre especial stash. 1 Sin embargo, lo más importante es lo que Git te permite, y te obliga , hacer con estas dos o tres confirmaciones. Para entender esto, necesitamos mirar qué hay en esas confirmaciones.
¿Qué hay dentro de un escondite?
Cada confirmación puede enumerar una o más confirmaciones principales . Estos forman un gráfico, donde las confirmaciones posteriores apuntan a las anteriores. El alijo normalmente contiene dos confirmaciones, que me gusta llamar ipara el contenido del índice / área de ensayo y wpara el contenido del árbol de trabajo. Recuerde también que cada confirmación contiene una instantánea. En una confirmación normal, esta instantánea se realiza a partir del contenido del índice / área de ensayo. ¡Así que el icompromiso es de hecho un compromiso perfectamente normal! Simplemente no está en ninguna rama:
...--o--o--o <-- branch (HEAD)
|
i
Si está haciendo un alijo normal, el git stashcódigo lo hace wahora copiando todos sus archivos de árbol de trabajo rastreados (en un índice auxiliar temporal). Git establece que el primer padre de esta wconfirmación apunte a la HEADconfirmación y el segundo padre apunte a la confirmación i. Por último, se establece stashpara apuntar a este wcompromiso:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
Si agrega --include-untrackedo --all, Git realiza una confirmación adicional u, entre hacer iy w. El contenido de la instantánea uson aquellos archivos que no se rastrean pero no se ignoran ( --include-untracked), o archivos que no se rastrean incluso si se ignoran ( --all). Esta uconfirmación adicional no tiene padre, y luego, cuando se git stashrealiza w, establece wel tercer padre en esta uconfirmación, de modo que obtiene:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
Git también, en este punto, elimina los archivos del árbol de trabajo que terminaron en la uconfirmación (usando git cleanpara hacer eso).
Restaurar un alijo
Cuando vas a restaurar un alijo, tienes la opción de usarlo --indexo no. Esto le dice git stash apply(o cualquiera de los comandos que usa internamente apply, como pop) que debe usar el icompromiso para intentar modificar su índice actual. Esta modificación se realiza con:
git diff <hash-of-i> <hash-of-i's-parent> | git apply --index
(más o menos; hay un montón de detalles importantes que se interponen en el camino de la idea básica aquí).
Si omite --index, git stash applyignora por completo la iconfirmación.
Si el alijo tiene solo dos confirmaciones, git stash applyahora puede aplicar la wconfirmación. Hace esto llamando a git merge2 (sin permitirle confirmar o tratar el resultado como una fusión normal), usando la confirmación original en la que se hizo el alijo ( iel padre y wel primer padre) como base de fusión, wcomo el --theirscommit, y su compromiso actual (HEAD) como el objetivo de la fusión. Si la fusión tiene éxito, todo está bien, bueno, al menos Git así lo cree, y la git stash applymisma tiene éxito. Si solía git stash popaplicar el alijo, el código ahora elimina el alijo. 3 Si la fusión falla, Git declara que la aplicación ha fallado. Si usastegit stash pop, el código conserva el alijo y entrega el mismo estado de falla que para git stash apply.
Pero si tiene esa tercera confirmación, si hay una uconfirmación en el alijo que está aplicando, ¡entonces las cosas cambian! No hay opción para fingir que la uconfirmación no existe. 4 Git insiste en extraer todos los archivos de esa uconfirmación en el árbol de trabajo actual. Esto significa que los archivos no deben existir en absoluto o tener el mismo contenido que en la uconfirmación.
Para que eso suceda, puede usarlo git cleanusted mismo, pero recuerde que los archivos sin seguimiento (ignorados o no) no tienen otra existencia dentro de un repositorio Git, ¡así que asegúrese de que todos estos archivos se puedan destruir! O puede crear un directorio temporal y mover los archivos allí para su custodia, o incluso hacer otro git stash save -uo git stash save -a, ya que estos se ejecutarán git cleanautomáticamente. Pero eso te deja con otro ualijo de estilo con el que lidiar más tarde.
1 Esto es de hecho refs/stash. Esto es importante si crea una rama llamada stash: el nombre completo de la rama es refs/heads/stash, por lo que no están en conflicto. Pero no hagas eso: a Git no le importará, pero te confundirás. :-)
2 El git stashcódigo realmente se usa git merge-recursivedirectamente aquí. Esto es necesario por varias razones y también tiene el efecto secundario de asegurarse de que Git no lo trate como una fusión cuando resuelva conflictos y se comprometa.
3 Por eso recomiendo evitar git stash pop, a favor de git stash apply. Tiene la oportunidad de revisar lo que se aplicó y decidir si realmente se aplicó correctamente. Si no, todavía tienes tu alijo, lo que significa que puedes usarlo git stash branchpara recuperar todo perfectamente. Bueno, asumiendo la falta de ese molesto ucompromiso.
4 Realmente debería haber: git stash apply --skip-untrackedo algo. También debería haber una variante que signifique colocar todos esos uarchivos de confirmación en un nuevo directorio , por ejemplo git stash apply --untracked-into <dir>, quizás.
git stash show -p | git apply --3