.eu:~/esbloggit-vimdiff/ clp txr pht lnk blg rcm src vid  

Mejoras en git 2.37 al resolver conflictos con vimdiff

-日付 2022 Jun 26- English Español

Git: resolución de conflictos con vimdiff

Si utilizas git para trabajar con repositorios de código estarás acostumbrado a la siguiente situación:

  1. Te bajas el repositorio.
  2. Haces cambios en algunos ficheros.
  3. Cuando terminas, intentas hacer un git push, pero éste falla porque te dice que tus cambios son en una parte del código que otra persona ha modificado desde que te bajaste el repositorio en el punto (1) y mientras trabajabas en (2).

Cuando esto ocurre, se utiliza git mergetool para abrir una herramienta auxiliar que te permita “resolver el conflicto” (es decir, quedarte o bien con tus cambios, o bien con los del otro que ha subido antes que tú, o bien con una mezcla -que tienes que hacer a mano- de los dos).

git mergetool acaba ejecutando la primera de una lista preconfigurada de herramientas especializadas en resolver conflictos que esté instalada, entre las cuales se encuentran meld, kdiff3, xxdiff, …, y vimdiff.

También puedes forzar a que se use una herramienta específica por medio de la variable de configuración merge.tool ó bien utilizando git mergetool --tool=xxx, donde xxx es el nombre de la herramienta a usar.

vimdiff con git version <= 2.36

Hasta la version 2.36 de git (que es la actual en el momento en que escribo esto, en Junio del 2022) para usar vimdiff tienes en realidad varias opciones:

  1. git mergetool --tool=vimdiff
  2. git mergetool --tool=vimdiff1
  3. git mergetool --tool=vimdiff2
  4. git mergetool --tool=vimdiff3

En todos los casos se abre vim con varias ventanas, pero la distribución de las mismas cambia.

Con --tool=vimdiff a secas aparecen cuatro ventanas dentro de vim distribuidas en dos filas:

El resultado es éste:

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

Podemos verlo en un ejemplo real a continuación:

git mergetool --tool=vimdiff
git mergetool --tool=vimdiff

En este caso, comparando “base” con “local” por un lado y “base” con “remote” con otro vemos que, en esencia, tanto nosotros como algún otro desarrollador hemos intentado hacer el mismo cambio al mismo tiempo (añadir “polkit” a la lista de paquetes a instalar) pero con un orden distinto (esta circunstancia también podemos verla en la ventana “merged”, donde las lineas marcadas con <<<<<<<, ======= y >>>>>>> sirven para indicar los cambios provenientes de uno u otro sitio).

Lo que tendríamos que hacer en este caso es, en la ventana de abajo (“merged”) quitar el bloque entero que hay entre <<<<<<< y >>>>>>> y quedarnos solo con una de las líneas que llaman a pacman… y luego simplemente guardar los cambios y, de vuelta a la consola, ejecutar git rebase --continue.

Con --tool=vimdiff1 la distribución de ventanas que se abre en vim es distinta. En esta ocasión solo hay dos ventanas: la de la izquierda con los cambios nuestros (locales) y la de la derecha con los cambios remotos:

------------------------------------------
|                  |                     |
|                  |                     |
|                  |                     |
|     LOCAL        |       REMOTE        |
|                  |                     |
|                  |                     |
|                  |                     |
------------------------------------------

Siguiendo con el ejemplo, esto es lo que veríamos:

git mergetool --tool=vimdiff1
git mergetool --tool=vimdiff1

Seguimos, esta vez con --tool=vimdiff2. Ahora la distribución es la siguiente:

------------------------------------------
|             |           |              |
|             |           |              |
|             |           |              |
|   LOCAL     |   MERGED  |   REMOTE     |
|             |           |              |
|             |           |              |
|             |           |              |
------------------------------------------
git mergetool --tool=vimdiff2
git mergetool --tool=vimdiff2

Y por último --tool=vimdiff3, que solo muestra la ventana “merged”:

------------------------------------------
|                                        |
|                                        |
|                                        |
|                 MERGED                 |
|                                        |
|                                        |
|                                        |
------------------------------------------
git mergetool --tool=vimdiff3
git mergetool --tool=vimdiff3

vimdiff con git version >= 2.37

La versión 2.37 de git que se hará pública la próxima semana (Julio del 2022) incluye un cambio mío (yujuuu!) que hace posible especificar una distribución arbitraria de ventanas dentro de vim. De esta manera ya no hará falta que nadie cree --tool=vimdiff4, --tool=vimdiff5, --tool=vimdiff6, etc… cada vez alguien descubra un nuevo caso de uso.

Curiosidad: el cambio original que envié para incluir en git 2.37 era justamente eso: una nueva variante llamada --tool=vimdiff4 que servía para mostrar las ventanas de otra forma que me parecía más conveniente. Pero la cosa se estaba empezando ya a ir un poco de madre y por eso se decidió, en su lugar, implementar este nuevo mecanismo genérico que permite especificar cualquier distribución (actual y/o que se nos ocurra en el futuro).

Funciona de la siguiente manera:

  1. Al llamar a “mergetool”, hazlo como siempre (git mergetool --tool=vimdiff)
  2. Si la variable de configuración mergetool.vimdiff.layout existe, se utilizará la distribución de ventanas que allí se especifique. En caso contrario se utilizará la misma distribución que se venía utilizando antes de la versión 2.37 (es decir, dos filas de ventanas: la de arriba con “local”, “base” y “remote” y la de abajo con “merged”)

Además, para mantener la compatibilidad hacia atrás, git mergetool --tool=vimdiff1, git mergetool --tool=vimdiff2 y git mergetool --tool=vimdiff3 seguirán existiendo y comportándose como antes (a pesar de que podría obtenerse el mismo resultado usando --tool=vimdiff y fijando la variable mergetool.vimdiff.layout al valor correspondiente).

Y… ¿cómo se usa la nueva variable de configuración mergetool.vimdiff.layout?

La sintaxis está explicada en git help mergetool (en la sección “BACKEND SPECIFIC HINTS):

Veamos algunos ejemplos sencillos:

layout = "LOCAL,BASE,REMOTE / MERGED"
------------------------------------------
|             |           |              |
|   LOCAL     |   BASE    |   REMOTE     |
|             |           |              |
------------------------------------------
|                                        |
|                MERGED                  |
|                                        |
------------------------------------------


layout = "LOCAL,BASE,REMOTE"
------------------------------------------
|             |           |              |
|             |           |              |
|   LOCAL     |   MERGED  |   REMOTE     |
|             |           |              |
|             |           |              |
------------------------------------------


layout = "@LOCAL,REMOTE"
------------------------------------------
|                  |                     |
|                  |                     |
|                  |                     |
|     LOCAL        |       REMOTE        |
|                  |                     |
|                  |                     |
|                  |                     |
------------------------------------------

¡Nuevas posibilidades!

Personalmente siempre he utilizado --tool=vimdiff y en general me ha funcionado muy bien cuando se trataba de conflictos sencillos.

El problema con los conflictos más complicados es que no es fácil ver cuál es la diferencia entre “base” y “local” y entre “base” y “remote” por culpa de cómo vim muestra siempre los cambios “a tres bandas”.

Gracias al nuevo mecanismo de git 2.37 ahora podemos hacer que vim muestre, por ejemplo, tres pestañas de la siguiente manera:

  1. La primera pestaña con lo mismo que se venía mostrando al utilizar git mergetool --tool=vimdiff
  2. La segunda pestaña con solo dos ventanas: a la izquierda el fichero “base” y a la derecha el “local”.
  3. La tercera pestaña con solo dos ventanas: a la izquierda el fichero “base” de nuevo y a la derecha el “remote”.

Lo bueno de las pestañas (2) y (3) es que, como solo hay dos ficheros, el modo “diff” de vimdiff muestra claramente las diferencias entre uno y otro, sin verse “ensuciado” por la presencia de un tercer fichero.

El resultado es éste:

------------------------------------------
| <TAB #1> |  TAB #2  |  TAB #3  |       |
------------------------------------------
|             |           |              |
|   LOCAL     |   BASE    |   REMOTE     |
|             |           |              |
------------------------------------------
|                                        |
|                MERGED                  |
|                                        |
------------------------------------------

------------------------------------------
|  TAB #1  | <TAB #2> |  TAB #3  |       |
------------------------------------------
|                   |                    |
|                   |                    |
|                   |                    |
|     BASE          |    LOCAL           |
|                   |                    |
|                   |                    |
|                   |                    |
------------------------------------------

------------------------------------------
|  TAB #1  |  TAB #2  | <TAB #3> |       |
------------------------------------------
|                   |                    |
|                   |                    |
|                   |                    |
|     BASE          |    REMOTE          |
|                   |                    |
|                   |                    |
|                   |                    |
------------------------------------------

…que en nuestro ejemplo tendría la siguiente pinta:

Tab #1
Tab #1
Tab #2
Tab #2
Tab #3
Tab #3

Para conseguir este efecto lo único que tenemos que hacer es añadir esta línea a nuestro fichero de configuración de git:

[mergetool "vimdiff"]
	layout = "LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE"

Personalmente me gusta ir un paso más allá y añadir una cuarta pestaña que muestra lo mismo que la primera pero en dos columas (en vez de en dos filas), que resulta útil cuando se trabaja con ficheros con líneas largas:

[mergetool "vimdiff"]
	layout = "LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE + (LOCAL/BASE/REMOTE) , MERGED"

Y con todo esto se vuelve a demostrar una vez más que vim es la herramienta definitiva, ya que con ninguna otra (que yo sepa) se puede hacer lo mismo a día de hoy ;)

Notas

Comentarios? Escríbeme! blog@u92.eu