Solución:
Logré recrear tu problema. Parece que si esconde archivos sin seguimiento y luego crea esos archivos (en su ejemplo, foo.txt
y bar.txt
), tiene cambios locales en los archivos sin seguimiento que se sobrescribirán cuando aplique git stash pop
.
Para solucionar este problema, puede utilizar el siguiente comando. Esta voluntad anular cualquier cambio local no guardado así que ten cuidado.
git checkout stash -- .
Aquí hay más información que encontré en el comando anterior.
Como explicación adicional, tenga en cuenta que git stash
realiza dos confirmaciones o tres confirmaciones. El valor predeterminado es dos; obtienes tres si usas cualquier ortografía del --all
o --include-untracked
opciones.
Estas dos o tres confirmaciones son especiales en una forma importante: están en no rama. Git los ubica a través del nombre especial stash
.1 Sin embargo, lo más importante es lo que te permite Git, y marcas tú, hazlo con estas dos o tres confirmaciones. Para entender esto, necesitamos mirar qué hay en esas confirmaciones.
¿Qué hay dentro de un escondite?
Cada compromiso puede enumerar uno o más padre se compromete. Estos forman un gráfico, donde las confirmaciones posteriores apuntan a las anteriores. El alijo normalmente contiene dos confirmaciones, a las que me gusta llamar i
para el índice / contenido del área de preparación, y w
para 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 de el índice / contenido del área de preparación. Entonces el i
¡commit 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 stash
el código hace w
ahora copiando todos sus archivos de árbol de trabajo rastreados (en un índice auxiliar temporal). Git establece el primer padre de este w
comprometerse a señalar el HEAD
commit, y el segundo padre para apuntar para confirmar i
. Por último, establece stash
para señalar esto w
cometer:
...--o--o--o <-- branch (HEAD)
|
i-w <-- stash
Si agrega --include-untracked
o --all
, Git realiza un compromiso adicional, u
, entre hacer i
y w
. El contenido de la instantánea para u
son aquellos archivos que no se rastrean pero que no se ignoran (--include-untracked
), o archivos que no se rastrean incluso si se ignoran (--all
). Este extra u
comprometerse tiene no padre, y luego cuando git stash
marcas w
, establece w
‘s tercera padre de esto u
comprometerse, de modo que obtenga:
...--o--o--o <-- branch (HEAD)
|
i-w <-- stash
/
u
Git también, en este punto, elimina cualquier archivo de árbol de trabajo que terminó en el u
comprometerse (usando git clean
Para hacer eso).
Restaurar un alijo
Cuando vas a restaurar un alijo, tienes la opción de usar --index
o no usarlo. Esto dice git stash apply
(o cualquiera de los comandos que utilizan internamente apply
, tal como pop
) que debería usar los i
comprometerse a 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 omites --index
, git stash apply
ignora completamente el i
cometer.
Si el alijo solo tiene dos confirmaciones, git stash apply
ahora puede aplicar el w
cometer. Hace esto llamando git merge
2 (sin permitirle confirmar o tratar el resultado como una fusión normal), utilizando la confirmación original en la que se hizo el alijo (i
padre, y w
es el primer padre) como base de fusión, w
como el --theirs
commit, 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 cree que sí, y el git stash apply
sí mismo tiene éxito. Si usaste git stash pop
para aplicar el alijo, el código ahora gotas El escondite.3 Si la fusión falla, Git declara que la solicitud ha fallado. Si usaste git stash pop
, el código retiene el alijo y entrega el mismo estado de falla que para git stash apply
.
Pero si tienes eso tercera cometer — si hay un u
confíe en el alijo que está aplicando, ¡entonces las cosas cambian! No hay opción para fingir que el u
el compromiso no existe.4 Git insiste en extraer todos los archivos de ese u
commit, en el árbol de trabajo actual. Esto significa que los archivos no deben existir en absoluto o tener el mismo contenido que en el u
cometer.
Para que eso suceda, puede usar git clean
usted mismo, pero recuerde que los archivos sin seguimiento (ignorados o no) no tienen otra existencia dentro de un repositorio de Git, ¡así que asegúrese de que todos estos archivos puedan ser destruidos! O puede crear un directorio temporal y mover los archivos allí para su custodia, o incluso hacer otra git stash save -u
o git stash save -a
, ya que esos correrán git clean
para ti. Pero eso te deja con otro u
– alijo de estilo para tratar más tarde.
1Esto es de hecho refs/stash
. Esto importa si crea una rama llamada stash
: el nombre completo de la sucursal es refs/heads/stash
, por lo que estos no están en conflicto. Pero no hagas eso: Git no te importará, pero te confundirás. 🙂
2los git stash
el código realmente usa git merge-recursive
directamente 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.
3Por eso recomiendo evitar git stash pop
, a favor de git stash apply
. Tiene la oportunidad de revisar lo que se aplicó y decidir si fue Realmente aplicado correctamente. Si no, tu todavía tienes tu alijo lo que significa que puedes usar git stash branch
para recuperar todo a la perfección. Bueno, asumiendo la falta de ese molesto u
cometer.
4Realmente debería haber: git stash apply --skip-untracked
o algo. También debería haber una variante que signifique soltar todos esos u
enviar archivos a un nuevo directorio, p.ej, git stash apply --untracked-into <dir>
, quizás.
Para ampliar la respuesta de Daniel Smith: ese código solo restaura el rastreado archivos, incluso si usó --include-untracked
(o -u
) al crear el alijo. El código completo requerido es:
git checkout stash -- .
git checkout stash^3 -- .
git stash drop
# Optional to unstage the changes (auto-staged by default).
git reset
Esto restaura completamente el contenido rastreado (en stash
) y contenido sin seguimiento (en stash^3
), luego elimina el alijo. Algunas notas:
- Ten cuidado – ¡Esto sobrescribirá todo con el contenido de tu alijo!
- Restaurar los archivos con
git checkout
hace que todos se pongan en escena automáticamente, así que agreguégit reset
para desarmarlo todo. - Algunos recursos utilizan
[email protected]{0}
y[email protected]{0}^3
, en mis pruebas funciona igual con o sin@{0}
Fuentes:
- Forzar git stash para sobrescribir archivos agregados (vinculado en la respuesta de Daniel Smith)
- ¿Cómo puedo retirar un archivo sin seguimiento en un alijo de git? (información sobre la magia
stash^3
cometer) - Guía de Atlassian para git stash (consulte “Cómo funciona git stash” para obtener detalles sobre las confirmaciones internas)