Saltar al contenido

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

Después de de nuestra larga compilación de información dimos con la solución este inconveniente que pueden tener ciertos de nuestros lectores. Te brindamos la respuesta y nuestro deseo es servirte de mucha apoyo.

Solución:

Los cuatro búferes 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 cómo se veía el archivo en su rama fuente (desde donde se está fusionando). El búfer intermedio (BASE) es el antepasado común de los dos (por lo que puede comparar cómo las versiones izquierda y derecha se han diferenciado entre sí).

Puede que me equivoque 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 en 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 realizar los cambios que desee; vim le muestra en qué se diferencia de cada una de las vistas superiores, que son las áreas que la combinación automática no pudo manejar. Extraiga los cambios de LOCAL si no desea los cambios REMOTOS. Extraiga los cambios de REMOTE si los prefiere a los cambios LOCALES. Tire de BASE si cree que REMOTE y LOCAL están equivocados. ¡Haz algo completamente diferente si tienes una mejor idea! Al final, los cambios que realice aquí son los que realmente se confirmarán.

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


Primero, para abordar la opción “cancelar todo”, si no desea utilizar “vimdiff” y desea cancelar la combinación: presione Esc, luego escriba :qa! y golpea Ingresar. (consulte también ¿Cómo salgo del editor de Vim?). Git te preguntará si la fusión se completó, responde con n.


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

  • navegar hasta el búfer inferior (resultado de la fusión): Ctrl-Wj
  • navegar a la siguiente diferencia con j/k; o, mejor, usa ]C y [c to navigate to the next and previous diff respectively
  • use zo while on a fold to open it, if you want to see more context
  • for each diff, as per @chepner’s answer, you can either get the code from a local, remote or base version, or edit it and redo as you see fit
    • to get it from the local version, use :diffget LO
    • from remote: :diffget RE
    • from base: :diffget BA
    • or, if you want to edit code yourself, get a version from local/remote/base first, and then go to the insert mode and edit the rest
  • once done, save the merge result, and quit all windows :wqa
    • if you want to abort merging the current file and not mark it as resolved, quit with :cquit instead: How do you cancel an external git diff?
  • normally, git detects that the merge was made and creates the merge commit

It does not appear to be possible to add both local and remote conflict hunks without copy pasting or custom shortcuts: https://vi.stackexchange.com/questions/10534/is-there-a-way-to-take-both-when-using-vim-as-merge-tool which is a shame since add add is such a common conflict type.

To prevent vimdiff from asking you to press enter every time it starts, add to your .vimrc:

set shortmess=Ot

as mentioned at: https://vi.stackexchange.com/questions/771/how-can-i-suppress-the-press-enter-prompt-when-opening-files-in-diff-mode

You can search the Internet for other vimdiff shortcuts. I have found this one useful: https://gist.github.com/hyamamoto/7783966

The ultimate mergetool to replace vimdiff

This is kind of tongue-in-cheek, but it is what I ended up converging to as a vimmer after trying vimdiff.

To resolve a merge conflict, what I almost always need is to see:

  • REMOTE
  • LOCAL
  • two diffs:
    • diff BASE REMOTE
    • diff BASE LOCAL

to then try to put both of them together.

While vimdiff does show BASE, LOCAL and REMOTE in the screen:

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

I don’t know how to make it clearly show those two diffs that I need besides by looking right left right left a bunch of times.

Furthermore, LOCAL and REMOTE are already visible in the git merge conflict markers, so I don’t gain that much from a tool that shows them again.

Therefore, I instead created my own tiny “difftool” that actually shows the diffs that I was missing:

~/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 upstream.

And install it with:

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'

Now, when you do:

git mergetool -t cirosantilli-mergetool

it shows the two diffs that I want on the terminal, e.g. something along:

--- ./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 , cargador de arranque, 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 diffs volcados 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é con mis superpoderes tmux.

TODO: para lograr Nirvana, lo último que quedaba sería una forma de mostrar solo diferencias para el trozo en conflicto. Porque si las diferencias son grandes pero solo un trozo pequeño entra en conflicto, es molesto encontrarlo.

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

Observar y diferenciar archivos mientras vimdiff Esta corriendo

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

Tiempo git mergetool Esta corriendo vimdiff, si hay un conflicto en un archivo llamado, digamos, main.py, git genera archivos para cada una de las versiones, denominados como:

main_BASE_1367.py
main_LOCAL_1367.py
main_REMOTE_1367.py

en el mismo directorio que main.py dónde 1367 es el PID de git mergetool y, por lo tanto, un entero “aleatorio”, como se menciona en: En un conflicto de fusión de git, ¿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 con git status, y luego abra nuevas terminales y haga 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, puede abrir el archivo:

vim main.py

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

Ir directamente a fusionar conflictos

Tiempo ]c salta 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 gm /v^<<<<<<< |=======$|>>>>>>> /

que encuentra los conflictos directamente.

git imerge

Tal vez la mejor opción sea simplemente renunciar al uso de vimdiff y confiar en vim + git imerge regular que se mencionó en: ¿Cómo puedo averiguar qué Git confirma que causa conflictos? ya que la curva de aprendizaje de vimdiff es molesta y no realiza las funciones que más necesitamos.

Sección de Reseñas y Valoraciones

Tienes la posibilidad compartir este tutorial si te valió la pena.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *