git describe para coding agents: nombre legible, no un Release
Resumen
git describe nombra un commit con el tag anotado más cercano. Default ignora lightweight; --tags los incluye. Sin match exacto imprime tag-N-gSHA. --dirty no admite commit-ish. --exact-match falla si no hay tag en el tip. Un agente no lo usa como GitHub Release ni como git tag. Git 2.50.1.

Qué resuelve
Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.
git describe no crea un tag ni un GitHub Release. El man (git-describe(1), Git 2.50.1 / Apple Git-155; git-scm.com/docs/git-describe HTTP 200, last-modified 2026-08-31; last updated in 2.55.0, 2026-06-29) busca el tag alcanzable más reciente desde un commit-ish (HEAD si omites el argumento). Si el tag apunta exactamente a ese commit, imprime solo el nombre. Si no, añade cuántos commits hay encima y una abreviatura del objeto: v1.2.3-14-g2414721. Ese string también sirve como nombre de objeto para otros comandos Git.
Default sin --all ni --tags: solo tags anotados. Lightweight no cuentan. GitHub Docs (About releases, HTTP 200 2026-09-04): un Release se basa en un Git tag, pide write para gestionarlo y empaqueta zip/tarball. describe no publica nada.
Esta guía no sustituye tag (crear o listar refs/tags/) ni show (inspeccionar el objeto). El contrato: qué forma corre un agente, qué exit code significa, y por qué el default no ve un tag ligero.
Tres salidas, un commit
| Pregunta | Comando | Qué imprime |
|---|---|---|
| ¿Qué nombre tiene HEAD? | git describe | tag anotado, o tag-N-gSHA |
| ¿Hay un tag exacto en este commit? | git describe --exact-match | el tag, o fatal 128 |
| ¿El working tree está sucio? | git describe --dirty | igual que HEAD, o sufijo -dirty |
| Incluir lightweight | git describe --tags | el tag más cercano en refs/tags/ |
| Fallback si no hay tags | git describe --always | SHA abreviado único |
El man (DESCRIPTION): el número extra es el de commits que mostraría git log <tag>..<commit>. El sufijo es -g + abreviatura del tip (la g es de Git, no un SHA). La longitud escala con el tamaño del repo; default mínimo 7. --abbrev=0 suprime el formato largo y deja solo el tag más cercano.
Exit verificado 2026-09-04 (Git 2.50.1 / Apple Git-155): sin tags → No names found, cannot describe anything., 128. Solo lightweight → No annotated tags can describe '…'. However, there were unannotated tags: try --tags., 128. --tags sobre ese lightweight → el nombre, 0. Objeto inválido → Not a valid object name, 128. Tree → is neither a commit nor blob, 128.

Lo que el agente sí / no corre
| Quiero | Comando | Trampa |
|---|---|---|
| Nombre de HEAD | git describe | git tag “para ver la versión” |
| ¿Este commit es un tag? | git describe --exact-match -- HEAD | parsear a ojo el string largo |
| Versión + sucio | git describe --dirty | --dirty HEAD |
| Lightweight | git describe --tags --abbrev=0 | asumir que default los ve |
| Sin tags en el repo | git describe --always | inventar v0.0.0 |
Prohibido en autónomo:
- Tratar la salida como un GitHub Release. GitHub Docs: el Release es un paquete (notas, binarios, zip/tarball del tag). Hasta 1000 assets; cada file < 2 GiB.
describeno sube assets ni pide write. - Crear o mover tags “para que describe funcione”. Eso es tag. Aquí solo lees.
--dirtycon un commit-ish. Verificado:git describe --dirty HEAD→ option '--dirty' and commit-ishes cannot be used together, 128.--dirty/--brokendescriben el working tree de este checkout.--allcomo “versión”. El man: cualquier ref enrefs/(branch, remote-tracking, lightweight). Verificado: con rama más cercana que el tag, imprimeheads/main, nov1. Eso no es SemVer.--debugvolcado al LLM. El man: la estrategia va a stderr; el nombre sigue en stdout. No es API.--containspara “el tag de este commit”. El man: busca el tag que viene después (implica--tags). Verificado: HEAD sin tag posterior → cannot describe, 128.--exact-match/--candidates=0y tragar el fatal. Verificado: commit con un commit encima del tag → no tag exactly matches, 128.- Describir un tree. El man (BUGS): trees y tags que no apuntan a commits no se describen. Un blob sí:
<commit-ish>:<path>del primer commit en un walk inverso desde HEAD. Verificado: blob def→v1-1-g…:f. Lightweight que apunta al blob se ignora. - Encadenar la salida a commit o a un tag
-f. El string nombra un objeto; no es un permiso.
--broken[=<mark>]: si el repo está corrupto y Git no puede saber si hay cambios locales, el default erra; --broken añade -broken (o el mark). Verificado en un árbol sucio y sano: git describe --broken sigue con -dirty, no inventa -broken.
Receta (60 segundos)
Solo en un worktree propio:
git status -sb
git describe
git describe --dirty
git describe --exact-match; echo $?
Si el humano pide “versión aunque no haya tags anotados”:
git describe --tags --always --dirty
--always (man): SHA abreviado único como fallback. Verificado: repo sin tags → 12f5b02, exit 0. Un agente que loguea “versión” en un clone fresco usa esto; no finge un tag.
--long: siempre tag-N-gSHA, incluso si el commit es el tag (v1.2-0-gdeadbee). Verificado: checkout del tag anotado v1 → git describe --long = v1-0-g12f5b02; --abbrev=0 = v1.
--first-parent: el walk no entra al segundo padre de un merge. Sirve para no casar tags de una rama mergeada. Verificado: merge --no-ff de topic (tag v1.1) sobre main (tag v1) → default v1.1-3-g…; --first-parent → v1-2-g…. Un agente de “versión de main” pasa --first-parent si el ticket lo pide; no lo adivina.

UI de GitHub vs clone local
GitHub Docs (About releases): cualquiera con read ve y compara Releases; write para gestionarlos. La fecha del tag puede diferir de la del Release. git describe corre en tu clone: ve tags que hayas fetcheado, no el paquete de github.com.
--match <glob> (sin el prefijo refs/tags/): solo tags que casan. Con --all, también branches y remotes, sin refs/heads/ ni refs/remotes/. Varios --match se acumulan (OR). --exclude resta. Verificado: --match 'v1*' nombra; --match 'nope*' → No names found, 128.
--candidates=<n>: default 10 tags recientes. 0 = solo exacto (--exact-match). Subirlo retrasa un poco y puede acercar el tag. Un agente one-shot no sube el número “por si acaso”.
SEARCH STRATEGY del man: primero match exacto (anotados ganan a lightweight; fecha más nueva gana). Si no, camina ancestros y elige el tag con menos commits de diferencia (git log tag..input mínimo).
Checklist
- Worktree propio.
git status -sb. Default = HEAD. - Default = anotados. Lightweight →
--tags. Cero tags →--always, no un SemVer inventado. -
--exact-matchsi el ticket es “¿este commit es un tag?”. Fatal 128 = no. -
--dirtysin commit-ish.--brokensolo si el ticket admite repo corrupto. - Cero
--allcomo versión, cero--debugal LLM, cero Release. - Crear el tag es tag. Inspeccionar el objeto es show.
FAQ
¿describe es lo mismo que git tag --list? No. tag lista o crea refs. describe nombra un commit con el tag más cercano. Un listado no te dice cuántos commits hay encima.
¿Por qué no veo v0 lightweight? El man: default solo anotados (-a / -s en tag). --tags abre refs/tags/ entero.
¿Puedo parsear tag-N-gSHA por guiones? El tag mismo puede traer guiones. El man fija el sufijo como -<n>-g<abbrev>. Si necesitas el SHA, log -1 --format=%H, no recortes a ciegas.
El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. describe no es tag: nombra un commit; no mueve refs/tags/.
Lecturas relacionadas
Sigue explorando Coding Agents y otras piezas para builders.

git cat-file para coding agents: tipo y talla, no el blob al LLM

git ls-tree para coding agents: tree object, no el disco

git submodule para coding agents: gitlink 160000, no un clone extra
