¿Cómo uso vimdiff para resolver un conflicto de fusión de git?

159

Acabo de fusionar una rama en mi maestro en git y obtuve Automatic merge failed; fix conflicts and then commit the result.Ahora corrí git mergetooly vimdiff se abrió con la imagen a continuación. No sé cómo usar vimdiff. ¿Qué significa cada panel aquí y cómo debo proceder para solucionar el conflicto de fusión?

ingrese la descripción de la imagen aquí

Cool Guy Yo
fuente
3
Ver esta página . Si eso es lo que quiere decir con "correcto", el estado actual de su código está en la parte superior izquierda.
romainl
@romainl Todavía estoy confundido después de leer eso, ¿cuáles son los accesos directos y cómo elijo qué archivo usar como rama principal?
Cool Guy Yo
2
@Danny http://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/ es una buena.
ᴠɪɴᴄᴇɴᴛ
Ver también: esto
skelliam

Respuestas:

142

Los cuatro buffers proporcionan una vista diferente del mismo archivo. El búfer superior izquierdo (LOCAL) es el aspecto del archivo en su rama de destino (en lo que se está fusionando). El búfer superior derecho (REMOTO) es el aspecto del archivo en su rama de origen (desde donde se está fusionando). El buffer intermedio (BASE) es el ancestro común de los dos (por lo que puede comparar cómo las versiones izquierda y derecha se han separado entre sí).

Puedo estar equivocado en el siguiente punto. Creo que la fuente del conflicto de fusión es que ambos archivos han cambiado la misma parte del archivo desde BASE; LOCAL ha cambiado las comillas de doble a simple, y REMOTE ha realizado el mismo cambio pero también ha cambiado el valor de fondo de un color a una URL. (Creo que la fusión no es lo suficientemente inteligente como para notar que todos los cambios a LOCAL también están presentes en REMOTE; solo sabe que LOCAL ha realizado cambios desde BASE en los mismos lugares que REMOTE).

En cualquier caso, el búfer inferior contiene el archivo que realmente puede editar, el que se encuentra en su directorio de trabajo. Puede hacer los cambios que desee; vimle muestra cómo difiere de cada una de las vistas superiores, que son las áreas que la fusión automática no pudo manejar. Extraiga los cambios de LOCAL si no desea los cambios REMOTOS. Extraiga los cambios desde REMOTO si lo prefiere a los cambios LOCALES. Tire de BASE si cree que tanto REMOTE como LOCAL están equivocados. ¡Haz algo completamente diferente si tienes una idea mejor! Al final, los cambios que realice aquí son los que realmente se comprometerán.

chepner
fuente
44
Pregunta rápida, ¿cómo guardo en vim?
Cool Guy Yo
66
:xo :w( :xtambién sale) más 'volver'.
Jonathan Leffler
44
Anders: existen otras herramientas de combinación que puede usar si no está familiarizado con el uso vim.
chepner
3
@AndersKitson, ya que está en Mac OS X, FileMerge es perfecto, gratuito y viene con XCode.
romainl
8
¿Por qué el voto negativo? Si hay algo incorrecto en los hechos, corríjalo o al menos indíquelo.
chepner
91

La respuesta de @ chepner es excelente, me gustaría agregar algunos detalles sobre la parte de la pregunta "¿cómo debo proceder para solucionar el conflicto de fusión?" Si observa cómo usar realmente vimdiff en este caso, se detalla a continuación.


Primero, para abordar la opción "abortar todo", si no desea usar "vimdiff" y desea abortar la fusión: presione Esc, luego escriba :qa!y presione Enter. (vea también ¿Cómo salgo del editor Vim? ). Git le preguntará si la fusión se completó, responda con n.


Si desea usar vimdiff, aquí hay algunos atajos útiles. Esto supone que conoce los conceptos básicos de Vim (navegación e inserción / modo normal):

  • navegue hasta el búfer inferior (resultado de fusión): Ctrl-W j
  • navegue al siguiente diferencial con j/ k; o, mejor, use ] cy [ cpara navegar a la diferencia siguiente y anterior respectivamente
  • use z omientras está en un pliegue para abrirlo, si desea ver más contexto
  • para cada diff, según la respuesta de @ chepner, puede obtener el código de una versión local, remota o base, o editarlo y rehacerlo como mejor le parezca
    • para obtenerlo de la versión local, use :diffget LO
    • desde remoto: :diffget RE
    • desde la base: :diffget BA
    • o, si desea editar el código usted mismo, obtenga primero una versión de local / remote / base, y luego vaya al modo de inserción y edite el resto
  • una vez hecho, guarde el resultado de la fusión y salga de todas las ventanas :wqa
  • normalmente, git detecta que se realizó la fusión y crea la confirmación de fusión

No parece posible agregar trozos de conflicto locales y remotos sin pegar copias o accesos directos personalizados: /vi/10534/is-there-a-way-to-take-both- when-using-vim-as-merge-tool, que es una pena, ya que add add es un tipo de conflicto tan común.

Para evitar que vimdiff le pida que presione enter cada vez que se inicia, agregue a su .vimrc:

set shortmess=Ot

como se menciona en: /vi/771/how-can-i-suppress-the-press-enter-prompt-when-opening-files-in-diff-mode

Puede buscar en Internet otros atajos de vimdiff. He encontrado este útil: https://gist.github.com/hyamamoto/7783966

Timur
fuente
10
Esto debería ser votado x1000 veces y aceptado como una mejor respuesta.
Andrey Portnoy
para saltar rápidamente al siguiente conflicto, solo busque ===. hacer "/ ===" y entrar
Apit John Ismail
Vea esta publicación ( stackoverflow.com/questions/51520705/… ) si se encuentra más de una coincidencia usando :diffget.
Jason
7

La mejor herramienta de combinación para reemplazar vimdiff

Esto es algo irónico, pero es a lo que terminé convergiendo como vimmer después de probar vimdiff.

Para resolver un conflicto de fusión, lo que casi siempre necesito es ver:

  • REMOTO
  • LOCAL
  • dos diferencias:
    • diff BASE REMOTE
    • diff BASE LOCAL

para luego tratar de juntarlos a ambos.

Mientras que vimdiff muestra BASE, LOCAL y REMOTE en la pantalla:

    +--------------------------------+
    | LOCAL  |     BASE     | REMOTE |
    +--------------------------------+
    |             MERGED             |
    +--------------------------------+

No sé cómo hacer que muestre claramente esos dos diferenciales que necesito además de mirar a la derecha, a la izquierda, a la izquierda, varias veces.

Además, LOCAL y REMOTO ya son visibles en los marcadores de conflicto de git merge, por lo que no obtengo mucho de una herramienta que los muestra nuevamente.

Por lo tanto, en su lugar, creé mi propio "difftool" pequeño que realmente muestra los diffs que me faltaba:

~ / bin / cirosantilli-mergetool

#!/usr/bin/env bash
BASE="$1"
LOCAL="$2"
REMOTE="$3"
diff --color -u "$BASE" "$LOCAL"
diff --color -u "$BASE" "$REMOTE"
exit 1

GitHub aguas arriba .

E instálalo con:

git config --global mergetool.cirosantilli-mergetool.cmd 'cirosantilli-mergetool $BASE $LOCAL $REMOTE'
git config --global mergetool.cirosantilli-mergetool.trustExitCode true
# If you want this to become your default mergetool.
#git config --global merge.tool 'cirosantilli-mergetool'

Ahora, cuando lo haces:

git mergetool -t cirosantilli-mergetool

muestra los dos diferenciales que quiero en el terminal, por ejemplo, algo junto:

--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_LOCAL_15560.py       2019-12-27 13:46:41.979021479 +0000
@@ -994,7 +994,7 @@                                                              

     def setupBootLoader(self, cur_sys, loc):
         if not cur_sys.boot_loader:                           
-            cur_sys.boot_loader = [ loc('boot_emm.arm64'), loc('boot_emm.arm') ]
+            cur_sys.boot_loader = [ loc('boot.arm64'), loc('boot.arm') ]
         cur_sys.atags_addr = 0x8000000                  
         cur_sys.load_offset = 0x80000000                    

@@ -1054,7 +1054,7 @@                                           
             ]                                                     

     def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = [ loc('boot_emm_v2.arm64') ]
+        cur_sys.boot_loader = [ loc('boot_v2.arm64') ]
         super(VExpress_GEM5_V2_Base,self).setupBootLoader(
                 cur_sys, loc)                             

--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_REMOTE_15560.py      2019-12-27 13:46:41.991021366 +0000
@@ -610,10 +610,10 @@           
     def attachIO(self, *args, **kwargs):              
         self._attach_io(self._off_chip_devices(), *args, **kwargs)

-    def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = loc('boot.arm') 
-        cur_sys.atags_addr = 0x100                           
-        cur_sys.load_offset = 0       
+    def setupBootLoader(self, cur_sys, boot_loader, atags_addr, load_offset):
+        cur_sys.boot_loader = boot_loader      
+        cur_sys.atags_addr = atags_addr     
+        cur_sys.load_offset = load_offset

Entonces puedes ver aquí los dos diferenciales descargados en la terminal:

  • RealView_BASE_15560.py vs RealView_LOCAL_15560.py
  • RealView_BASE_15560.py vs RealView_REMOTE_15560.py

Si las diferencias son grandes, solo buscaré mis superpoderes tmux .

Sí, usted pierde algunos accesos directos que proporciona vimdiff, pero en general la resolución de conflictos requiere copiar y pegar cuidadosamente las dos versiones, lo que puedo hacer bien dentro de una sesión vim normal con los marcadores de conflicto git.

Observar y diferenciar archivos mientras se vimdiffestá ejecutando

Antes de sentarme y automatizar mi configuración perfecta cirosantilli-mergetool, esto es lo que estaba haciendo para obtener los dos diferenciales que necesitaba.

Mientras se git mergetoolestá ejecutando vimdiff, si hay un conflicto en un archivo llamado, digamos main.py, git genera archivos para cada una de las versiones, nombradas como:

main_BASE_1367.py
main_LOCAL_1367.py
main_REMOTE_1367.py

en el mismo directorio main.pydonde 1367está el PID de git mergetool y, por lo tanto, un entero "aleatorio", como se menciona en: En un conflicto de git merge, ¿cuáles son los archivos BACKUP, BASE, LOCAL y REMOTE que se generan?

Entonces, para ver las diferencias que quiero, primero encuentro los archivos generados git statusy luego abro nuevos terminales y hago un vimdiff entre los pares de archivos que me interesan:

vim -d main_BASE_1367.py main_LOCAL_1367.py
vim -d main_BASE_1367.py main_REMOTE_1367.py

Juntos con git mergetool , esta información ayuda MUCHO a descubrir lo que está sucediendo rápidamente!

Además, incluso mientras se está ejecutando mergetool, solo puede abrir el archivo:

vim main.py

directamente y edítelo allí si cree que va a ser más fácil con una ventana de editor más grande.

Salta directamente para fusionar conflictos

Mientras ]csalta al siguiente punto de diferencia dentro de vimdiff, no siempre hay un conflicto de fusión allí.

Para ayudar con esto, tengo en mi ~/.vimrc:

# Git Merge conflict
nnoremap <leader>gm /\v^\<\<\<\<\<\<\< \|\=\=\=\=\=\=\=$\|\>\>\>\>\>\>\> /<cr>

que encuentra los conflictos directamente.

git imerge

Quizás la mejor opción es simplemente renunciar al uso de vimdiff y confiar en vim + git imerge regular, que se mencionó en: ¿Cómo puedo averiguar qué compromisos de Git causan conflictos? dado que la curva de aprendizaje de vimdiff es molesta y no cumple las funciones que más necesitamos.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1
Votado Creo que mencioné eso hace 9 años en stackoverflow.com/a/3052118/6309 . (ver la última parte de la respuesta)
VonC
@VonC sí, ¡creo que ganaste este! XD
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功