Qué es un commit en Git (y por qué es una foto, no un parche)
Un commit de Git es una foto completa del proyecto en un instante. Entiende qué guarda de verdad y por qué te deja volver atrás en el tiempo.
Casi todo el mundo que empieza con Git cree lo mismo: que un commit guarda «los cambios que hiciste». Suena razonable, y es el error de modelo mental número uno. Cuando entiendes qué es un commit de Git de verdad, cosas como git reset, git checkout o eso de «volver atrás en el tiempo» dejan de parecer magia negra. Para seguir esto solo necesitas haber escrito git commit alguna vez, aunque fuera copiando el comando de un tutorial sin entender qué pasaba por dentro. Si acabas de aterrizar en Git, la guía de Git desde cero te da el contexto que rodea a todo esto.
¿Qué es realmente un commit?
Un commit es una foto completa de tu proyecto en un instante concreto. Guarda todos los archivos con todo su contenido, tal y como estaban en el momento de crearlo, no solo las líneas que tocaste esa vez.
Piensa en la foto de tu escritorio. Cuando la haces, no anotas «he movido el bolígrafo dos centímetros a la izquierda». Capturas la mesa entera: el bolígrafo, el café, los papeles, todo junto. Un commit hace lo mismo con tu carpeta de código. Es la mesa entera congelada.
Ese matiz parece pequeño, pero cambia por completo cómo piensas sobre Git. Y explica por qué la gente confunde el commit con un parche.
La foto completa: por qué importa el modelo mental
Aquí está la idea central: cada commit guarda el estado completo del proyecto. Otros sistemas de control de versiones más antiguos guardaban solo el diff, la lista de líneas que cambiaron respecto a la versión anterior. Git guarda la foto entera [1].
Cuando crees que un commit es un parche, te imaginas Git como una pila de post-its: cada commit es una notita que dice «cambia esta línea, borra esta otra». Para reconstruir el proyecto en un punto del pasado tendrías que aplicar todos los post-its en orden, uno detrás de otro. Si te falta uno por el medio, la reconstrucción se rompe.
Con el modelo de foto, cada commit ya contiene el proyecto completo. No hay que sumar nada ni aplicar parches en cadena. Quieres el estado de ayer, coges la foto de ayer. Punto.
| Foto (lo que Git hace de verdad) | Parche (el modelo mental equivocado) | |
|---|---|---|
| Qué guarda | El estado completo del proyecto en ese instante | Solo las líneas que cambiaron |
| Restaurar un estado antiguo | Coges esa foto y listo | Aplicas los cambios en orden inverso, uno a uno |
| Si falta un commit intermedio | Cada foto es independiente, las demás siguen enteras | Se rompe la cadena: sin ese parche no reconstruyes lo que vino después |
| Coste de espacio | Git reutiliza los archivos que no cambian y guarda cada versión distinta una sola vez | Parece más barato, pero obliga a recalcular estados sumando parches |
La última fila esconde la duda que todo el mundo tiene la primera vez. Si Git guarda el proyecto entero en cada commit, ¿no ocupa una barbaridad? Lo veremos, pero primero abramos la foto y miremos qué hay dentro.
¿Qué guarda un commit por dentro?
Un commit guarda cinco cosas. La foto del contenido es la más importante, pero no está sola:
- La foto del contenido: internamente Git la representa como un objeto
treeque apunta a todos tus archivos y carpetas con su contenido exacto. - El autor: quién hizo el commit, con su nombre y su correo.
- La fecha y hora en que se creó.
- El mensaje que escribiste para describir la foto.
- Un puntero a su commit padre, es decir, el commit que había justo antes.
El mensaje lo pones tú al hacer el commit. Es la etiqueta que le pegas a la foto para saber qué contiene sin tener que abrirla:
# El mensaje describe la foto que estás guardando
git commit -m "Añade validación de email"
Lo que decides meter en esa foto no es automático. El staging area decide qué entra en la foto, y es el paso previo al commit: eliges qué cambios entran y cuáles se quedan fuera. Todos esos commits, junto con su historial, viven dentro de tu repositorio.
Si quieres ver el interior de un commit tal cual lo guarda Git, existe un comando de bajo nivel que lo abre en crudo:
$ git cat-file -p 9c7a3f2
tree 8d3f9a0c1b2e4d5f6a7b8c9d0e1f2a3b4c5d6e7f
parent 4f8e2d1a6b0c9d3e5f7a1b2c4d6e8f0a2b4c6d8e
author Sofía <sofia@ejemplo.com> 1730000000 +0100
committer Sofía <sofia@ejemplo.com> 1730000000 +0100
Añade validación de email
Ahí lo tienes todo: el tree (la foto del contenido), el parent (el commit anterior), quién y cuándo, y el mensaje al final. Fíjate en ese 9c7a3f2 que escribí después de cat-file. Ese es el nombre del commit, y es la siguiente pieza del puzzle.
El hash SHA: el nombre único de cada commit
El nombre de un commit es un hash SHA, una cadena larga de letras y números que Git calcula a partir del contenido del propio commit [1]. No lo eliges tú: sale de la foto, del autor, de la fecha, del mensaje y del padre, todo pasado por una función matemática que produce siempre el mismo resultado para la misma entrada.
Cuando miras el historial con git log --oneline, ves esos nombres abreviados a sus primeros caracteres:
$ git log --oneline
9c7a3f2 Añade validación de email
4f8e2d1 Crea el formulario de registro
a1b2c30 Primer commit
Cada hash es hexadecimal, así que solo verás dígitos del 0 al 9 y letras de la a a la f. El completo tiene cuarenta caracteres, pero Git te muestra los primeros porque con siete suele bastar para identificarlo sin ambigüedad.
Que el nombre salga del contenido tiene una consecuencia preciosa. Si cambias una sola coma del mensaje, o el autor, o cualquier archivo, el hash resultante es completamente distinto. Es imposible falsificar un commit sin que su nombre cambie. Y como dos contenidos idénticos producen el mismo hash, Git usa esto para ahorrar espacio: un archivo que no cambia entre commits se guarda una única vez y los dos commits apuntan a la misma copia [1]. Esa es la respuesta a la duda de antes. La foto es completa, pero por dentro Git no duplica lo que ya tenía.
El commit padre: cómo se encadenan las fotos
Cada commit apunta al commit que vino antes, su padre, y así se forma la historia. Una foto sola no cuenta ninguna historia. Lo que la cuenta es la secuencia: esta foto viene después de aquella, que viene después de aquella otra.
Ese puntero al padre es lo que convierte un montón de fotos sueltas en una línea de tiempo. Cuando haces un commit nuevo, Git anota automáticamente cuál era el commit anterior y lo guarda como padre. Por eso git log te muestra el historial en orden: va saltando de hijo a padre hacia atrás.
¿Y el primer commit de todos? Ese no tiene padre. Es la primera foto, no había nada antes, así que su puntero al padre simplemente está vacío. En Git se le llama commit raíz.
¿Por qué el modelo de foto te deja volver atrás en el tiempo?
Volver a un estado antiguo es trivial porque cada commit ya contiene el proyecto entero. Cuando haces git checkout de un commit viejo, Git no deshace cambios uno a uno ni recompone nada: coge esa foto y la restaura tal cual estaba.
Aquí es donde el modelo mental correcto te ahorra horas de confusión. Si creías que un commit era un parche, «volver atrás» te sonaba a algo peligroso, como deshacer paso a paso arriesgándote a que algo se quede a medias. Con el modelo de foto es directo: pides un commit, Git te devuelve el proyecto exactamente como estaba en ese instante.
Esto es también lo que hace que perder trabajo en Git sea muchísimo más difícil de lo que parece. Como cada commit es una foto completa e independiente, mientras el commit exista en algún sitio, ese estado del proyecto está a salvo. Recuperarlo es volver a su foto.
Antes de seguir, pruébalo tú. En este ejercicio interactivo tienes que decidir qué guarda de verdad un commit y qué se queda fuera:
Cargando ejercicio...
Errores de principiante con los commits
Estos son los tropiezos que la mayoría de la gente comete las primeras semanas con Git. Ninguno es grave si entiendes qué está pasando por debajo.
Creer que git commit -a añade archivos nuevos
El flag -a (o -am junto con el mensaje) no incluye archivos nuevos. Automatiza el staging de los archivos que Git ya está rastreando y que has modificado o borrado, pero un archivo que acabas de crear y que Git no conoce todavía queda fuera [2].
Esto pilla a mucha gente: creas validaciones.js, haces git commit -am "Añade validaciones", y el archivo no aparece en el commit. Git no lo ignora por capricho, es que nunca le dijiste que lo rastreara. Para eso está git add validaciones.js primero.
Confundir el hash con el mensaje
git blame te dice qué commit tocó cada línea de un archivo, con su hash, su autor y su fecha. Lo que no te muestra es el mensaje del commit [3]. Mucha gente ejecuta git blame, ve el hash, y se frustra porque esperaba leer ahí la descripción de por qué se cambió esa línea.
# git blame: qué commit y quién tocó cada línea (sin el mensaje)
git blame formulario.js
# git show: el mensaje del commit y el diff respecto a su padre
git show 9c7a3f2
El mensaje lo tienes con git show <hash> o con git log. El blame te da el nombre del commit; para leer la historia detrás de ese nombre, usas los otros dos.
Mensajes de commit vacíos o «wip»
Un commit con un mensaje como cambios, wip o arreglo es una foto sin etiqueta. Dentro de tres meses, cuando busques cuándo se rompió algo, esos mensajes no te dirán nada. El mensaje es lo único del commit que escribes tú con lenguaje humano: aprovéchalo para explicar qué captura esa foto y, mejor aún, por qué.
Pensar que perder un commit rompe la historia
Este miedo viene directo del modelo de parche. Si un commit fuera un parche encadenado, perder uno por el medio dejaría la historia coja. Pero cada commit es una foto completa y autónoma. La cadena de padres organiza las fotos en orden, y por eso los commits que quedan siguen conteniendo el proyecto entero cada uno por su cuenta.
Entender el modelo de foto es el primer paso. El segundo es tener las manos en el teclado hasta que crear commits, leer el historial y moverte entre fotos te salga sin pensar. Eso solo se aprende tecleando. En el curso Git de cero a profesional trabajas todo esto con ejercicios guiados, desde tu primer commit hasta manejar el historial con soltura.
Si quieres que te avise cuando publique las siguientes guías de esta serie sobre Git, déjame tu correo:
Un concepto nuevo cada semana
Fuentes
- Pro Git, Git Internals: Git Objects (git-scm.com). Modelo de almacenamiento por objetos, contenido del objeto commit (tree, parent, autor, committer, mensaje), cálculo del hash SHA-1 y deduplicación por contenido.
- Documentación oficial de
git commit(git-scm.com). Descripción del flag-a/--all: solo re-stagea archivos ya rastreados que se han modificado o borrado, nunca archivos nuevos sin rastrear. - Documentación oficial de
git blame(git-scm.com). La salida por defecto muestra hash, autor, fecha y número de línea, sin el mensaje del commit.
Preguntas Frecuentes
¿Un commit guarda todo el proyecto cada vez? ¿No ocupa muchísimo?
Guarda el proyecto entero como concepto, pero no duplica lo que no cambia. Git calcula un hash del contenido de cada archivo y, si un archivo es idéntico entre dos commits, lo almacena una sola vez y ambos commits apuntan a esa misma copia. Así que tener el historial completo del proyecto ocupa mucho menos de lo que temes.
¿Puedo cambiar un commit después de hacerlo?
No en el sentido de editar el que ya existe. Un commit es inmutable: su hash sale de su contenido, así que si cambias cualquier cosa, ya es otro commit con otro nombre. Comandos como git commit --amend no editan el commit anterior, crean uno nuevo que lo reemplaza:
git commit --amend -m "Mensaje corregido"
¿Qué diferencia hay entre hacer commit y guardar el archivo?
Guardar el archivo escribe los cambios en el disco de tu ordenador, sin más. Hacer commit toma una foto de ese estado y la mete en el historial de Git con su autor, su fecha y su mensaje. Puedes guardar un archivo mil veces sin hacer ni un commit; Git solo registra los estados que decides fotografiar.
¿Qué pasa si el mensaje queda vacío por accidente?
Git aborta el commit por defecto cuando el mensaje está vacío, así que no llega a crearse nada y tu historial se queda intacto. Solo se crea si fuerzas la mano con git commit --allow-empty-message, algo que casi nunca querrás: un commit sin mensaje es una foto sin etiqueta.
¿Puedo tener dos commits con el mismo hash?
En la práctica no: como el hash se calcula a partir del contenido del commit, dos commits distintos generan nombres distintos.