¿Cómo resolver el conflicto de git stash sin comprometerse?
Como se pregunta en esta pregunta , también quiero saber cómo resolver un conflicto git stash pop
sin agregar todas las modificaciones a una confirmación (al igual que lo hace "git stash pop" sin conflicto).
Mi enfoque actual es muy desagradable porque lo hago de esta manera:
git stash pop # -> CONFLICT
git stash drop
# [resolve conflict]
# [add conflict files]
git reset HEAD # <all files that are in commit-mode>
Cómo reproducirse:
mkdir foo; cd foo; git init
echo "1" > one
echo "2" > two
git add -A; git commit -m "first"
echo "1.1" > one
echo "2.1" > two
git stash
echo "2.2" > two
git commit -a -m "second"
echo "Only this file would stay in HEAD without the conflict" > third
git add third
git stash pop
git status
27/06/2016: Se agregó un nuevo archivo llamado 'tercero' al ejemplo para mostrar que soluciones alternativas como la solución de scy solo funcionan para HEAD vacíos pero no solucionan el problema inicial de que el HEAD no tiene el mismo contenido que para un git stash pop
sin conflicto.
Solución limpia
La siguiente solución me parece mucho más clara y también la sugiere el propio Git : intente ejecutarla git status
en el repositorio con un conflicto:
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
Nota: El restore
comando se introdujo en la versión 2.23.0 de Git . Las versiones anteriores de Git sugerían utilizar el comando git reset HEAD <file>...
en lugar de git restore --staged <file>...
. También puede utilizar git reset
para eliminar todos los archivos del área de preparación (llamado índice ). El equivalente del comando de restauración es git restore --staged .
(el punto es necesario y especifica cualquier archivo). Actualmente, se puede utilizar cualquiera de esos comandos y el resultado es el mismo . Si desea conocer las diferencias entre esos comandos, consulte la documentación .
Entonces, hagamos lo que sugiere Git ( sin realizar ni revertir confirmaciones inútiles ):
- Resuelva los conflictos manualmente (o idealmente utilizando alguna herramienta de combinación , ver más abajo).
- Úselo
git restore --staged .
para marcar conflictos como resueltos y retirar todos los archivos del área de preparación. Si desea eliminar solo archivos específicos, use el comandogit restore --staged <file>
en su lugar. No es necesario ejecutargit add
antes. - Finalmente, elimine el alijo con
git stash drop
, porque Git no lo hace automáticamente en caso de conflicto.
Traducido a los comandos de la línea de comandos:
$ git stash pop
# ...resolve conflict(s)
$ git restore --staged .
$ git stash drop
Explicación del comportamiento predeterminado.
Hay dos formas de marcar los conflictos como resueltos: git add
y git restore --staged <file>...
. Mientras git restore --staged <file>...
marca los conflictos como resueltos y elimina archivos del índice, git add
también marca los conflictos como resueltos, pero mantiene los archivos en el índice.
Agregar archivos al índice después de resolver un conflicto es intencionado. De esta manera, puede diferenciar los cambios del alijo anterior y los cambios que realizó después de que se resolvió el conflicto. Si no te gusta, siempre puedes utilizar git restore --staged .
para eliminar todo del índice.
Fusionar herramientas
Recomiendo encarecidamente utilizar cualquiera de las herramientas de combinación de 3 vías para resolver conflictos, por ejemplo, KDiff3 , Meld , etc., en lugar de hacerlo manualmente. Por lo general, resuelve todos o la mayoría de los conflictos automáticamente. ¡Es un gran ahorro de tiempo!
Supongamos que tiene este escenario en el que guarda sus cambios para poder extraerlos desde el origen. Posiblemente porque sus cambios locales están solo debug: true
en algún archivo de configuración. Ahora tiras y alguien ha introducido una nueva configuración allí, creando un conflicto.
git status
dice:
# On branch master
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: src/js/globals.tpl.js
no changes added to commit (use "git add" and/or "git commit -a")
Bueno. Decidí seguir lo que sugirió Git: resolví el conflicto y comprometí:
vim src/js/globals.tpl.js
# type type type …
git commit -a -m WIP # (short for "work in progress")
Ahora mi copia de trabajo está en el estado que quiero, pero he creado una confirmación que no quiero tener. ¿Cómo me deshago de ese compromiso sin modificar mi copia de trabajo? Espera, ¡hay un comando popular para eso!
git reset HEAD^
Mi copia de trabajo no ha sido modificada, pero la confirmación WIP desapareció. ¡Eso es exactamente lo que quería! (Tenga en cuenta que no lo estoy usando --soft
aquí, porque si hay archivos fusionados automáticamente en su alijo, se organizan automáticamente y, por lo tanto, terminaría con estos archivos preparándose nuevamente después reset
).
Pero queda una cosa más: la página de manual de git stash pop
nos recuerda que "la aplicación del estado puede fallar con conflictos; en este caso, no se elimina de la lista oculta. Debe resolver los conflictos manualmente y llamar git stash drop
manualmente después". Eso es exactamente lo que hacemos ahora:
git stash drop
Y hecho.
En lugar de agregar los cambios que realiza para resolver el conflicto, puede utilizar git reset HEAD file
para resolver el conflicto sin organizar los cambios.
Sin embargo, es posible que tengas que ejecutar este comando dos veces. Una vez para marcar el conflicto como resuelto y otra para eliminar los cambios que se realizaron mediante la rutina de resolución de conflictos.
Es posible que haya un modo de reinicio que haga ambas cosas simultáneamente, aunque ahora no existe.
git checkout stash -- .
trabajó para mi.
Nota : esto puede ser peligroso ya que no intenta fusionar los cambios del alijo en su copia de trabajo, sino que los sobrescribe con los archivos escondidos. Entonces puedes perder tus cambios no confirmados.