Capítulo 4 de 5

Tutorial de Git: ramas, fusiones y resolución de conflictos

verificado el 7 septiembre 2026 · 10 min

Respuesta rápida

Crea una rama con git switch -c nom, trabaja en ella y fusiónala con git merge desde la rama de destino. Si hay conflicto, Git inserta marcadores en el archivo: escribe el contenido correcto, borra los marcadores y lanza git add y git commit. git merge --abort lo cancela todo en cualquier momento.

Una rama es una línea de desarrollo paralela. Te permite trabajar en una funcionalidad sin tocar la versión estable, y a tu compañero hacer lo mismo por su lado. Este capítulo cubre el ciclo completo: crear una rama, fusionarla, resolver un conflicto a mano y elegir entre merge y rebase.

git switch en lugar de git checkout

Históricamente, git checkout servía para todo: cambiar de rama, crear una, restaurar un archivo, situarse en un commit concreto. Esa acumulación de papeles es la primera fuente de confusión para quien empieza, y una fuente de accidentes para el resto.

Git 2.23, publicado en agosto de 2019, partió el comando en dos: git switch para moverse entre ramas y git restore para deshacer cambios. Al salir se marcaron como experimentales, ya no lo están en la documentación oficial. Este capítulo los usa y da siempre el equivalente con checkout, que seguirás encontrando durante mucho tiempo en tutoriales y en Stack Overflow.

Intención Comando moderno Forma antigua
Cambiar de rama git switch nom git checkout nom
Crear una rama y situarse en ella git switch -c nom git checkout -b nom
Volver a la rama anterior git switch - git checkout -
Deshacer los cambios de un archivo git restore fichier git checkout -- fichier
Sacar un archivo del índice git restore --staged fichier git reset HEAD fichier

Crear una rama y trabajar en ella

bash
git switch -c feature/panier
Sortie réelle · git switch -c
Switched to a new branch 'feature/panier'

La rama arranca en el commit en el que estabas. A partir de ahí puedes modificar, añadir y hacer commits con normalidad: nada de eso aparecerá en main mientras no lo fusiones.

Git no impone ninguna regla sobre los nombres de rama, pero los prefijos feature/, fix/ y hotfix/ son una convención extendida y las interfaces de los servicios de alojamiento los agrupan visualmente.

Para ver en qué punto estás:

bash
git branch              # las ramas locales
git branch -vv          # con el último commit y la rama remota que sigue
git branch -a           # locales y remotas
git switch -            # volver a la rama anterior

Para publicar la rama y que el equipo la vea:

bash
git push -u origin feature/panier

Traerte la rama de un compañero

En el otro sentido, git fetch actualiza lo que sabes del servidor sin tocar nada en tus ramas:

bash
git fetch origin feature/collegue   # una rama concreta
git fetch --all                     # todos los repositorios remotos
git branch -r                       # lo que sabes del servidor
Sortie réelle · git branch -r après un fetch
  origin/HEAD -> origin/main
  origin/feature/collegue
  origin/main

Solo queda cambiarte a ella. Git crea automáticamente la rama local y la enlaza con la del servidor:

bash
git switch feature/collegue
Sortie réelle · git switch sur une branche distante
branch 'feature/collegue' set up to track 'origin/feature/collegue'.
Switched to a new branch 'feature/collegue'

Cuando Git se niega a cambiar de rama

Es uno de los primeros errores con los que te vas a encontrar:

Sortie réelle · git switch avec des modifications non committées
error: Your local changes to the following files would be overwritten by checkout:
	index.html
Please commit your changes or stash them before you switch branches.
Aborting

Git protege tu trabajo: el archivo que has modificado no tiene el mismo contenido en la otra rama, y cambiar de rama lo sobrescribiría. Tres soluciones, por orden de preferencia.

Hacer un commit, si el trabajo es coherente. Casi siempre es la respuesta correcta, un commit imperfecto en una rama de trabajo no cuesta nada y se corrige más tarde con los comandos para deshacer que vimos en el capítulo anterior.

Guardarlo aparte, si el trabajo está a medias:

bash
git stash          # guarda los cambios aparte
git switch main    # …haces lo que tenías que hacer…
git switch -
git stash pop      # recupera los cambios
Sortie réelle · git stash
Saved working directory and index state WIP on main: 6d495d9 Page d accueil

Tirarlo, con git restore ., si los cambios no valían nada. Sin vuelta atrás.

Fusionar una rama

Cuando la funcionalidad esté terminada, sitúate en la rama de destino y fusiona:

bash
git switch main
git merge feature/panier

Cuando las dos ramas han tocado archivos distintos, Git fusiona solo y no tiene nada que preguntarte. El caso interesante es el otro.

Resolver un conflicto

Esta es la situación reproducida para el capítulo. En feature/panier, la segunda línea de index.html se ha sustituido por un mensaje de carrito vacío. En main, la primera línea se ha modificado para añadir el nombre de la marca. Las dos ramas han tocado el mismo archivo.

Sortie réelle · git merge feature/panier
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

No se ha roto nada. Git ha fusionado lo que ha podido y te deja decidir el resto. Lo primero, preguntar en qué punto estás:

bash
git status
Sortie réelle · git status pendant un conflit
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

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:   index.html

no changes added to commit (use "git add" and/or "git commit -a")

Para obtener solo la lista de archivos pendientes:

bash
git diff --name-only --diff-filter=U

Abre el archivo. Git ha insertado marcadores:

index.html pendant le conflit
<<<<<<< HEAD
<h1>Boutique Gekkode</h1>
<p>Bienvenue sur la boutique.</p>
=======
<h1>Boutique</h1>
<p>Votre panier est vide.</p>
>>>>>>> feature/panier

Leerlos es mecánico. Entre <<<<<<< HEAD y ======= está la versión de la rama en la que estás, aquí main. Entre ======= y >>>>>>> está la versión de la rama que estás fusionando.

Te toca escribir el resultado correcto. No tiene por qué ser una u otra: aquí los dos cambios son buenos y deben convivir.

index.html après résolution
<h1>Boutique Gekkode</h1>
<p>Votre panier est vide.</p>

Borra los tres marcadores, no pueden quedarse en el archivo. Después avisa a Git de que está resuelto y termina la fusión:

bash
git add index.html
git commit -m "Fusionne feature/panier dans main"
Sortie réelle · git log --oneline --graph après la fusion
*   0899b0c Fusionne feature/panier dans main
|  
| * aacf4c0 Panier : message quand le panier est vide
* | 11ff16b Ajoute le nom de la marque au titre
|/  
* 6d495d9 Page d accueil

El commit de fusión tiene dos padres, y el grafo lo deja a la vista.

Cancelarlo todo y empezar de cero

Si el conflicto te supera, no estás obligado a nada:

bash
git merge --abort

El repositorio vuelve exactamente al estado anterior al comando merge. No se pierde nada: puedes volver a intentarlo más tarde o hablarlo con quien escribió la otra rama.

merge o rebase

Los dos integran el trabajo de una rama en otra, pero no de la misma manera.

git merge crea un commit de fusión que enlaza los dos historiales. El historial conserva el rastro exacto de lo que pasó, incluido el hecho de que dos ramas existieron en paralelo. El grafo es fiel, pero se ramifica.

git rebase vuelve a aplicar tus commits encima de la rama de destino, como si hubieras trabajado a partir de su última versión. El historial se queda en línea recta, más fácil de leer, pero se reescribe: tus commits cambian de identificador.

bash
git switch feature/panier
git rebase main

Un rebase puede encontrarse con los mismos conflictos que una fusión, commit a commit. La resolución es idéntica, solo cambia el comando para continuar:

Sortie réelle · conflit pendant un rebase
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
error: could not apply 4937109... Promo
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Could not apply 4937109... Promo
bash
git add index.html
git rebase --continue
# o, para cancelarlo todo
git rebase --abort

La regla de oro cabe en una frase: no hagas nunca rebase de una rama que otras personas ya se han bajado. Reescribir un historial compartido obliga a tus compañeros a reparar el suyo a mano. En tu propia rama de trabajo, antes de proponerla, el rebase es en cambio perfectamente legítimo y deja un historial mucho más legible.

El caso de cada día: alguien ha subido antes que tú

Quieres subir tus cambios y Git se niega:

Sortie réelle · git push refusé
To /root/o.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to '/root/o.git'
hint: Updates were rejected because the remote contains work that you do not

El servidor tiene commits que tú no tienes. Primero hay que integrarlos. Y ahí, en un repositorio recién creado, Git te hace una pregunta en lugar de actuar:

Sortie réelle · git pull sans stratégie configurée
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:

Desde la versión 2.27, Git se niega a elegir por ti. Responde de una vez por todas, como se propone en el capítulo de instalación:

bash
git config --global pull.rebase true

El desarrollo pasa entonces a ser este:

bash
git pull --rebase
git push
Sortie réelle · git pull --rebase puis git log
From /root/o
   622a60b..38b7dc5  main       -> origin/main
Rebasing (1/1)Successfully rebased and updated refs/heads/main.

* 5b45117 Ajoute app.js
* 38b7dc5 Ajoute la doc
* 622a60b Depart

Tu commit se ha recolocado encima del de tu compañero y el historial se ha quedado lineal, sin ningún commit de fusión de más. Es el ajuste que hay que recomendar a un equipo que empieza.

Un aviso para terminar: no intentes nunca desbloquear un push rechazado con git push --force. Ese comando aplasta el trabajo que hay en el servidor. Si de verdad tienes que forzar, después de un rebase de tu propia rama, usa git push --force-with-lease, que se niega a sobrescribir commits que no habías visto.

Hacer limpieza

Una rama fusionada ya no tiene razón de existir. Si se publicó con -u, empuje primero sus últimos commits: Git se niega a borrar una rama local adelantada respecto a su rama remota, aunque esté fusionada en main.

bash
git push origin feature/panier
git branch -d feature/panier
Sortie réelle · suppression d'une branche fusionnée
Deleted branch feature/panier (was bc2203a).

Sin ese push previo, el mensaje es explícito:

Sortie réelle · branche locale en avance sur origin
warning: not deleting branch 'feature/panier' that is not yet merged to
         'refs/remotes/origin/feature/panier', even though it is merged to HEAD
error: the branch 'feature/panier' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/panier'
hint: Disable this message with "git config set advice.forceDeleteBranch false"

Si la rama no se ha fusionado, Git te frena, y es una red de seguridad muy útil:

Sortie réelle · suppression d'une branche non fusionnée
error: the branch 'feature/promo' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/promo'
hint: Disable this message with "git config set advice.forceDeleteBranch false"

Para borrar también la rama en el servidor y limpiar las referencias locales que se han quedado obsoletas:

bash
git push origin --delete feature/panier
git fetch --prune

git fetch trae el estado del servidor sin fusionar nada en tus ramas, al contrario que git pull. La opción --prune aprovecha para borrar las ramas remotas que ya no existen, las que tus compañeros han fusionado y eliminado.

Ya sabes colaborar. El último capítulo trata de la organización: qué modelo de ramas adoptar según tu equipo.

Errores frecuentes

Your local changes would be overwritten by checkout Haz commit de tus cambios, o guárdalos aparte con git stash, antes de cambiar de rama.
You have divergent branches Desde Git 2.27, git pull se niega a elegir por su cuenta entre fusión y rebase. Zanja la cuestión con git config --global pull.rebase true.
! [rejected] non-fast-forward El servidor tiene commits que tú no tienes. Intégralos con git pull --rebase. No uses nunca git push --force para saltártelo: aplasta el trabajo de los demás.
Marcadores de conflicto olvidados Las líneas que empiezan por <<<<<<<, ======= y >>>>>>> tienen que desaparecer del archivo antes del git add.
Rebase de una rama compartida Reescribir un historial que otros ya se han bajado los obliga a reparar el suyo. Haz rebase solo de tus propias ramas sin publicar.
Newsletter

Las nuevas pruebas, tutoriales y proyectos, por correo.

Pruebas reproducibles, código versionado, resultados fechados. Nunca spam.