Mejoras en git 2.37 al resolver conflictos con vimdiff
-日付 2022 Jun 26- English EspañolSi utilizas git para trabajar con repositorios de código estarás acostumbrado a la siguiente situación:
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.
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:
git mergetool --tool=vimdiff
git mergetool --tool=vimdiff1
git mergetool --tool=vimdiff2
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:
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:
Seguimos, esta vez con --tool=vimdiff2
. Ahora la distribución es la siguiente:
------------------------------------------
| | | |
| | | |
| | | |
| LOCAL | MERGED | REMOTE |
| | | |
| | | |
| | | |
------------------------------------------
Y por último --tool=vimdiff3
, que solo muestra la ventana “merged”:
------------------------------------------
| |
| |
| |
| MERGED |
| |
| |
| |
------------------------------------------
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:
git mergetool --tool=vimdiff
)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):
@
no está presente, se utilizará
la ventana “merged” automáticamente.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 |
| | |
| | |
| | |
------------------------------------------
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:
git mergetool --tool=vimdiff
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:
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 ;)
git help mergetool