Guía9 min

git blame para coding agents: anota líneas, no culpa al autor

Resumen

git blame anota la revisión que última modificó cada línea. No es autoría legal ni a quién pinguear. Un agente usa -L o --porcelain; no vuelca el archivo al contexto. -C/-M siguen copias; --ignore-rev salta reformats. Git 2.50.1. GitHub Blame lee .git-blame-ignore-revs.

GitHub
Cada línea apunta a un SHA; blame no mueve HEAD ni el working tree

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 blame no es “quién escribió esto”. El man (Git 2.50.1 / Apple Git-155): anota cada línea del archivo con la revisión que última la modificó. Opcionalmente parte de un <rev>. -L (repetible) recorta el rango. Sigue renames de archivo entero; no hay flag para apagarlo. Para líneas movidas o copiadas entre archivos: -M y -C.

El informe no dice nada de líneas borradas o reemplazadas. Eso es git diff o pickaxe (git log -S). Un agente que “busca al dueño” y pega el blame de 2 000 líneas al prompt está quemando contexto, no investigando.

Esta guía no sustituye worktrees ni PRs con gh. El contrato: cuándo un agente puede anotar, qué flags caben en un harness, y cómo no mentir con el autor.

Blame no toca el repo

git blame es solo lectura. No mueve HEAD, index ni working tree. No es reset ni add.

Default (ni --porcelain ni --incremental): por línea, SHA abreviado, ident del autor (nombre+fecha, salvo -s/-e) y número de línea, antes del contenido.

--porcelain / --line-porcelain / --incremental son para máquina. El man muestra el conteo por autor:

git blame --line-porcelain src/lib/foo.ts |
  sed -n 's/^author //p' |
  sort | uniq -c | sort -rn

--progress no convive con --porcelain ni --incremental.

SHA + autor + n° de línea; el working tree no cambia

Lo que el agente sí / no corre

QuieroComandoTrampa
Origen de un rangogit blame -L 40,60 -- src/foo.tsSin --, un path que parezca opción se come
Funcióngit blame -L ':nombreFn' -- src/foo.tsEl funcname es el mismo criterio que el hunk-header de git diff / gitattributes
Parseargit blame --porcelain -- src/foo.tsDefault humano no es estable para scripts
Saltar un reformat--ignore-rev <sha> o --ignore-revs-fileSin eso, un Prettier masivo “dueña” el archivo
Copias entre archivos-C / -C -C / -C -C -CTres -C buscan copias en cualquier commit; caro
Líneas ya no vivasgit log --pretty=oneline -S'snippet' -- src/foo.tsBlame no reporta borrados

Prohibido en autónomo:

  • git blame sin -L sobre un archivo grande. Recorta primero (-L <start>,<end> o -L 40,+21; el man: ambos piden 21 líneas desde 40).
  • Tratar el campo author como “pinguea a X”. El man anota la revisión; el autor del commit puede ser un bot, un squash o un --ignore-rev mal aplicado.
  • --contents contra un archivo que el agente no acaba de leer. Anota el contenido de <file> (o stdin con -) partiendo de <rev> o HEAD. Sirve para working tree sucio; no para “inventar” un path.
  • -C -C -C por default en un monorepo. El man: un -C mira otros files del mismo commit; dos, también el commit que crea el file; tres, cualquier commit. Umbral default 40 caracteres alfanuméricos (último -C gana).
  • Volcar porcelain completo al LLM. Parsea SHA + summary + rango; cita 5–20 líneas.
  • -p interactivo no aplica aquí; blame no tiene pager obligatorio, pero un dump de 10k líneas es el mismo fallo que truncar tool results evita.

--reverse <start>..<end> camina hacia adelante: última revisión en la que la línea existió, no en la que apareció. El path debe existir en <start>. git blame --reverse STARTSTART..HEAD. Un agente no lo usa para “quién lo metió”.

--first-parent solo sigue el primer padre en merges: cuándo la línea entró a la rama de integración, no a la historia global.

Receta (60 segundos)

Solo lectura, worktree propio o no: no escribe.

git blame -L 120,160 -- src/lib/foo.ts
git log -1 --format='%H%n%an <%ae>%n%s' -- <sha>

Si el archivo es generated o un rename, pide el path original con -f (--show-name): el man lo muestra cuando hay líneas de otro nombre.

Reformateo masivo en el repo:

# raíz del repo (GitHub Docs: el archivo debe vivir ahí)
printf '%s\n' '# prettier 2026-08' a8940f7fbddf7fad9d7d50014d4e8d46baf30592 \
  > .git-blame-ignore-revs
git blame --ignore-revs-file .git-blame-ignore-revs -L 120,160 -- src/lib/foo.ts

blame.ignoreRevsFile en config hace lo mismo. --ignore-revs-file "" limpia la lista ya cargada. Líneas de un rev ignorado que Git reasigna: ? si blame.markIgnoredLines; * si no pudo (blame.markUnblamableLines). En porcelain: tags ignored / unblamable.

GitHub Docs (HTTP 200, 2026-09-03, Viewing and understanding files): la vista Blame del archivo es el historial línea a línea (autor, descripción, fecha). Las revisiones en .git-blame-ignore-revs se ocultan usando --ignore-revs-file. Tip oficial: en CLI, git blame. Existe “Bypassing .git-blame-ignore-revs in the blame view” para ver el historial crudo en la UI; un agente en CLI pasa o no el flag, no “bypassea” la UI.

Conflictos / merges no aplican. Si necesitas el diff del commit culpable: git show <sha> -- src/lib/foo.ts. Cero reset.

Ignore-revs salta el commit de format; el SHA útil es el cambio semántico

Checklist

  • Path con --. Rango -L o función ':name'. No el archivo entero.
  • Máquina: --porcelain / --line-porcelain. Humano: default.
  • Autor ≠ dueño del módulo. Lee summary + git show.
  • Borrados: git log -S, no blame.
  • Reformat: --ignore-rev o .git-blame-ignore-revs en la raíz.
  • -C extra solo si hay evidencia de copy/paste. Tres -C es último recurso.
  • No meter el dump al prompt: SHA, rango, 5–20 líneas.

FAQ

¿git annotate? El man de blame: SEE ALSO git-annotate(1). -c usa el modo de annotate. Un agente se queda en blame --porcelain.

¿Whitespace? -w ignora whitespace al comparar padre e hijo. Útil junto a ignore-revs de formatters. No es EditorConfig.

¿Untracked / ignored? Blame necesita un path en el tree del <rev> (default HEAD). Untracked: no hay líneas que anotar; mira gitignore. --contents anota un contenido que aún no está commiteado.

¿Boundary / root? -b / blame.blankBoundary: SHA en blanco en commits frontera. --root no trata el root como frontera (blame.showRoot). Un agente no cambia esos defaults salvo que el ticket lo pida.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. Blame no es sandbox (sandboxing).

Verificado 2026-09-03 contra git-blame(1) (Git 2.50.1 / Apple Git-155; el sitio git-scm lista el man actualizado en 2.53.0 sin cambiar el contrato de anotar líneas) y GitHub Docs “Viewing and understanding files” (HTTP 200, URL canónica /repositories/working-with-files/using-files/viewing-and-understanding-files).