Introducción
El repositorio de Git es, en esencia, un almacén de objetos direccionado por contenido. Hay exactamente cuatro tipos de objetos: blob, tree, commit y tag. Cada operación eventualmente toca alguno de ellos. Entender estos cuatro tipos desmitifica la mayor parte de Git.
Blobs
Un blob almacena el contenido de un archivo, nada más. Sin nombre, sin permisos, sin historial. Dos archivos con contenido idéntico comparten un blob, sin importar dónde vivan en el árbol.
echo "hello" | git hash-object --stdin
# ce013625030ba8dba906f756967f9e9ca394464a
Trees
Un tree es un listado de directorio: un conjunto ordenado de entradas (mode, type, sha, name). Los subdirectorios son ellos mismos trees, recursivamente. Cada commit apunta a exactamente un tree raíz.
git ls-tree HEAD
# 100644 blob a1b2... README.md
# 040000 tree c3d4... src
Commits
Un objeto commit contiene:
- Un puntero a un tree raíz.
- Cero o más commits padre (cero para la raíz, dos o más para merges).
- Author y committer con timestamps.
- Un mensaje.
- Firma GPG/SSH opcional.
git cat-file -p HEAD
# tree 9f1a...
# parent b2c3...
# author Ada <[email protected]> 1714300000 +0000
# committer Ada <[email protected]> 1714300000 +0000
#
# Add greeting
Tags
Un tag anotado es su propio objeto que apunta a otro objeto (casi siempre un commit), con info del tagger y un mensaje. Los tags lightweight son solo refs y no tienen objeto.
git cat-file -t v1.0.0
# tag
git cat-file -p v1.0.0
Juntándolo todo
Recorre el tree de un commit manualmente:
git cat-file -p HEAD^{tree}
git cat-file -p HEAD^{tree}:src
git cat-file -p HEAD:README.md
El peeling ^{tree} y la sintaxis commit:path son cómo todas las herramientas de Git navegan.
Almacenamiento
Cada objeto se comprime con zlib y se direcciona por el SHA-1 (o SHA-256) de su contenido sin comprimir más una cabecera. El contenido idéntico, en cualquier punto del historial, se deduplica automáticamente.
Modos de entrada de tree
El campo mode en una entrada de tree es un pequeño conjunto de modos de archivo tipo POSIX:
100644: archivo regular no ejecutable (blob).100755: archivo ejecutable (blob).120000: enlace simbólico (blob cuyo contenido es el destino).040000: subdirectorio (tree).160000: gitlink (referencia de submódulo a un SHA de commit).
git ls-tree HEAD
git update-index --chmod=+x scripts/run.sh
git ls-tree HEAD scripts/
Git es intencionalmente limitado; permisos arbitrarios y propiedad no se almacenan.
Errores comunes
Creer que Git almacena diffs. No lo hace; almacena instantáneas completas, deduplicadas por hash y luego comprimidas con delta en pack files. Confundir trees con directorios en disco; los trees son objetos inmutables. Confundir tags lightweight con anotados; solo los tags anotados llevan metadatos y firmas. Finalmente, esperar que renombrar un archivo cambie el blob; el blob es el mismo, solo cambia la entrada del nombre en el tree. Pasa diez minutos con git cat-file -p en un repositorio real y el modelo se vuelve segunda naturaleza.