Guía9 min

git check-attr para coding agents: qué attr gana, no leer .gitattributes a ojo

Resumen

git check-attr resuelve un pathname contra gitattributes. Un attr: path COLON SP attr COLON SP info. --all omite unspecified. --cached ignora el working tree. --source mira un tree-ish. Cero dump de .gitattributes. No es check-ignore. Git 2.50.1.

GitHub
Un pathname se resuelve contra gitattributes; el working tree no se mueve

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 check-attr resuelve atributos de gitattributes sobre un pathname. El man (git-check-attr(1); git-scm.com/docs/git-check-attr HTTP 200, last-modified 2026-08-31; pie del man local Git 2.50.1.428.g0e8243, 2025-07-22; binario Git 2.50.1 / Apple Git-155) imprime path COLON SP attr COLON SP info. HEAD, index y working tree no se mueven. No reescribe archivos.

Eso no es check-ignore. Ignore responde si un path está excluido. check-attr responde cómo Git trata ese path: text, eol, diff, binary, linguist-generated. Leer .gitattributes a ojo pierde macros (binary), líneas posteriores que pisan y el working tree sucio.

GitHub Docs (Customizing how changed files appear on GitHub, HTTP 200 2026-09-04) documenta linguist-generated. No sustituye este comando: Linguist corre en github.com. Aquí preguntas el clone.

Contrato: un attr o --all, paths explícitos, ramificar set / unset / valor / unspecified, cero --stdin al LLM.

Un path, un attr, cuatro infos

OUTPUT del man: <path> COLON SP <attribute> COLON SP <info> LF. Verificado con od sobre -z: NUL entre los tres campos, no dos puntos. Sin -z, hay espacio después de cada dos puntos.

PreguntaComandoQué imprime
¿Este path tiene diff?git check-attr diff -- pathpath: diff: java o unspecified
¿Varios attrs?git check-attr crlf diff myAttr -- pathuna línea por attr
¿Todos los que aplican?git check-attr --all -- pathsolo asociados; sin unspecified
¿Index, no disco?git check-attr --cached text -- pathattrs del blob en el index
¿En un commit?git check-attr --source=HEAD text -- pathattrs del tree-ish

El man: si no usas --stdin, --all ni --, el primer argumento es el attr y el resto son paths. Con --, lo de atrás son paths. Un agente que pone el path primero (git check-attr README text) trata README como nombre de atributo.

<info> (man):

  • unspecified — el attr no está definido para ese path.
  • unset — definido como falso (-crlf, -text).
  • set — definido como verdadero (myAttr, binary).
  • un valor — eol=lf, diff=java, linguist-generated=true.

Verificado 2026-09-04 (Git 2.50.1 / Apple Git-155), repo con el .gitattributes del man (*.java diff=java -crlf myAttr, NoMyAttr.java !myAttr, README caveat=unspecified) más *.png binary, *.sh text eol=lf, pnpm-lock.yaml linguist-generated=true:

  • git check-attr diff MyClass.javaMyClass.java: diff: java, 0.
  • git check-attr crlf diff myAttr -- MyClass.javacrlf: unset, diff: java, myAttr: set, 0.
  • --all -- MyClass.java: las mismas tres; no lista text ni ident si no aplican. Tras añadir * text=auto en el working tree, --all también imprime text: auto.
  • git check-attr myAttr -- MyClass.java NoMyAttr.javaset y unspecified. !myAttr no imprime unset: el man del ejemplo sale unspecified.
  • git check-attr caveat READMEREADME: caveat: unspecified. Escribir caveat=unspecified en el archivo sigue siendo unspecified. El man lo marca como valor ambiguo.
  • Attr que no existe: git check-attr foo READMEfoo: unspecified, 0. No es error.
  • pic.png: binary: set, text: unset. --all añade diff: unset y merge: unset (macro binary de gitattributes).
  • run.sh: text: set, eol: lf.
  • pnpm-lock.yaml: linguist-generated: true (valor, no set).
  • Path inexistente que matchea *.java: no-such.java recibe diff: java. El archivo no tiene que existir.
  • Untracked extra.txt con * text=auto en el working tree: text: auto, 0.
  • --cached después de editar .gitattributes sin git add: working tree dice text: auto; --cached y --source=HEAD dicen unspecified.
  • --source=deadbeef: not a valid tree-ish source, 128.
  • Sin args: No attribute specified, 129. --all sin path: No file specified, 129.
  • -z sin --stdin sí funciona (a diferencia de check-ignore): README\0text\0auto\0.

--all lista attrs asociados; unspecified no sale

Lo que el agente sí / no corre

QuieroComandoTrampa
¿Este lockfile es generated?git check-attr linguist-generated -- pnpm-lock.yamlAbrir .gitattributes y “parece que sí”
¿Este script es eol=lf?git check-attr eol -- run.shdos2unix a ciegas
¿El PNG es binary?git check-attr binary -- pic.pngReformatear bytes
¿El index, no el disco sucio?--cached --all -- pathConfiar en un .gitattributes unstaged
¿En main publicado?--source=origin/main --all -- pathAsumir que HEAD local es github.com

Prohibido en autónomo:

  • Dump de .gitattributes al LLM. El man resuelve macros y precedencia. El archivo crudo no.
  • --stdin / -z --stdin sobre find o git ls-files. Eso es el dump. Pregunta un path, o un lote acotado que el agente ya tiene.
  • Tratar unspecified como error. Default 0. El 129 es usage. El 128 es tree-ish inválido.
  • Tratar caveat=unspecified en el archivo como un valor. El man: no todos los valores son igual de unívocos; sale unspecified.
  • Confundir !myAttr con unset. Verificado: NoMyAttr.java imprime myAttr: unspecified, no unset. -crlf sí es unset.
  • Usar check-attr para saber si un path está ignorado. Eso es check-ignore. Un untracked puede tener text: auto y seguir fuera del index.
  • Escribir linguist-generated creyendo que Git lo aplica en el clone. Git almacena el attr; GitHub Linguist lo lee al pintar el PR. check-attr solo te dice el valor local.
  • --all sobre la raíz del repo “por si acaso”. Pide el path del archivo que vas a tocar.

Receta (60 segundos)

Solo en un worktree propio:

git status -sb
git check-attr --all -- "$path"
git check-attr --cached --all -- "$path"

Si el ticket es “¿puedo correr prettier / dos2unix aquí?”: text, eol, binary. binary: set o text: unset → no toques el archivo como texto.

Si el ticket es “¿oculto el diff del lockfile?”: linguist-generated. Si sale unspecified, no inventes que GitHub lo va a colapsar.

Cero --stdin masivo. Cero reescritura de .gitattributes para “pasar” el check.

--cached mira el index; el working tree sucio no cuenta

check-attr vs check-ignore vs el archivo

check-attr responde cómo se trata un path trackeable. check-ignore responde si el exclude lo cubre. gitignore es el archivo de exclude. gitattributes es el archivo de attrs: check-attr es la pregunta, no la receta de qué escribir.

ls-files lista el index. No resuelve eol=lf. Un agente que “lee el .gitattributes y actúa” se salta macros (binary-diff -merge -text) y el --cached vs disco.

Checklist

  • Worktree propio. git status -sb. Un path por pregunta.
  • Attr concreto o --all. Paths detrás de --.
  • set / unset / valor / unspecified. unspecified es 0.
  • Disco sucio: --cached o --source=<tree-ish>.
  • Cero --stdin al LLM. Cero dump del archivo.
  • Ignore = check-ignore. Generated en GitHub = valor local, no Linguist remoto.

FAQ

¿--all me lista todo lo posible? No. El man: no incluye unspecified. Verificado: sin * text=auto, --all -- MyClass.java no imprime text. Pregunta el attr si lo necesitas.

¿El path tiene que existir? No. Verificado: no-such.java hereda *.java. check-attr no es lstat.

¿-z exige --stdin? En check-ignore, sí (128). Aquí no. Verificado: git check-attr -z text -- README sale 0 con NULs.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. check-attr no es check-ignore: resuelve attrs, no exclude.