git merge: cómo fusionar ramas y resolver conflictos
git merge une el historial de otra rama con la actual: si la rama actual no ha avanzado, Git simplemente mueve el puntero (fast-forward); si no, crea un commit de merge con dos padres. Cambia a la rama de destino y ejecuta git merge nombre-rama; si hay conflicto, corrige los archivos, haz git add y git commit, o cancélalo todo con git merge --abort.
Qué hace git merge
El comando git merge toma los cambios de la rama indicada y los integra en la rama en la que estás. Recuerda la dirección: se fusiona «hacia ti». Para integrar feature/login en main, primero cambias a main y después lanzas la fusión.
git switch main
git pull # traer el main actualizado del servidor
git merge feature/login
Git no copia nada línea a línea «a mano». Busca el ancestro común de las dos ramas (merge base), compara con él ambas versiones y suma los cambios. Si cada lado tocó sitios distintos, el resultado sale automáticamente. Si tocaron el mismo sitio, surge un conflicto y la decisión es tuya.
Dos modos: fast-forward y fusión a tres bandas
Fast-forward: cuando no hace falta commit de merge
Supón que creaste feature desde main, hiciste tres commits y mientras tanto nadie añadió nada a main. Entonces el historial de main es simplemente el principio del de feature. Git mueve el puntero main hacia delante, hasta el último commit de feature, y no se crea ningún commit nuevo.
$ git merge feature
Updating 3f2a1c4..9b7e0d2
Fast-forward
src/auth.js | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
El historial queda lineal, pero se pierde el dato de que «aquí hubo una rama feature». A veces eso está bien y a veces no; lo vemos más abajo, en el apartado sobre --no-ff.
Fusión a tres bandas y commit de merge
Si ambas ramas han avanzado desde el ancestro común, ya no se puede mover el puntero sin más. Git hace una fusión a tres bandas: la base, el lado «nuestro» (la rama actual) y el lado «suyo» (la rama que se fusiona). El resultado se registra en un commit de merge aparte, que tiene dos padres.
$ git merge feature
Merge made by the 'ort' strategy.
src/auth.js | 42 ++++++++++++++++++++
src/router.js | 3 +-
2 files changed, 44 insertions(+), 1 deletion(-)
Git abrirá el editor con un mensaje como Merge branch 'feature'. Puedes dejarlo así o añadir por qué se hizo la fusión. La «bifurcación» resultante se ve cómodamente así:
git log --oneline --graph --all
La estrategia ort es la predeterminada desde Git 2.34; antes era recursive. Para el trabajo diario no hace falta cambiarla.
Opciones útiles de git merge
| Opción | Qué hace | Cuándo sirve |
|---|---|---|
--no-ff | Crea siempre un commit de merge, aunque sea posible un fast-forward | Quieres ver en el historial qué commits venían de una misma funcionalidad |
--ff-only | Solo fusiona por avance rápido; si no, termina con error | Actualizar una rama local sin commits de merge accidentales |
--squash | Mete todos los cambios de la rama en el índice, pero no hace commit ni crea vínculo con la rama | Convertir una funcionalidad en un único commit limpio |
--no-commit | Hace la fusión pero se detiene antes del commit | Quieres revisar el resultado o pasar los tests antes de registrarlo |
-m "texto" | Define el mensaje del commit de merge | Fusiones en scripts y sin editor |
--abort | Cancela la fusión en curso y vuelve al estado anterior | Hay demasiados conflictos y quieres empezar de nuevo |
--continue | Termina la fusión después de resolver los conflictos | Alternativa a git commit en el último paso |
-X ours / -X theirs | En los fragmentos en conflicto toma automáticamente nuestra versión o la suya | Sabes de antemano qué versión es la correcta (¡con cuidado!) |
--no-ff: conservar los límites de la funcionalidad
Muchos equipos fusionan las funcionalidades solo con --no-ff. Así, en el grafo cada tarea aparece como una «burbuja» y se puede deshacer con un solo git revert -m 1 <commit-de-merge>. Con fast-forward no existe ese punto y habría que revertir cada commit por separado.
--squash: un commit en lugar de diez
git switch main
git merge --squash feature/login
git commit -m "Inicio de sesión con e-mail y contraseña"
Después de --squash, Git no considera fusionada la rama: git branch -d feature/login se negará a borrarla con el aviso «not fully merged». Bórrala con git branch -D cuando hayas comprobado que todo está en su sitio. Y no sigas trabajando en esa misma rama tras el squash: la siguiente fusión volverá a traer los cambios ya integrados y casi seguro provocará conflictos.
Conflicto de fusión: cómo resolverlo
Un conflicto es una situación normal de trabajo, no una avería. Solo significa que dos ramas cambiaron las mismas líneas (o que una borró un archivo que la otra editaba) y Git no se arriesga a adivinar qué versión es la buena.
$ git merge feature/login
Auto-merging src/config.js
CONFLICT (content): Merge conflict in src/config.js
Automatic merge failed; fix conflicts and then commit the result.
Paso 1. Ver qué está en conflicto
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/config.js
Paso 2. Editar el archivo
En el archivo en conflicto Git coloca marcadores:
<<<<<<< HEAD
const TIMEOUT = 5000;
=======
const TIMEOUT = 10000;
>>>>>>> feature/login
La parte de arriba es tu rama actual (HEAD) y la de abajo, la que se fusiona. Deja la versión correcta (o combina ambas), borra las tres líneas de marcadores y guarda el archivo. Un error habitual es olvidar un marcador en mitad del archivo; antes del commit búscalos: git diff --check resaltará los que queden.
Paso 3. Marcar la solución y terminar la fusión
git add src/config.js
git commit # o git merge --continue
Si el conflicto se resuelve entero eligiendo uno de los lados, no hace falta editar el archivo a mano:
git checkout --ours src/config.js # quedarse con la versión de la rama actual
git checkout --theirs src/config.js # tomar la versión de la rama que se fusiona
git add src/config.js
Más cómodo en el editor
VS Code, los IDE de JetBrains y otros editores muestran el conflicto en tres columnas con botones de «aceptar actual / entrante / ambos». También puedes conectar una herramienta externa con git mergetool. Un ajuste útil es git config --global merge.conflictStyle zdiff3: en los marcadores aparecerá además la versión original del ancestro común, y se entiende mejor quién cambió qué.
Si cambias de idea: cancelar la fusión
git merge --abort
El comando devuelve la carpeta de trabajo y el índice al estado anterior a git merge. Por eso conviene tener el árbol de trabajo limpio antes de fusionar: haz commit de los cambios a medias o apártalos con git stash; si no, será difícil separarlos del resultado de la fusión.
Cómo deshacer un merge ya hecho
Si el commit de merge aún no se ha enviado al servidor, lo más sencillo es devolver la rama atrás:
git reset --hard ORIG_HEAD
Git guarda ORIG_HEAD antes de las operaciones peligrosas, incluida la fusión: es la posición de la rama antes del merge. --hard descartará los cambios sin commit, así que revisa git status.
Si la fusión ya está en el repositorio compartido, el historial no se reescribe. Se crea un commit inverso:
git revert -m 1 a1b2c3d
La opción -m 1 indica que el «principal» es el primer padre (la rama en la que se fusionó). Ojo con la trampa: después de este revert, Git considera que los commits de la funcionalidad ya están integrados. Si más adelante quieres volver a fusionar la funcionalidad corregida, primero tendrás que hacer revert del propio commit de revert.
Merge en el trabajo en equipo
git pull es fetch más merge
Un git pull normal descarga los cambios y fusiona la rama remota en la local. Si tú y un compañero habéis hecho commits en paralelo, aparecerá un commit «Merge branch 'main' of ...». Para evitar estas fusiones técnicas, configura git config --global pull.ff only (pull falla si no es posible el avance rápido y decides tú) o usa git pull --rebase.
Pull request y el botón Merge
En GitHub y GitLab la fusión suele hacerse mediante un pull/merge request. El botón «Create a merge commit» equivale a git merge --no-ff, «Squash and merge» a una fusión comprimida y «Rebase and merge» a un rebase. Qué modo usar es un acuerdo del equipo; el mecanismo es el mismo que en la consola.
Integrar main en tu rama
Una rama de larga vida diverge de main y los conflictos se acumulan. Trae la rama principal a la tuya con regularidad:
git switch feature/login
git fetch origin
git merge origin/main
Así los conflictos se resuelven en porciones pequeñas a medida que aparecen, y la fusión final en main va como la seda.
Errores frecuentes
- Fusionar en la dirección equivocada. Ejecutar
git merge mainestando enmain, o fusionar una funcionalidad en otra. Comprueba la rama actual congit branch --show-currentantes de fusionar. - Fusionar con una rama desactualizada. Fusionar el
mainlocal que lleva una semana sin actualizarse. Primerogit pull, luego merge. - «Your local changes would be overwritten by merge». En la carpeta de trabajo hay cambios sin commit en los mismos archivos. Haz commit o guárdalos con stash.
- «refusing to merge unrelated histories». Las ramas no tienen ancestro común; por ejemplo, el repositorio local se creó con
git inity el remoto ya tenía un README. Si de verdad hay que unirlos, añade--allow-unrelated-histories. - Hacer commit con marcadores de conflicto. La compilación falla en
<<<<<<<. Revisagit diff --checky pasa los tests antes del commit.
Resumen
git merge es una forma segura de unir ramas: no reescribe los commits existentes, solo añade uno nuevo (o avanza el puntero). Fusiona en la rama actual, mantén limpio el árbol de trabajo, usa --no-ff si te importan los límites de las funcionalidades y --squash si quieres un solo commit. Los conflictos se resuelven en tres pasos (editar el archivo, git add, git commit), y git merge --abort siempre te devuelve al punto de partida. La fusión de ramas y la resolución de conflictos se practican en detalle en el curso de Git.
Preguntas frecuentes
¿En qué se diferencia git merge de git pull?
git pull primero descarga los cambios del servidor (git fetch) y después fusiona la rama remota en la actual con git merge o, si está configurado, con rebase. git merge solo trabaja con lo que ya está en local.
¿Se pueden fusionar varias ramas a la vez?
Sí, git merge feature-a feature-b hará una fusión pulpo (octopus) con varios padres, pero solo si no hay conflictos. En la práctica las ramas se suelen fusionar de una en una.
¿Cómo veo qué ramas ya están fusionadas en la actual?
git branch --merged muestra las ramas integradas por completo en la actual y git branch --no-merged las que aún no. Las ramas fusionadas se pueden borrar sin problema con git branch -d.
¿Cómo veo de antemano qué traerá una fusión?
Ejecuta git log main..feature para ver los commits que no están en main, y git diff main...feature para ver los cambios de la rama respecto al ancestro común.
¿Qué es ORIG_HEAD después de un merge?
Es una referencia que Git guarda antes de fusionar y que apunta a la posición de la rama antes del merge. Con git reset --hard ORIG_HEAD puedes deshacer una fusión local mientras no se haya enviado al servidor.
Este tema se explica a fondo y con práctica en el curso «Git desde cero hasta desarrollador seguro».
- Ramas: la realidad paralela de tu código
- Conflictos de fusión: no entres en pánico, comprende y resuelve