Las dos lecciones anteriores resolvieron cómo se propone un cambio y cómo se revisa. Queda la pregunta de arriba: qué ramas existen en el repositorio y para qué sirve cada una.

Hasta ahora, gestor-tareas ha vivido con un modelo implícito: una rama main y ramas de trabajo con los prefijos que acordaron en la lección 03-06. Funcionaba porque el equipo desplegaba la aplicación web y punto. Pero la situación ha cambiado: la empresa ha empezado a vender gestor-tareas como producto instalable en los servidores del cliente. Ahora hay una versión 1.4 funcionando en tres clientes que no quieren actualizarse, una versión 2.0 en preparación, y la obligación de publicar correcciones de seguridad para la 1.4 sin arrastrar nada de la 2.0.

Ese escenario —versiones explícitas, varias vivas a la vez, entregas planificadas— es exactamente para el que se diseñó Git Flow, el modelo de ramificación que Vincent Driessen publicó en enero de 2010 y que durante años fue la respuesta por defecto a la pregunta "¿cómo organizamos las ramas?".

Esta lección explica el modelo completo, sin caricaturizarlo y sin venderlo. Es un modelo con una lógica interna impecable y con costes reales, y ambas cosas hay que entenderlas antes de decidir si conviene.

Contenido

  1. El problema que resuelve Git Flow
  2. Las cinco clases de rama
  3. El ciclo completo en un vistazo
  4. develop y main: las dos ramas permanentes
  5. Ramas de funcionalidad: abrir y cerrar
  6. Ramas de versión: estabilizar sin bloquear
  7. Ramas de hotfix: por qué se integran en dos sitios
  8. Dónde encajan las etiquetas
  9. La herramienta git flow
  10. Cuándo tiene sentido Git Flow
  11. Las críticas, y la nota del propio autor

  1. El problema que resuelve Git Flow

Antes del modelo, el problema. Imagina el equipo de gestor-tareas sin ninguna convención de ramas, a punto de publicar la versión 2.0:

  • Ana ha terminado la exportación a CSV y quiere integrarla.
  • Carla está a mitad de la sincronización con el calendario, que no entrará en la 2.0.
  • Bruno está corrigiendo los últimos fallos detectados en las pruebas de la 2.0.
  • Y un cliente acaba de reportar un fallo grave en la 1.4, que hay que arreglar hoy.

Si todo el mundo trabaja contra una sola rama, esto es imposible de gestionar. El trabajo de Carla no puede entrar todavía, pero tampoco puede quedarse tres semanas sin integrar. El arreglo de la 1.4 no puede construirse sobre el código de la 2.0, porque el cliente no quiere la 2.0.

Git Flow responde con una idea sencilla y consecuente: cada tipo de trabajo tiene su propia clase de rama, con reglas explícitas de dónde nace y dónde termina. No se improvisa nada.

  1. Las cinco clases de rama

El modelo define cinco, divididas en dos grupos.

Ramas permanentes (existen siempre, nunca se borran):

  • main (llamada master en el artículo original): contiene exclusivamente código publicado. Cada commit de main es una versión que salió al mundo, y lleva su etiqueta. Nadie confirma directamente aquí.
  • develop: la línea de integración. Contiene todo lo terminado y aceptado que saldrá en la próxima versión. Es el estado "siguiente release".

Ramas de apoyo (nacen, cumplen su función y se borran):

  • feature/*: una funcionalidad en desarrollo.
  • release/*: la preparación y estabilización de una versión concreta.
  • hotfix/*: una corrección urgente sobre lo que hay en producción.

La tabla completa, que es la referencia práctica del modelo:

Rama Nace de Se integra en ¿Permanente? Nombre habitual
main main / master
develop main (una vez, al inicio) develop
feature/* develop develop No feature/exportar-csv
release/* develop main y develop No release/2.0.0
hotfix/* main main y develop No hotfix/2.0.1

Hay dos filas que concentran casi toda la complejidad del modelo, y conviene fijarse en ellas desde ya:

  1. hotfix/* nace de main, no de develop. Es la única clase que lo hace, y es la razón de ser del modelo: para arreglar lo que está en producción hay que partir de lo que está en producción, no del código en desarrollo que lleva quince funcionalidades a medias.
  2. release/* y hotfix/* se integran en dos sitios. Esta doble integración es lo que garantiza que nada se pierda, y es también la fuente de los errores más caros del modelo (apartado 7).

En gestor-tareas, el equipo ya usa los prefijos funcionalidad/, correccion/, documentacion/ y hotfix/ (lección 03-06). Git Flow propone feature/, release/ y hotfix/. El nombre concreto da igual: lo que importa es que a cada clase le corresponda un papel y que todo el mundo lo conozca. Usaremos los nombres canónicos del modelo para que reconozcas la terminología cuando la veas fuera.

  1. El ciclo completo en un vistazo

Este grafo recoge el ciclo entero: dos funcionalidades, una versión, y un hotfix posterior.

gitGraph
   commit id: "inicial" tag: "v1.4.0"
   branch develop
   checkout develop
   commit id: "arranque develop"
   branch feature/exportar-csv
   checkout feature/exportar-csv
   commit id: "F1"
   commit id: "F2"
   checkout develop
   merge feature/exportar-csv
   branch feature/orden-alfabetico
   checkout feature/orden-alfabetico
   commit id: "G1"
   checkout develop
   merge feature/orden-alfabetico
   branch release/2.0.0
   checkout release/2.0.0
   commit id: "subir version a 2.0.0"
   commit id: "corregir fallo de QA"
   checkout main
   merge release/2.0.0 tag: "v2.0.0"
   checkout develop
   merge release/2.0.0
   checkout main
   branch hotfix/2.0.1
   checkout hotfix/2.0.1
   commit id: "arreglar borrado masivo"
   checkout main
   merge hotfix/2.0.1 tag: "v2.0.1"
   checkout develop
   merge hotfix/2.0.1

Léelo de arriba abajo y quédate con la forma general:

  • main es una línea corta de hitos etiquetados. Cinco commits al año, quizá. Cada uno, una versión.
  • develop es la línea de trabajo continuo, donde desemboca todo lo terminado.
  • Las ramas de funcionalidad salen y vuelven a develop.
  • Las ramas de versión salen de develop y desembocan en las dos permanentes.
  • Las de hotfix salen de main y también desembocan en las dos.

Esa forma de "doble carril con puentes" es la firma visual de Git Flow. Si ves un grafo así, ya sabes qué modelo sigue el proyecto.

  1. develop y main: las dos ramas permanentes

main es un registro, no un lugar de trabajo

La regla es absoluta: en main solo entran fusiones de release/* y de hotfix/*. Ningún commit directo, ninguna funcionalidad, ninguna corrección que no venga por uno de esos dos caminos.

La consecuencia práctica es muy valiosa: git log --oneline main es literalmente el historial de versiones del producto, y git checkout v1.4.0 reconstruye exactamente lo que tiene instalado el cliente. Eso es lo que permite depurar un fallo reportado por un cliente concreto sin adivinanzas.

git log --oneline --first-parent main
8f3c2a1 Fusionar rama hotfix/2.0.1
4d9e7b3 Fusionar rama release/2.0.0
b1a6f28 Fusionar rama hotfix/1.4.1
7c2d5e9 Fusionar rama release/1.4.0

El --first-parent (lección 06-04) muestra solo la línea principal, ignorando lo que entró por cada fusión.

develop es la sala de espera de la próxima versión

Todo lo terminado se integra aquí. Y aquí está la primera restricción importante del modelo, que conviene decir en voz alta:

develop no está garantizado como desplegable. Está garantizado como integrado.

Es una diferencia sustancial con los modelos que veremos después. En Git Flow, la garantía de calidad no se aplica en develop: se aplica en la rama de versión, que es donde se estabiliza. develop puede tener funcionalidades a medio pulir, cambios que no se han probado juntos y regresiones que aún nadie ha detectado. Es un estado intermedio, y el modelo lo asume.

Creación inicial, en un proyecto que arranca desde main:

git switch main
git switch -c develop
git push -u origin develop

Y un detalle operativo importante: en la plataforma, la rama por defecto del repositorio debe ser develop, no main. Así las pull requests apuntan por defecto al sitio correcto y los clones nuevos aterrizan donde se trabaja. Es un ajuste de un minuto que evita decenas de PRs abiertas contra la rama equivocada.

  1. Ramas de funcionalidad: abrir y cerrar

Es el ciclo más frecuente y el más simple.

Abrir

git switch develop
git pull                                  # partir de lo último integrado
git switch -c feature/exportar-csv

Nace de develop, siempre. Nacer de main es el error clásico del principiante en este modelo: te llevas una base sin las últimas funcionalidades y acabas con conflictos al integrar.

Trabajar

Commits normales, tantos como haga falta. Enviar a origin si el trabajo dura más de un día o si hay que colaborar:

git push -u origin feature/exportar-csv

Cerrar

Cuando la funcionalidad está terminada y revisada (lecciones 07-01 y 07-02), se integra en develop:

git switch develop
git pull
git merge --no-ff feature/exportar-csv
git push origin develop
git branch -d feature/exportar-csv
git push origin --delete feature/exportar-csv

El --no-ff no es opcional en Git Flow, es parte del modelo. Recuerda de la lección 03-03 que sin él, si develop no ha avanzado, Git haría un fast-forward y los commits de la funcionalidad quedarían diluidos en la línea principal sin dejar rastro de que formaban un conjunto. Con --no-ff se crea siempre un commit de fusión, y eso proporciona tres cosas:

  • El grafo muestra qué commits pertenecían a qué funcionalidad.
  • Se puede revertir la funcionalidad entera con git revert -m 1 <fusión> (lección 05-06).
  • git log --first-parent develop da la lista de funcionalidades integradas, una línea por cada una.

Si el equipo trabaja con pull requests, la plataforma hace este merge --no-ff por ti al pulsar el botón de integrar.

Mantenerse al día durante el desarrollo

Si develop avanza mucho mientras trabajas, actualiza tu rama para no acumular conflictos:

git switch feature/exportar-csv
git rebase develop           # historial lineal; solo si la rama no está compartida
# o bien
git merge develop            # sin reescribir; seguro siempre

La elección entre las dos es política del proyecto y se trata en la lección 08-02. Lo que no se debe hacer es dejar la rama sin actualizar durante semanas: ese es el mecanismo por el que Git Flow acumula conflictos grandes, del que hablaremos en el apartado 11.

  1. Ramas de versión: estabilizar sin bloquear

Esta es la clase de rama que más gente no entiende, y la que justifica todo el modelo.

El problema que resuelve: llega el momento de publicar la 2.0. Hay que subir el número de versión en los ficheros, actualizar el README.md y el registro de cambios, pasar la batería de pruebas manuales, corregir lo que aparezca. Eso lleva una semana. Durante esa semana, ¿qué hace el resto del equipo? Si todos esperan, se pierde una semana de trabajo de tres personas. Si siguen integrando en develop, la versión nunca se congela porque siempre entra algo nuevo.

La solución: se abre una rama release/2.0.0 desde develop. A partir de ese instante:

  • La versión 2.0 es exactamente lo que hay en esa rama. Está congelada.
  • develop queda libre inmediatamente para recibir el trabajo de la 2.1.
  • En la rama de versión solo entran correcciones de fallos y ajustes de publicación. Ninguna funcionalidad nueva. Ninguna.
# Abrir la rama de versión
git switch develop
git pull
git switch -c release/2.0.0

# Ajustes propios de la publicación
# (editar el número de versión en los ficheros del proyecto)
git commit -am "Subir la version a 2.0.0"

# (actualizar CHANGELOG.md con lo que entra en esta versión)
git commit -am "Actualizar el registro de cambios para la 2.0.0"

git push -u origin release/2.0.0

Durante los días siguientes, las pruebas revelan fallos y se corrigen en esta rama:

git switch release/2.0.0
git commit -am "Corregir el desbordamiento del titulo largo en la vista movil"
git push origin release/2.0.0

El cierre: la doble integración

Cuando la versión está lista, se integra en main y en develop:

# 1. A main, que es lo que se publica
git switch main
git pull
git merge --no-ff release/2.0.0 -m "Fusionar rama release/2.0.0"

# 2. Etiquetar la versión (lección 05-05): anotada y firmada
git tag -a v2.0.0 -m "Version 2.0.0

Exportacion a CSV, orden alfabetico configurable y
sincronizacion con calendario."

git push origin main --follow-tags
# 3. Y de vuelta a develop, para no perder las correcciones de estabilización
git switch develop
git pull
git merge --no-ff release/2.0.0 -m "Fusionar rama release/2.0.0 en develop"
git push origin develop

# 4. Borrar la rama
git branch -d release/2.0.0
git push origin --delete release/2.0.0

El paso 3 es el que se olvida, y su consecuencia es desagradable: las cinco correcciones que se hicieron durante la estabilización existen en main pero no en develop. Es decir, la versión 2.1 reintroducirá los cinco fallos que acabas de arreglar. Aparecerán como "regresiones misteriosas" y costarán un día entero de git bisect (lección 06-02) hasta que alguien entienda qué pasó.

Cómo comprobar que no ha ocurrido:

# ¿Hay algo en main que no esté en develop?
git log --oneline develop..main

Si esa orden devuelve algo distinto de los commits de fusión propios de main, tienes trabajo pendiente. Merece la pena convertirlo en una comprobación automática del CI (lección 07-06).

¿Cuándo abrir la rama de versión?

Cuando develop contiene el alcance previsto para esa versión. El criterio no es temporal, es de contenido. Y una precisión: abrir la rama pronto es mejor que tarde, porque libera develop antes. Muchos equipos la abren en cuanto la última funcionalidad prevista está integrada, aunque falte toda la fase de pruebas.

  1. Ramas de hotfix: por qué se integran en dos sitios

Escenario: la 2.0.0 lleva tres días en producción y un cliente descubre que el botón de borrado masivo elimina también las tareas completadas de otros usuarios. Es grave. Hay que arreglarlo hoy.

Y aquí está el punto crítico del modelo: no se puede publicar lo que hay en develop. develop ya lleva cuatro funcionalidades de la 2.1 sin probar. Publicar eso para arreglar un fallo sería cambiar un problema por cinco.

La solución de Git Flow: partir de main, que es exactamente lo que el cliente tiene instalado.

# 1. Nace de main, en la etiqueta de la versión afectada
git switch main
git pull
git switch -c hotfix/2.0.1

# 2. El arreglo mínimo. Nada más.
git commit -am "Corregir el borrado masivo que afectaba a tareas ajenas

El filtro por usuario no se aplicaba en borrarCompletadas().
Refs GT-318."

# 3. Subir el número de versión de parche
git commit -am "Subir la version a 2.0.1"
git push -u origin hotfix/2.0.1

Y el cierre, también doble:

# A main, con su etiqueta
git switch main
git merge --no-ff hotfix/2.0.1 -m "Fusionar rama hotfix/2.0.1"
git tag -a v2.0.1 -m "Version 2.0.1: corregir el borrado masivo"
git push origin main --follow-tags

# A develop, para que el arreglo no se pierda
git switch develop
git merge --no-ff hotfix/2.0.1 -m "Fusionar rama hotfix/2.0.1 en develop"
git push origin develop

git branch -d hotfix/2.0.1
git push origin --delete hotfix/2.0.1

La razón de la doble integración, dicha explícitamente: si el arreglo solo entrara en main, la próxima versión saldría de develop, que no lo tiene, y el fallo volvería. El cliente vería reaparecer en la 2.1 el mismo problema que le arreglaste en la 2.0.1. Es el error más caro que se puede cometer con este modelo, porque destruye la confianza del cliente.

Un caso especial: hotfix mientras hay una versión abierta

Si en ese momento existe una release/2.1.0 en estabilización, el arreglo debe llegar a las tres: main, release/2.1.0 y develop. La regla práctica: se integra en main y en la rama de versión abierta; develop lo recibirá cuando esa versión se cierre. Y si prefieres no razonarlo cada vez, usa git cherry-pick (lección 05-03) para llevar el commit del arreglo a donde falte, y comprueba después con git log --oneline <rama>..main.

Si hay que arreglar una versión antigua

El cliente que sigue en la 1.4 también necesita el arreglo, y main está en la 2.0.1. Aquí el modelo canónico se queda corto y la práctica habitual es tener una rama de mantenimiento de vida larga:

# Rama de mantenimiento creada desde la etiqueta correspondiente
git switch -c soporte/1.4 v1.4.0

# Llevar el arreglo que ya existe en main
git cherry-pick <hash-del-arreglo>
git tag -a v1.4.2 -m "Version 1.4.2: corregir el borrado masivo"
git push -u origin soporte/1.4 --follow-tags

Esta capacidad de mantener varias versiones vivas a la vez es la razón principal por la que un producto instalable elige Git Flow. Ningún otro modelo lo hace tan bien.

  1. Dónde encajan las etiquetas

Las etiquetas de la lección 05-05 tienen en Git Flow un sitio exacto y sin ambigüedad:

Cada commit de fusión en main recibe una etiqueta anotada con el número de versión. Ninguna otra rama se etiqueta.

Origen de la fusión en main Etiqueta Componente de SemVer que cambia
release/2.0.0 con funcionalidades nuevas v2.0.0 o v2.1.0 major o minor
hotfix/2.0.1 v2.0.1 patch

Y el encaje con SemVer es casi automático: release/* sube minor (o major si rompe compatibilidad), hotfix/* sube patch siempre. Por eso el nombre de la rama incluye el número de versión: release/2.1.0 te dice qué etiqueta va a producir antes de abrirla.

Recuerda de la 05-05 las dos reglas que aquí importan:

  • Etiquetas anotadas (git tag -a), no ligeras: llevan autor, fecha y mensaje, y son objetos reales del repositorio.
  • Las etiquetas no se envían solas. git push origin main no las lleva. Usa --follow-tags (envía las anotadas alcanzables desde lo que envías) o git push origin v2.0.1.

Comprobaciones útiles sobre el historial de versiones:

# Todas las versiones publicadas, en orden
git tag -l 'v*' --sort=-v:refname

# Qué cambió entre dos versiones
git log --oneline v2.0.0..v2.0.1

# En qué versión se publicó por primera vez un commit
git describe --contains <hash>

# Versión actual legible, útil para el número de build
git describe --tags

  1. La herramienta git flow

Existe una extensión de línea de comandos, git-flow (originalmente de Vincent Driessen, hoy mantenida sobre todo en la variante AVH), que automatiza las secuencias de los apartados anteriores.

# Instalación
sudo apt install git-flow          # Ubuntu — Ana
brew install git-flow-avh          # macOS — Bruno
# Windows — Carla: incluido en Git for Windows, o vía Chocolatey
# Inicializar en el repositorio (pregunta los nombres de rama y prefijos)
git flow init

Los comandos y su traducción exacta a lo que ya sabes:

Comando de git flow Lo que hace realmente
git flow feature start exportar-csv git switch -c feature/exportar-csv develop
git flow feature publish exportar-csv git push -u origin feature/exportar-csv
git flow feature finish exportar-csv switch develop + merge --no-ff + branch -d
git flow release start 2.0.0 git switch -c release/2.0.0 develop
git flow release finish 2.0.0 merge a main + tag + merge a develop + borrar rama
git flow hotfix start 2.0.1 git switch -c hotfix/2.0.1 main
git flow hotfix finish 2.0.1 merge a main + tag + merge a develop + borrar rama

Insistimos en algo importante: git flow no añade ninguna capacidad a Git. Es azúcar sintáctico sobre switch, merge, tag, push y branch -d. No hay estado oculto ni magia; lo único que guarda es la configuración de nombres de rama en .git/config:

[gitflow "branch"]
    master = main
    develop = develop
[gitflow "prefix"]
    feature = feature/
    release = release/
    hotfix = hotfix/
    versiontag = v

Ventajas: elimina el olvido de la segunda integración, que es el error caro del modelo, y unifica la forma de trabajar del equipo.

Inconvenientes reales, y no son menores:

  • Encaja mal con las pull requests. git flow feature finish fusiona en local y envía. Si el proyecto exige revisión antes de integrar (lecciones 07-01 y 07-02), ese comando se salta el proceso entero. En equipos con revisión obligatoria se usa git flow feature publish y después una PR normal, ignorando finish.
  • Oculta lo que pasa. Quien aprende Git Flow solo con la herramienta no entiende el grafo que está construyendo, y el día que algo falla no sabe por dónde empezar.
  • Es una dependencia más que instalar en tres sistemas operativos distintos.

Consejo práctico: aprende primero los comandos manuales, y adopta la herramienta después si el equipo quiere. Al revés, no.

  1. Cuándo tiene sentido Git Flow

Git Flow tiene mala prensa hoy, y buena parte de esa mala prensa viene de haberse aplicado en proyectos para los que no se diseñó. El modelo es bueno cuando se dan estas condiciones:

Condición Por qué importa
Versiones explícitas y numeradas Todo el modelo gira alrededor de release/* y las etiquetas. Sin versiones, main y develop son redundantes
Varias versiones mantenidas a la vez La combinación hotfix/* + ramas de soporte es la mejor respuesta a este problema
Entregas planificadas, no continuas La rama de versión tiene sentido si hay una fase de estabilización real
El usuario decide cuándo actualiza Software instalable, móvil, empotrado, bibliotecas
Fase de QA manual antes de publicar La rama de versión es exactamente el lugar donde vive esa fase
Equipo mediano o grande con roles definidos Alguien "gestiona la versión" como tarea propia

Casos concretos donde encaja bien:

  • Software instalable en cliente, como el gestor-tareas que la empresa ahora vende.
  • Aplicaciones móviles, donde la tienda impone un ciclo de revisión y el usuario decide cuándo actualiza.
  • Bibliotecas y SDK con compromiso de compatibilidad y varias ramas mantenidas (1.x, 2.x).
  • Sistemas empotrados y firmware, donde una versión mala no se puede retirar.
  • Entornos regulados donde cada versión requiere documentación y aprobación formal.

Y donde encaja mal, que es donde más se ha usado:

  • Aplicaciones web con despliegue continuo. Si publicas cinco veces al día, la rama de versión es un trámite vacío y develop es una copia de main con un día de retraso.
  • SaaS con una sola versión viva. No hay nada que mantener en paralelo; hotfix/* no aporta nada que no aporte una rama normal.
  • Equipos pequeños. El coste de coordinación del modelo se reparte entre pocas personas y pesa demasiado.

  1. Las críticas, y la nota del propio autor

Las objeciones a Git Flow son serias y conviene conocerlas antes de adoptarlo.

Crítica 1: complejidad

Cinco clases de rama, dos permanentes, reglas distintas de origen y destino para cada una, y dos integraciones que hay que recordar. Es mucho protocolo que mantener en la cabeza. En la práctica, todo equipo que usa Git Flow acumula un documento interno de "cómo hacemos las cosas" y aun así alguien se equivoca cada pocas semanas.

Crítica 2: ramas de vida larga e integración tardía

Esta es la crítica de fondo, y va más allá de la comodidad.

Una funcionalidad puede vivir semanas en su rama antes de tocar develop. Y la relación entre el tiempo que una rama pasa separada y el coste de integrarla no es lineal: es explosiva. Cada día que pasa, más commits en develop, más probabilidad de que alguien haya tocado los mismos ficheros, más conflictos y más difíciles.

flowchart LR
    A["Rama abierta<br/>más tiempo"] --> B["Más divergencia<br/>respecto a develop"]
    B --> C["Conflictos más grandes<br/>y difíciles"]
    C --> D["Miedo a integrar"]
    D --> A

Ese bucle se realimenta: cuanto peor es integrar, más se pospone, y cuanto más se pospone, peor es. El modelo no lo causa por sí solo, pero tampoco empuja en contra: al no exigir integración frecuente, la permite.

Hay un segundo efecto derivado: el trabajo terminado tarda mucho en llegar al usuario. Una funcionalidad puede estar acabada en marzo, integrarse en develop en abril, entrar en una release en mayo y publicarse en junio. Tres meses de valor parado en un repositorio.

Crítica 3: develop y main se solapan

En proyectos con una sola versión viva y despliegue continuo, develop es simplemente "main dentro de un rato". Dos ramas que representan casi lo mismo generan trabajo de sincronización sin aportar información. Es la crítica que más se oye, y en ese contexto es acertada.

Crítica 4: fricción con la integración continua

El CI (lección 07-06) quiere ejecutar todo contra la línea principal en cada envío. Con dos ramas permanentes y ramas de versión efímeras, hay que decidir qué se prueba dónde, y suele acabar en configuraciones complicadas de mantener.

La nota del propio Vincent Driessen

En marzo de 2020, diez años después del artículo original, Driessen añadió una nota de reflexión en la cabecera de su propio texto. Su contenido, en resumen:

  • El modelo se escribió en 2010, cuando el mundo del software era distinto y la web todavía se entregaba por versiones.
  • Git Flow sigue siendo adecuado para software con versiones explícitas, que es para lo que se pensó.
  • Para el desarrollo web continuo, donde se despliega constantemente y solo hay una versión en producción, recomienda no adoptar Git Flow y usar un modelo más simple, y menciona explícitamente GitHub Flow como alternativa razonable.
  • Y una advertencia general que vale para todo este módulo: no adoptes ninguna metodología dogmáticamente. Escoge según tu proyecto.

Que el autor de un modelo publique una nota así diez años después es raro y honesto, y es la mejor forma de cerrar este apartado. Git Flow no es una mala idea que hay que evitar: es una herramienta específica que se popularizó más allá de su ámbito.

Errores Comunes y Consejos

Error 1: crear una feature/* desde main. Se parte de una base sin las funcionalidades ya integradas y se acumulan conflictos. Nace de develop, siempre.

Error 2: olvidar integrar la release de vuelta en develop. Las correcciones de estabilización solo quedan en main y los fallos reaparecen en la versión siguiente. Compruébalo con git log --oneline develop..main.

Error 3: olvidar integrar el hotfix en develop. Idéntico problema, y peor, porque es un fallo que el cliente ya te reportó una vez.

Error 4: meter funcionalidades nuevas en una rama de versión. La rama de versión existe para estabilizar. Si entra código nuevo, no se estabiliza nunca y el ciclo se alarga indefinidamente.

Error 5: fusionar con fast-forward. Se pierde la agrupación de commits por funcionalidad y la posibilidad de revertirla entera. --no-ff siempre.

Error 6: etiquetar en develop o en la rama de versión. Las etiquetas van en main, sobre el commit de fusión. Solo ahí.

Error 7: usar etiquetas ligeras. git tag v2.0.0 crea una referencia sin metadatos. Usa git tag -a.

Error 8: no enviar las etiquetas. git push origin main no las lleva. --follow-tags.

Error 9: dejar la rama por defecto del repositorio en main. Todas las PRs se abren contra la rama equivocada. En Git Flow, la rama por defecto es develop.

Error 10: adoptar Git Flow porque es "el estándar". Ya no lo es, y probablemente nunca debió serlo para aplicaciones web. Adóptalo si tu producto tiene versiones explícitas.

Consejo 1: automatiza la comprobación de la doble integración. Un trabajo de CI que ejecute git log --oneline develop..main y falle si hay commits sin propagar elimina el error más caro del modelo.

Consejo 2: usa el número de versión en el nombre de la rama. release/2.1.0 dice qué etiqueta va a producir.

Consejo 3: abre la rama de versión pronto. Libera develop antes y reduce la presión sobre el equipo.

Consejo 4: actualiza tus feature/* con develop a menudo. Es el antídoto directo contra la crítica 2, y está en tu mano.

Consejo 5: si adoptas git flow, empieza por los comandos manuales. Entender el grafo que construyes vale más que ahorrar teclas.

Consejo 6: ramas de soporte (soporte/1.4) para versiones antiguas. El modelo canónico no las incluye y casi todos los productos reales las necesitan.

Ejercicios

Ejercicio 1: montar el ciclo completo

Construye desde cero un repositorio gestor-tareas con el ciclo entero de Git Flow:

  1. Inicializa el repositorio con index.html, app.js y README.md, y etiqueta ese estado como v1.4.0 en main.
  2. Crea develop desde main.
  3. Desarrolla feature/exportar-csv con dos commits e intégrala en develop con --no-ff.
  4. Desarrolla feature/orden-alfabetico con un commit e intégrala igual.
  5. Abre release/2.0.0, sube el número de versión en README.md y corrige un fallo.
  6. Cierra la versión: fusiona en main, etiqueta v2.0.0 como etiqueta anotada, fusiona también en develop y borra la rama.
  7. Muestra el grafo completo y comprueba que se parece al del apartado 3.

Ejercicio 2: hotfix y verificación de la doble integración

Continuando con el repositorio anterior:

  1. Añade una funcionalidad más a develop (simulando el trabajo de la 2.1).
  2. Abre hotfix/2.0.1 desde main, corrige un fallo y sube la versión de parche.
  3. Ciérralo correctamente: main con etiqueta v2.0.1 y develop.
  4. Escribe una orden que compruebe que no queda nada en main sin propagar a develop.
  5. Repite el ejercicio mal a propósito (sin integrar en develop) en una copia del repositorio y observa qué detecta esa comprobación.
  6. Arregla la situación con git cherry-pick.

Ejercicio 3: mantener una versión antigua

  1. Crea una rama de soporte soporte/1.4 desde la etiqueta v1.4.0.
  2. Lleva a esa rama el arreglo del hotfix del ejercicio anterior con git cherry-pick.
  3. Etiqueta el resultado como v1.4.1.
  4. Lista todas las etiquetas ordenadas por versión y comprueba con git describe --contains en qué versión entró cada arreglo.
  5. Muestra con git log --graph --oneline --all --decorate la topología completa, con las tres líneas vivas.

Soluciones

Solución 1:

mkdir /tmp/gitflow && cd /tmp/gitflow
git init -qb main
printf '<h1>Gestor de Tareas</h1>\n' > index.html
printf 'const tareas = [];\n' > app.js
printf '# gestor-tareas\n\nVersion: 1.4.0\n' > README.md
git add . && git commit -q -m "Version inicial de la aplicacion"
git tag -a v1.4.0 -m "Version 1.4.0"
git switch -qc develop
# 3. Primera funcionalidad
git switch -qc feature/exportar-csv
echo "function exportarCSV() { /* ... */ }" >> app.js
git commit -qam "Anadir exportacion a CSV"
echo "function descargar(nombre, datos) { /* ... */ }" >> app.js
git commit -qam "Anadir descarga del fichero generado"
git switch -q develop
git merge -q --no-ff feature/exportar-csv -m "Fusionar rama feature/exportar-csv"
git branch -qd feature/exportar-csv
# 4. Segunda funcionalidad
git switch -qc feature/orden-alfabetico
echo "function ordenar() { tareas.sort(); }" >> app.js
git commit -qam "Anadir ordenacion alfabetica"
git switch -q develop
git merge -q --no-ff feature/orden-alfabetico -m "Fusionar rama feature/orden-alfabetico"
git branch -qd feature/orden-alfabetico
# 5. Rama de versión
git switch -qc release/2.0.0
sed -i 's/Version: 1.4.0/Version: 2.0.0/' README.md
git commit -qam "Subir la version a 2.0.0"
echo "// corregido: comillas en el titulo al exportar" >> app.js
git commit -qam "Corregir el escapado de comillas en la exportacion"
# 6. Cierre con doble integración
git switch -q main
git merge -q --no-ff release/2.0.0 -m "Fusionar rama release/2.0.0"
git tag -a v2.0.0 -m "Version 2.0.0: exportacion a CSV y orden alfabetico"
git switch -q develop
git merge -q --no-ff release/2.0.0 -m "Fusionar rama release/2.0.0 en develop"
git branch -qd release/2.0.0
# 7. El grafo
git log --graph --oneline --all --decorate

Solución 2:

# 1. Trabajo de la 2.1 en develop
git switch -q develop
git switch -qc feature/etiquetas-de-tarea
echo "function etiquetar(id, etiqueta) { /* ... */ }" >> app.js
git commit -qam "Anadir etiquetas a las tareas"
git switch -q develop
git merge -q --no-ff feature/etiquetas-de-tarea -m "Fusionar rama feature/etiquetas-de-tarea"
git branch -qd feature/etiquetas-de-tarea
# 2. Hotfix desde main
git switch -q main
git switch -qc hotfix/2.0.1
echo "// corregido: filtrar por usuario en borrarCompletadas()" >> app.js
git commit -qam "Corregir el borrado masivo que afectaba a tareas ajenas

El filtro por usuario no se aplicaba en borrarCompletadas().
Refs GT-318."
sed -i 's/Version: 2.0.0/Version: 2.0.1/' README.md
git commit -qam "Subir la version a 2.0.1"
# 3. Cierre doble
git switch -q main
git merge -q --no-ff hotfix/2.0.1 -m "Fusionar rama hotfix/2.0.1"
git tag -a v2.0.1 -m "Version 2.0.1: corregir el borrado masivo"
git switch -q develop
git merge -q --no-ff hotfix/2.0.1 -m "Fusionar rama hotfix/2.0.1 en develop"
git branch -qd hotfix/2.0.1
# 4. La comprobación
git log --oneline develop..main
(vacío: todo lo de main está en develop)
# 5. La versión incorrecta, en una copia
cp -r /tmp/gitflow /tmp/gitflow-mal && cd /tmp/gitflow-mal
git switch -q main
git switch -qc hotfix/2.0.2
echo "// corregido: fecha de vencimiento con zona horaria" >> app.js
git commit -qam "Corregir la zona horaria de la fecha de vencimiento"
git switch -q main
git merge -q --no-ff hotfix/2.0.2 -m "Fusionar rama hotfix/2.0.2"
git tag -a v2.0.2 -m "Version 2.0.2"
git branch -qD hotfix/2.0.2          # ¡sin integrar en develop!
git log --oneline develop..main
d4c1f8a Fusionar rama hotfix/2.0.2
9b2e7a3 Corregir la zona horaria de la fecha de vencimiento

La comprobación detecta el commit huérfano: existe en main y no en develop. La próxima versión reintroduciría el fallo.

# 6. Arreglo
git switch -q develop
git cherry-pick 9b2e7a3
git log --oneline develop..main

Ahora solo queda el commit de fusión, que es propio de main y no necesita propagarse.

Solución 3:

cd /tmp/gitflow

# 1. Rama de soporte desde la etiqueta antigua
git switch -qc soporte/1.4 v1.4.0

# 2. Traer el arreglo (el hash del commit de corrección del hotfix)
ARREGLO=$(git log --format=%h --all --grep='borrado masivo que afectaba' -1)
git cherry-pick "$ARREGLO"
# 3. Etiquetar
sed -i 's/Version: 1.4.0/Version: 1.4.1/' README.md
git commit -qam "Subir la version a 1.4.1"
git tag -a v1.4.1 -m "Version 1.4.1: corregir el borrado masivo"
# 4. Inventario de versiones
git tag -l 'v*' --sort=-v:refname
git describe --contains "$ARREGLO"
v2.0.1
v1.4.1
v2.0.0
v1.4.0
# 5. Topología completa
git log --graph --oneline --all --decorate

Se ven las tres líneas vivas: main con sus versiones etiquetadas, develop con el trabajo de la 2.1, y soporte/1.4 colgando de la etiqueta antigua.

Conclusión

Git Flow es el modelo de ramificación más estructurado de los que veremos, y su lógica interna es impecable para el problema que resuelve. Lo esencial:

  • Cinco clases de rama: main (solo versiones publicadas y etiquetadas), develop (integración de lo terminado), feature/* (de develop a develop), release/* (de develop a main y develop) y hotfix/* (de main a main y develop).
  • develop está integrado, no garantizado como desplegable. La estabilización ocurre en la rama de versión, y esa es la diferencia de fondo con los modelos que veremos a continuación.
  • La rama de versión es la pieza clave: congela el alcance y libera develop inmediatamente, permitiendo estabilizar sin parar al equipo.
  • La doble integración de release/* y hotfix/* no es un capricho: sin ella, los arreglos existen en main pero no en develop y los fallos reaparecen en la versión siguiente. Automatiza la comprobación con git log --oneline develop..main.
  • Los hotfix nacen de main porque hay que arreglar lo que está en producción, no lo que está a medias en desarrollo.
  • Las etiquetas anotadas van exclusivamente sobre los commits de fusión en main, y encajan de forma natural con SemVer: release/* sube minor/major, hotfix/* sube patch.
  • git flow es azúcar sintáctico, no capacidad nueva. Cómodo para no olvidar la doble integración, incómodo con las pull requests.
  • Encaja con software de versiones explícitas, varias versiones mantenidas, entregas planificadas y QA manual: producto instalable, móvil, bibliotecas, empotrados.
  • No encaja con web de despliegue continuo y una sola versión viva, y así lo reconoció el propio Driessen en su nota de 2020, donde recomienda modelos más simples para ese caso.

Esa nota es justamente el puente hacia lo que viene. Si tu producto es una aplicación web que se despliega varias veces al día, ¿de verdad hacen falta dos ramas permanentes, ramas de versión y una fase de estabilización? La respuesta del modelo que veremos ahora es un no rotundo: una sola rama de larga vida, siempre desplegable, y ramas cortas que entran por pull request. Eso es el contenido de la lección 07-04: GitHub Flow.

Dominando Git: De Principiante a Avanzado

Módulo 1: Introducción a Git

Módulo 2: Operaciones Básicas de Git

Módulo 3: Ramas y Fusión

Módulo 4: Trabajando con Repositorios Remotos

Módulo 5: Operaciones Avanzadas de Git

Módulo 6: Herramientas y Técnicas de Git

Módulo 7: Estrategias de Colaboración y Flujo de Trabajo

Módulo 8: Mejores Prácticas y Consejos de Git

Módulo 9: Solución de Problemas y Depuración

Módulo 10: Git en el Mundo Real

© Copyright 2026. Todos los derechos reservados