Abortar un pop escondido en Git

257

Abrí un alijo y hubo un conflicto de fusión. A diferencia de la pregunta que aparece como duplicado, ya tenía algunos cambios no confirmados en el directorio que quería conservar. No solo quiero hacer desaparecer el conflicto de fusión, sino también hacer que mi directorio vuelva al estado anterior al pop.

Lo intenté git merge --abort, pero git afirmó que no había ninguna fusión en curso. ¿Hay una manera fácil de abortar un pop sin destruir los cambios que originalmente tuve en el directorio?

Casebash
fuente
En cuanto a sus cambios no comprometidos: ¿estos cambios ya estaban en el índice?
jørgensen
¿Puedes publicar la versión de git que estás usando?
Tinman
2
La respuesta aceptada parece complicada. Creo que es una buena práctica general nunca hacer algo como intentar git stash popun directorio de trabajo impuro. En cuyo caso, puede simplemente git reset --hardy su alijo todavía está intacto. (Esto es más o menos lo que sugiere el tema vinculado de @ BradKoch)
Steven Lu
1
@StevenLu, estoy de acuerdo, pero el problema puede ocurrir en un directorio de trabajo limpio si está ocultando los cambios para moverlos a una rama diferente. El alijo entra en conflicto con las confirmaciones que existen en la nueva rama que no existía en la anterior.
Jake Stevens-Haas

Respuestas:

55

Ok, creo que he resuelto "git stash sin aplicar". Es más complejo que git apply --reverseporque necesita una acción de fusión inversa en caso de que la fusión la haya realizado git stash apply.

La fusión inversa requiere que todos los cambios actuales se introduzcan en el índice:

  • git add -u

Luego invierta lo merge-recursiveque hizo git stash apply:

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

Ahora solo te quedarán los cambios que no sean alijo. Estarán en el índice. Puede usar git resetpara desestabilizar sus cambios si lo desea.

Dado que su original git stash applyfalló, supongo que lo contrario también podría fallar, ya que algunas de las cosas que quiere deshacer no se hicieron.

Aquí hay un ejemplo que muestra cómo la copia de trabajo (vía git status) termina limpia nuevamente:

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)
Ben Jackson
fuente
2
después de hacer esto, ¿se perderán para siempre las ediciones ocultas anteriormente, o están de vuelta en el alijo?
Brian H.
77
git stash applynunca deja caer un alijo, y cuando falla una fusión, git stash poptambién retiene el alijo
Xerus
Las cosas malas sucederán si hubo un conflicto de fusión durante el stash pop original
3oceno del
286

Mi caso de uso: solo intenté aparecer en la rama incorrecta y obtuve conflictos. Todo lo que necesito es deshacer el pop, pero mantenerlo en la lista oculta para que pueda salir en la rama correcta. Hice esto:

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

Fácil.

jmoz
fuente
22
Entonces, ¿el alijo que desea permanece en la lista de alijo hasta que tenga un pop exitoso?
Ryan Clark
52
Esta no es una respuesta para la pregunta original, ya que eliminaría los cambios locales que no se confirmaron.
Dmitry
13
@RyanClark Vea la respuesta de DavidG a continuación. Básicamente, sí, permanece en la lista de escondites.
fkorsa
1
Creo que la gran mayoría de los usuarios que se encuentran en esta situación encontrarán esta solución ideal. No puedo imaginar una situación en la que haya realizado cambios no confirmados en mi directorio de trabajo antes de intentar acceder a stash popél. Eso suena como una receta para el desastre.
Shadoninja
2
Agradable. Después de leer esto, miré los documentos pop de git stash y dice "Aplicar el estado puede fallar con conflictos; en este caso, no se elimina de la lista de escondites". Entonces, esta es la razón por la cual el alijo puede volver a aparecer después de un reinicio / pago.
Brady Holt
48

Editar: de la git help stashdocumentación en la sección emergente:

Aplicar el estado puede fallar con los conflictos; en este caso, no se elimina de la lista oculta. Debe resolver los conflictos a mano y llamar a git stash drop manualmente después.

Si se utiliza la opción --index, intenta restablecer no solo los cambios del árbol de trabajo, sino también los cambios del índice. Sin embargo, esto puede fallar cuando tiene conflictos (que se almacenan en el índice, por lo que ya no puede aplicar los cambios como estaban originalmente).

Intente copiar todo su repositorio en un nuevo directorio (para que tenga una copia del mismo) y ejecute:

git stash show y guarde esa salida en algún lugar si le importa.

entonces: git stash droppara soltar el alijo en conflicto entonces:git reset HEAD

Eso debería dejar su repositorio en el estado que tenía antes (con suerte, todavía no he podido reprobar su problema)

===

Estoy tratando de reprochar tu problema, pero todo lo que obtengo cuando usin git stash popes:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting

En un directorio limpio:

git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

No veo a git tratando de fusionar mis cambios, simplemente falla. ¿Tienes algún paso de repro que podamos seguir para ayudarte?

DavidG
fuente
Intenta guardar los cambios en un archivo diferente.
asmeurer
Intentar guardar un archivo diferente no funcionó (ver nuevos pasos de repro). Simplemente no puedo reprochar el problema ...
DavidG
1
Esta solución no funciona si hay cambios no confirmados en el directorio de trabajo.
aquí
@here Esto no es una solución real, esto es solo para mostrar que no puedo reproducir el problema que tiene el OP y que no ha proporcionado ningún paso para llegar a donde está.
DavidG
1
El escenario real es: 1) Cambiar el archivo A. 2) Cambios de escondite 3) Hacer cambios conflictivos en el archivo A y confirmar (por ejemplo, cambiar la misma línea) 4) Cambiar el archivo B 5) Hacer 'git stash pop'. Ahora tienes conflictos y cambios locales. En general, estarán en diferentes archivos, pero nunca sabrá qué archivos modificados no conflictivos de stash y cuáles son cambios locales no organizados.
Dmitry
17

Siempre he usado

git reset --merge

No puedo recordar que nunca haya fallado.

Kenn Sebesta
fuente
@GauravPaliwal, lo miraré la próxima vez que lo haga git reset. ¿Sabes si es funcionalmente idéntico a git reset --merge?
Kenn Sebesta
5

Si no tiene que preocuparse por ningún otro cambio que haya realizado y solo desea volver a la última confirmación, puede hacer lo siguiente:

git reset .
git checkout .
git clean -f
hugo der hungrige
fuente
4

OK, creo que he logrado encontrar un flujo de trabajo que lo llevará de regreso a donde necesita estar (como si no hubiera hecho el pop).

HAGA UNA COPIA DE SEGURIDAD ANTES! No sé si esto funcionará para usted, así que copie todo su repositorio en caso de que no funcione.

1) Solucione los problemas de fusión y solucione todos los conflictos seleccionando todos los cambios que provienen del parche (en tortoisemerge, esto se muestra como one.REMOETE (suyo)).

git mergetool

2) Confirme estos cambios (ya se agregarán mediante el comando mergetool). Dele un mensaje de compromiso de "fusión" o algo que recuerde.

git commit -m "merge"

3) Ahora aún tendrá sus cambios locales sin etapas que comenzó originalmente, con una nueva confirmación del parche (podemos deshacernos de esto más adelante). Ahora confirma tus cambios no organizados

git add .
git add -u .
git commit -m "local changes"

4) Invierta el parche. Esto se puede hacer con el siguiente comando:

git stash show -p | git apply -R

5) Cometer estos cambios:

git commit -a -m "reversed patch"

6) Deshazte de los parches / descomprime confirmaciones

git rebase -i HEAD^^^

a partir de esto, elimine las dos líneas con 'fusionar' y 'parche invertido'.

7) Recupere sus cambios sin cambios y deshaga el compromiso de 'cambios locales'

git reset HEAD^

Lo he revisado con un ejemplo simple y lo lleva de regreso a donde desea estar, directamente antes de que se descubriera el alijo, con sus cambios locales y con el alijo todavía disponible para reventar.

agentgonzo
fuente
Si eso no funciona del todo, ¡espero que te lleve la mayor parte del camino! :-)
agentgonzo
git stash show -p | git apply -Reso no funciona si se git stash applyhizo una fusión real. Mira mi respuesta ...
Ben Jackson
3

Resolví esto de una manera algo diferente. Esto es lo que pasó.

Primero, aparecí en la rama equivocada y obtuve conflictos. El alijo permaneció intacto pero el índice estaba en resolución de conflictos, bloqueando muchos comandos.

Un simple git reset HEADabortó la resolución del conflicto y dejó los cambios no comprometidos (y NO DESEADOS ).

Varios git co <filename>volvieron el índice al estado inicial. Finalmente, cambié de rama git co <branch-name>y ejecuté una nueva git stash pop, que se resolvió sin conflictos.

pid
fuente
2

Algunas ideas:

  • Use git mergetoolpara dividir los archivos de fusión en partes originales y nuevas. Con suerte, uno de esos es el archivo con sus cambios que no son de escondite.

  • Aplique la diferencia de la reserva al revés, para deshacer solo esos cambios. Probablemente tendrá que dividir manualmente los archivos con los conflictos de fusión (que con suerte el truco anterior funcionará).

No probé ninguno de estos, así que no estoy seguro de que funcionen.

asmeurer
fuente
1

Podría reproducir limpio git stash popen el directorio "sucio", con cambios no confirmados, pero aún no emergente que genera un conflicto de fusión.

Si al fusionar el conflicto, el alijo que intentó aplicar no desapareció, puede intentar examinar git show stash@{0}(opcionalmente con --ourso --theirs) y comparar con git statisy git diff HEAD. Debería poder ver qué cambios provienen de la aplicación de un alijo.

Jakub Narębski
fuente
1

Si DavidG está en lo correcto al decir que no apareció el alijo debido al conflicto de fusión, entonces simplemente necesita limpiar su directorio de trabajo. Rápidamente git committodo lo que te importa. (Puede reseto puede squashconfirmar más tarde si no ha terminado). Luego, con todo lo que le importa a salvo, git resettodo lo demás que git stash popvolcó en su directorio de trabajo.

Robrich
fuente
1

Si no hubo cambios por etapas antes del git stash pop, como en la pregunta, entonces los dos comandos siguientes deberían funcionar.

git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm

El primero revierte cualquier fusión del alijo, exitoso o no. El segundo elimina cualquier archivo sin seguimiento introducido por el alijo.

De man git stash: The working directory must match the index. que @DavidG señala, stash popfallará si alguno de los archivos modificados actualmente no en escena entra en conflicto. Como tal, no deberíamos tener que preocuparnos por resolver conflictos de fusión más allá de volver a HEAD. Los archivos modificados restantes no están relacionados con el alijo y se modificaron antes delstash pop

Si hubo cambios por etapas, no estoy claro si podemos confiar en los mismos comandos y es posible que desee probar la técnica de @Ben Jackson. Sugerencias apreciadas ..

Aquí hay una configuración de prueba para todos los diversos casos https://gist.github.com/here/4f3af6dafdb4ca15e804

# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d

# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c
aquí
fuente
Creo que esta es la respuesta más correcta hasta ahora. Muy bien pensado.
Agente el viernes
0

Use git reflogpara enumerar todos los cambios realizados en su historial de git. Copie una identificación de acción y escribagit reset ACTION_ID

Fatih Acet
fuente
2
Git no crea una entrada de reflog antes de hacer estallar (necesita una confirmación)
Casebash
-1

Estoy publicando aquí esperando que otros puedan encontrar mi respuesta útil. Tuve un problema similar cuando traté de hacer un escondite en una rama diferente de la que había escondido. En mi caso, no tenía archivos no confirmados o en el índice, pero aún me metí en el caso de conflictos de fusión (el mismo caso que @pid). Como otros señalaron anteriormente, el git stash pop fallido realmente conservó mi alijo, luego un git reset HEAD plus rápido volviendo a mi rama original y haciendo el alijo desde allí resolvió mi problema.

Victor Camacho
fuente