3 Referencias y el grafo de commits

3.1 Qué es una rama, en realidad

Una rama (branch) en Git no es una copia de archivos ni una estructura compleja: es, literalmente, un archivo de texto de 41 bytes que contiene el hash de un commit. Vive en .git/refs/heads/nombre-de-la-rama.

Cuando se hace un nuevo commit estando en una rama, Git:

  1. crea el nuevo objeto commit, con el commit actual como padre;
  2. actualiza el archivo de la rama para que apunte al nuevo commit.

Eso es todo. Por eso crear una rama en Git es prácticamente instantáneo: no se copia nada, solo se crea un archivo de 41 bytes apuntando al commit actual.

3.3 El historial es un grafo, no una línea

Como cada commit conoce a su padre (o padres), el historial completo forma un grafo dirigido acíclico: dirigido porque cada enlace va de un commit a su padre, y acíclico porque nunca hay ciclos —un commit no puede ser antepasado de sí mismo—. Las ramas son simplemente distintos puntos de entrada a ese mismo grafo.

      A---B---C  (main)
           \
            D---E  (feature)

Aquí main y feature comparten los commits A y B; a partir de ahí, cada rama tiene su propio camino.

3.4 Notación relativa: HEAD~N y HEAD^

Git ofrece una notación compacta para referirse a commits relativos a uno dado, en vez de escribir hashes completos.

HEAD~1

El padre del commit actual. Equivalente a HEAD^.

HEAD~2

El abuelo del commit actual (el padre del padre). Equivalente a HEAD^^.

HEAD~N

El antepasado que está N generaciones atrás, siguiendo siempre el primer padre en cada commit de fusión.

HEAD^

El primer padre del commit actual.

HEAD^2

El segundo padre del commit actual. Solo tiene sentido en un commit de fusión, que tiene más de un padre. HEAD^ y HEAD^1 son equivalentes; HEAD^2 es distinto y se refiere a la otra rama que se fusionó.

La diferencia entre ~ y ^ importa solo cuando hay commits de fusión de por medio: ~N siempre sigue el camino "principal" N pasos, mientras que ^N elige entre padres en un único paso. Se pueden combinar: HEAD~2^2 es el segundo padre del commit que está dos generaciones atrás.

Esta misma notación relativa funciona con cualquier referencia, no solo con HEAD: por ejemplo, main~3 o a1b2c3d~1.

3.5 Rangos de commits

Para referirse a un conjunto de commits, en vez de a uno solo, Git usa dos operadores:

A..B

Todos los commits alcanzables desde B pero no desde A. Es el rango más común, y responde a la pregunta "¿qué commits tiene B que A no tiene?".

A...B

La diferencia simétrica: los commits alcanzables desde A o desde B, pero no desde ambos. Es decir, todo lo que diverge entre las dos ramas, en cualquier dirección.

Por ejemplo, git log main..feature muestra los commits que existen en feature y todavía no llegaron a main: útil para revisar qué se está a punto de fusionar.

3.6 Otras referencias útiles

ORIG_HEAD

Guarda automáticamente el valor anterior de HEAD antes de operaciones que lo mueven de forma drástica, como reset o merge. Sirve de red de seguridad: git reset ORIG_HEAD deshace el movimiento anterior.

@{-1}

Se refiere a la rama en la que se estaba justo antes de la última vez que se cambió de rama. Equivalente informal a "cd -" en la línea de comandos, pero para ramas.

Entender esta notación es lo que permite escribir comandos como git reset --hard HEAD~2 o git branch nueva-rama HEAD~3 sin tener que copiar hashes de commits a mano.