Arquitectura interna de Git
Git parece una herramienta de comandos, pero por dentro es una base de datos de objetos con referencias humanas encima. Entender esto cambia mucho la forma de resolver errores, leer historial y trabajar con ramas.
Idea principal
Un repositorio Git local contiene:
- Un directorio de trabajo con tus archivos.
- Un area de staging llamada index.
- Una base de datos de objetos dentro de
.git/objects. - Referencias dentro de
.git/refs. - Configuracion, hooks, logs y metadatos dentro de
.git.
flowchart LR
W["Working tree"] --> I["Index / staging"]
I --> O["Object database"]
O --> C["Commits"]
R["Refs: branches and tags"] --> C
H["HEAD"] --> RDirectorio .git
Cuando ejecutas:
git initGit crea un directorio .git. Ese directorio es el repositorio real. El resto de carpetas son tu copia de trabajo.
Estructura simplificada:
.git/
HEAD
config
index
objects/
refs/
logs/
hooks/Working tree
El working tree es lo que ves en tu editor: archivos modificados, borrados o nuevos.
Git no guarda automaticamente cada cambio. Primero compara el working tree con el index y con el ultimo commit.
git status
git diffIndex
El index es la fotografia que estas preparando para el siguiente commit.
git add app.js
git diff --stagedUn error comun es pensar que git add solo "marca archivos". En realidad actualiza el index con el contenido exacto de esos archivos en ese momento.
Object database
Git guarda contenido como objetos inmutables identificados por hash. Los tipos principales son:
blob: contenido de un archivo.tree: estructura de directorios.commit: snapshot, autor, fecha, mensaje y padres.tag: etiqueta anotada.
Puedes inspeccionarlos:
git cat-file -t HEAD
git cat-file -p HEADCommits como snapshots
Git no piensa principalmente en diferencias. Un commit apunta a un tree que representa el estado completo del proyecto en ese momento.
flowchart TB
C1["commit A"] --> T1["tree"]
C2["commit B"] --> T2["tree"]
C2 --> C1
T2 --> B1["blob app.js"]
T2 --> B2["blob readme.md"]Git puede calcular diffs entre snapshots:
git diff HEAD~1 HEADHEAD
HEAD indica donde estas ahora.
Normalmente apunta a una rama:
ref: refs/heads/mainEn detached HEAD apunta directamente a un commit.
git switch --detach abc1234Detached HEAD no es peligroso por si mismo, pero los commits creados ahi pueden quedarse sin rama si no los guardas.
Refs
Una rama no es una copia de archivos. Es un puntero movible a un commit.
main -> a1b2c3
feature-login -> d4e5f6Cuando haces commit en una rama, Git crea un commit nuevo y mueve el puntero de la rama.
Flujo completo de un commit
sequenceDiagram
participant Dev as Developer
participant WT as Working tree
participant IX as Index
participant OBJ as Objects
participant REF as refs/heads/main
Dev->>WT: Edit files
Dev->>IX: git add
Dev->>OBJ: git commit creates blobs, trees and commit
OBJ->>REF: main moves to new commitComandos de inspeccion
git status
git log --oneline --decorate --graph --all
git cat-file -p HEAD
git ls-tree HEAD
git rev-parse HEAD
git rev-parse --show-toplevelDiagnostico mental
Cuando Git se comporta raro, pregunta:
- Que hay en mi working tree?
- Que hay en el index?
- A que commit apunta HEAD?
- A que commit apunta mi rama?
- Mi rama local y la remota apuntan al mismo sitio?
Buenas practicas
- Mira
git statusantes de operaciones destructivas. - Usa
git diffygit diff --stagedpara separar trabajo sin preparar y trabajo preparado. - Aprende a inspeccionar
HEAD, ramas y commits antes de forzar cambios. - Recuerda que las ramas son punteros, no carpetas.
