Ramas en Git: crear, fusionar y resolver conflictos sin pánico

Qué es realmente una rama en Git, cómo crearla, fusionarla con git merge y resolver un conflicto paso a paso. Más merge vs rebase sin miedo.

Ramas en Git: crear, fusionar y resolver conflictos sin pánico

Hay un mensaje que a casi todo el mundo le ha subido las pulsaciones alguna vez: CONFLICT (content): Merge conflict in App.tsx. Te quedas mirando esos <<<<<<< HEAD que han aparecido dentro de tu archivo y piensas que acabas de romper el repo del equipo. No has roto nada. Un conflicto no es un error: es Git diciéndote que hay una decisión que solo tú puedes tomar, porque él no sabe cuál de las dos versiones es la buena.

El día que entiendes qué es realmente una rama, ese miedo desaparece. Las ramas dejan de ser magia negra y se convierten en la herramienta más útil que tienes para trabajar sin pisar a nadie. Aquí desmonto el modelo mental completo: qué es una rama, cómo crearla y cambiarte a ella, qué pasa de verdad cuando fusionas, cómo resolver un conflicto sin sudar, y por qué el rebase, bien entendido, no muerde.

¿Qué es realmente una rama en Git?

Una rama en Git es un puntero móvil que apunta a un commit. Nada más. Nada de carpetas, copias de tus archivos ni directorios paralelos escondidos: un puntero que señala a un commit concreto y avanza solo cada vez que haces uno nuevo.

Grafo de commits de Git mostrando que main y feature/login son punteros que señalan a distintos commits de una misma cadena, y que HEAD apunta a la rama activa
Una rama es una etiqueta que apunta a un commit dentro del grafo, nunca una copia de archivos. HEAD indica en cuál de esas etiquetas estás situado ahora mismo.

Para que esto encaje hay que ver qué es lo que hay debajo. Cada vez que haces git commit, Git guarda una foto del estado de tu proyecto y le pone un identificador. Ese commit apunta a su commit anterior, que apunta al suyo, y así hacia atrás hasta el principio del repositorio. El resultado es un grafo: una cadena de commits enlazados donde cada uno sabe de dónde viene. Ese grafo, técnicamente un DAG (grafo acíclico dirigido), es lo único real en Git. Es la historia de tu proyecto.

La rama es solo una pegatina sobre uno de esos commits.

Cuando trabajas en main y haces un commit, Git crea el commit nuevo y desplaza la pegatina main para que apunte a él. No copia nada, no mueve archivos de sitio. Solo actualiza a qué commit apunta la pegatina. Por eso crear una rama en Git es instantáneo aunque tu proyecto pese dos gigas: crear una rama es escribir un fichero de 41 bytes con el identificador de un commit. Comparado con otros sistemas de control de versiones antiguos, donde ramificar significaba duplicar el árbol de directorios entero, esto es una diferencia brutal.

Falta una pieza para cerrar el modelo: HEAD. HEAD es el puntero que indica en qué rama estás ahora mismo. Normalmente HEAD no apunta directamente a un commit, sino a una rama, y esa rama apunta a un commit. Cuando haces git switch otra-rama, lo que cambias es a dónde apunta HEAD. Ese es el mecanismo entero: mover punteros. Tenlo presente, porque explica absolutamente todo lo que viene después.

Crear y cambiar de rama

Para crear una rama nueva y cambiarte a ella en un solo paso, usa git switch -c. La -c es de create.

# Crea la rama feature/login a partir del commit actual y salta a ella
git switch -c feature/login

# Ver en qué rama estás y qué otras hay (la actual sale con un asterisco)
git branch

# Volver a la rama principal cuando termines
git switch main

git switch es el comando moderno: existe desde Git 2.23 (agosto de 2019) y hoy es estable y recomendado (fue experimental hasta Git 2.46, en 2024). Antes de que llegara, y todavía hoy en muchísimos tutoriales y en la memoria muscular de medio mundo, se usaba git checkout:

# Forma clásica, exactamente equivalente a git switch -c feature/login
git checkout -b feature/login

Las dos funcionan y las dos seguirán funcionando. git switch existe porque git checkout hacía demasiadas cosas distintas: cambiaba de rama, pero también restauraba archivos individuales y podía dejarte en estados raros sin avisar. Partieron esa navaja suiza en dos comandos con nombres que dicen lo que hacen: git switch para moverte entre ramas y git restore para recuperar archivos. Si estás aprendiendo hoy, quédate con switch. Si trabajas en un equipo donde todos escriben checkout, no pasa nada por seguir la costumbre.

¿Qué ocurre exactamente cuando cambias de rama? HEAD deja de apuntar a main y pasa a apuntar a feature/login, y Git ajusta los archivos de tu directorio de trabajo para que reflejen el commit al que apunta esa rama. Si las dos ramas están en el mismo commit, no verás cambiar nada en tus archivos. En cuanto empieces a commitear en feature/login, esa rama avanzará por su cuenta y main se quedará donde estaba.

Cuando necesites el paso a paso completo, con casos como renombrar ramas o crear una a partir de un commit antiguo, tienes una guía dedicada a crear y cambiar de ramas con más detalle.

La rama como trabajo en paralelo

Aquí está el motivo por el que las ramas importan: te dejan trabajar en algo sin tocar lo que ya funciona. Creas una rama para una feature, haces tus commits ahí, y main sigue intacta y desplegable en todo momento. Si tu experimento sale mal, borras la rama y no ha pasado nada. Si sale bien, la fusionas.

Piensa en el grafo. Cuando creas feature/login y empiezas a commitear, esa rama se separa de main y avanza por su lado. Mientras tanto, otra persona del equipo puede tener su propia fix/checkout avanzando desde el mismo punto de partida. El grafo se abre en dos caminos que crecen en paralelo. Nadie pisa el trabajo del otro porque cada commit vive en su rama, en su rama de la historia.

Esto es lo que habilita el flujo de trabajo real de cualquier equipo. Una rama por feature, una rama por arreglo, una rama para probar esa idea que no sabes si va a funcionar. Y cuando programas con ayuda de un LLM, esto vale doble: dejas que el modelo genere una implementación entera en una rama aislada y la revisas con calma antes de que toque main. Si el código que ha escrito es un desastre, git switch main y a otra cosa. La rama es tu red de seguridad.

El grafo diverge sin problema. Lo interesante, y donde aparece todo el drama, es cuando esos caminos separados tienen que volver a juntarse.

Fusionar: fast-forward vs 3-way merge

Juntar dos ramas se hace con git merge, y Git lo resuelve de dos formas distintas según la forma que tenga el grafo. Te sitúas en la rama que quieres que reciba los cambios y le pasas el nombre de la que quieres integrar.

# Estás en main y quieres traerte lo de feature/login
git switch main
git merge feature/login
Comparación visual entre un fast-forward merge, donde el puntero de main simplemente avanza sobre una historia lineal, y un 3-way merge, donde dos historias divergentes se unen en un nuevo commit de merge con dos padres
Fast-forward solo desplaza el puntero cuando la historia es lineal; el 3-way merge crea un commit con dos padres cuando main y la rama han divergido.

El primer caso es el fast-forward. Ocurre cuando main no se ha movido desde que creaste tu rama. Es decir, main es un ancestro directo del último commit de feature/login: no hay nada nuevo en main que integrar. Git no necesita inventar nada. Solo desplaza el puntero de main hacia adelante hasta el commit donde está feature/login, como si arrastrara la pegatina por la cadena de commits. La salida se ve así:

Updating a1b2c3d..e4f5a6b
Fast-forward
 src/login.ts | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

El historial queda lineal, como si nunca hubieras ramificado.

El segundo caso es el 3-way merge. Ocurre cuando las dos ramas han avanzado por separado: tú commiteaste en feature/login y, mientras tanto, alguien metió commits en main. Ahora las historias han divergido y ya no hay una línea recta que seguir. Git no puede limitarse a arrastrar el puntero, porque perdería los commits que hay en main. Lo que hace es coger tres commits: la punta de main, la punta de feature/login, y el ancestro común del que salieron las dos (el punto donde el grafo se abrió). Con esos tres calcula el resultado combinado y crea un commit de merge: un commit especial que tiene dos padres en vez de uno, uniendo las dos ramas en el grafo.

Merge made by the 'ort' strategy.
 src/login.ts   | 24 ++++++++++++++++++++++++
 src/header.tsx |  8 ++++----
 2 files changed, 28 insertions(+), 4 deletions(-)

La mayoría de las veces Git combina todo esto él solo, sin que tengas que hacer nada. Si los cambios de las dos ramas tocan archivos distintos, o líneas distintas del mismo archivo, no hay ambigüedad y el merge sale limpio. El problema aparece solo cuando las dos ramas modifican exactamente las mismas líneas. Ahí Git levanta las manos.

Un concepto nuevo cada semana

Qué es realmente un conflicto de merge (y por qué no es tu culpa)

Un conflicto de merge ocurre cuando las dos ramas que intentas fusionar han modificado las mismas líneas del mismo archivo, y Git no tiene forma de saber cuál de las dos versiones quieres conservar. Nada se ha corrompido y nada has hecho mal: Git simplemente te está avisando de que aquí hay dos verdades distintas y que no puede elegir por ti.

Cuando pasa, verás algo así al hacer git merge:

Auto-merging src/App.tsx
CONFLICT (content): Merge conflict in src/App.tsx
Automatic merge failed; fix conflicts and then commit the result.

Fíjate en la última línea, porque es la instrucción: arregla los conflictos y luego commitea el resultado. Git no ha roto nada. Ha hecho todo lo que podía hacer solo y te ha dejado el resto de trabajo marcado en los archivos afectados. El merge está en pausa, esperándote.

Si ahora abres src/App.tsx, en la zona conflictiva encontrarás los marcadores:

function App() {
<<<<<<< HEAD
  const title = "Panel de control";
=======
  const title = "Dashboard";
>>>>>>> feature/login
  return <h1>{title}</h1>;
}

La anatomía es sencilla una vez la ves con calma. Todo lo que hay entre <<<<<<< HEAD y ======= es la versión de tu rama actual, la que tenías antes de fusionar. Todo lo que hay entre ======= y >>>>>>> feature/login es la versión que viene de la rama que estás integrando. Los <<<<<<<, ======= y >>>>>>> no son código: son señales que Git ha inyectado en el archivo para que tú veas las dos opciones enfrentadas. Tu trabajo es borrar esas señales y dejar el archivo como debe quedar de verdad.

Nadie ha hecho nada mal. Dos personas (o tú en dos momentos distintos) tuvieron una idea distinta para la misma línea. Git se limita a poner las dos sobre la mesa.

Resolver un conflicto paso a paso, sin pánico

Resolver un conflicto es un proceso mecánico de cuatro pasos, y ninguno de ellos puede romper tu repositorio. Lo primero, respira. Lo segundo, ejecuta git status, que en medio de un merge se convierte en tu mapa:

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/App.tsx

Ahí tienes todo lo que necesitas. La sección Unmerged paths lista exactamente qué archivos están en conflicto. Solo esos. El resto del merge ya está resuelto y esperando. Y Git mismo te recuerda las dos salidas: resolver y commitear, o abortar.

El paso siguiente es abrir cada archivo de esa lista y decidir. Editas la zona de los marcadores hasta que quede lo que tiene que quedar y borras las tres líneas de marcadores. Puede que te quedes con tu versión, con la de la otra rama, o con una mezcla de las dos. Siguiendo el ejemplo de antes, si la buena es “Panel de control”, el archivo debe terminar así:

function App() {
  const title = "Panel de control";
  return <h1>{title}</h1>;
}

Sin marcadores, sin restos, con el código exacto que quieres. Este es el paso donde de verdad piensas: lees las dos versiones y eliges con criterio. Cuando el archivo esté como debe, se lo dices a Git con git add, que aquí significa “este conflicto ya está resuelto”:

# Marca el archivo como resuelto
git add src/App.tsx

# Si hay más archivos en conflicto, repítelo con cada uno
# Cuando no quede ninguno, cierra el merge:
git commit

Al hacer git commit sin más, Git te ofrece un mensaje de merge ya redactado; puedes aceptarlo tal cual. Como alternativa, existe un comando pensado exactamente para este momento:

git merge --continue

git merge --continue hace lo mismo que git commit en este contexto: cierra el merge una vez has resuelto todos los conflictos. Usa el que prefieras.

¿Y si te agobias, o te das cuenta de que este merge era mala idea, o simplemente quieres volver atrás y pensarlo? Esa es la salida de emergencia, y es la razón por la que nunca hay que entrar en pánico:

# Cancela el merge y devuelve tu repo al estado exacto de antes de empezar
git merge --abort

git merge --abort reconstruye el estado previo al merge como si nunca lo hubieras intentado. Los marcadores desaparecen, tus archivos vuelven a estar como estaban, y no queda ni rastro. Este es el comando que hay que grabarse a fuego. La inmensa mayoría de los desastres de “he entrado en pánico y he borrado el repositorio para clonarlo de nuevo” se resuelven con este único comando.

Si quieres el flujo completo con casos más peliagudos, como conflictos en archivos binarios o herramientas visuales de resolución, tienes una guía dedicada a resolver conflictos de merge.

Pruébalo tú mismo. En esta simulación acompañas a Alex en su primer merge con conflicto: verás aparecer los marcadores, decidirás qué versión conservar y cerrarás el merge sin romper nada. Es el sitio perfecto para equivocarte, porque no hay ningún repositorio de verdad que puedas estropear.

Cargando ejercicio...

Cuando resolver conflictos deje de imponerte respeto, el resto de Git va sobre ruedas. En el curso Git de cero a profesional practicas ramificar, fusionar y resolver conflictos en simuladores seguros, sin tocar código real; y llegas también al rebase, que es justo lo que vemos a continuación.

Rebase con calma: qué es y cuándo NO usarlo

git rebase es la otra forma de integrar cambios entre ramas, y hace algo distinto al merge: en lugar de crear un commit que une las dos historias, coge tus commits y los vuelve a aplicar uno a uno sobre la punta de otra rama, como si los hubieras escrito ahí desde el principio.

# Estás en feature/login y quieres reconstruirla sobre la main actualizada
git switch feature/login
git rebase main

El resultado es una historia lineal, sin commits de merge, como si tu rama hubiera salido de la versión más reciente de main en vez de una vieja. Para muchos equipos, ese historial recto y fácil de leer es justo lo que buscan. Si aparece un conflicto durante el rebase, el flujo es idéntico al del merge pero con sus propios comandos: editas, haces git add, y luego git rebase --continue; y si te quieres bajar, git rebase --abort te devuelve al estado anterior.

Aquí viene lo importante, la única regla que tienes que memorizar sobre rebase:

Nunca hagas rebase de commits que ya has pusheado o compartido con tu equipo.

El motivo es directo. Rebase no mueve tus commits: los reescribe. Crea commits nuevos con identificadores nuevos y descarta los viejos. Mientras esos commits solo existan en tu máquina, reescribirlos no afecta a nadie. Pero si ya los habías subido a una rama compartida y otra persona los tenía, al reescribir la historia le dejas su copia apuntando a commits que ya no existen. El siguiente git pull de tu compañero se convierte en un caos de divergencias, y ese es exactamente el tipo de lío que da mala fama al rebase sin que la culpa sea suya.

La regla en corto: rebase para limpiar tu propio trabajo local antes de compartirlo, merge para integrar lo que ya es público. Con esa línea no te equivocas casi nunca.

merge vs rebase de un vistazo

git mergegit rebase
Qué le hace a la historiaLa preserva tal cual, con sus bifurcaciones realesLa reescribe: crea commits nuevos sobre otra base
Forma del grafoAñade un commit de merge con dos padresDeja una línea recta, sin commits de merge
Seguro en ramas compartidasSí, siempreNo: solo en commits que aún no has compartido
Cuándo usarloIntegrar una feature terminada en mainPoner al día tu rama local o limpiarla antes del push
Deshacerlogit merge --abort antes de commitear, o revertir el commit de mergeMás delicado; hay que recuperar la posición previa con el reflog

La tabla resume la decisión, pero cada fila da para un artículo. Si quieres profundizar en cuándo elegir merge o rebase según tu flujo de equipo, ahí está el detalle.

Errores comunes

Tratar la rama como una carpeta o una copia

Es el error del que nacen casi todos los demás. Si piensas que una rama es una copia de tus archivos, cambiar de rama te da miedo (¿dónde está mi código?), fusionar te da miedo (¿va a mezclar dos carpetas?), y un conflicto te parece una catástrofe. En cuanto interiorizas que una rama es solo un puntero a un commit, y que el grafo de commits es lo único real, el miedo se va. Los archivos de tu directorio de trabajo simplemente reflejan el commit al que apuntas ahora mismo. Cambias de puntero, cambian los archivos. Nada se pierde.

Entrar en pánico ante un conflicto

El clásico: aparece CONFLICT, cunde el nervio, y acabas borrando la carpeta del proyecto para clonarla otra vez desde cero. Perdiendo, de paso, todo el trabajo sin commitear. No hace falta nada de eso. git merge --abort te devuelve al segundo exacto anterior al merge. Si el conflicto te supera hoy, abortas, respiras, y lo miras mañana con la cabeza fría. El repositorio no se ha movido.

Rebase de historia ya compartida

Ya lo hemos visto arriba, pero es tan común que merece repetirse: hacer git rebase de commits que ya están pusheados en una rama que otros usan reescribe la historia bajo sus pies. Sus repositorios apuntan a commits que ya no existen y la sincronización se rompe. Si la rama es compartida y ya la has subido, intégrala con merge y déjate de reescrituras.

”Resolver” un conflicto borrando el lado del otro

Cuando ves los dos bloques del conflicto, la tentación de borrar todo un lado sin leerlo es real, sobre todo con prisa. El problema es que en el lado que borras sin mirar puede haber trabajo legítimo de un compañero que ahora desaparece en silencio, sin que nadie se entere hasta que algo falla en producción. Resolver un conflicto significa leer las dos versiones y decidir con criterio, en vez de quedarte con la tuya por defecto.

Días enteros trabajando en main sin ramificar

Cuanto más tardas en integrar, más divergen las historias y más grande y doloroso es el conflicto cuando por fin fusionas. Ramas pequeñas y de vida corta, que se integran a menudo, casi nunca dan conflictos serios. Una rama que lleva tres semanas suelta es una bomba de relojería.

Practica hasta que deje de dar miedo

Una rama es solo un puntero. Un conflicto es una decisión que Git te delega. Y cuando algo se tuerce, git merge --abort te devuelve al punto de partida. Con esas ideas interiorizadas, Git deja de dar miedo, pero la soltura se gana repitiendo el ciclo hasta tenerlo en los dedos. El curso Git de cero a profesional te lleva por todo el flujo (ramas, merge, conflictos y rebase) con simuladores interactivos donde puedes romper cosas sin consecuencias y aprender de verdad haciéndolo.

Preguntas Frecuentes

¿Cuál es la diferencia entre git switch y git checkout?

git switch es un comando más moderno (disponible desde Git 2.23; estable y recomendado desde hace años) que sirve solo para moverte entre ramas y crearlas con -c. git checkout es el clásico y hace más cosas: cambia de rama, pero también restaura archivos individuales, lo que lo hacía confuso y propenso a sorpresas. Las dos formas hacen lo mismo aquí:

git switch -c feature/login   # moderno
git checkout -b feature/login # clásico, equivalente

git checkout -b sigue siendo perfectamente válida, pero si estás aprendiendo hoy, git switch es más claro porque su nombre dice exactamente lo que hace.

¿Un conflicto de merge significa que he roto algo?

No. Un conflicto significa que dos ramas modificaron las mismas líneas del mismo archivo y Git no puede decidir por su cuenta cuál conservar. Es una pausa en la que Git te pide una decisión. Resuelves el archivo, haces git add y git commit, y sigues. Y si te agobia, git merge --abort cancela el merge y devuelve todo al estado de antes.

¿Merge o rebase en el día a día?

Regla simple: usa merge para integrar en ramas compartidas y rebase solo para limpiar tu trabajo local antes de subirlo. Merge preserva la historia real y es seguro siempre. Rebase reescribe la historia y deja un grafo lineal más limpio, pero es peligroso sobre commits que ya has pusheado. Si dudas, haz merge; nunca te va a romper la historia del equipo.

¿Cómo cancelo un merge a medias?

Con git merge --abort. Este comando reconstruye el estado exacto anterior al merge: quita los marcadores de conflicto, devuelve tus archivos como estaban y actúa como si nunca lo hubieras intentado. Es la salida de emergencia y la razón por la que nunca hace falta borrar el repositorio ni volver a clonarlo cuando algo se tuerce.

¿Qué es HEAD en Git?

HEAD es el puntero que indica dónde estás situado ahora mismo: en qué rama y, por tanto, en qué commit. Cuando haces git switch otra-rama, lo que cambias es a dónde apunta HEAD, y Git ajusta los archivos de tu directorio de trabajo para reflejar ese commit.