Guía9 min

git pack-objects para coding agents: plumbing de packs, no compactar el repo

Resumen

git pack-objects lee objetos por stdin y escribe un .pack/.idx o un stream. Es el motor de gc, repack y bundle. Un agente no lo corre autónomo: no escribe en objects/pack, no usa --thin ni --cruft. Distinto de repack, gc, prune y pack-refs. Git 2.50.1.

GitHub
Flujo de objetos Git comprimidos a un pack; el working tree permanece intacto

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 pack-objects crea un archivo empaquetado de objetos. El man (git-pack-objects(1); git-scm.com/docs/git-pack-objects 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): Create a packed archive of objects. SYNOPSIS: lee una lista por stdin y escribe o un par .pack + .idx con <base-name>, o el pack a --stdout.

Es plumbing. No lista refs. No mueve HEAD. No toca el índice ni el working tree. Solo comprime objetos (enteros o como delta) y los deja en un archivo. Por eso es más peligroso que repack: git repack -d -q es porcelain que llama a pack-objects con flags seguros (--delta-base-offset en Git moderno) y deja el pack dentro de $GIT_OBJECT_DIRECTORY/pack. pack-objects crudo escribe donde le digas el <base-name> o al stdout. Un agente que lo dispara “para compactar” puede dejar un .pack huérfano en el cwd, un pack thin inválido, o un cruft pack que el humano no pidió.

No es gc. gc decide si compactar (--auto) y orquesta pack-objects + prune. Tampoco es prune (borra loose inalcanzables) ni count-objects (diagnóstico -v). Ni pack-refs: ese comando empaqueta nombres de refs en packed-refs, no blobs ni trees.

Tampoco es bundle. El man de bundle lo dice: Bundles are .pack files (see git-pack-objects(1)) with a header. Bundle añade un encabezado de refs; pack-objects solo el archivo de objetos. Un .pack suelto no se clona con git clone.

Ni rev-list: ese comando enumera. pack-objects consume esa enumeración. El man: --revs Read the revision arguments from the standard input, instead of individual object names. The revision arguments are processed the same way as git rev-list with the --objects flag.

Git 2.50.1. Contrato: no corras git pack-objects autónomo. Compactar el repo = repack si el humano lo pide, o maintenance / gc --auto --quiet. Diagnóstico = count-objects -v. Transferir un snapshot = bundle o archive. Si el humano pide explícitamente un pack: --non-empty -q + <base-name> fuera de .git/objects/pack, lista de objetos acotada, cero --thin, cero --cruft, cero --filter, cero --stdout hacia un pipe que nadie consume.

Stdin, base-name y stdout

El man parte el contrato en dos salidas:

FormaQué escribeAgente
<base-name><base-name>-<SHA-1>.pack + .idx; imprime el SHA a stdoutSolo si el humano da el path fuera del object store
--stdoutel .pack crudo al stdout; sin .idxCero autónomo; el receptor debe ser index-pack o un peer
--non-emptyno crea pack vacíoObligatorio si el humano pide el comando
-qsilencia progreso en stderrPreferible; --progress ensucia logs

Sin --revs, stdin son nombres de objeto (SHA), no commits. Con --revs, stdin son argumentos de revisión como git rev-list --objects. --unpacked implica --revs y limita a objetos que aún no están en un pack. --all implica --revs y finge que todas las refs bajo refs/ están en la lista.

Un agente que hace git pack-objects pack sin stdin se queda colgado esperando. Uno que redirige el repo entero a --stdout y lo tira al log del LLM descarga megas de binario. Cero dumps.

Diagrama de pipeline: lista de objetos entra por stdin y sale un par pack más idx

Deltas, window y thin packs

El man: In a packed archive, an object is either stored as a compressed whole or as a difference from some other object. El formato .pack es autocontenido: each object that a delta depends upon must be present within the pack.

--window=<n> (default 10) y --depth=<n> (default 50, máximo 4095) controlan la búsqueda de deltas. Subir window/depth “para compactar más” multiplica CPU y RAM (--threads multiplica la memoria de la ventana). --window-memory escala la ventana para no OOM. Un agente no toca esos knobs.

--thin rompe el formato: A thin pack violates the packed archive format by omitting required objects and is thus unusable by Git without making it self-contained. El arreglo canónico es git index-pack --fix-thin (git-index-pack(1); git-scm.com/docs/git-index-pack HTTP 200, last-modified 2026-08-31). Porcelain de red (fetch/push) lo usa entre peers. Un coding agent no escribe thin packs en disco.

--delta-base-offset ahorra 3–5 % usando offset en el stream en vez de 20 bytes de SHA. El man: Porcelain commands such as git gc, git repack pass this option by default in modern Git when they put objects in your repository into pack files. So does git bundle. Por eso el agente no reimplementa gc con pack-objects a mano: perdería ese flag (y --sparse, bitmaps, cruft) y dejaría un pack peor.

--no-reuse-delta / --no-reuse-object fuerzan recomputar todo. Útiles solo si el humano pide un nivel de compresión uniforme. Cero autónomo: en un repo grande son minutos de CPU.

Qué no es (y qué no toques)

ComandoRolpack-objects
repackporcelain: reescribe packs del object storeel motor; el agente llama a repack, no al motor
gcorquesta auto + pruneno sustituye --auto
pruneborra loose inalcanzablespack-objects no borra
count-objectsdiagnóstico count/size-packno compacta
bundlepack + header de refsno es un bundle
pack-refspacked-refsotro archivo, otras refs
unpack-objectsloose desde un .pack por stdininverso; tampoco autónomo
index-packconstruye .idx (y --fix-thin)pareja de --stdout

Flags vetados para un agente, aunque el humano pida “compacta”:

  • --cruft / --cruft-expiration: packs de inalcanzables con .mtimes. El man: Typically used by git repack --cruft. No es tu trabajo.
  • --filter=<spec>: omite blobs (partial clone). Cambia qué objetos existen.
  • --keep-unreachable / --pack-loose-unreachable / --unpack-unreachable: implican --revs y mueven inalcanzables.
  • --max-pack-size: parte el pack; may result in a larger and slower repository.
  • --stdin-packs / --incremental / --local / --honor-pack-keep: semántica de qué se ignora; fácil dejar objetos sueltos.
  • --index-version: intended to be used by the test suite only.
  • --missing=allow-any: omite objetos faltantes en silencio (partial clone debug).

--sparse con --revs acelera packs chicos (only walks trees that appear in paths that introduce new objects) y es el default de pack.useSparse. No lo apagues. No lo enciendas a mano.

Checklist del agente

  1. Diagnóstico, no packing. git count-objects -v. Si size-pack es enorme, reporta; no compactes.
  2. Si el humano pide compactar el repo: repack -d -q (su guía: cero -a/-A/-f autónomos). No pack-objects.
  3. Si el humano pide un archivo .pack concreto: confirma path fuera de .git/objects/pack. git rev-list --objects <rango> acotado | git pack-objects --non-empty -q <base-name>. Verifica que salieron .pack + .idx.
  4. Cero --stdout al chat. El pack es binario.
  5. Cero --thin. Sin index-pack --fix-thin el archivo no es un pack Git.
  6. Cero --cruft, --filter, --no-reuse-object, --threads inventados.
  7. No pipes a unpack-objects sobre el repo de trabajo: Objects that already exist in the repository will not be unpacked — y si el pack trae objetos nuevos, los deja loose.
  8. Tras cualquier packing pedido: git status -sb y git count-objects -v. El working tree no debe cambiar. Si cambió, parar.

Escudo: un pack thin queda fuera del formato autocontenido

FAQ

¿Puedo usarlo para “acelerar clone” del worktree del humano? No. Clone ya negocia packs. Un pack local no sustituye clone ni bundle create.

¿--stdout + index-pack --stdin es el flujo de fetch? Sí, entre peers. En un coding agent es un side-channel para meter objetos en .git/objects sin commit visible. Cero autónomo.

¿Por qué no copiar git gc a mano? Porque gc pasa --delta-base-offset, respeta pack.*, puede escribir bitmaps y cruft, y decide con --auto. pack-objects suelto no.

¿--all es seguro? Empaqueta todo lo alcanzable desde refs/. En un repo con notes, replace y stash es más de lo que el humano suele querer. Cero autónomo.

¿Y unpack-objects -n? Dry-run del inverso. Sigue siendo plumbing de pack. Si hay duda de integridad, fsck, no unpack.

Relacionado