Saltar al contenido
Guía9 min

git diff-index para coding agents: tree vs disco o index

git diff-index compara un tree-ish con el working tree o, con --cached, con el index. Sin tree-ish sale 129. Default raw, no parche. Cero -p autónomo. Distinto de git diff, diff-files y diff-tree. Git 2.50.1.

Por · Reportar un error

Continúa con un curso práctico

git diff-index compara un tree con el disco o con el index. El man (git-diff-index(1); git-scm.com/docs/git-diff-index HTTP 200, last-modified 2026-08-31; pie del man local Git 2.50.1.428.g0e8243; binario Git 2.50.1 / Apple Git-155): Compare a tree to the working tree or index. DESCRIPTION: Compare the content and mode of the blobs found in a tree object with the corresponding tracked files in the working tree, or with the corresponding paths in the index. SYNOPSIS: [-m] [--cached] [--merge-base] [<common-diff-options>] <tree-ish> [<path>...].

Sin <tree-ish> falla. Verificado 2026-09-06: git diff-index → usage, exit 129. Lo mismo con --cached solo. No es diff: el porcelain asume working tree vs index sin args. Aquí el tree es obligatorio.

No es status: status es porcelain XY. No es ls-files: ls-files lista, no compara. El man RAW OUTPUT FORMAT lo separa de sus hermanos: diff-index <tree-ish> = tree vs filesystem; diff-index --cached <tree-ish> = tree vs index; diff-files = index vs filesystem; diff-tree = dos trees. Untracked no aparece.

Contrato: no vuelques el raw ni el parche al LLM. Pregunta binaria = --quiet --exit-code. Lista = --name-only / --name-status + pathspec. Cero -p. Cero dump.

Qué hace (y qué no)

Default: raw, no patch. Una línea por path cambiado:

:100644 100644 <src-sha> 0000000 M	a.txt

El man: el sha1 dst es 0{40} si a file on the filesystem is out of sync with the index. Verificado: unstaged dirty vs HEAD → status M, dst todo ceros, exit 0. --cached HEAD con el mismo unstaged → silencio, exit 0: el index aún coincide con el tree. Tras git add, --cached HEAD muestra M con ambos sha reales.

--quiet implica --exit-code. Verificado: dirty vs HEAD → exit 1; working tree igual al tree → 0. Hay diff ≠ error de comando. 1 = “hay diferencias”, 0 = “no hay”.

-h / sin tree: usage, 129. Tree inválido: unknown revision or path not in the working tree, 128.

-m: By default, files recorded in the index but not checked out are reported as deleted. This flag makes git diff-index say that all non-checked-out files are up to date. Verificado: rm a.txt vs HEAD → D; con -m → silencio. --quiet --exit-code sin -m = 1; con -m = 0. Sparse / skip-worktree no es un delete.

--merge-base: use the merge base between <tree-ish> and HEAD instead. <tree-ish> must be a commit. No es el three-dot de un PR. GitHub Docs About branches (/en/pull-requests/reference/branches, HTTP 200 tras 301 desde about-comparing-branches…, 2026-09-06) habla de la UI de PRs, no de este plumbing.

tree-ish a la izquierda; working tree o index a la derecha; --cached no mira el disco

Lo que el agente sí / no corre

QuieroComandoTrampa
¿El worktree coincide con HEAD?git diff-index --quiet --exit-code HEADsin --quiet imprime raw
¿El index coincide con HEAD?git diff-index --cached --quiet --exit-code HEADsin --cached mira el disco
Lista de paths sucios vs un commit--name-only HEAD -- <path>-p es el parche
Untrackedstatus porcelaindiff-index no los ve
Diff de un PRdiff A...B o gh pr diff--merge-base no es la UI
Index vs disco, sin treeno este verboeso es diff-files; aquí el tree es obligatorio

Prohibido en autónomo:

  • -p / -u / --patch. El default raw ya es denso. El parche cabe en el contexto y se va.
  • Sin <tree-ish>. 129. No “adivines HEAD”.
  • Dump del raw al prompt. Parsea --name-only / --name-status o el exit de --quiet.
  • -m para “ocultar deletes”. Esconde paths no chequeados. En sparse es mentira útil solo si un humano lo pidió.
  • --cached creyendo que ves unstaged. --cached no considera el on-disk file at all (man). Unstaged desaparece.
  • --find-copies-harder / -C / -M autónomos. El man de -C: very expensive en proyectos grandes.
  • --ext-diff / textconv. Plumbing no corre textconv por default; no lo enciendas para “ver el binario”.
  • Encadenar read-tree + diff-index -m para fingir un working tree limpio.

El porcelain de diff cubre el caso “quiero ver el cambio”. Este verbo existe para scripts que necesitan raw estable y un exit code. Un tick de agente pregunta si hay diff, no cómo se ve el hunk.

Receta (60 segundos)

Solo en un worktree propio:

git status -sb
git diff-index --quiet --exit-code HEAD
git diff-index --name-only HEAD -- src/foo.ts

Tres líneas. status enseña untracked que este verbo no ve. --quiet --exit-code HEAD responde 0/1. --name-only + -- limita el pathspec. Cero -p. Cero repo entero al modelo.

Si el ticket pide “el diff del PR”, usa diff base...HEAD --stat o gh pr diff --name-only, no --merge-base a ciegas.

Trampas que ya vimos

  1. “Sin args es como git diff.” No. 129 y usage. El tree-ish no es opcional.
  2. “Exit 1 es fallo.” Con --quiet --exit-code, 1 = hay diferencias. El comando funcionó.
  3. “--cached muestra lo sucio.” Muestra index vs tree. Unstaged no existe para --cached. Verificado: dirty unstaged + --cached HEAD → silencio.
  4. dst 0000000 no es delete. En raw, dst ceros + M = working tree out of sync. Delete es D y mode dst 000000.
  5. Untracked invisible. echo extra > b.txt no aparece. Quien quiso untracked pedía status -u.
  6. -m “limpia” un rm. rm a.txt es D; -m lo declara up to date. Sparse-checkout vive de ese bit. No lo uses para “arreglar” el exit.
  7. --merge-base = three-dot de GitHub. No. Es merge-base(<tree-ish>, HEAD) contra el working tree/index. La UI de PRs es otro contrato.
  8. Pathspec sin --. Un path que también es ref: ambiguous argument, 128. Siempre -- antes de files.
  9. Creer que escribe. diff-index es read-only. No toca index ni disco. El inverso de materializar es checkout-index.

Checklist

  • Worktree propio, no el checkout del humano.
  • Tree-ish explícito (HEAD o un SHA). Cero invocación vacía.
  • Pregunta 0/1 → --quiet --exit-code. No parsees el raw a ojo.
  • Lista → --name-only o --name-status + -- <path>.
  • Untracked: status, no este verbo.
  • Cero -p. Cero dump al LLM.
  • --cached solo si comparas el index.
  • Cero -m autónomo. Cero -C/-M/--find-copies-harder.
  • Diff de PR: porcelain diff o gh, no este plumbing.

FAQ

¿diff-index mueve HEAD? No. Solo lee un tree y compara. Index y working tree no cambian.

¿Por qué el default es raw? Repeatability en scripts: una línea por path, NUL con -z, status A/C/D/M/R/T/U/X. El parche es para humanos o para git apply, no para el prompt.

¿Cuándo sí -p? Cuando un humano pide el hunk en un path acotado. Nunca autónomo sobre el árbol.

¿Y diff-files / diff-tree? diff-files no toma tree (index vs disco). diff-tree compara dos trees. Este verbo es el que exige un tree contra disco o index.

¿Dónde encaja esto? En el hub de comparativas y decisiones, junto a diff y status. Si estás armando el primer agente, empieza por el curso.

sin tree-ish sale 129; --quiet 1 significa hay diff, no un crash

De la guía a la práctica

Pi agent (Pi.dev): instala tu agente de coding

Lleva tus convenciones de trabajo a Pi.dev: instalación, instrucciones, sesiones y operación segura.

Ver el curso gratis

Contenido gratis, sin registro. Los servicios externos pueden tener costos.

Avisos cuando actualicemos cursos y guías

Recibe actualizaciones por correo. Es opcional: puedes seguir leyendo y abrir cualquier curso sin suscribirte.

Al suscribirte, solicitas avisos editoriales. Consulta la política de privacidad. Puedes pedir la baja en contacto.