Guía9 min

git ls-remote para coding agents: refs del remoto, cero fetch

Resumen

git ls-remote lista refs de un remoto sin bajar objetos. Formato oid TAB ref. --branches y --tags filtran; --refs oculta HEAD y tags pelados. --exit-code sale 2 si no hay match. Un agente nombra origin y un patrón; no dump, no fetch, no REST write. Git 2.50.1.

GitHub
Un remoto responde con refs y SHA; el working tree y FETCH_HEAD 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 ls-remote no descarga el remoto. El man (git-ls-remote(1), Git 2.50.1 / Apple Git-155; git-scm.com/docs/git-ls-remote HTTP 200, last-modified 2026-08-31; last updated in 2.48.0) lista referencias de un repositorio remoto junto con el commit ID asociado. No toca HEAD, index, working tree ni FETCH_HEAD. Eso no es fetch (trae objetos y actualiza origin/*) ni remote (nombra el set de remotes).

<repository> es una URL o el nombre de un remote (secciones GIT URLS y REMOTES de git-fetch(1)). Sin argumento, usa el remote configurado. Verificado 2026-09-04: repo sin remote → No remote configured to list refs from., 128. Nombre que no es repo → does not appear to be a git repository, 128.

GitHub Docs (REST API endpoints for Git references, HTTP 200 2026-09-04): una ref es un archivo con un SHA. List matching references pide Contents read (público sin auth). Create / Update / Delete a reference piden Contents write. Un agente de lectura no escribe refs por REST.

Contrato: remote explícito, patrón acotado, --exit-code si la pregunta es “¿existe?”, cero dump al LLM.

Lista, no objetos

OUTPUT del man: <oid> TAB <ref> LF. Verificado: SHA de 40 hex y un TAB real, no espacios. Tag anotado, salvo --refs: dos líneas — la ref del tag y la misma con ^{} (el objeto al que apunta). Lightweight no se pela.

PreguntaComandoQué imprime
¿Qué hay en origin?git ls-remote originHEAD, ramas, tags; tag anotado + ^{}
¿Solo ramas?git ls-remote --branches originrefs/heads/*
¿Tags sin pelar ni HEAD?git ls-remote --refs --tags originrefs/tags/* sin ^{} ni HEAD
¿HEAD apunta a qué rama?git ls-remote --symref origin HEADref: refs/heads/… TAB HEAD, luego el SHA
¿Existe esta rama?git ls-remote --exit-code --branches origin mainla fila, 0; sin match, 2

--branches / -b y --tags / -t no son excluyentes: juntas muestran refs/heads y refs/tags. El man: --heads y -h son sinónimos deprecados de --branches / -b. Verificado: git ls-remote -h sin más argumentos imprime usage y sale 129 (igual que otros subcomandos). Usa --branches, no -h.

--refs: no muestra tags pelados ni pseudorefs como HEAD. --quiet / -q: no imprime la URL en stderr. Verificado: sin -q, stderr es From <url>; con -q, stderr vacío.

--get-url: expande url.<base>.insteadOf y sale sin hablar con el remoto. Verificado: sin remote origin configurado, git ls-remote --get-url origin imprime el literal origin y sale 0. No es un chequeo de existencia.

ls-remote consulta el remoto; FETCH_HEAD y el working tree no cambian

Lo que el agente sí / no corre

QuieroComandoTrampa
SHA de origin/main sin fetchgit ls-remote --exit-code --branches origin maingit fetch “para mirar”
¿Hay tag v1 en el remoto?git ls-remote --exit-code --refs --tags origin 'v1'git tag local
URL efectivagit ls-remote --get-url origintratar origin impreso como “existe”
Parseargit ls-remote --refs -q origin -- 'refs/heads/main'partir por espacio; el TAB es el separador

Prohibido en autónomo:

  • Dump sin patrón. El default lista todas las refs (tras --branches / --tags). Un monorepo con cientos de heads llena el prompt. Nombra el patrón.
  • Tratar stdout vacío como “no existe”. Sin --exit-code, el man: sale 0 si habló con el remoto, haya o no match. Verificado: patrón nope* → stdout vacío, 0. Con --exit-code, el mismo patrón → 2.
  • -h como “solo ramas”. Es help. --heads todavía lista refs/heads (verificado) pero el man lo marca deprecado.
  • --upload-pack=<exec> o --server-option. Cambian el binario o el protocolo v2 del otro lado. Un agente no reconfigura el pack del host.
  • --sort=committerdate sobre refs cuyos objetos no están locales. El man: esas keys piden el objeto; si no llegó, error de missing object. version:refname / v:refname sí ordenan el nombre.
  • REST Create / Update / Delete a reference. GitHub Docs: Contents write. List sin :ref devuelve todo el namespace, incluidas notes y stashes si el servidor las tiene. No sustituye el clone ni un patrón de ls-remote.
  • Pegar una URL de issue o PR desconocido como <repository>. SECURITY de fetch: el peer puede ver objetos que no ibas a compartir. Usa el remote de git remote -v.

<patterns> son globs contra la cola del ref: desde el inicio (refs/heads/foo) o desde un slash (bar matchea refs/heads/bar, no refs/heads/foobar). Verificado 2026-09-04: patrón bar → solo refs/heads/bar; foobar es otra fila.

Patrón bar no atrapa foobar; --exit-code distingue 0 de 2

Receta (60 segundos)

Solo en un worktree propio:

git remote -v
git ls-remote --get-url origin
git ls-remote --exit-code --branches origin main

Si el ticket nombra un tag:

git ls-remote --exit-code --refs --tags origin 'v1.0.0'

--symref (el man: upload-pack hoy solo anuncia el symref de HEAD) confirma a qué rama apunta HEAD en el remoto. No actualiza refs/remotes/origin/HEAD local; eso es fetch.

La UI de GitHub (Viewing branches in your repository, HTTP 200 2026-09-04) parte ramas en Yours / Active (commits en tres meses) / Stale / All. El buscador es substring case-insensitive, sin query extra. Eso no es ls-remote. Checking out pull requests locally recomienda gh pr checkout, no listar refs a mano.

Checklist

  • Worktree propio. Remote de git remote -v, no una URL suelta.
  • Patrón o --branches / --tags / --refs. Cero dump del namespace.
  • “¿Existe?” = --exit-code. 2 = no hay match. 0 sin esa flag no prueba presencia.
  • Parsear oid TAB ref. SHA de 40 hex. --refs si no quieres HEAD ni ^{}.
  • -q en scripts (stderr limpio). Cero -h. Cero --upload-pack.
  • Cero REST write. Cero fetch “para mirar un SHA”.

FAQ

¿ls-remote actualiza origin/main? No. Solo imprime. Para tener el objeto y el remote-tracking usa fetch. ls-remote no deja nada que git show local pueda abrir si ese SHA no estaba ya.

¿Por qué --exit-code y no grep? Sin la flag, cero filas sigue siendo conversación OK (0). Un wrapper que mira solo stdout se cree que el remoto no existe cuando el patrón no matcheó. 2 es el contrato del man.

¿Puedo usar la REST Get a reference? GitHub Docs: :ref va como heads/<rama> o tags/<tag>; si no existe, 404. Prefijo feature sin rama exacta puede devolver featureA / featureB. Para un agente local, ls-remote --exit-code --branches origin feature con glob explícito es más predecible que un prefix HTTP.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. ls-remote no es fetch: lista refs; no trae objetos.

Verificado 2026-09-04 contra git-ls-remote(1) (Git 2.50.1 / Apple Git-155; git-scm.com/docs/git-ls-remote HTTP 200, last-modified 2026-08-31, last updated in 2.48.0), git-fetch(1) (HTTP 200), GitHub Docs “REST API endpoints for Git references” y “Viewing branches in your repository” (HTTP 200). En esta máquina: sin remote → 128; --exit-code sin match → 2; sin --exit-code y sin match → 0; -h → usage 129; --get-url origin sin remote → imprime origin y 0; patrón bar no lista foobar; --refs omite HEAD y ^{}; SHA 40 hex; git ls-remote --exit-code https://github.com/git/git.git master → 0 y refs/heads/master.