Guía9 min

git hash-object para coding agents: SHA del contenido, no un commit

Resumen

git hash-object calcula el OID de un blob. Sin -w no escribe. -w mete el objeto suelto: no mueve HEAD ni el index. Un agente no usa --literally, no fabrica commit/tree, no llama Create a blob. Git 2.50.1.

GitHub
Un blob se hashea a un SHA; HEAD y el index no se mueven

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 hash-object no crea un commit. El man (git-hash-object(1); git-scm.com/docs/git-hash-object HTTP 200, last-modified 2026-08-31; last updated in 2.43.0, 2.43.1 → 2.55.0 sin cambios; pie del man local Git 2.54.0, 2026-04-19; binario Git 2.50.1 / Apple Git-155) calcula el object ID de un contenido con un tipo (default blob) y, si pasas -w, lo escribe al object database. El file puede estar fuera del work tree. Imprime el SHA a stdout. HEAD, index y working tree no se mueven.

Sin -w no hay objeto suelto. Verificado 2026-09-04: printf 'hello\n' | git hash-object --stdince013625030ba8dba906f756967f9e9ca394464a; find .git/objects -type f0. Con -w, el mismo SHA queda como loose object; cat-file -tblob, -s6. git status -sb sigue mostrando ??; git ls-files vacío. Eso no es add.

GitHub Docs (REST API endpoints for Git blobs, HTTP 200 2026-09-04, API version 2026-03-10): Create a blob pide Contents write y responde 201 con sha. Get a blob pide Contents read, tope 100 MB, JSON siempre Base64. Ninguno mueve una rama.

Contrato: hashear un path, cero -w, cero --literally, cero REST de escritura.

SHA no es commit

PreguntaComandoQué imprime
¿Qué SHA tendría este file?git hash-object -- path40 hex; no escribe
¿Y si lo meto al store?git hash-object -w -- pathel mismo SHA; objeto suelto
¿Stdin?git hash-object --stdinSHA del payload
¿Vacío?git hash-object --stdin </dev/nulle69de29bb2d1d6434b8b29ae775ad8c2e48c5391
¿Tipo inválido?git hash-object -t nope pathinvalid object type, 128

Verificado 2026-09-04 (Git 2.50.1 / Apple Git-155):

  • Default -t = blob. -t blob explícito da el mismo SHA que sin -t.
  • -t commit / tree / tag con hello\n: object fails fsck / refusing to create malformed object, 128.
  • File que no existe: could not open … No such file or directory, 128.
  • --stdin-paths con nope.txt: el mismo 128.
  • --path junto a --no-filters: Can't use --path with --no-filters, 129.
  • -h: usage, 129.
  • Dos paths: dos SHA, uno por línea, exit 0.
  • -w con repo sin commits: HEAD sigue inexistente. El objeto vive; la rama no.

El man: --path no cambia el hash por el pathname; elige filtros (gitattributes, conversión EOL) como si el file viviera ahí. --no-filters hashea bytes crudos. Con --stdin, --no-filters va implícito salvo que pases --path.

Sin -w solo imprime el SHA; con -w aparece un objeto suelto

Lo que el agente sí / no corre

QuieroComandoTrampa
SHA de este pathgit hash-object -- pathgit hash-object -w “por si acaso”
¿Coincide con HEAD?hash-object -- path vs rev-parse HEAD:pathasumir que el SHA es el commit
Varios filesgit hash-object -- a b--stdin-paths con globs del shell
Inspeccionar un SHA ya en el storecat-file -t / -s / -e-p del blob al LLM

Prohibido en autónomo:

  • -w. El man: Actually write the object into the object database. No actualiza index ni refs. Un objeto suelto huérfano sobrevive a gc un tiempo y ensucia el clone. Si el ticket pide “commitear este file”, el camino es add + commit, no hash-object -w.
  • --literally. El man: permite a --stdin hashear cualquier basura que no pasaría git-fsck. Verificado: printf 'not-a-commit' | git hash-object -t commit --stdin128; con --literally → SHA 1ee52eb1e0a77f156810678e1c8cc3f33cca9da3, exit 0. Sirve para stress-test de Git, no para un agente.
  • -t commit / tree / tag sin un payload válido y un humano. El default blob cubre el 99 % de “hashea este file”.
  • Mezclar --stdin con paths. Verificado: git hash-object --stdin f.txt con stdin vacío imprime el blob vacío y el SHA del file. Un modo por invocación.
  • Encadenar el SHA a git update-index --cacheinfo. Ese plumbing mete el blob al index sin pasar por el working tree. Un agente no lo corre.
  • Create a blob (POST /repos/{owner}/{repo}/git/blobs). GitHub Docs: Contents write, content obligatorio, encoding utf-8 o base64 (default utf-8), 201. No actualiza la rama. Un agente de lectura no lo llama.
  • Pegar secretos a --stdin. El SHA no oculta el contenido: cualquiera con el objeto lo recupera con cat-file.

--stdin-paths: un pathname por línea, no el contenido. Verificado: pathname inexistente → 128, igual que un <file> faltante.

Receta (60 segundos)

Solo en un worktree propio:

git status -sb
git hash-object -- src/lib/posts.ts
git rev-parse --verify --quiet HEAD:src/lib/posts.ts

Si los SHA coinciden, el working tree de ese path es el blob de HEAD. Si rev-parse sale 1, el path no está en el tree; el hash-object igual funciona: el file puede estar fuera del work tree.

Para un payload chico por stdin (test, no secretos):

printf 'hello\n' | git hash-object --stdin

Cero -w. Cero --literally. Cero -t distinto de blob.

Create a blob en GitHub escribe el remoto; hash-object local no mueve la rama

UI de GitHub vs clone local

Create a blob calcula el SHA-1 del contenido y lo guarda en la base Git del remoto. Get a blob lee hasta 100 MB. Ninguno sustituye el clone. Un agente que quiere el SHA de este file corre hash-object; el que quiere el blob de github.com usa Get a blob con Contents read, no inventa 40 hex ni hace POST.

hash-object no es cat-file: uno produce (o escribiría) el objeto; el otro inspecciona uno que ya existe. Tampoco es show.

Checklist

  • Worktree propio. git status -sb. Un modo por invocación.
  • Default: git hash-object -- path. Cero -w.
  • Comparar con HEAD:path vía rev-parse, no asumir que el SHA es la rama.
  • Cero --literally, cero -t commit|tree|tag, cero --stdin + files.
  • Cero update-index --cacheinfo. Cero Create a blob.
  • Inspección del objeto ya escrito: cat-file -t / -s / -e, no dump.

FAQ

¿hash-object -w es un commit? No. Escribe un objeto suelto. Sin add no entra al index; sin commit no hay snapshot.

¿Por qué -t commit con texto suelto falla? El man lista commit, tree, blob, tag. Git corre fsck al crear. Un commit sin línea tree es basura: 128, salvo --literally.

¿Puedo hashear un file fuera del repo? Sí. El man lo dice. El SHA sale; -w lo mete a este object database, no al otro directorio.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. hash-object no es cat-file: calcula el SHA; no pretty-printa el objeto.