git merge comando: resolver conflicto, rebase aplastar diferencias, fusionar estrategias
En este artículo cubriremos el método git merge
. Desde cómo fusionar hasta resolver conflictos, pasando por las características del merge commit, estrategias de fusión, diferencias con rebase y squash, y otras opciones para el comando git merge
.
1. Cómo fusionar una rama
El comando git merge
se usa para fusionar una rama en otra.
En el post git branch mencioné que el nombre de la rama es el último commit en esa rama.
Por la misma razón, fusionar ramas acaba creando un nuevo commit que contiene todos los cambios del último commit de ambas ramas,
También puedes pensar en ello como un nuevo commit en la rama de trabajo donde ejecutaste el comando git merge
.
El comando git merge
siempre realiza la confirmación de la fusión en la rama donde se crea.
Por ejemplo, si estuvieras fusionando la rama feature-a
desde la rama main
, usarías un comando como este
$ git switch main
switched to branch 'main'
$ git merge feature-a
Merge made by the 'ort' strategy
En el ejemplo anterior, puedes ver que usamos la estrategia merge-ort
, que es la última estrategia de fusión por defecto de Git, cambiada en 2022.
1.1. estrategia ort
Antes de pasar a la siguiente sección, echemos un vistazo rápido al algoritmo de fusión más reciente de Git, la estrategia ort
.
ort
son las siglas de "Ostensibly Recursive's Twin", lo que significa que fue creado para reemplazar al antiguo algoritmo por defecto, la estrategia recursive
.
Comparada con la estrategia recursiva
, se dice que reduce los conflictos y se ocupa de los ficheros renombrados.
Básicamente se basa en el algoritmo de fusión de 3 vías, que compara los cambios de tres fuentes: el mismo commit antepasado de ambas ramas, el commit más reciente de ambas ramas, y el commit más reciente de ambas ramas, y luego realiza una
fusión. Al fusionar dos ramas, utilizaremos principalmente la estrategia ort
, porque funciona mejor que las estrategias resolve
y recursive
utilizadas anteriormente.
Otras estrategias que vale la pena conocer son la estrategia octopus
para fusionar dos o más ramas, o la estrategia subtree
para fusionar en un subdirectorio.
Por ahora, vamos a familiarizarnos con los nombres.
Estas estrategias pueden especificarse con la opción -s
o --strategy
, como sigue.
$ git merge -s recursive feature-a
2. Cómo resolver un conflicto de fusión
Un conflicto de fusión ocurre cuando la misma línea en el mismo archivo en dos ramas diferentes tiene diferente contenido. Para preservar los datos, Git nunca borrará algo arbitrariamente a menos que le des una opción en la estrategia. En su lugar, pausará la fusión y nos dará una opción.
Veamos esto en acción.
Primero, modificamos el archivo 1.md
con diferente contenido en las ramas main
y develop
.
Entonces, cuando vamos a la rama main
y tratamos de fusionar la rama develop
, obtenemos un conflicto de fusión.
Si no conoces la opción -a
del comando git commit
, consulta git commit post.
En este punto, Git añadirá símbolos como <<<<<<<
HEAD
, =======
, >>>>>>>
branch-name
a los contenidos de los archivos en conflicto para que sepas exactamente lo que hay que arreglar.
Abramos el archivo 1.md
.
Este es el archivo 1.md
abierto en el editor Neovim. Sólo tenemos que mantener lo que queremos en esta parte, eliminar los símbolos que Git ha añadido y proceder a la confirmación.
Fusionar es aún más fácil en un editor que soporte Git, como VSCode. El resaltado es intuitivo, y pulsando el botón de las cuatro frases anteriores <<<<<<< HEAD
se eliminará automáticamente el contenido excluido y los símbolos de Git.
Después de modificar el contenido, completa la fusión con el comando git commit
como una confirmación normal.
3. Diferencia con rebase
Los comandos git merge
y git rebase
tienen algo en común: ambos son formas de fusionar dos ramas y resolver conflictos de fusión.
Sin embargo, hay diferencias en la forma en que funcionan, y es importante entenderlas para que puedas elegir la correcta para tus necesidades.
El comando git merge
crea un nuevo commit de fusión con el último commit de las dos ramas como padre, como se muestra arriba.
Como resultado, hay confirmaciones en el historial de confirmaciones que corresponden a dos ramas juntas.
Las rutas feature-a
, feature-b
, etc. van y vienen en la ruta principal, llamada la rama main
.
El comando git rebase
fusiona las dos ramas para que no haya callejones sin salida en el historial de confirmaciones.
Cómo funciona esto es más fácil de lo que piensas. Echemos un vistazo a la imagen de abajo.
Suponiendo que quieras fusionar la rama feature-a
con la rama main
, que diverge de un commit A
común, el comando git rebase
crea un nuevo commit con los mismos cambios que el commit en feature-a
.
En la figura, estos son los commits C
y D
.
Estos dos commits se añaden a B
, el último commit de la rama main
.
Esto crea un único historial de confirmaciones, pero con el resultado de que todos los cambios en la rama feature-a
pueden fusionarse en la rama main
.
La historia también es más simple, ya que no tenemos que crear una nueva confirmación de fusión.
Sin embargo, no es fácil decir a simple vista si los commits C
y D
son commits fusionados de diferentes ramas, o commits que estaban previamente en la rama main
.
Así que desde una perspectiva histórica, estás perdiendo información.
Por esta razón, deberías usar merge y rebase en situaciones apropiadas, dependiendo de la dirección que esté tomando tu equipo.
Para más información sobre el comando git rebase
, ver este post.
4. Diferencia con squash
Squash es un concepto en Git que comprime múltiples confirmaciones en una.
No existe como un comando separado, pero puede usarse como una opción de los comandos merge o rebase.
Cuando el comando git merge
se utiliza con la opción --squash
, se denomina squash merge.
Veamos la siguiente imagen para entender la fusión squash.
En el commit actual 8c12
, las ramas main
y develop
están divididas.
He probado a hacer un squash merge en la rama main
, y ha funcionado sin problemas.
El resultado del comando git status
muestra que todos los commits de la rama develop
han sido puestos en la rama main
.
Si compruebas el historial desde la confirmación, verás que la rama develop
permanece sin fusionar, pero sus cambios se han registrado como nuevas confirmaciones en main
.
Esto es lo que hace una fusión squash.
La opción squash
también se puede utilizar durante el proceso de rebase, con la diferencia de que la historia de la rama develop
no se deja como antes.
Cubriremos esto de nuevo en el post rebase.
5. Reflexiones finales
Creo que los conflictos de fusión son una de las primeras cosas en las que se atascan los principiantes de Git. Sin embargo, la fusión y la resolución de conflictos de fusión son el núcleo de la colaboración en Git, por lo que es importante entenderlas y dominarlas. Espero que este post te ayude a empezar.
