git read-tree para coding agents: carga el índice, no el disco
Resumen
git read-tree lee un tree al índice y no toca el working tree salvo -u. Plumbing, no porcelain. Un tree reemplaza el index; -m hace merge 1/2/3-way; --reset descarta unmerged; --empty vacía. Cero autónomo con -u/--reset/--prefix. Distinto de checkout, restore y merge-tree. Git 2.50.1.

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 read-tree escribe el índice. El man (git-read-tree(1); git-scm.com/docs/git-read-tree 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): Reads tree information into the index, but does not actually update any of the files it "caches". SYNOPSIS mezcla --empty, un <tree-ish>, dos trees (fast-forward) y tres trees (3-way) con -m / --reset / --prefix.
Sin -u, el working tree no cambia. El índice sí: un tree reemplaza lo staged. Con -u, además pisa archivos. --reset -u no aborta si eso borra cambios locales o untracked. --empty deja el índice en cero y deja el disco. --prefix mete un tree bajo un directorio y falla si el path ya existe.
No es checkout. checkout mueve HEAD o restaura paths. No es restore: restore pisa paths, no reconstruye el index desde un tree crudo. No es merge-tree: merge-tree fusiona sin tocar índice ni working tree. read-tree es el índice.
Contrato: no corras git read-tree autónomo sobre el índice del humano. Inspección = ls-tree. Preview de merge = merge-tree. Sparse = sparse-checkout, no skip-worktree a mano. Cero -u. Cero --reset. Cero --prefix. Cero --empty. Cero -m de tres trees en el index vivo.
Qué hace (y qué no)
Un <tree-ish>: Reads the tree information given by <tree-ish> into the index. El man apunta a git-checkout-index(1) para materializar archivos. read-tree solo cachea.
-m: Perform a merge, not just a read. Se niega si el índice ya tiene unmerged. Un tree + -m conserva stat() del index cuando el contenido coincide (menos falsos hits en git diff-files). Dos trees = fast-forward $H → $M con reglas de “carry forward”. Tres trees = stages 1/2/3 (ancestro, ours, theirs); los paths idénticos colapsan a stage 0. El resto queda unmerged para que el porcelain decida.
--reset: igual que -m, pero descarta unmerged en vez de fallar. Con -u, updates leading to loss of working tree changes or untracked files or directories will not abort.
-n / --dry-run: Check if the command would error out, without updating the index or the files. Sigue siendo un comando de merge plumbing: no es un “ls” seguro.
Verificado 2026-09-06 (Git 2.50.1 / Apple Git-155). Repo base → main cambia f.txt; topic cambia el mismo archivo:
git read-tree HEAD^{tree}: 0.git statuslimpio. Working tree igual.- Working tree sucio (
changedenf.txt),git read-tree HEADsin-u: 0. Disco siguechanged. El index vuelve al blob del tree (a). git read-tree --empty: 0.git ls-filescuenta 0.lsdel disco sigue conf.txt.- Sin args: warning emptying the index with no arguments is deprecated; use --empty. 0. No lo uses: el default vacío es trampa.
git read-tree --foo: 129, unknown option `foo'.- Fuera de un repo: fatal: not a git repository, 128.
- Tree inventado
deadbeef…: 128, failed to unpack tree object. git read-tree --prefix=nested/ HEAD(path libre): 0. Aparecenested/f.txten el index.- Segundo
--prefix=nested/ HEAD: 128, Entry 'nested/f.txt' overlaps with 'nested/f.txt'. Cannot bind. --prefix=./ HEAD: 128, invalid path './f.txt'.- 3-way
git read-tree -m <base> main topiccon conflicto de contenido: 0.git ls-files -smuestra stages 1/2/3 def.txt.git write-treeentonces 128 (error building trees). --reset -u HEADcon working tree sucio: 0. El archivo en disco vuelve al tree. Los edits locales desaparecen.

Lo que el agente sí / no corre
| Quiero | Comando | Trampa |
|---|---|---|
| Ver el tree | ls-tree HEAD^{tree} | read-tree “para mirar” |
| ¿Estas ramas fusionan? | merge-tree --quiet --write-tree | -m de tres trees en el index vivo |
| Sparse checkout | sparse-checkout cone | skip-worktree / info/sparse-checkout a mano |
| Restaurar un path | restore -- <path> | read-tree -u |
| Cambiar de rama | switch | checkout dual o read-tree + update-ref |
| Índice temporal | --index-output en un worktree propio, con confirmación | $GIT_INDEX_FILE del humano |
Prohibido en autónomo:
-u. Materializa el merge/read en el disco. El man lo dice: update the files in the work tree with the result of the merge.--resety--reset -u. El segundo no aborta ante pérdida de cambios o untracked. Verificado: unf.txtsucio volvió al tree.--emptyo cero args. Vacía el index. El disco queda huérfano respecto a Git. El default sin args está deprecated.--prefix. El man: Keep the current index contents, and read the contents of the named tree-ish under<prefix>. Verificado: el segundo bind 128. No es un “vendor copiado”.-mcon 2 o 3 trees sobre el index del humano. Un 3-way exit 0 con stages 1/2/3 rompewrite-tree(128). El humano se queda a medias.-i. disables the check with the working tree. Sirve para un index temporal. En el index vivo mezcla un merge ajeno con edits locales.--aggressive/--trivial. Cambian qué colapsa a stage 0. Política de porcelain, no de agente.--recurse-submodules. Detacha HEAD de submódulos. No es el ticket.--no-sparse-checkoutpara “traer todo”. El man: el skip-worktree de read-tree predatesgit sparse-checkout; users are encouraged to use the sparse-checkout command.- Encadenar a
checkout-index -f -u -ao awrite-tree+commit-tree. El man lo muestra como receta de scripts de más alto nivel. Un agente no mueve refs ni pisa el árbol. - Tratar exit 0 de un 3-way como “merge limpio”. Verificado: conflicto de contenido = 0 + unmerged. El 0 solo dice “el index ahora tiene stages”.
Receta (60 segundos)
Si el humano pide “carga este tree en un index aparte” y hay un worktree tuyo:
git rev-parse --verify --quiet "$tree^{tree}"
GIT_INDEX_FILE=/tmp/agent.idx git read-tree -n -- "$tree"
# solo si el humano confirmó el index-output:
# git read-tree --index-output=/tmp/agent.idx -- "$tree"
-n no escribe. --index-output exige el mismo filesystem que el index (man: rename(2)). No uses el path por defecto .git/index.
Si el pedido es “¿mergea?”: no entres a read-tree. Eso es merge-tree. Si el pedido es “muéstrame src/ en ese SHA”: ls-tree, no prefix.
Checklist
- ¿Hace falta ver un tree? →
git ls-tree -r --name-only <tree>. - ¿Hace falta probar un merge? →
git merge-tree --quiet --write-tree. - ¿Hace falta cambiar paths? →
git restore -- <path>en un worktree propio. - ¿Alguien pidió sparse? →
git sparse-checkout, noread-tree. - Cero
-u,--reset,--empty,--prefix,-men el index del checkout humano. - Cero args =
--emptydeprecated. No.
FAQ
¿Puedo usarlo como git reset --mixed? No. reset mueve HEAD según el modo. read-tree no mueve HEAD. Un tree al index con HEAD intacto deja el repo en un estado que status describe como staged/unstaged raro. El humano no pidió eso.
¿--dry-run es seguro siempre? Es el único flag que el man promete sin escribir. Sigue parseando trees y reglas de merge. No lo uses como substituto de ls-tree. Un dry-run de 3-way no te da el resumen de conflictos: te da “¿abortaría?”.
¿Por qué el 3-way sale 0 con conflicto? Porque read-tree no resuelve contenido. Only trivial merges are done by git read-tree itself. Only conflicting paths will be in an unmerged state when git read-tree returns. El 0 significa “ya puse stages”. Porcelain (merge-index / merge-file) viene después. Un agente que lea “0 = ok” mentiría.
¿Sparse checkout va aquí? El man dedica una sección: $GIT_DIR/info/sparse-checkout y skip-worktree. También dice que eso predates git sparse-checkout y que el usuario debe preferir el porcelain. No reimplementes cone mode con read-tree.
¿Dónde vive el hub? En comparativas y decisiones. El curso gratis sigue en /curso/instalar-agente.

Lecturas relacionadas
Sigue explorando Coding Agents y otras piezas para builders.

git checkout-index para coding agents: index al disco, no checkout

git mktree para coding agents: tree desde ls-tree, no desde el index

git commit-tree para coding agents: commit sin mover HEAD
