Guía9 min

git ls-files para coding agents: index y disco, no find

Resumen

git ls-files mezcla el index con el working tree. Default: tracked. -o son untracked; -i exige -c o -o y un --exclude*. Un agente parsea -z, nombra paths, no vuelca el árbol al LLM y no usa find. Git 2.50.1.

GitHub
El index de Git lista paths tracked; el disco queda aparte hasta -o

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-files no es find. El man (git-ls-files(1), Git 2.50.1 / Apple Git-155; git-scm.com/docs/git-ls-files HTTP 200, last updated in 2.55.0; 2.46.2 → 2.54.0 sin cambios) mezcla la lista del index con el working tree y enseña combinaciones. Default si no pasas -c/-s/-d/-o/-u/-k/-m/--resolve-undo: tracked del index (--cached). Un path untracked no aparece. Un path que Git no rastrea no es “el árbol”.

GitHub Docs (Finding files on GitHub, HTTP 200 2026-09-04): el file finder de la UI (t / Go to file) busca en el remoto, no en tu worktree. Por default excluye .git, .hg, .sass-cache, .svn, build, dot_git, log, tmp y vendor. Eso no es ls-files. Contrato: flags explícitas, -z para parsear, cero dump del árbol al prompt.

Esta guía no sustituye status (porcelain XY) ni gitignore (qué patrón aplica). El contrato: qué forma corre un agente, qué flags están prohibidas, y por qué find no es el index.

Index y disco no son lo mismo

Sin flags, ls-files imprime paths del index. -o / --others añade untracked. -d son deletes unstaged. -m son modificaciones unstaged; el man: un delete unstaged también cuenta como modified. -s / --stage imprime modo, objeto y stage (0 en un merge limpio; 1/2/3 en conflicto). -u / --unmerged fuerza --stage y esconde el resto de tracked.

-i / --ignored no va solo. El man: exige -c o -o. Con -c, lista tracked que coinciden con un exclude. Con -o, untracked que coinciden. Las reglas estándar no se activan solas: hace falta al menos un --exclude*. --exclude-standard carga .gitignore por directorio, $GIT_DIR/info/exclude y el ignore global.

-z: NUL al final, paths sin quoting C. Sin -z, core.quotePath puede entrecomillar. Un parser que parte por espacio sobre un path con espacio se rompe.

-o --exclude-standard lista untracked; el index no cambia

Lo que el agente sí / no corre

QuieroComandoTrampa
¿Este path está en el index?git ls-files --error-unmatch -- pathfind . -name y asumir que Git lo rastrea
Untracked visiblesgit ls-files -o --exclude-standard -z-o sin exclude: mete node_modules
Ignored untrackedgit ls-files -o -i --exclude-standard-i solo (fatal: must be used with either -o or -c)
Conflictogit ls-files -u-t “porque las letras se ven mejor”

Prohibido en autónomo:

  • find . / ls -R volcado al LLM. El man lista index + worktree según flags. Un find cruza ignored, build y el scratch del agente.
  • -i sin -c/-o. Verificado 2026-09-04: git ls-files -imust be used with either -o or -c.
  • -i -o sin --exclude*. Verificado: needs some exclude pattern.
  • -t / -v / -f para “entender el status”. El man: para scripts, status --porcelain o git-diff-files --name-status son casi siempre superiores. Tags: H tracked limpio, S skip-worktree, M unmerged, R delete unstaged, C change unstaged, K killed, ? untracked, U resolve-undo. -v minúsculas si assume-unchanged; -f si fsmonitor valid. Un agente no mezcla esas letras con porcelain XY.
  • --format combinado con -s, -o, -k, -t, --resolve-undo, --eol. Verificado: cannot be used with… (el man local 2.50.1 también nombra --deduplicate en el fatal). --format='%(objectname) %(path)' es para cached.
  • --debug. El man: formato puede cambiar. No es API.
  • --recurse-submodules autónomo. Solo --cached y --stage. Recorre submodules activos.
  • --sparse para “resumir” el cone. Si el index es sparse, enseña directorios con slash (x/) sin expandir. Un agente no pide el cone entero “para listar”.
  • --with-tree + -s/-u. El man: does not make any sense. Pretende que paths quitados del index desde un tree-ish siguen.
  • -k / --killed como “limpia el disco”. Son untracked que chocan con un tracked (file vs dir) y bloquean el checkout. Borrar a ciegas no es el ticket.
  • --pathspec globs de shell sin comillas. El man acepta <file>…; el shell expande primero.
  • Encadenar ls-files -z | xargs -0 rm -f (el ejemplo de vendor drop vive en rm). Listar no es borrar.

-c es el default. Pedirlo otra vez no cambia el set. --deduplicate aplasta duplicados de merge stages o de -d+-m solo cuando la salida es filename; con -t/-u/-s no hace nada.

Receta (60 segundos)

Solo en un worktree propio:

git status -sb
git ls-files --error-unmatch -- src/lib/foo.ts
git ls-files -o --exclude-standard -z | tr '\0' '\n' | head

Si el path no está en el index, --error-unmatch sale 1 (pathspec … did not match, Did you forget to 'git add'? ). Verificado 2026-09-04: git ls-files --error-unmatch -- no-such-file-xyz → exit 1. Sin el flag, un path ausente no imprime nada y sale 0: un typo silencioso.

Untracked a borrar: clean -n primero, no xargs rm. Untracked a stagear: add con paths nombrados. Diff del contenido: diff, no el listado.

Desde un subdirectorio los paths salen relativos al cwd. --full-name los ancla a la raíz del repo. Verificado: cd src/lib && git ls-files -- brands.tsbrands.ts; con --full-namesrc/lib/brands.ts. Un parser de sesión espera la raíz.

-i sin exclude falla; --exclude-standard carga gitignore

UI de GitHub vs clone local

GitHub Docs (Finding files on GitHub): Go to file busca el commit que ves en github.com. No ve untracked locales ni el index sucio. Los dirs default-excluidos se pisan con .gitattributes linguist-generated=false y glob **; no con ls-files.

Viewing and understanding files (HTTP 200 2026-09-04): Raw, Blame y Copilot miran el file en GitHub. git ls-files no abre contenido. Para historia por línea, blame. Para el blob, show.

--eol imprime i/<eolinfo> w/<eolinfo> attr/<eolattr> y un tab. Valores de eolinfo: -text, none, lf, crlf, mixed o vacío (no regular / no en index / no accesible). No es un linter; no lo vuelques entero.

Verificado 2026-09-04 en esta máquina (Git 2.50.1 / Apple Git-155): git ls-files -- src/lib/brands.ts lista el path (exit 0). git ls-files -s -- src/lib/brands.ts100644 <sha> 0. git ls-files -z -- src/lib/brands.ts termina en NUL. git ls-files -o -i --exclude-standard lista artefacto de build (.next/…) que Git ignora y el agente sigue viendo en disco.

Checklist

  • Worktree propio. git status -sb. No find.
  • --error-unmatch si el path tiene que existir en el index.
  • Untracked: -o --exclude-standard -z. Ignored: añade -i.
  • Cero -i solo, cero -t como API, cero --debug, cero --format con -o.
  • Parsear -z. Paths desde la raíz (--full-name si el cwd no es la raíz).
  • Borrar untracked → clean. Stagear → add.
  • File finder de GitHub no sustituye el clone.

FAQ

¿ls-files es lo mismo que status? No. Status es porcelain para humanos/scripts (XY, rama, untracked). ls-files es plumbing: elige qué conjunto (cached / others / deleted / unmerged). El man de -t manda a status para scripting.

¿Por qué no veo .env? Default = index. Un .env ignored es -o -i --exclude-standard. Git no lo rastrea; el agente lo lee en disco (gitignore).

¿--directory? Solo con -o. Si un dir entero es “other”, imprime dir/ y no el contenido. --no-empty-directory oculta dirs vacíos. Un agente que busca un file no usa esto para “ahorrar tokens”: nombra el path.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. ls-files no es status: lista conjuntos del index, no el resumen XY.