Saltar al contenido principal

Git avanzado para DevOps

Git no es solo donde guardas el código. Es el disparador de todo el pipeline: un push lanza los tests, un merge despliega a staging, un tag publica una release.

Por eso la estrategia de ramas que elijas no es una preferencia estética. Determina con qué frecuencia puedes desplegar.

Las estrategias de branching

GitFlow

Ramas separadas para desarrollo, releases, hotfixes y funcionalidades. Fue el estándar durante años.

Su problema para DevOps es estructural: está pensado para releases planificadas, con versiones que se preparan y se publican cada cierto tiempo. Si quieres desplegar varias veces al día, la ceremonia de ramas sobra.

Sigue teniendo sentido en un caso concreto: software que se distribuye y del que mantienes varias versiones a la vez, como una aplicación de escritorio o una librería con soporte a versiones antiguas.

GitHub Flow

Una sola rama permanente, main, siempre desplegable. Ramas de funcionalidad cortas que se fusionan mediante pull request.

main ──●──●──●──●──●──●── (siempre desplegable)
╲ ╱ ╲ ╱
●──● ●──● (ramas de días, no semanas)

Es lo que quieres para entrega continua, y la opción por defecto para la mayoría de los equipos que despliegan servicios web.

La condición que lo hace funcionar: las ramas tienen que ser cortas. Una rama de tres semanas convierte cualquier estrategia en integración esporádica, con sus conflictos gigantes correspondientes.

Trunk-based development

El extremo: todo el mundo integra en main a diario, con ramas de horas o directamente sin ramas.

Es el modelo que la investigación de DORA correlaciona con mejor rendimiento, pero exige una base sólida: tests fiables, feature flags para el código a medias y una cultura donde romper main se arregla en minutos.

GitFlowGitHub FlowTrunk-based
Ramas permanentesVariasUnaUna
Vida de una ramaSemanasDíasHoras
Encaja conReleases planificadasEntrega continuaDespliegue continuo
RequierePocoTests decentesTests sólidos y feature flags

Elige por la frecuencia de despliegue que necesitas, no por lo que hace la empresa de al lado.

Conventional commits

Un formato de mensaje que las herramientas pueden interpretar:

feat(auth): añadir autenticación con JWT
fix(api): corregir fuga de memoria en el servicio de usuarios
docs(readme): actualizar instrucciones de instalación
chore(deps): actualizar dependencias

feat!: cambiar el método de autenticación de la API

El valor no está en la estética, sino en lo que permite automatizar:

  • Generar el changelog sin escribirlo a mano
  • Deducir la siguiente versión automáticamente
  • Filtrar el historial por tipo de cambio

Esa exclamación del último ejemplo marca un cambio incompatible, y es la que hace saltar la versión mayor.

Versionado semántico

MAYOR.MENOR.PARCHE → 2.4.1
  • MAYOR — rompe compatibilidad
  • MENOR — añade funcionalidad sin romper nada
  • PARCHE — corrige errores

Combinado con conventional commits, la versión se calcula sola: un fix sube el parche, un feat sube la menor y un ! sube la mayor.

Ahí está el truco de todo esto: si los mensajes de commit son consistentes, no hay que decidir la versión ni escribir el changelog. Sale del historial.

Releases automáticas

name: Release
on:
push:
branches: [main]

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # se necesita el historial completo
- uses: cycjimmy/semantic-release-action@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Con eso, cada merge a main analiza los commits nuevos, calcula la versión, crea el tag, genera el changelog y publica la release. Nadie decide nada a mano.

El fetch-depth: 0 es imprescindible: sin el historial completo, la herramienta no puede saber qué ha cambiado desde el último tag.

Hooks

Los hooks ejecutan comprobaciones en momentos concretos del flujo de Git. El más útil es pre-commit:

# .git/hooks/pre-commit
#!/bin/sh
npm run lint || exit 1
npm test || exit 1

Con dos advertencias importantes:

No se versionan. Los ficheros de .git/hooks/ no viajan con el repositorio, así que cada persona tiene que instalárselos. Herramientas como pre-commit o husky resuelven esto versionando la configuración.

Cualquiera puede saltárselos con git commit --no-verify. Por eso un hook nunca es un control de seguridad: sirve para evitar despistes y dar feedback rápido. El control de verdad va en CI, donde nadie puede saltárselo.

Un buen candidato para un hook de pre-commit es la detección de secretos, que tienes con laboratorio en el capítulo 2 del curso de DevSecOps.

Rebase o merge

Una discusión eterna que tiene una respuesta razonable:

  • Rebase en tu rama, antes de abrir el pull request, para tener un historial limpio y lineal
  • Merge para integrar en main, para conservar el contexto de que esos commits eran una unidad

Y la regla que no se negocia: nunca hagas rebase de algo que ya has compartido. Reescribir commits que otros tienen en su copia local es la forma más rápida de arruinarle la mañana a alguien.

Proteger la rama principal

Si main tiene que estar siempre desplegable, hay que impedir que no lo esté:

  • Prohibir el push directo
  • Exigir un pull request con al menos una aprobación
  • Exigir que el CI esté en verde
  • Exigir que la rama esté actualizada antes de fusionar

Esa última evita un fallo sutil: dos ramas que pasan los tests por separado pueden romper al juntarse.

Firmar commits

git config --global commit.gpgsign true

Sin firma, cualquiera puede hacer un commit con tu nombre y tu correo: Git no lo comprueba. En proyectos abiertos o con requisitos de trazabilidad, la firma es lo que convierte la autoría en algo verificable.

Resumen

  • La estrategia de ramas determina con qué frecuencia puedes desplegar
  • GitHub Flow para la mayoría; GitFlow solo para releases planificadas
  • Las ramas cortas son lo que hace que cualquier estrategia funcione
  • Conventional commits permiten calcular versión y changelog automáticamente
  • Los hooks evitan despistes; el control real está en CI
  • Rebase en tu rama, merge para integrar, y nunca rebase de lo compartido
  • Protege main, incluida la exigencia de estar actualizada antes de fusionar

Recursos adicionales


💡 Recuerda: si tus ramas viven semanas, no estás integrando de forma continua por muchos jobs en verde que tengas.

En el próximo capítulo vemos cómo encajan las metodologías ágiles con todo esto.