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
- El problema que resuelve Git Flow
- Las cinco clases de rama
- El ciclo completo en un vistazo
developymain: las dos ramas permanentes- Ramas de funcionalidad: abrir y cerrar
- Ramas de versión: estabilizar sin bloquear
- Ramas de hotfix: por qué se integran en dos sitios
- Dónde encajan las etiquetas
- La herramienta
git flow - Cuándo tiene sentido Git Flow
- Las críticas, y la nota del propio autor
- 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.
- Las cinco clases de rama
El modelo define cinco, divididas en dos grupos.
Ramas permanentes (existen siempre, nunca se borran):
main(llamadamasteren el artículo original): contiene exclusivamente código publicado. Cada commit demaines 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 |
— | — | Sí | main / master |
develop |
main (una vez, al inicio) |
— | Sí | 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:
hotfix/*nace demain, no dedevelop. 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.release/*yhotfix/*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 prefijosfuncionalidad/,correccion/,documentacion/yhotfix/(lección 03-06). Git Flow proponefeature/,release/yhotfix/. 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.
- 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:
maines una línea corta de hitos etiquetados. Cinco commits al año, quizá. Cada uno, una versión.developes 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
developy desembocan en las dos permanentes. - Las de hotfix salen de
mainy 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.
develop y main: las dos ramas permanentes
develop y main: las dos ramas permanentesmain 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.
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:
developno 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:
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.
- Ramas de funcionalidad: abrir y cerrar
Es el ciclo más frecuente y el más simple.
Abrir
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:
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-csvEl --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 developda 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 siempreLa 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.
- 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.
developqueda 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.0Durante 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.0El 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.0El 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:
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.
- 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.1Y 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.1La 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-tagsEsta 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.
- 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
mainrecibe 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 mainno las lleva. Usa--follow-tags(envía las anotadas alcanzables desde lo que envías) ogit 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
- La herramienta
git flow
git flowExiste 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 ChocolateyLos 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 = vVentajas: 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 finishfusiona 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 usagit flow feature publishy después una PR normal, ignorandofinish. - 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.
- 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-tareasque 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
developes una copia demaincon 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.
- 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:
- Inicializa el repositorio con
index.html,app.jsyREADME.md, y etiqueta ese estado comov1.4.0enmain. - Crea
developdesdemain. - Desarrolla
feature/exportar-csvcon dos commits e intégrala endevelopcon--no-ff. - Desarrolla
feature/orden-alfabeticocon un commit e intégrala igual. - Abre
release/2.0.0, sube el número de versión enREADME.mdy corrige un fallo. - Cierra la versión: fusiona en
main, etiquetav2.0.0como etiqueta anotada, fusiona también endevelopy borra la rama. - 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:
- Añade una funcionalidad más a
develop(simulando el trabajo de la 2.1). - Abre
hotfix/2.0.1desdemain, corrige un fallo y sube la versión de parche. - Ciérralo correctamente:
maincon etiquetav2.0.1ydevelop. - Escribe una orden que compruebe que no queda nada en
mainsin propagar adevelop. - Repite el ejercicio mal a propósito (sin integrar en
develop) en una copia del repositorio y observa qué detecta esa comprobación. - Arregla la situación con
git cherry-pick.
Ejercicio 3: mantener una versión antigua
- Crea una rama de soporte
soporte/1.4desde la etiquetav1.4.0. - Lleva a esa rama el arreglo del hotfix del ejercicio anterior con
git cherry-pick. - Etiqueta el resultado como
v1.4.1. - Lista todas las etiquetas ordenadas por versión y comprueba con
git describe --containsen qué versión entró cada arreglo. - Muestra con
git log --graph --oneline --all --decoratela 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.0Solució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# 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..mainLa comprobación detecta el commit huérfano: existe en main y no en develop. La próxima versión reintroduciría el fallo.
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"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/*(dedevelopadevelop),release/*(dedevelopamainydevelop) yhotfix/*(demainamainydevelop). developestá 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
developinmediatamente, permitiendo estabilizar sin parar al equipo. - La doble integración de
release/*yhotfix/*no es un capricho: sin ella, los arreglos existen enmainpero no endevelopy los fallos reaparecen en la versión siguiente. Automatiza la comprobación congit log --oneline develop..main. - Los hotfix nacen de
mainporque 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 flowes 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
- ¿Qué es Git?
- Instalando Git
- Terminología Básica de Git
- El Modelo de Datos de Git
- Configurando Git
- Configuración Inicial
Módulo 2: Operaciones Básicas de Git
- Creando un Repositorio
- Clonando un Repositorio
- Flujo de Trabajo Básico de Git
- Preparando y Confirmando Cambios
- Inspeccionando Cambios con git diff
- Visualizando el Historial de Confirmaciones
Módulo 3: Ramas y Fusión
- Entendiendo las Ramas
- Creando y Cambiando Ramas
- Fusionando Ramas
- Estrategias de Fusión
- Resolviendo Conflictos de Fusión
- Gestión de Ramas
Módulo 4: Trabajando con Repositorios Remotos
- Entendiendo los Repositorios Remotos
- Agregando un Repositorio Remoto
- Autenticación con Repositorios Remotos
- Obteniendo y Extrayendo Cambios
- Enviando Cambios
- Rastreando Ramas
Módulo 5: Operaciones Avanzadas de Git
- Rebase
- Rebase Interactivo
- Cherry-Picking de Confirmaciones
- Guardando Cambios Temporales
- Etiquetando Confirmaciones
- Revirtiendo Confirmaciones
Módulo 6: Herramientas y Técnicas de Git
- Usando Git Hooks
- Git Bisect
- Git Blame
- Git Log y Alias
- Submódulos de Git
- Múltiples Copias de Trabajo con git worktree
Módulo 7: Estrategias de Colaboración y Flujo de Trabajo
- Forks y Pull Requests
- Revisiones de Código con Git
- Flujo de Trabajo Git Flow
- GitHub Flow
- Trunk Based Development
- Integración Continua con Git
Módulo 8: Mejores Prácticas y Consejos de Git
- Escribiendo Buenos Mensajes de Confirmación
- Manteniendo un Historial Limpio
- Ignorando Archivos con .gitignore
- Atributos de Fichero con .gitattributes
- Mejores Prácticas de Seguridad
- Consejos de Rendimiento
Módulo 9: Solución de Problemas y Depuración
- Problemas Comunes de Git
- Deshaciendo Cambios
- Resolviendo Divergencias con el Remoto
- Recuperando Confirmaciones Perdidas
- Tratando con Repositorios Corruptos
- Técnicas Avanzadas de Depuración
