1. La Unidad de Cambio: git commit
En su nivel más elemental, un commit es una instantánea (snapshot) del estado de los archivos en un momento dado. Técnicamente, Git no guarda solo las diferencias, sino un árbol de objetos que representan el estado completo. Cada vez que se realiza un commit, se genera un nodo en el historial que apunta a su predecesor (parent node).
Cada commit se identifica mediante un hash SHA-1 único. Podemos interactuar con estos nodos no solo para guardar cambios, sino para extraer parches específicos mediante comandos de exportación.
# Realizar un commit con un mensaje descriptivo
git commit -m "Refactorización del módulo de autenticación"
# Generar parches a partir de los hashes o la relación con otras ramas
git format-patch main # Parches de commits que no están en main
git format-patch -1 [HASH_DEL_COMMIT] # Generar parche de un commit específico
git format-patch -n 5 # Extraer los últimos 5 commits
2. Punteros de Referencia: git branch
A diferencia de otros sistemas de control de versiones, las ramas en Git no son copias de archivos o carpetas, sino punteros móviles que hacen referencia a un commit específico. Al crear una rama, simplemente se crea un nuevo archivo de texto en el directorio .git/refs/heads que contiene el hash de un commit.
# Crear una nueva rama sin cambiar el contexto actual
git branch feature-api
# Cambiar a una rama existente
git checkout feature-api
# Creación y salto inmediato a una nueva rama
git checkout -b fix-header-issue
# Listar ramas locales (el asterisco indica la rama activa)
git branch
Es fundamental entender que las ramas son volátiles y ligeras. Al realizar un nuevo commit, el puntero de la rama activa se desplaza automáticamente hacia el nuevo nodo generado.
3. Integración de Historiales: git merge
La operación de fusión o merge combina dos líneas de desarrollo independientes. Git identifica el ancestro común más cercano entre ambas ramas y realiza una fusión de tres vías. Si no hay conflictos, se crea un "commit de fusión" especial que tiene la particularidad de poseer dos nodos padres.
# Situarse en la rama receptora (ej. main)
git checkout main
# Fusionar la rama de trabajo
git merge feature-api
Tras la fusión, la rama receptora incluye todos los cambios realizados en la rama de origen, manteniendo la integridad cronológica de los eventos.
4. Reescritura y Linealidad: git rebase
El rebase es una alternativa al merge que permite mantener un historial de commits lineal. Su funcionamiento consiste en tomar los commits de la rama actual, "levantarlos" temporalmente y volver a aplicarlos uno a uno sobre la punta de otra rama seleccionada.
Esto resulta en un historial mucho más limpio, ya que evita los commits de fusión innecesarios, aunque debe usarse con precaución en ramas compartidas.
# Mover los commits de la rama actual sobre la base de 'main'
git rebase main
# En caso de que 'main' necesite alcanzar a la rama rebasada (fast-forward)
git checkout main
git merge feature-api
5. El Indicador de Posición: HEAD
HEAD es una referencia simbólica que apunta al commit o rama en el que nos encontramos trabajando actualmente. Por lo general, HEAD apunta a un nombre de rama (como main), la cual a su vez apunta al último commit. Cuando realizamos un commit, Git utiliza la ubicación de HEAD para determinar quién será el padre del nuevo nodo.
Si movemos HEAD directamente a un hash de commit en lugar de a una rama, entramos en un estado de "HEAD desprendido" (deatched HEAD), donde los nuevos commits no pertenecerán a ninguna rama a menos que se cree una manualmente.
6. Estrategias de Deshacer: git reset y git revert
Git ofrece dos mecanismos distintos para revertir cambios, cada uno con implicaciones diferentes para el historial.
Git Reset (Modificación del historial)
git reset desplaza el puntero de la rama actual hacia atrás, a un estado anterior. Es ideal para corregir errores locales antes de compartir el código.
# Retroceder un commit descartando el registro del historial (los archivos se mantienen)
git reset HEAD~1
Git Revert (Preservación del historial)
git revert es la opción segura para ramas públicas o compartidas. En lugar de borrar commits antiguos, crea un nuevo commit que aplica los cambios inversos al commit que queremos anular. De esta forma, el historial permanece intacto y es fácil de seguir para otros desarrolladores.
# Crear un nuevo commit que deshace los cambios del HEAD actual
git revert HEAD