8. Stash, Tags y Releases
1. Git Stash — Guardar trabajo sin confirmar
git stash guarda temporalmente los cambios del directorio de trabajo y el staging area sin hacer commit, permitiendo cambiar de rama con el espacio de trabajo limpio.
1.1. Comandos básicos
# Guardar todos los cambios (tracked)
git stash
# Guardar con nombre descriptivo
git stash push -m "wip: formulario de login"
# Incluir archivos nuevos sin seguimiento
git stash push -u -m "wip: login + nuevos archivos"
# Ver lista de stashes guardados
git stash list
# stash@{0}: On main: wip: formulario de login
# stash@{1}: On develop: wip: navbar
# Aplicar el último stash (lo mantiene en la lista)
git stash apply
# Aplicar stash específico
git stash apply stash@{1}
# Aplicar y eliminar de la lista
git stash pop
# Eliminar un stash sin aplicar
git stash drop stash@{0}
# Eliminar todos los stashes
git stash clear
1.2. Inspeccionar un stash
# Ver qué contiene un stash
git stash show stash@{0}
# Ver el diff completo
git stash show -p stash@{0}
1.3. Crear rama desde un stash
# Muy útil si el stash ya no aplica limpiamente sobre la rama actual
git stash branch feature/nuevo-desde-stash stash@{0}
1.4. Flujo de uso típico
# 1. Tienes cambios a medias en main
echo "trabajo en curso" > login.js
git add login.js
# 2. Necesitas cambiar urgentemente a otra rama
git stash push -m "wip: login form"
# 3. Trabajas en la otra rama
git checkout hotfix
# ... haces tu trabajo ...
git checkout main
# 4. Recuperas tu trabajo
git stash pop
2. Git Tag — Marcar versiones
Los tags son referencias fijas a commits específicos, ideales para marcar versiones de software.
2.1. Tipos de tags
| Tipo | Descripción |
|---|---|
| Lightweight | Simple puntero a un commit (como un alias) |
| Annotated | Objeto completo con autor, fecha, mensaje y firma GPG opcional |
Siempre usa tags anotados para releases.
2.2. Crear tags
# Tag ligero
git tag v1.0.0
# Tag anotado (recomendado)
git tag -a v1.0.0 -m "Primera versión estable"
# Tag en un commit anterior
git tag -a v0.9.0 abc1234 -m "Release candidate"
2.3. Listar y ver tags
# Listar todos los tags
git tag
# Filtrar por patrón
git tag -l "v1.*"
# Ver detalles de un tag anotado
git show v1.0.0
# tag v1.0.0
# Tagger: Amador <amador@email.com>
# Date: ...
# Primera versión estable
# commit abc123...
2.4. Compartir tags con el remoto
# Push de un tag específico
git push origin v1.0.0
# Push de todos los tags locales
git push origin --tags
# Eliminar tag remoto
git push origin --delete v1.0.0
# Eliminar tag local
git tag -d v1.0.0
2.5. Versionado semántico (SemVer)
MAJOR.MINOR.PATCH
│ │ └── Bug fixes compatibles
│ └──────── Nuevas funcionalidades compatibles
└────────────── Cambios incompatibles con versiones anteriores
| Versión | Cuándo usarla |
|---|---|
v1.0.0 |
Primera versión estable |
v1.1.0 |
Nueva funcionalidad sin romper nada |
v1.1.1 |
Bug fix en producción |
v2.0.0 |
Cambio incompatible en la API |
# Pre-releases
git tag -a v2.0.0-alpha.1 -m "Alpha release"
git tag -a v2.0.0-beta.2 -m "Beta release"
git tag -a v2.0.0-rc.1 -m "Release candidate"
3. GitHub Releases
Las GitHub Releases envuelven un tag con notas de la versión, binarios descargables y changelogs.
3.1. Crear release desde la web
- Repositorio → Releases → Draft a new release
- Selecciona o crea un tag (
v1.0.0) - Añade título y descripción (changelog, breaking changes, etc.)
- Adjunta binarios si procede
- Marca como pre-release si aún no es estable
- Publish release
3.2. Crear release con GitHub CLI
# Release básica desde el tag existente
gh release create v1.0.0 --title "v1.0.0 — Primera versión" --notes "Primera versión estable."
# Release con fichero de notas
gh release create v1.1.0 --title "v1.1.0" --notes-file CHANGELOG.md
# Pre-release
gh release create v2.0.0-beta.1 --prerelease --title "v2.0.0 Beta"
# Adjuntar binarios
gh release create v1.0.0 ./dist/app-linux ./dist/app-macos --title "v1.0.0"
3.3. Changelog automático
GitHub puede generar changelogs automáticos basándose en los títulos de los PRs mergeados desde el último release.
gh release create v1.2.0 --generate-notes
O en la web, al crear la release: Generate release notes.
Ejercicios
8.1 — Tienes cambios sin terminar en main. Guárdalos con git stash (con mensaje descriptivo), crea una rama hotfix donde apliques un commit de fix, vuelve a main y recupera tu trabajo con git stash pop.
8.2 — Lista todos tus stashes con git stash list. Aplica el stash número 1 (sin eliminarlo de la lista) con git stash apply stash@{1}. ¿Qué diferencia hay entre apply y pop?
8.3 — Crea un tag anotado v1.0.0 en el último commit de tu repositorio. Súbelo a GitHub. Después crea un segundo tag v1.1.0 en el mismo commit y sube todos los tags de una vez.
8.4 — Usando la GitHub CLI (gh), crea una release para v1.0.0 con título “Primera versión estable” y al menos 2 líneas de notas de la versión. Verifica que aparece en la sección Releases de tu repositorio.
8.5 — Explica cuándo usarías v2.0.0 en lugar de v1.5.0 según SemVer. Pon un ejemplo concreto de cambio que obligaría a incrementar la versión MAJOR.
Soluciones
8.1
# Cambios en curso
echo "nueva funcionalidad" > feature.js
git add feature.js
# Guardar stash
git stash push -m "wip: nueva funcionalidad de búsqueda"
# Ir a hotfix
git checkout -b hotfix
echo "fix" > bugfix.js && git add . && git commit -m "fix: correct null check"
git checkout main
git merge hotfix --no-ff -m "merge: hotfix"
# Recuperar trabajo
git stash pop
git status # feature.js aparece de nuevo en staging
8.2
git stash list
# stash@{0}: wip más reciente
# stash@{1}: wip anterior
git stash apply stash@{1} # aplica pero mantiene el stash en la lista
# Diferencia:
# git stash apply → aplica y MANTIENE en la lista (puedes aplicarlo otra vez)
# git stash pop → aplica y ELIMINA de la lista (uso habitual)
8.3
git tag -a v1.0.0 -m "Primera versión estable"
git push origin v1.0.0
git tag -a v1.1.0 -m "Añade formulario de contacto"
git push origin --tags # sube v1.0.0 y v1.1.0
8.4
gh release create v1.0.0 \
--title "Primera versión estable" \
--notes "## Novedades
- Autenticación con JWT
- Formulario de contacto
- Soporte multiidioma"
# Verificar
gh release list
# v1.0.0 Latest Primera versión estable
8.5
Se incrementa MAJOR cuando hay cambios incompatibles hacia atrás (breaking changes).
Ejemplo concreto: una API REST que tenía GET /users devolviendo un array plano:
[{"id": 1, "name": "Ana"}]
Pasa a devolver un objeto envuelto:
{"data": [{"id": 1, "name": "Ana"}], "total": 1}
Cualquier cliente que accediera al índice [0] directamente dejaría de funcionar. Eso es un breaking change → v2.0.0.
Un campo nuevo opcional (p.ej. añadir "email" al objeto usuario) sería v1.x.0 porque no rompe los clientes existentes.