La lección anterior terminó con la nota que el propio Vincent Driessen añadió a su artículo diez años después: para desarrollo web continuo, Git Flow es demasiado. Esta lección explica qué hay al otro lado de esa recomendación.
GitHub Flow es el modelo minimalista. Nació de la práctica interna de GitHub y lo formalizó Scott Chacon en 2011 en un artículo titulado, sin rodeos, "GitHub Flow", con una premisa que era casi una provocación en su momento: para muchos proyectos, una sola rama de larga vida es suficiente.
Y conviene entender bien de dónde sale esa afirmación. No es simplificar por comodidad. Es que Git Flow resuelve un problema —coordinar varias versiones publicadas y una fase de estabilización— que muchos proyectos no tienen. Si tu aplicación se despliega desde un servidor que tú controlas, si solo existe una versión en producción en cada momento, y si puedes publicar un arreglo veinte minutos después de escribirlo, entonces develop, release/* y hotfix/* no te están resolviendo nada: te están cobrando complejidad a cambio de nada.
En gestor-tareas conviven las dos realidades. La empresa vende la versión instalable —y para esa mantienen Git Flow, como vimos— pero también opera una versión en la nube, app.ejemplo.es, que despliegan ellos varias veces por semana. Para esa segunda línea, el equipo ha decidido probar GitHub Flow.
Contenido
- La idea en una frase
- Las seis reglas
- El ciclo completo, con grafo
- La restricción que lo sostiene todo:
mainsiempre desplegable - Qué exige a cambio
- El método de integración: fusión, squash o rebase
- Versiones sin ramas de versión: etiquetas sobre
main - Ramas protegidas: el mecanismo que hace cumplir el flujo
- Cuándo encaja GitHub Flow
- Sus límites
- La idea en una frase
Hay una única rama de larga vida,
main, que está siempre en estado desplegable. Todo lo demás son ramas cortas que salen demain, se revisan en una pull request y vuelven amain.
Eso es todo el modelo. No hay develop, no hay release/*, no hay hotfix/*. Un arreglo urgente no necesita una clase de rama propia porque cualquier rama es igual de rápida: sale de main, se revisa, entra y se despliega. La distinción entre "corrección urgente" y "funcionalidad normal" desaparece del modelo de ramas y pasa a ser, como mucho, una etiqueta en el ticket.
La comparación de topologías, sin entrar todavía en la comparación completa que haremos en la lección 07-05:
| Git Flow | GitHub Flow | |
|---|---|---|
| Ramas permanentes | 2 (main, develop) |
1 (main) |
| Clases de rama de apoyo | 3 (feature, release, hotfix) |
1 (rama de trabajo) |
| Reglas de origen y destino | 5 combinaciones distintas | 1: de main a main |
| Integraciones dobles | Sí (release, hotfix) |
Ninguna |
Y una consecuencia que se nota desde el primer día: es imposible olvidarse de la segunda integración, porque no existe.
- Las seis reglas
Chacon las enunció así. Las reproducimos con lo que significa cada una en la práctica.
Regla 1: todo lo que hay en main es desplegable
No "compila". No "casi está". Desplegable: si ahora mismo alguien despliega main a producción, el resultado es correcto. Es la regla de la que dependen las otras cinco, y le dedicamos el apartado 4 entero.
Regla 2: crea ramas con nombres descriptivos a partir de main
Siempre desde main, siempre actualizado. El nombre debe explicar el trabajo a alguien que solo vea la lista de ramas: sigue las convenciones de la lección 03-06 —funcionalidad/, correccion/, documentacion/— y evita pruebas, temporal o rama-de-ana.
Regla 3: envía a la rama con nombre constantemente
git commit -am "Filtrar la lista por etiqueta seleccionada"
git push -u origin funcionalidad/filtro-por-etiquetaNo una vez al final: a menudo. Tres motivos, y los tres son prácticos:
- Copia de seguridad. Si el portátil de Bruno muere esta tarde, el trabajo está en el servidor.
- Visibilidad. El resto del equipo ve en qué se está trabajando y no duplica esfuerzos.
- El CI se ejecuta en cada envío (lección 07-06). Un fallo detectado a los diez minutos cuesta minutos; el mismo fallo detectado tres días después cuesta horas de reconstruir el contexto.
Regla 4: abre una pull request en cualquier momento
En cualquier momento, incluidos el primer día y el primer commit. Esta regla desconcierta a mucha gente: ¿para qué abrir una PR de algo sin terminar?
Porque la PR no es solo una petición de integrar: es el sitio donde vive la conversación (lección 07-01). Abrirla pronto sirve para pedir opinión sobre el enfoque antes de invertir tres días, para dejar constancia de en qué estás, para que el CI lo pruebe, y para que alguien pueda decirte "eso ya lo hizo Carla en otro sitio" antes de que sea tarde. Para eso están las PRs en borrador.
Regla 5: integra solo después de la revisión
Nada entra en main sin que otra persona lo haya mirado (lección 07-02). Y sin que el CI esté en verde. Estos dos requisitos no dependen de la buena voluntad: se imponen técnicamente con ramas protegidas (apartado 8).
Regla 6: despliega inmediatamente después de integrar
Fusionar en main y desplegar son, idealmente, el mismo acto. Cuanto más corto sea el hueco entre las dos cosas, mejor: si algo va mal, sabes exactamente qué cambio lo causó, porque solo ha entrado uno.
Una variante frecuente en la práctica invierte el orden: desplegar la rama primero y fusionar después de comprobar que funciona en producción. Es lo que GitHub hace internamente. Tiene la ventaja de que
mainnunca llega a contener nada roto, y el coste de necesitar infraestructura para desplegar ramas arbitrarias. La mecánica de ese despliegue —entornos, promoción, reversión— es materia de la lección 10-05; aquí nos quedamos en la parte que toca a Git.
- El ciclo completo, con grafo
gitGraph commit id: "C1" tag: "despliegue" branch funcionalidad/filtro-por-etiqueta checkout funcionalidad/filtro-por-etiqueta commit id: "F1" commit id: "F2" checkout main merge funcionalidad/filtro-por-etiqueta tag: "despliegue" branch correccion/foco-tras-borrar checkout correccion/foco-tras-borrar commit id: "B1" checkout main merge correccion/foco-tras-borrar tag: "despliegue" branch funcionalidad/exportar-csv checkout funcionalidad/exportar-csv commit id: "E1" commit id: "E2" checkout main merge funcionalidad/exportar-csv tag: "despliegue"
Compáralo mentalmente con el grafo de la lección anterior. Aquí no hay doble carril, no hay puentes entre ramas permanentes, no hay fase de estabilización. Una línea recta con ramas cortas colgando, y un despliegue en cada punto de reunión.
El ciclo completo de Ana, de principio a fin:
# 1. Partir de main actualizado
git switch main
git pull
# 2. Rama con nombre descriptivo
git switch -c funcionalidad/filtro-por-etiqueta
# 3. Trabajar y enviar a menudo
git commit -am "Anadir el selector de etiquetas a la barra superior"
git push -u origin funcionalidad/filtro-por-etiqueta
# ... más commits, más envíos
# 4. Abrir la PR (en borrador si aún no está lista)
# 5. Revisión: aplicar los comentarios en la misma rama
git commit -am "Conservar el filtro al recargar la pagina"
git push
# 6. CI en verde + aprobación -> integrar desde la plataforma
# 7. Limpieza
git switch main
git pull
git branch -d funcionalidad/filtro-por-etiquetaUn detalle que importa más de lo que parece: la rama debería durar días, no semanas. Si una rama de GitHub Flow lleva tres semanas abierta, el modelo ha dejado de funcionar: has reconstruido una rama de vida larga con otro nombre, y con ella todos los problemas de integración tardía que criticábamos en la lección anterior. La lección 07-05 llevará esta idea a su extremo.
Mantener la rama al día
Como main avanza mientras trabajas, y la rama es corta, esto suele ser trivial:
git switch funcionalidad/filtro-por-etiqueta
git fetch origin
git rebase origin/main # historial lineal
# o
git merge origin/main # sin reescribirMuchos equipos configuran la plataforma para exigir que la rama esté actualizada con main antes de poder integrar. Es una garantía adicional (el CI habrá probado la combinación real) a cambio de tener que actualizar la rama cuando alguien se te adelanta. En equipos con mucha actividad, esa exigencia genera una carrera continua que se resuelve con una merge queue, que veremos en la lección 07-06.
- La restricción que lo sostiene todo:
main siempre desplegable
main siempre desplegableEste apartado es el que hay que entender de verdad. Lo demás es procedimiento.
GitHub Flow parece más simple que Git Flow porque tiene menos ramas. Pero no es gratis: ha cambiado complejidad de proceso por una restricción muy fuerte. Git Flow te permitía tener develop roto durante tres días porque la estabilización ocurría después, en la rama de versión. GitHub Flow elimina esa red de seguridad. No hay rama de versión. No hay fase de QA posterior. main es lo que se despliega, y punto.
Piensa en lo que eso implica:
- Si
mainestá roto, nadie puede desplegar. Ni la persona que lo rompió ni ninguna otra. Todo el equipo está bloqueado. - Si
mainestá roto y no se detecta, se despliega roto. - Y si
mainestá roto, tampoco se puede publicar un arreglo urgente, porque el arreglo saldría acompañado de lo que está roto.
De ahí la regla informal más importante de este modelo: arreglar main es la máxima prioridad del equipo, por delante de cualquier funcionalidad. Si el CI de main se pone en rojo, se para lo que se esté haciendo hasta volver a verde. Muchos equipos aplican una política de reversión automática: si main se rompe y no se arregla en diez minutos, se revierte el commit culpable (git revert, lección 05-06) y se investiga con calma en una rama. Revertir primero, entender después.
Y otra consecuencia menos obvia: el tamaño del despliegue baja mucho. Si despliegas después de cada PR, cada despliegue contiene un cambio. Cuando algo falla en producción, la lista de sospechosos tiene un elemento. Compáralo con desplegar una versión trimestral con ciento veinte cambios dentro: cuando algo va mal, empieza la arqueología. Esa reducción del riesgo por despliegue es el beneficio real del modelo, y es lo que compensa la restricción.
- Qué exige a cambio
La restricción de la regla 1 no se sostiene sola. Exige tres cosas, y si alguna falta, el modelo no funciona: se degrada en "empujamos a main y rezamos".
Exigencia 1: pruebas automáticas fiables
Es la condición imprescindible. Nadie puede garantizar manualmente que main sea desplegable después de cada fusión: hace falta una batería de pruebas que se ejecute en cada envío y en cada PR (lección 07-06).
Y fiables es la palabra clave. Una batería que falla aleatoriamente una de cada diez ejecuciones —lo que se llama una prueba flaky— es peor que no tener pruebas, porque enseña al equipo a ignorar los fallos. En cuanto alguien dice "vuelve a lanzarlo, a veces falla", la señal ha muerto y con ella la garantía de que main es desplegable.
Exigencia 2: pull requests pequeñas
Ya lo cuantificamos en la lección 07-02: por encima de unos centenares de líneas, la revisión deja de detectar defectos. En un modelo donde la revisión es la última barrera antes de producción, eso es directamente riesgo.
Además, una PR pequeña se revisa en el día, se integra en el día y se despliega en el día. Una PR de mil líneas espera tres días a que alguien tenga un hueco, y en esos tres días acumula conflictos.
Exigencia 3: integración frecuente
Ramas de días, no de semanas. Es la misma idea que la exigencia anterior vista desde el tiempo en lugar del tamaño: cuanto más tiempo vive una rama separada, más diverge, más conflictos genera y más tarda en llegar el valor al usuario.
Y si el trabajo es grande de verdad y no se puede trocear en PRs pequeñas? Hay respuesta —banderas de funcionalidad y branch by abstraction— y es uno de los temas centrales de la lección 07-05.
La trampa de adoptar el modelo sin las exigencias
Merece la pena decirlo claro, porque es el fracaso más habitual: un equipo lee que GitHub Flow es "más simple", elimina develop y las ramas de versión, y no invierte en pruebas automáticas. El resultado no es simplicidad: es que main está roto la mitad del tiempo y nadie se atreve a desplegar. Han quitado la red de seguridad de Git Flow sin construir la de GitHub Flow.
GitHub Flow no es Git Flow menos ramas. Es Git Flow menos ramas más automatización.
- El método de integración: fusión, squash o rebase
Al cerrar una pull request hay que decidir cómo entran esos commits en main. Las plataformas ofrecen tres opciones, y la elección determina cómo será el historial del proyecto para siempre.
Todo esto ya lo conoces de los módulos 3 y 5: aquí solo vemos qué produce cada opción en el contexto de una PR.
Opción A: fusión normal (merge commit)
Equivale a git merge --no-ff (lección 03-03). Se conservan todos los commits de la rama y se añade un commit de fusión.
gitGraph commit id: "C1" branch rama checkout rama commit id: "F1" commit id: "F2" commit id: "F3" checkout main merge rama id: "M"
Opción B: squash (aplastado)
Equivale al merge --squash de la lección 03-04. Todos los commits de la rama se condensan en uno solo sobre main, y la rama original desaparece del historial.
gitGraph commit id: "C1" commit id: "F (todo aplastado)"
Opción C: rebase and merge
Equivale a un git rebase (lección 05-01) seguido de un fast-forward. Los commits se reescriben sobre la punta de main, en línea recta y sin commit de fusión.
gitGraph commit id: "C1" commit id: "F1'" commit id: "F2'" commit id: "F3'"
La comparación
| Aspecto | Fusión normal | Squash | Rebase and merge |
|---|---|---|---|
Commits en main por PR |
Todos + 1 de fusión | Exactamente 1 | Todos |
| Historial | Con ramificaciones | Lineal | Lineal |
| ¿Se ve qué commits eran una PR? | Sí, por el commit de fusión | Sí, es un solo commit | No, se mezclan |
git log --first-parent |
Una línea por PR: excelente resumen | Igual que git log |
No aporta nada |
git bisect (06-02) |
Puede entrar en commits intermedios rotos | Óptimo: cada paso es una PR completa | Puede entrar en commits intermedios rotos |
| Revertir la PR entera | git revert -m 1 <fusión> |
git revert <commit>, trivial |
Hay que revertir N commits |
git blame (06-03) |
Apunta al commit original, con su contexto | Apunta al commit aplastado: se pierde el detalle | Apunta al commit original |
| Detalle histórico | Máximo | Mínimo: se pierde el paso a paso | Máximo |
| Reescribe hashes | No | Sí | Sí |
| Exige disciplina en los commits | Sí: quedan a la vista | No: la limpieza es automática | Sí |
Observaciones prácticas sobre cada una:
El squash es el más popular en equipos que usan GitHub Flow, y por una razón muy concreta: absorbe los commits desordenados. Los wip, arreglado y ahora si de la rama desaparecen y en main queda una línea limpia por cada cambio integrado. El precio es real: pierdes el detalle de cómo se construyó el cambio, y en una PR grande el commit resultante es un bloque enorme que git blame no sabe desglosar. Es la elección correcta si las PRs son pequeñas; si son grandes, el aplastado empeora las cosas.
La fusión normal conserva todo, pero solo aporta valor si los commits de la rama estaban bien construidos (lección 05-02). Si no, ensucia main con ruido permanente. Su gran ventaja es git log --first-parent main: un resumen perfecto del proyecto, una línea por PR.
El rebase and merge da historial lineal sin commits de fusión, pero pierde por completo la agrupación: no hay forma de saber qué commits llegaron juntos, y revertir "esa PR" significa revertir N commits a mano. Es el que menos se usa de los tres.
Y ahora el punto importante:
Elige uno y aplícalo siempre. Un repositorio donde cada PR se integra con un método distinto tiene un historial que no se puede leer de forma consistente:
--first-parentno significa nada,bisectda resultados desiguales y nadie sabe qué esperar. La decisión concreta y sus consecuencias sobre el historial son el contenido de la lección 08-02: Manteniendo un Historial Limpio, donde trataremos la política de historial de forma completa. Aquí basta con saber que la decisión existe, que se toma una vez, y que las plataformas permiten desactivar los métodos que no se quieran para que nadie se equivoque.
- Versiones sin ramas de versión: etiquetas sobre
main
mainSin release/*, ¿cómo se sabe qué se publicó y cuándo? Con etiquetas sobre main (lección 05-05).
La idea es simple: cada despliegue a producción se etiqueta. La etiqueta no cambia lo que hay en el repositorio; añade un nombre estable a un commit para poder volver a él.
# Etiqueta anotada sobre el commit desplegado
git switch main
git pull
git tag -a v2.4.0 -m "Version 2.4.0: filtro por etiqueta y exportacion a CSV"
git push origin v2.4.0Como no hay fase de estabilización, muchos equipos con despliegue frecuente abandonan SemVer para la aplicación —no tiene mucho sentido decidir si un despliegue es minor o patch cuando hay cuatro al día— y usan etiquetas por fecha y secuencia:
git tag -a despliegue-2026-08-01.1 -m "Despliegue a produccion"
git push origin despliegue-2026-08-01.1| Esquema | Ejemplo | Cuándo usarlo |
|---|---|---|
| SemVer | v2.4.0 |
Bibliotecas, APIs públicas, cualquier cosa con contrato de compatibilidad |
| Fecha + secuencia | despliegue-2026-08-01.1 |
Aplicaciones web con varios despliegues diarios |
| Número incremental | build-1482 |
Cuando el número lo genera el propio CI |
Lo que sí conviene mantener es SemVer para las bibliotecas, y componentes-ui es exactamente ese caso: otros proyectos dependen de ella y necesitan saber si una versión rompe compatibilidad.
Las etiquetas son además el mecanismo natural para tres cosas cotidianas:
# ¿Qué entró entre los dos últimos despliegues?
git log --oneline v2.3.0..v2.4.0
# ¿En qué despliegue salió este commit?
git describe --contains <hash>
# Volver al estado exacto de un despliegue para depurar
git switch --detach v2.3.0Y una tercera pieza que ofrecen las plataformas: los entornos de despliegue, un registro de qué commit está activo en cada entorno. Es útil, pero su gestión es materia de la lección 10-05; desde el punto de vista de Git, lo que hay es una etiqueta y una referencia.
¿Y si hay que arreglar algo urgente?
Aquí GitHub Flow brilla, porque no necesita mecanismo especial. El arreglo urgente sigue exactamente el mismo camino que cualquier otro cambio:
git switch main && git pull
git switch -c correccion/perdida-de-tareas-al-filtrar
# arreglar
git commit -am "Corregir la perdida de tareas al aplicar dos filtros"
git push -u origin correccion/perdida-de-tareas-al-filtrar
# PR, revisión rápida, CI, integrar, desplegarVeinte minutos de principio a fin. En Git Flow eso mismo requería una clase de rama propia, dos integraciones y una etiqueta de parche. La velocidad de despliegue hace innecesario el hotfix/*: cuando desplegar cuesta minutos, todo arreglo es un hotfix.
- Ramas protegidas: el mecanismo que hace cumplir el flujo
Todo lo anterior son acuerdos. Y como vimos en la lección 06-01 al hablar de los hooks de cliente, los acuerdos que dependen de la disciplina humana fallan un viernes a las siete de la tarde.
La pieza que convierte GitHub Flow en algo real es la rama protegida: un conjunto de reglas que el servidor aplica sobre una rama concreta y que nadie puede saltarse desde su portátil.
Las protecciones habituales sobre main:
| Protección | Qué impide | Qué regla del flujo sostiene |
|---|---|---|
| Prohibir el envío directo | git push origin main desde un portátil |
Reglas 4 y 5: todo pasa por PR |
| Exigir N aprobaciones | Integrar sin revisión | Regla 5 |
| Exigir comprobaciones en verde | Integrar con el CI en rojo | Regla 1: main desplegable |
Exigir rama actualizada con main |
Integrar sin haber probado la combinación real | Regla 1 |
| Descartar aprobaciones al llegar commits nuevos | Aprobar una versión e integrar otra | Regla 5 |
| Prohibir el envío forzado | git push --force sobre main |
Regla de oro (05-06) |
| Prohibir el borrado de la rama | Borrar main por accidente |
Sentido común |
| Exigir conversaciones resueltas | Integrar con comentarios bloqueantes abiertos | Regla 5 |
Cuando Ana intenta enviar directamente a una main protegida, el rechazo llega del servidor:
remote: error: GH006: Protected branch update failed for refs/heads/main. remote: error: Required status check "pruebas" is expected. Changes must be remote: made through a pull request. To [email protected]:equipo/gestor-tareas.git ! [remote rejected] main -> main (protected branch hook declined) error: failed to push some refs to 'git.ejemplo.es:equipo/gestor-tareas.git'
Fíjate en la última línea entre paréntesis: protected branch hook declined. Eso es literalmente un hook de servidor rechazando la actualización de la referencia. Es la familia pre-receive/update que la lección 06-01 dejó anunciada, y es también la respuesta a la pregunta que quedó abierta allí: esto no se salta con --no-verify, porque --no-verify solo afecta a los hooks que se ejecutan en tu máquina. Aquí decide el servidor.
Lo desarrollaremos por completo en la lección 07-06, incluido cómo una comprobación de estado se convierte en requisito obligatorio. Por ahora, la idea que hay que retener: GitHub Flow con ramas protegidas es un proceso; GitHub Flow sin ellas es una recomendación.
Un apunte de diseño organizativo: casi todas las plataformas permiten que ciertos roles se salten las protecciones. Es tentador dejar esa puerta abierta "por si acaso", pero conviene pensarlo dos veces. La excepción se usa siempre en el peor momento posible —un incidente en producción, con prisa y sin dormir— que es justo cuando más falta hacen las comprobaciones.
- Cuándo encaja GitHub Flow
| Condición | Por qué encaja |
|---|---|
| Aplicación web o SaaS | Tú controlas el despliegue; no hay versiones en manos del usuario |
| Una sola versión en producción | No hay nada que mantener en paralelo, así que sobra develop |
| Despliegue frecuente y barato | Hace innecesario hotfix/*: todo arreglo llega en minutos |
| Pruebas automáticas sólidas | Es la condición imprescindible de la regla 1 |
| Equipo pequeño o mediano | Pocas ramas simultáneas, pocos conflictos de integración |
| Cultura de revisión establecida | La PR es la única barrera antes de producción |
Ejemplos típicos: aplicaciones web internas, SaaS, APIs desplegadas por el propio equipo, sitios estáticos, herramientas internas. Y el caso concreto de este curso: app.ejemplo.es, la versión en la nube de gestor-tareas.
Es también, con diferencia, el modelo más usado hoy, y es el que encontrarás por defecto en la mayoría de proyectos de código abierto: un colaborador externo hace un fork (lección 07-01), abre una rama, envía una PR contra main y ya está. Sin develop, sin ramas de versión, sin más protocolo del necesario.
- Sus límites
Ser honesto con las limitaciones es lo que permite elegir bien.
Límite 1: no sirve para mantener versiones antiguas
Es el límite fundamental. Si tienes clientes en la 1.4 y en la 2.0 y ambas necesitan correcciones de seguridad, una sola rama de larga vida no puede representar dos estados de producción simultáneos. No hay forma de arreglar la 1.4 desde main, que ya está en la 2.0.
La solución práctica es un híbrido: GitHub Flow para el desarrollo diario más ramas de mantenimiento creadas desde las etiquetas cuando hagan falta:
# Solo cuando un cliente lo necesita
git switch -c soporte/1.4 v1.4.0
git cherry-pick <arreglo-de-seguridad>
git tag -a v1.4.3 -m "Version 1.4.3: parche de seguridad"
git push -u origin soporte/1.4 --follow-tagsEs exactamente lo que hace el equipo de gestor-tareas para la versión instalable. Y fíjate en el matiz: no es "cambiar a Git Flow", es añadir la pieza concreta que falta cuando hace falta, que es siempre mejor que adoptar un modelo entero por adelantado.
Límite 2: exige madurez técnica que no todos los equipos tienen
Sin pruebas fiables y sin CI rápido, el modelo se degrada rápidamente. Un equipo sin esa base está más seguro con Git Flow, donde la rama de versión da un margen de estabilización manual. Es una conclusión incómoda pero cierta: el modelo más simple exige más madurez, no menos.
Límite 3: no dice qué hacer con el trabajo grande
Una migración de base de datos, una reescritura de la interfaz, un cambio de arquitectura. No caben en una rama de tres días y no se pueden integrar a medias en una main que debe ser desplegable. GitHub Flow no ofrece respuesta; hay que traerla de fuera, y esa respuesta —banderas de funcionalidad y branch by abstraction— es uno de los temas de la lección siguiente.
Límite 4: entornos regulados o con aprobación formal
Si cada versión requiere firma de un responsable de calidad, documentación formal o una ventana de despliegue autorizada, el "integrar y desplegar" de la regla 6 no es aplicable tal cual. Se puede adaptar (integrar continuamente, desplegar en ventanas), pero entonces main acumula cambios sin desplegar y parte del beneficio se pierde.
Errores Comunes y Consejos
Error 1: adoptar GitHub Flow sin pruebas automáticas. Se quita la red de Git Flow sin construir la propia. main acaba roto la mitad del tiempo.
Error 2: ramas de tres semanas. Es una rama de vida larga con otro nombre, con todos los problemas de integración tardía que el modelo pretendía evitar.
Error 3: dejar main roto y seguir trabajando. Bloquea a todo el equipo, incluidas las correcciones urgentes. Arreglar main es prioridad absoluta; si tarda más de unos minutos, revierte.
Error 4: no proteger main. Sin ramas protegidas el modelo es una recomendación que alguien se saltará. Con ellas es un proceso.
Error 5: mezclar métodos de integración. Unas PRs con squash, otras con fusión, otras con rebase. El historial resultante no se puede leer de forma consistente.
Error 6: aplastar PRs enormes. El squash funciona con PRs pequeñas. Aplastar mil líneas produce un commit que git blame no puede desglosar y que nadie podrá entender dentro de un año.
Error 7: no etiquetar los despliegues. Sin etiquetas no hay forma de saber qué había en producción el martes pasado ni de volver a ello para depurar.
Error 8: crear la rama sin actualizar main antes. git switch main && git pull primero, siempre. Partir de una main de hace una semana genera conflictos evitables.
Error 9: convivir con pruebas inestables. Una prueba que falla una de cada diez veces enseña al equipo a ignorar el rojo, y con eso muere la garantía de la regla 1. Arréglala o quítala, pero no la ignores.
Consejo 1: PRs de menos de 200 líneas. Es lo que más impacto tiene en la velocidad y la calidad del flujo (lección 07-02).
Consejo 2: abre la PR el primer día, en borrador. El CI empieza a probar antes y alguien puede corregirte el rumbo a tiempo.
Consejo 3: borra la rama al integrar. Actívalo en la plataforma y usa git fetch --prune en local. Un repositorio con doscientas ramas muertas es inutilizable.
Consejo 4: desactiva en la plataforma los métodos de integración que no uses. Es más fiable que confiar en que nadie se equivoque de botón.
Consejo 5: mide el tiempo de vida de tus ramas. Si la media supera la semana, no estás haciendo GitHub Flow, aunque lo llames así.
Consejo 6: añade ramas de mantenimiento solo cuando un cliente real las necesite. No adoptes Git Flow entero por un problema que quizá nunca tengas.
Ejercicios
Ejercicio 1: el ciclo de GitHub Flow
Sobre un repositorio nuevo con index.html, app.js y estilos.css:
- Crea
maincon tres commits iniciales y etiquétala comov2.3.0. - Simula tres ciclos completos: rama desde
main, dos commits, vuelta amaincon--no-ff, borrado de la rama y etiqueta de despliegue. - Usa nombres de rama con las convenciones del proyecto: una
funcionalidad/, unacorreccion/y unadocumentacion/. - Muestra el grafo y comprueba que se parece al del apartado 3.
- Ejecuta
git log --oneline --first-parent mainy explica qué información te da.
Ejercicio 2: comparar los tres métodos de integración
- Crea una rama
funcionalidad/exportarcon cuatro commits, dos de ellos claramente ruido (wip,arreglado). - Haz tres copias del repositorio en
/tmp/metodo-merge,/tmp/metodo-squashy/tmp/metodo-rebase. - En cada copia, integra la rama con el método correspondiente (
merge --no-ff,merge --squash+ commit,rebase+merge --ff-only). - Compara en las tres:
git log --oneline,git log --oneline --first-parenty el número de commits enmain. - En cada copia, intenta revertir la funcionalidad entera con un solo comando. ¿En cuál es posible?
- Ejecuta
git blame app.jsen las tres y comenta las diferencias.
Ejercicio 3: la simulación del hotfix
- Partiendo del repositorio del ejercicio 1, simula un fallo en producción: crea
correccion/perdida-de-tareasdesdemain, arregla, integra y etiqueta el despliegue. - Mide cuántos comandos han hecho falta desde el
switchinicial hasta la etiqueta. - Compara ese número con el procedimiento equivalente de
hotfix/*en Git Flow (lección 07-03, apartado 7). ¿Cuántos pasos te has ahorrado y cuál es el paso de Git Flow que aquí sencillamente no existe? - Ahora la otra cara: simula que un cliente sigue en
v2.3.0y necesita ese mismo arreglo sin recibir nada más. Creasoporte/2.3desde la etiqueta, lleva el arreglo concherry-picky etiquetav2.3.1. - Explica por qué el paso 4 no forma parte de GitHub Flow y qué te dice eso sobre cuándo el modelo se queda corto.
Soluciones
Solución 1:
mkdir /tmp/github-flow && cd /tmp/github-flow
git init -qb main
printf '<h1>Gestor de Tareas</h1>\n' > index.html
git add . && git commit -q -m "Anadir la pagina principal"
printf 'const tareas = [];\n' > app.js
git add . && git commit -q -m "Anadir el estado inicial de la aplicacion"
printf 'body { margin: 0; }\n' > estilos.css
git add . && git commit -q -m "Anadir la hoja de estilos base"
git tag -a v2.3.0 -m "Version 2.3.0"# Ciclo 1
git switch -qc funcionalidad/filtro-por-etiqueta
echo "function filtrarPorEtiqueta(e) { /* ... */ }" >> app.js
git commit -qam "Anadir el filtrado por etiqueta"
echo ".filtro { display: flex; }" >> estilos.css
git commit -qam "Estilar la barra de filtros"
git switch -q main
git merge -q --no-ff funcionalidad/filtro-por-etiqueta -m "Fusionar funcionalidad/filtro-por-etiqueta (#101)"
git branch -qd funcionalidad/filtro-por-etiqueta
git tag -a despliegue-2026-08-01.1 -m "Despliegue a produccion"# Ciclo 2
git switch -qc correccion/foco-tras-borrar
echo "// corregido: devolver el foco al campo tras borrar" >> app.js
git commit -qam "Devolver el foco al campo de entrada tras borrar"
git switch -q main
git merge -q --no-ff correccion/foco-tras-borrar -m "Fusionar correccion/foco-tras-borrar (#102)"
git branch -qd correccion/foco-tras-borrar
git tag -a despliegue-2026-08-01.2 -m "Despliegue a produccion"# Ciclo 3
git switch -qc documentacion/instalacion
printf '# gestor-tareas\n\n## Instalacion\n\nAbrir index.html.\n' > README.md
git add . && git commit -q -m "Documentar la instalacion en el README"
git switch -q main
git merge -q --no-ff documentacion/instalacion -m "Fusionar documentacion/instalacion (#103)"
git branch -qd documentacion/instalacion
git tag -a despliegue-2026-08-02.1 -m "Despliegue a produccion"c8a2f1e Fusionar documentacion/instalacion (#103) 9d4b7c3 Fusionar correccion/foco-tras-borrar (#102) 2f8e1a6 Fusionar funcionalidad/filtro-por-etiqueta (#101) 7b3c9d2 Anadir la hoja de estilos base 4a1e6f8 Anadir el estado inicial de la aplicacion 1c5d2b9 Anadir la pagina principal
--first-parent da una línea por PR integrada: es el resumen legible del proyecto. Ese es el beneficio principal de integrar con fusión normal.
Solución 2:
cd /tmp/github-flow
git switch -qc funcionalidad/exportar
echo "function exportar() { /* ... */ }" >> app.js
git commit -qam "Anadir el esqueleto de la exportacion"
echo "// wip" >> app.js
git commit -qam "wip"
echo "function generarCSV() { /* ... */ }" >> app.js
git commit -qam "Generar el contenido CSV"
echo "// corregido el separador" >> app.js
git commit -qam "arreglado"
git switch -q main# Fusión normal
cd /tmp/metodo-merge
git merge --no-ff funcionalidad/exportar -m "Fusionar funcionalidad/exportar (#104)"
git log --oneline main | head -6# Squash
cd /tmp/metodo-squash
git merge --squash funcionalidad/exportar
git commit -q -m "Anadir la exportacion a CSV (#104)"
git log --oneline main | head -6# Rebase
cd /tmp/metodo-rebase
git switch -q funcionalidad/exportar
git rebase -q main
git switch -q main
git merge -q --ff-only funcionalidad/exportar
git log --oneline main | head -6# 4. Comparación
for m in merge squash rebase; do
echo "== $m: $(git -C /tmp/metodo-$m rev-list --count main) commits en main"
git -C /tmp/metodo-$m log --oneline --first-parent -3 main
done# 5. Revertir la funcionalidad entera
git -C /tmp/metodo-merge revert -m 1 HEAD --no-edit # funciona
git -C /tmp/metodo-squash revert HEAD --no-edit # funciona
git -C /tmp/metodo-rebase revert HEAD~3..HEAD --no-edit # hacen falta 4 reversionesCon fusión y con squash basta un comando. Con rebase hay que revertir cada commit, porque nada indica que fueran un conjunto.
# 6. blame
for m in merge squash rebase; do
echo "== $m"; git -C /tmp/metodo-$m blame --date=short -- app.js | tail -4
doneEn squash, todas las líneas de la funcionalidad apuntan al mismo commit y al mismo mensaje: se ha perdido la información de qué parte se hizo en qué paso.
Solución 3:
cd /tmp/github-flow
git switch -q main
git switch -qc correccion/perdida-de-tareas
echo "// corregido: no vaciar el array al combinar dos filtros" >> app.js
git commit -qam "Corregir la perdida de tareas al aplicar dos filtros
Refs GT-341."
git switch -q main
git merge -q --no-ff correccion/perdida-de-tareas -m "Fusionar correccion/perdida-de-tareas (#105)"
git branch -qd correccion/perdida-de-tareas
git tag -a despliegue-2026-08-02.2 -m "Despliegue urgente a produccion"Seis comandos. En Git Flow: crear desde main, arreglar, subir versión de parche, fusionar en main, etiquetar, fusionar también en develop, borrar en dos sitios. El paso que aquí no existe es la segunda integración, y es precisamente el que más se olvida y el más caro cuando se olvida.
# 4. Mantener una versión antigua: el límite del modelo
ARREGLO=$(git log --format=%h --grep='perdida de tareas al aplicar' -1)
git switch -qc soporte/2.3 v2.3.0
git cherry-pick "$ARREGLO"
git tag -a v2.3.1 -m "Version 2.3.1: parche para clientes en la 2.3"
git switch -q main
git log --graph --oneline --all --decorate | head -20# 5.
# soporte/2.3 es una SEGUNDA rama de larga vida: contradice la premisa
# de GitHub Flow. El modelo asume una sola versión en producción.
# En cuanto hay dos estados de producción vivos, hace falta una pieza
# que el modelo no tiene, y hay que traerla prestada de Git Flow.Conclusión
GitHub Flow es el modelo dominante hoy, y su simplicidad es real pero condicionada. Lo esencial:
- Una sola rama de larga vida,
main, siempre desplegable. Todo lo demás son ramas cortas que salen demain, se revisan en una PR y vuelven amain. No haydevelop, nirelease/*, nihotfix/*, ni integraciones dobles que olvidar. - Las seis reglas:
maindesplegable; ramas descriptivas desdemain; enviar constantemente; abrir la PR en cualquier momento; integrar solo tras revisión; desplegar inmediatamente después. - "
mainsiempre desplegable" es la restricción que sostiene el modelo, y no es gratis: simainse rompe, todo el equipo queda bloqueado, incluidas las correcciones urgentes. Arreglarmaines prioridad absoluta; si tarda, se revierte. - A cambio de quitar ramas, el modelo exige pruebas automáticas fiables, PRs pequeñas e integración frecuente. GitHub Flow no es Git Flow menos ramas: es Git Flow menos ramas más automatización. Sin esa base, se degrada.
- El método de integración —fusión normal, squash o rebase— determina el historial para siempre: el squash absorbe commits desordenados y es óptimo para
bisect, la fusión conserva el detalle y permite--first-parent, el rebase pierde la agrupación. Elige uno y aplícalo siempre; la política completa está en la lección 08-02. - Sin ramas de versión, las etiquetas sobre
mainregistran qué se desplegó y cuándo: SemVer para bibliotecas comocomponentes-ui, fecha y secuencia para aplicaciones con varios despliegues diarios. - Los arreglos urgentes no necesitan mecanismo propio: cuando desplegar cuesta minutos, todo arreglo es un hotfix.
- Las ramas protegidas convierten el acuerdo en proceso: prohibir el envío directo, exigir aprobaciones y comprobaciones en verde. Y ese
protected branch hook declinedes un hook de servidor, precisamente lo que--no-verifyno puede tocar. - Encaja en web y SaaS con una sola versión en producción, despliegue frecuente y buenas pruebas. Su límite es mantener versiones antiguas: para eso hace falta añadir ramas de soporte desde las etiquetas.
Queda una pregunta que este modelo deja abierta. GitHub Flow pide ramas cortas, pero ¿cuánto de cortas? ¿Y qué se hace con el trabajo que no cabe en tres días? Hay una corriente que responde llevando la idea al extremo: integrar en el tronco al menos una vez al día, cada persona, siempre, y desacoplar el despliegue de la publicación con banderas de funcionalidad para que eso sea posible incluso con trabajo a medias. Es el planteamiento de la lección 07-05: Trunk Based Development, donde además compararemos por fin los tres flujos en una sola tabla y construiremos un árbol de decisión para elegir.
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
