Skip to content
This page has been auto-translated and may contain errors.View in English

El ciclo de commits ​

docs.scrimba.com

Digamos que llevas veinte minutos trabajando en weather-app. Corregiste un error real en src/index.js (la conversión de temperatura estaba mal calculada), y mientras estabas ahí también empezaste a retocar los estilos en styles.css, pero ese cambio está a la mitad y no está listo para que nadie más lo vea. Quieres guardar la corrección del error como su propio punto de guardado ahora mismo, sin arrastrar con ella el estilo sin terminar.

Para eso exactamente sirve el ciclo de commits. Si todavía no has configurado un repositorio, Tu primer repositorio explica primero git init y cómo clonar. A partir de aquí, este capítulo asume que ya tienes uno abierto.

Las tres paradas: directorio de trabajo, área de staging, commit ​

Cada cambio que haces en Git pasa por tres paradas antes de convertirse en historial permanente. Tu directorio de trabajo es el proyecto tal como está en el disco en este momento, los archivos que estás editando activamente. El área de staging es un lugar de espera donde colocas exactamente los cambios que quieres guardar a continuación. Un commit es la instantánea guardada en sí, una vez que la escribes, junto con el mensaje que la describe.

Imagina que estás empacando para una mudanza. Toda tu casa es el directorio de trabajo: todo lo que posees, en el estado en que se encuentre. Las cajas que ya empacaste y cerraste con cinta junto a la puerta principal son el área de staging: solo lo que decidiste deliberadamente llevarte. El camión de mudanza que se aleja con esas cajas es el commit: un registro exacto de lo que salió, en ese momento, con una etiqueta afuera que indica qué hay dentro.

Aquí tienes ese modelo mental aplicado en una terminal real. Después de editar los dos archivos en weather-app, git status muestra el estado actual sin cambiar nada:

bash
$ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   src/index.js
        modified:   styles.css

no changes added to commit (use "git add" to commit)

Ambos archivos aparecen como "not staged" porque editar un archivo solo modifica tu directorio de trabajo. Nada se mueve al área de staging hasta que le digas a Git que lo ponga ahí con git add, que es el tema de la siguiente sección.

Aquí es donde queda claro para qué existe realmente el área de staging. El área de staging te permite elegir exactamente qué entra en tu commit, sin importar qué otras cosas sigan editadas en tu directorio de trabajo. Sin ella, guardar un punto de control te obligaría a incluir de golpe todos los cambios del disco, estuvieran listos o no.

Con ella, puedes agregar al staging la corrección terminada en src/index.js, dejar afuera el styles.css a medio terminar, y hacer commit solo de la parte que en realidad ya está lista. Este detalle, que el staging y tu directorio de trabajo pueden contener cosas distintas al mismo tiempo, confunde a casi todo el mundo en la primera semana. Una vez que lo entiendes, el resto del flujo diario de Git tiene mucho más sentido.

En el trabajo del día a día, convierte git status en un paso habitual antes de cada add y cada commit, sientas o no que algo anda mal. No cuesta nada, nunca modifica tu proyecto, y evita el error común de hacer commit de más de lo que querías. Acostúmbrate a revisar el estado, agregar al staging, y volver a revisar el estado antes de hacer commit: toma unos segundos y asegura que el mensaje de tu commit siempre coincida con lo que realmente incluiste.

El área de staging es un archivo real, llamado índice, ubicado en .git/index dentro de tu repositorio. Cada vez que ejecutas git add, Git actualiza ese archivo para registrar qué versión de qué archivo está en staging. Cuando ejecutas git commit, Git construye el nuevo commit únicamente a partir de lo que indica el índice, sin volver a revisar tu directorio de trabajo. Ese es todo el mecanismo detrás de la separación que viste hace un momento en git status: dos archivos editados en el disco, pero el índice todavía no lleva registro de ninguno de los dos, así que el commit que siguiera en ese momento no contendría nada.

JunoLas tres áreas de Git Tu directorio de trabajo son tus archivos tal como están ahora mismo. El área de staging es donde colocas exactamente los cambios que quieres guardar a continuación, y un commit es la instantánea guardada una vez que la escribes. Editar un archivo nunca lo pone en staging por sí solo: tú eliges qué entra con git add, y eso es lo que te permite guardar un cambio terminado mientras dejas afuera otro que está a medias.
JunoLas tres áreas de Git Directorio de trabajo, área de staging, commit: las ediciones viven en el primero, git add mueve exactamente lo que elijas hacia el segundo, y git commit guarda el segundo como una instantánea permanente. Ejecuta git status antes de agregar al staging y de nuevo antes de hacer commit; no cuesta nada y asegura que tus commits coincidan con lo que realmente querías guardar.
JunoLas tres áreas de Git El área de staging es un archivo real, el índice en .git/index, y git commit construye su instantánea a partir del índice, nunca directamente desde tu directorio de trabajo. Por eso dos archivos editados pueden aparecer ambos como sin staging: al índice todavía no se le ha informado de ninguno de los dos. Entender el índice de esta manera hace que cada comando de staging se sienta menos como magia y más como leer y escribir un solo archivo.

Agregando cambios al staging: git add ​

git add mueve un cambio de tu directorio de trabajo al área de staging. Apúntalo al archivo cuyos cambios quieres incluir en el próximo commit:

bash
$ git add src/index.js
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   src/index.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   styles.css

src/index.js se movió a "Changes to be committed": la corrección del error ya está en staging y lista. styles.css sigue apareciendo bajo "not staged", justo donde quieres que esté mientras el rediseño no está terminado. Ejecutar también git add styles.css lo pondría en staging a él también; ejecutar git add . pone en staging todos los archivos modificados de la carpeta actual de una sola vez, lo cual es conveniente una vez que confías en que todo lo que hay en el disco realmente está listo.

A veces un solo archivo tiene tanto un cambio que quieres guardar como uno que no, y dividirlo en dos archivos no es práctico. git add -p <archivo> (abreviatura de --patch) recorre los cambios del archivo en pequeños fragmentos, llamados hunks, y te pide que decidas sobre cada uno:

bash
$ git add -p src/index.js
@@ -12,7 +12,7 @@ function celsiusToFahrenheit(celsius) {
-  return celsius * (9 / 5 + 32)
+  return (celsius * 9) / 5 + 32
Stage this hunk [y,n,q,a,d,e,?]?

Responde y para agregar ese hunk al staging, n para dejarlo para después, y q para detenerte. Esto te da un control del tamaño de un commit incluso dentro de un solo archivo, algo que vale la pena usar cada vez que "la mitad de este archivo está terminada" describe tu situación.

git add hace dos cosas a la vez, y conocer ambas explica un detalle confuso con el que tarde o temprano te vas a topar. Primero, escribe el contenido actual del archivo en un nuevo blob, un objeto que Git almacena de forma permanente, identificado por un hash igual que los commits, que contiene el contenido exacto de un archivo en un momento dado. Segundo, actualiza el índice para que apunte a ese blob para ese archivo. add no vuelve a revisar el archivo después de eso.

Por eso, si haces git add src/index.js y luego sigues editando ese mismo archivo antes de hacer commit, git status lo muestra como "staged" y "modified" al mismo tiempo: el índice sigue apuntando al blob de cuando ejecutaste add, mientras que tu directorio de trabajo ya avanzó. git commit solo ve el blob que está en staging, así que un segundo git add es lo que trae las ediciones más recientes antes de que guardes.

JunoAgregando cambios al staginggit add <archivo> mueve los cambios actuales de ese archivo al área de staging, listos para el próximo commit. Los archivos que aún no has agregado se quedan afuera, así que puedes terminar una cosa y dejar otra a medio editar. git add . pone en staging todos los archivos modificados a la vez, útil cuando todo realmente ya está listo.
JunoAgregando cambios al staginggit add <archivo> pone en staging un archivo completo, y git add -p <archivo> te permite hacerlo hunk por hunk cuando solo una parte del archivo está lista. Esa segunda opción es la herramienta que necesitas apenas "la mitad de este archivo está terminada" sea cierto.
JunoAgregando cambios al staginggit add escribe el contenido actual de tu archivo en un blob y apunta el índice hacia él, pero no vigila el archivo después de eso. Edita el archivo de nuevo antes de hacer commit y status lo mostrará como "staged" y "modified" a la vez, porque el índice todavía guarda el blob anterior. Vuelve a ejecutar add para traer la edición más reciente. Esto me costó diez minutos de confusión la primera vez que me pasó.

Guardando una instantánea: git commit ​

Una vez que el área de staging tiene lo que quieres, git commit lo guarda como una instantánea permanente, con la opción -m (abreviatura de "message", mensaje) proporcionando la descripción que la acompaña:

bash
$ git commit -m "Fix temperature conversion in Celsius-to-Fahrenheit formula"
[main 4f2a1c9] Fix temperature conversion in Celsius-to-Fahrenheit formula
 1 file changed, 1 insertion(+), 1 deletion(-)

Solo entró src/index.js, porque eso fue lo único que pusiste en staging. styles.css sigue editado en tu directorio de trabajo, intacto, esperando a que termines el rediseño. El identificador corto, 4f2a1c9, nombra este commit específico, y el capítulo Historial explica cómo leer la lista completa de commits de un proyecto, como este.

Escribe los mensajes de commit en modo imperativo, como una instrucción. "Fix temperature conversion" coincide con la forma en que el propio Git describe la acción ("this commit will...") y se lee de forma clara en herramientas que listan los commits uno por línea. Para cualquier cambio más complejo que una corrección de una línea, agrega un cuerpo que explique por qué fue necesario el cambio. El diff ya muestra qué cambió, así que reserva el cuerpo del mensaje para explicar el razonamiento detrás de eso:

bash
$ git commit -m "Fix temperature conversion in Celsius-to-Fahrenheit formula

The formula was applying operator precedence in the wrong order,
which rounded warm temperatures down by a degree in the UI."

Si notas un error de tipeo o un detalle que se te escapó justo después de hacer commit, git commit --amend reemplaza tu commit más reciente con uno nuevo en lugar de agregar otro encima:

bash
$ git commit --amend -m "Fix temperature conversion in Celsius-to-Fahrenheit formula"

Solo modifica con amend un commit que todavía no hayas compartido en ningún lado. Una vez que lo has hecho push (enviado a una copia compartida del proyecto), modificarlo con amend reescribe historial que otras personas ya podrían tener, y Deshaciendo cambios explica las formas seguras de corregir un commit después de ese punto.

Un commit en sí mismo es un objeto pequeño con tres partes: un puntero a un árbol (tree), el autor y el mensaje, y un puntero a su commit padre. El árbol es una instantánea de la estructura completa de directorios de tu proyecto en ese momento: para cada archivo y carpeta, registra un nombre y un puntero a un blob (el contenido de un archivo) o a otro árbol (una subcarpeta). Ejecutar git add escribe blobs y actualiza el índice archivo por archivo; git commit es el momento en que Git convierte el índice actual en un árbol terminado y lo envuelve en un objeto de commit.

Dos archivos con contenido idéntico apuntan exactamente al mismo blob, incluso si están en carpetas distintas, así que un commit nunca almacena esos bytes dos veces. Esa estructura también hace que comparar dos commits sea eficiente: Git recorre sus dos árboles en paralelo y solo abre los blobs cuyos punteros realmente difieren, saltándose todos los archivos que se mantuvieron iguales.

JunoGuardando un commitgit commit -m "mensaje" guarda lo que esté en staging en ese momento como una instantánea permanente, con el mensaje que le pongas. Todo lo que no pusiste en staging se queda afuera y sigue siendo editable. Mantén el mensaje corto y claro sobre qué hace el commit; tu yo del futuro lo leerá más veces de las que crees.
JunoGuardando un commit Escribe los mensajes de commit como una instrucción, "Fix" en lugar de "Fixed", y agrega un cuerpo cuando el diff por sí solo deje a alguien adivinando por qué hiciste el cambio. git commit --amend reemplaza tu último commit en lugar de apilar uno nuevo, algo perfecto para corregir un error de tipeo en el mensaje, siempre y cuando no hayas hecho push de ese commit todavía.
JunoGuardando un commit Un commit apunta a un árbol, y un árbol asocia nombres con blobs y otros árboles, una instantánea anidada completa del proyecto construida a partir de lo que tenía el índice al momento del commit. add escribe los blobs y commit convierte el índice en un árbol terminado, y el contenido idéntico en cualquier parte del proyecto comparte un solo blob en lugar de almacenarse dos veces. Imagina esa estructura y dos cosas dejan de parecer magia: por qué nada se duplica, y por qué comparar dos commits significa recorrer dos árboles en paralelo en lugar de releer cada archivo.