Guía9 min

git for-each-ref para coding agents: refs locales, cero update-ref

Resumen

git for-each-ref lista refs del clone con format y count. Default: SHA, tipo y TAB al nombre. Un agente acota patrón, %(refname:short) y --count. Cero --shell eval, cero update-ref y cero REST de escritura. Verificado hoy contra el man de Git 2.50.1.

GitHub
Refs locales se listan con format; HEAD y el working tree no se mueven

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 for-each-ref no mueve una rama. El man (git-for-each-ref(1); git-scm.com/docs/git-for-each-ref HTTP 200, last-modified 2026-08-31; last updated in 2.54.0, 2.55.0 sin cambios; pie del man local Git 2.54.0, 2026-04-19; binario Git 2.50.1 / Apple Git-155) itera las refs que matchean un patrón, las ordena y las interpola con %(fieldname). Default de --format: %(objectname) espacio %(objecttype) TAB %(refname). HEAD, index y working tree no cambian.

No es branch (porcelain que también crea o borra) ni ls-remote (habla con un remoto, no con el store local). Tampoco es git update-ref: ese man (git-update-ref(1), HTTP 200 2026-09-04) escribe el OID de una ref y puede reescribir HEAD.

GitHub Docs (REST API endpoints for Git references, HTTP 200 2026-09-04, API version 2026-03-10): List matching references y Get a reference piden Contents read (público sin auth). Create / Update / Delete a reference piden Contents write. Un agente de lectura no escribe refs por REST.

Contrato: patrón acotado, --format corto, --count si hay tope, cero eval, cero escritura.

Lista local, no dump

Verificado 2026-09-04 (Git 2.50.1 / Apple Git-155), default con un commit en main:

<40 hex> commit<TAB>refs/heads/main

El TAB es real. El SHA son 40 hex. Repo vacío (solo git init -b main): stdout vacío, 0. Patrón sin match (refs/tags sin tags): stdout vacío, 0. Eso no es “error”; no hay --exit-code aquí.

PreguntaComandoQué imprime
¿Qué refs hay?git for-each-ref --count=20 --format='%(refname:short)' refs/headsnombres cortos, tope 20
¿HEAD y pseudorefs?git for-each-ref --include-root-refs --format='%(refname)'HEAD más refs/…
¿Quién apunta a este commit?git for-each-ref --points-at HEAD --format='%(refname)'refs cuyo tip es ese objeto
¿Tips no alcanzables desde HEAD?git for-each-ref --no-merged HEAD --format='%(refname:short)' refs/headsramas divergentes
¿Tag anotado?git for-each-ref --format='%(objecttype) %(refname)' refs/tagstag refs/tags/v1

--count=<n> corta tras n refs. Verificado: --count=2 con tres heads (aa, main, zz) → aa y main. --count=-1invalid --count argument, 129. --count=0 en este binario no corta: listó las tres heads igual que sin la flag (--[no-]count trata 0 como ausente). No uses 0 como “silencio”.

--sort=<key>: default refname. Prefijo - invierte. Varias --sort hacen primaria la última. Verificado: --sort=-committerdate puso main (commit más nuevo) antes que topic.

--merged / --contains / --no-merged / --no-contains en Apple Git-155 exigen el commit. Verificado: git for-each-ref --merged --format='…'malformed object name --format=…. Pasa HEAD explícito.

--start-after está en el man de git-scm (2.54.0). En 2.50.1 / Apple Git-155: unknown option, 129. Un agente en esta máquina no pagina con esa flag.

Patrón refs/heads y format corto; el working tree no cambia

Lo que el agente sí / no corre

QuieroComandoTrampa
Ramas localesgit for-each-ref --format='%(refname:short)' refs/headsgit branch -a al LLM
Tagsgit for-each-ref --format='%(refname:short)' refs/tagstag -l con anotación larga
¿Existe main?git for-each-ref --format='%(refname)' refs/heads/maintratar stdout vacío como fallo (sigue 0)
SHA de una refrev-parse --verify --quiet refs/heads/maindump de %(objectname) de todas las refs

Prohibido en autónomo:

  • Dump sin patrón ni --count. El default recorre todas las refs. Un clone con refs/pull, notes o cientos de heads llena el prompt.
  • --shell / --perl / --python / --tcl. El man: citan el valor para eval en ese lenguaje. Verificado: --shell --format='%(refname)''refs/heads/main'. Un agente no evalúa ese scriptlet.
  • git update-ref. El man: con dos args guarda <new-oid> en la ref (y puede ir a HEAD); con tres, solo si el valor actual coincide con <old-oid>; -d borra. Un agente no reescribe refs a mano.
  • REST Create a reference (POST …/git/refs). GitHub Docs: Contents write, ref fully-qualified (refs/heads/…, si no empieza por refs y tiene dos slashes se rechaza), sha obligatorio, 201 / 409 / 422. No crea refs en un repo vacío (sin ramas), aunque el SHA exista.
  • REST Update a reference (PATCH). Contents write. force default false (fast-forward). 200 / 409 / 422.
  • REST Delete a reference (DELETE). Contents write. 204. 422 si intentas borrar la rama default.
  • Mezclar --stdin con patrones en argv. El man: los patrones vienen de stdin en vez de la lista de argumentos.
  • --color=always hacia el LLM. Color en el format ensucia el parseo.

<pattern>: fnmatch o literal; el literal matchea completo o desde el inicio hasta un slash. --exclude usa las mismas reglas. --stdin: un patrón por línea.

--include-root-refs: añade HEAD y pseudorefs. Verificado: sin la flag, HEAD como patrón no lista nada; con la flag, HEAD aparece. En un init sin commits, también sale vacío.

Create/Update/Delete a reference escriben el remoto; for-each-ref solo lee el clone

Receta (60 segundos)

Solo en un worktree propio:

git status -sb
git for-each-ref --count=20 --format='%(refname:short)' refs/heads
git for-each-ref --format='%(objectname:short) %(refname:short)' refs/heads/main

Si el ticket pregunta “¿qué ramas no están en HEAD?”:

git for-each-ref --no-merged HEAD --format='%(refname:short)' refs/heads

Cero --shell. Cero update-ref. Cero REST write. Para el remoto, ls-remote, no este comando.

Checklist

  • Worktree propio. git status -sb.
  • Patrón refs/heads / refs/tags / una ref concreta. --count si el set puede ser grande.
  • --format con %(refname:short) o un campo. Default solo si vas a parsear SHA + tipo + TAB + ref.
  • --merged / --contains con objeto explícito (HEAD), no colgando la flag.
  • Cero --shell/--python. Cero update-ref. Cero Create/Update/Delete a reference.
  • --start-after no existe en Git 2.50.1: 129. No paginar con esa flag aquí.

FAQ

¿for-each-ref cambia HEAD? No. Verificado: git status -sb sigue ## main después de listar. Para mover el árbol usa switch, no este plumbing.

¿Por qué no git branch --format? branch reusa los mismos %(fieldname), pero también crea y borra. Un agente que solo lee refs corre for-each-ref con patrón; no pasa cerca de -D / -M / -f.

¿Puedo usar Get a reference? GitHub Docs: Contents read, 200 / 404 / 409. :ref va como heads/<rama> o tags/<tag>. Para el clone local, for-each-ref sobre refs/heads/<rama> no habla con la red.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. for-each-ref no es ls-remote: lee el store local; no lista el remoto.