Git tag vs rama vs release: qué es cada uno y cuándo usarlo
Un tag es una etiqueta fija sobre un commit, una rama se mueve y un release de GitHub se construye sobre un tag. Comandos probados y errores típicos.
Un tag de Git es una etiqueta fija pegada a un commit: siempre señala el mismo punto del historial. Una rama también señala un commit, pero se mueve sola cada vez que haces uno nuevo. Y un release de GitHub no es un concepto de Git: es una página pública que GitHub construye sobre un tag.
Para seguir esto necesitas saber hacer un commit y subirlo (si no, mira antes cómo funcionan git add, commit y push). Todos los comandos de abajo los ejecuté con Git 2.43 en un repositorio de prueba nuevo, empezando con tres commits y añadiendo alguno más por el camino. Las salidas son de esa misma sesión, por eso los hashes coinciden de principio a fin. Solo quité de las salidas de push la línea To ... con la ruta del remoto.
El modelo mental: un puntero que se queda quieto y otro que avanza
Piensa en el historial como una fila de fotos del proyecto, una por commit. Una rama es un post-it que se despega solo y se pega en la foto más reciente cada vez que haces un commit. Un tag es un post-it de los que no se despegan: lo pegas en una foto y ahí se queda.
Esa es toda la diferencia. Lo ves en cuanto haces un commit después de crear un tag. Aquí tengo tres commits, dos tags puestos sobre el tercero (cómo se crean, justo debajo) y un cuarto commit encima:
git log --oneline --decorate
534b15d (HEAD -> main) Nueva función
61a8d72 (tag: v1.0.0, tag: v0.1.0) Arregla bug del login
ed86469 Añade login
80f68fb Primera versión
main ha avanzado hasta 534b15d. Los tags siguen donde los dejé. Si quieres el modelo completo de las ramas, está en la guía completa de ramas en Git.
Tag, rama y release en una tabla
El release vive fuera de tu repositorio local, en GitHub:
| Tag | Rama | Release de GitHub | |
|---|---|---|---|
| Qué es | Nombre fijo para un commit | Puntero que avanza con cada commit | Página en GitHub con notas y descargas |
| ¿Se mueve? | No (salvo que lo fuerces) | Sí, con cada commit | No: depende de su tag |
| ¿Es de Git? | Sí | Sí | No, es una función de GitHub |
| Para qué lo usas | Marcar v1.0.0, v2.3.1 | Trabajar en algo sin tocar main | Dar una versión a quien no usa Git |
| Se crea con | git tag | git branch o git switch -c | Botón o gh release create en GitHub |
La documentación de GitHub lo dice sin rodeos: los releases se basan en tags[3]. Y GitHub añade solo un zip y un tar.gz del código tal como estaba en ese tag. Un release publicado siempre cuelga de un tag, pero no hace falta crearlo antes: según la ayuda de gh release create, si el tag no existe se crea automáticamente[5]. Y puedes tener tags sin ningún release.
Cómo crear un tag en Git: ligero o anotado
Git tiene dos tipos de tag y se crean con comandos casi iguales. Un tag ligero es solo un nombre apuntando a un commit. Un tag anotado es un objeto propio que guarda quién lo creó, cuándo y con qué mensaje[1].
git tag v0.1.0 # ligero: solo el nombre
git tag -a v1.0.0 -m "Primera versión estable" # anotado: con mensaje
git tag # lista los tags
v0.1.0
v1.0.0
Para ver la diferencia usa git show. En el anotado aparece el bloque del autor del tag antes del commit:
git show v1.0.0 --no-patch
tag v1.0.0
Tagger: Ana <ana@example.com>
Date: Thu Jan 1 10:00:00 2026 +0000
Primera versión estable
commit 61a8d72a83795f218425eee835c68fe9cb5be102
Author: Ana <ana@example.com>
Date: Thu Jan 1 10:00:00 2026 +0000
Arregla bug del login
En el ligero, git show v0.1.0 --no-patch te enseña directamente el commit, sin cabecera de tag.
Mi regla es corta: anotado para todo lo que vaya a salir a otras personas, ligero para marcas privadas o temporales. La documentación de git tag dice que los anotados “are meant for release” (están pensados para publicar versiones) y los ligeros para etiquetas privadas o temporales[1].
Hay un efecto práctico: git describe, que te dice en qué versión estás, ignora los ligeros por defecto. Lo explico con un ejemplo en las preguntas frecuentes, más abajo.
Etiquetar un commit antiguo, listar y buscar
Si te olvidaste de etiquetar en su momento, añade el commit al final del comando. Como el estado del repositorio cambia con cada commit, no cuentes “commits hacia atrás”: saca el hash del historial.
git log --oneline # localiza el commit; el más antiguo es 80f68fb
534b15d Nueva función
61a8d72 Arregla bug del login
ed86469 Añade login
80f68fb Primera versión
git tag -a v0.0.1 -m "Primer commit" 80f68fb
git tag -l "v0.*" # filtra por patrón
git tag -n # muestra la primera línea del mensaje
v0.0.1
v0.1.0
---
v0.0.1 Primer commit
v0.1.0 Arregla bug del login
v1.0.0 Primera versión estable
(El --- lo puse yo para separar las salidas de -l y -n.) Fíjate en que v0.1.0, que es ligero, enseña en git tag -n el mensaje del commit al que apunta. Con los anotados ves el del tag.
Subir los tags a GitHub: el paso que todo el mundo olvida
Crear un tag no lo sube. git push envía commits y ramas, pero tus tags se quedan en tu máquina hasta que los pidas explícitamente. Es el error número uno, y se nota tarde: alguien clona el repositorio y no encuentra v1.0.0.
Aquí doy por hecho que ya tienes un remoto llamado origin y que main está subida. Primero subo un solo tag y después todos los que faltan:
git push origin v1.0.0 # sube un solo tag
* [new tag] v1.0.0 -> v1.0.0
git push --tags # sube todos los que faltan
* [new tag] v0.0.1 -> v0.0.1
* [new tag] v0.1.0 -> v0.1.0
Para comprobar qué tags tiene el remoto sin descargar nada, existe git ls-remote --tags origin. Cada línea es un hash y la ruta refs/tags/<nombre>, que es donde Git guarda los tags. Las líneas acabadas en ^{} muestran el commit al que apunta un tag anotado:
4eb61fdb7424f99b73fd9f8e702b0d9a61462e0b refs/tags/v0.0.1
80f68fb54cd50b73efca7a20fd656c405fb3ad9d refs/tags/v0.0.1^{}
61a8d72a83795f218425eee835c68fe9cb5be102 refs/tags/v0.1.0
a48d566c13329da723a61389e5c49ea6ed3c6ab8 refs/tags/v1.0.0
61a8d72a83795f218425eee835c68fe9cb5be102 refs/tags/v1.0.0^{}
Fíjate en que v0.1.0, que es ligero, no tiene línea ^{}: apunta directo al commit. Y un clon nuevo trae los tags de golpe, sin pedir nada más.
El matiz de --tags es que sube todo lo que tengas en refs/tags[2]: un tag ligero que creaste como marca temporal, también. Para evitarlo existe --follow-tags, que sube los tags anotados que apuntan a commits que estás subiendo[2]:
git commit -am "Mejora el login" # un commit nuevo
git tag v2.0.0-rc1 # ligero
git tag -a v2.0.0 -m "dos" # anotado
git push --follow-tags origin main
534b15d..1e97514 main -> main
* [new tag] v2.0.0 -> v2.0.0
Subió v2.0.0 y se dejó v2.0.0-rc1. A mí me parece el comportamiento más sano para releases: lo anotado sale, lo temporal no.
Trabajar con un tag: checkout y rama
Puedes ir a la versión que marca un tag con git checkout v0.1.0. Git te avisa de que estás en detached HEAD: ya no estás sobre ninguna rama, solo mirando una foto concreta. Si el concepto te suena raro, el post sobre detached HEAD lo explica con calma.
Mirar es seguro. Lo que no conviene es hacer commits ahí, porque ninguna rama los sostiene. Si quieres trabajar a partir de esa versión (un arreglo urgente sobre v0.1.0, por ejemplo), crea una rama desde el tag:
git switch -c hotfix-0.1 v0.1.0 # crea la rama en ese commit y te cambia a ella
Switched to a new branch 'hotfix-0.1'
Y aquí se ve cómo se complementan: el tag marca la versión que salió, la rama es donde sigues trabajando. Para decidir qué ramas usar en un equipo, mira GitFlow, GitHub Flow y trunk-based.
Borrar un tag, en local y en remoto
Borrar un tag no elimina ningún commit, solo el tag. Pero son dos operaciones: la local no toca el remoto, igual que pasa con las ramas (en la guía para borrar ramas tienes el caso paralelo).
git tag -d v0.0.1 # borra el tag en tu copia
git push origin --delete v0.0.1 # lo borra en el remoto
Deleted tag 'v0.0.1' (was 4eb61fd)
- [deleted] v0.0.1
El hash entre paréntesis es el del objeto del tag anotado (el mismo que vimos en ls-remote), no el del commit.
Cómo se relaciona con el número de versión
Los nombres tipo v1.2.3 suelen seguir el versionado semántico, un acuerdo entre personas que Git ignora: la especificación dice que subes MAJOR si haces cambios incompatibles en la API, MINOR si añades funcionalidad compatible y PATCH si corriges errores compatibles[4]. Git no comprueba nada de eso, y puedes llamar a un tag banana y funciona igual. Si después de v1.0.0 corriges un bug, el siguiente sería un PATCH:
git tag -a v1.0.1 -m "Arregla el bug del login"
Errores típicos con los tags
El tag que existe pero no se subió. Ya lo has visto: git push no los envía. Compruébalo con git ls-remote --tags origin.
“tag already exists”. Si repites un nombre, Git se niega:
fatal: tag 'v1.0.0' already exists
Se puede forzar con git tag -f, y ahí viene el verdadero problema.
Mover un tag ya publicado. Con -f el tag local pasa a otro commit (Updated tag 'v1.0.0' (was a48d566)), pero al subirlo el remoto lo rechaza:
! [rejected] v1.0.0 -> v1.0.0 (already exists)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tag already exists in the remote.
Se podría forzar también el push, pero la propia documentación de Git lo llama “the insane thing” (la locura): quien ya tiene el tag viejo no lo recibe actualizado, y dos personas acaban con dos v1.0.0 distintos[1]. Lo sensato es admitir el fallo y publicar v1.0.1.
Confundir un release con un tag. Borrar un release no borra su tag por defecto: la CLI de GitHub ofrece gh release delete --cleanup-tag, descrita como “delete the specified tag in addition to its release”, es decir, borrar el tag además del release[5]. Sin esa opción, el tag se queda. Esto lo he comprobado en el manual y en la ayuda de gh, no contra un repositorio real.
Cuándo usar cada uno
Usa una rama mientras el trabajo está en marcha: un arreglo, una función, un experimento. Usa un tag cuando algo está terminado y quieres poder volver exactamente a ese punto dentro de seis meses. Crea un release si además quieres notas de versión y archivos descargables para quien no usa Git. En la práctica los tres se encadenan: trabajas en una rama, la fusionas en main, etiquetas ese commit y publicas el release sobre el tag. Desde la web de GitHub o con gh release create el tag puede crearse a la vez que el release.
Ejemplo completo: etiquetar y publicar v1.0.0
Con main ya fusionada y subida, el ejemplo entero son dos comandos de Git. El release, si lo quieres, va después en GitHub:
git tag -a v1.0.0 -m "Primera versión estable"
git push origin v1.0.0
Fuentes
- git-tag, documentación de Git (git/git en GitHub) respalda la diferencia entre tags anotados y ligeros, que
git describeignore los ligeros y la sección “On Re-tagging”. - git-push, documentación de Git (git/git en GitHub) respalda el comportamiento de
--tagsy--follow-tags. - About releases, documentación de GitHub respalda que los releases se basan en tags y que GitHub añade zip y tarball.
- Especificación de Versionado Semántico (semver/semver) respalda las reglas de MAJOR, MINOR y PATCH.
- gh release delete y gh release create, manual de la CLI de GitHub respalda la opción
--cleanup-tagy quegh release createcrea el tag si no existe.
Preguntas Frecuentes
¿Hace falta crear un release en GitHub para tener una versión?
No. Con el tag ya tienes la versión marcada y recuperable en Git. El release es un añadido de GitHub (notas y descargas) que se apoya en un tag existente.
¿Cuál es la diferencia entre git push —tags y —follow-tags?
--tags sube todos los tags que no estén en el remoto, ligeros y anotados. --follow-tags sube solo los anotados que apuntan a commits que estás subiendo en ese push, así que se deja los tags temporales.
¿Cómo sé en qué versión estoy ahora mismo?
Ejecuta git describe. En mi repositorio, con un commit nuevo encima de v1.0.0, devolvió:
v1.0.0-1-g534b15d
Se lee en tres trozos: v1.0.0 es el último tag anotado alcanzable desde donde estás, 1 son los commits que has hecho después, y g534b15d es una g (de git) más el hash abreviado de tu commit actual. Los tags ligeros no cuentan: con uno llamado solo-ligero en ese mismo commit, git describe siguió devolviendo lo anterior, y solo git describe --tags mostró solo-ligero.
¿Cuál es la diferencia entre un tag y una rama?
Un tag se queda fijo en un commit; una rama avanza con cada commit nuevo.
Si quieres practicar tags, ramas y el resto del flujo con ejercicios guiados, el curso Git de cero a profesional cubre todo ese camino.