2 El modelo de objetos

Internamente, un repositorio de Git es una base de datos de objetos guardada en .git/objects. Cada objeto se identifica por el hash SHA-1 (o SHA-256 en repositorios más nuevos) de su contenido. Hay cuatro tipos de objetos, y entender qué es cada uno es la clave para entender todo lo demás.

2.1 Blob

Un blob guarda el contenido de un archivo, sin ningún metadato: ni nombre, ni permisos, ni fecha. Dos archivos con el mismo contenido exacto —aunque estén en carpetas distintas o tengan nombres distintos— comparten el mismo blob. Esto es lo que hace que Git sea tan eficiente con el espacio: el contenido nunca se duplica.

2.2 Tree

Un tree representa un directorio. Es una lista de entradas, y cada entrada asocia un nombre de archivo (o subdirectorio) con:

Un tree es, en definitiva, una fotografía de la estructura de carpetas y archivos en un momento dado.

2.3 Commit

Un commit apunta a un único tree —la raíz del proyecto en ese momento— y añade:

Un commit normal tiene un solo padre. Un commit de fusión (merge commit) tiene dos o más. Esta cadena de padres es lo que forma el historial del proyecto: no una lista, sino un grafo dirigido acíclico (DAG), como se explica en Referencias y el grafo de commits.

Un punto importante: un commit no guarda una diferencia (diff) respecto al commit anterior. Guarda una fotografía completa del proyecto entero en ese momento, en forma de árbol de trees y blobs. Git calcula las diferencias al vuelo, comparando dos fotografías, cuando hace falta mostrarlas (por ejemplo con git diff o git show). Esto explica por qué operaciones como cambiar de rama son rápidas: Git no tiene que "deshacer" diferencias acumuladas, solo cambia qué fotografía está activa.

2.4 Tag

Un tag anotado es un objeto que apunta a un commit específico (normalmente) y añade un mensaje, un autor y, opcionalmente, una firma criptográfica. Se usa típicamente para marcar versiones publicadas, como v2.1.0. Existen también los tags ligeros, que no son objetos independientes sino simples punteros directos a un commit, sin metadatos propios.

2.5 Cómo encajan las piezas

commit ---> tree (raíz)
              |
              +-- archivo.txt  ---> blob
              +-- src/         ---> tree
                    |
                    +-- main.c ---> blob

Cada commit apunta a exactamente un tree raíz, que a su vez puede contener otros trees (subdirectorios) y blobs (archivos), formando una jerarquía igual a la del sistema de archivos en ese momento.

Todo lo que hace Git a partir de aquí —ramas, HEAD, el índice, reset, rebase— consiste en manipular a qué commit apunta una referencia, o en construir nuevos commits a partir de combinaciones de trees existentes. No hay magia adicional: son estructuras de datos relativamente simples combinadas de formas poderosas.