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

El bucle de commit

docs.scrimba.com

Digamos que has estado trabajando en weather-app durante los últimos veinte minutos. Corregiste un error real en src/index.js, la conversión de temperatura estaba mal, y mientras estabas ahí también comenzaste a restylear styles.css, pero ese cambio está a medio terminar y no está listo para que otros lo vean. Quieres guardar la corrección del error como su propio punto de control ahora mismo, sin arrastrar el estilo sin terminar junto con él.

Para eso es exactamente el bucle de commit. Si aún no has configurado un repositorio, Tu primer repositorio te guía a través de git init y clonación primero. De aquí en adelante, este capítulo asume que tienes uno abierto.

Las tres paradas: directorio de trabajo, área de preparación, commit

Cada cambio que hagas en Git pasa por tres paradas antes de convertirse en un historial permanente. Tu directorio de trabajo es el proyecto tal como está en el disco ahora mismo, los archivos que estás editando activamente. El área de preparación es un lugar de espera donde colocas exactamente los cambios que quieres guardar después. Un commit es la instantánea guardada en sí, una vez que la escribes, más el mensaje que la describe.

Imagina que estás empacando para una mudanza. Tu casa completa es el directorio de trabajo: todo lo que posees, en el estado en que está. Las cajas que has empacado y sellado cerca de la puerta son el área de preparación: solo lo que has elegido deliberadamente llevar. El camión de mudanzas que se aleja con esas cajas es el commit: un registro de exactamente qué se fue, en ese momento, con una etiqueta en el exterior diciendo qué hay adentro.

Aquí está ese modelo mental apareciendo en una terminal real. Después de editar ambos archivos en weather-app, git status lee 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 "no preparados" porque editar un archivo solo cambia tu directorio de trabajo. Nada se mueve al área de preparación hasta que le digas a Git que lo ponga ahí con git add, que es la siguiente sección.

Aquí es donde la razón completa de existencia del área de preparación se vuelve clara. El área de preparación te permite elegir exactamente qué va en tu commit, independientemente de qué más siga editado en tu directorio de trabajo. Sin ella, guardar un punto de control te forzaría a tomar cada cambio en el disco a la vez, esté listo o no.

Con ella, puedes preparar la corrección del error terminada en src/index.js, dejar fuera el styles.css a medio terminar, y hacer commit solo de la parte que está realmente hecha. Este detalle, que la preparación y tu directorio de trabajo pueden contener cosas diferentes al mismo tiempo, confunde a casi todos en la primera semana. Una vez que lo captas, el resto del flujo de trabajo diario de Git tiene mucho más sentido.

En el trabajo del día a día, haz git status un paso rutinario antes de cada add y cada commit, ya sea que algo se vea mal o no. No cuesta nada, nunca cambia tu proyecto, y detiene el error común de hacer commit de más de lo que pretendías. Acostúmbrate a revisar el estado, preparar, luego revisar el estado de nuevo antes de hacer commit: toma segundos y significa que el mensaje en tu commit siempre coincide con lo que realmente entró en él.

El área de preparación es un archivo real, llamado el índice, ubicado en .git/index dentro de tu repositorio. Cada vez que ejecutas git add, Git actualiza ese archivo para registrar cuál versión de cuál archivo está preparada. Cuando ejecutas git commit, Git construye el nuevo commit puramente a partir de lo que el índice dice, sin verificar tu directorio de trabajo de nuevo. Ese es el mecanismo completo detrás de la división que viste hace un momento en git status: dos archivos editados en el disco, pero el índice sin registrar ninguno de ellos aún, así que el commit que sigue actualmente no contendría nada.

JunoLas tres áreas de Git Tu directorio de trabajo es tus archivos tal como están ahora mismo. El área de preparación es donde colocas exactamente los cambios que quieres guardar después, y un commit es la instantánea guardada una vez que la escribes. Editar un archivo nunca lo prepara por sí solo, eliges qué va dentro con git add, y eso es lo que te permite guardar un cambio terminado mientras dejas uno a medio hacer fuera.
JunoLas tres áreas de Git Directorio de trabajo, área de preparación, commit: las ediciones viven en la primera, git add mueve exactamente lo que eliges a la segunda, y git commit guarda la segunda como una instantánea permanente. Ejecuta git status antes de preparar y de nuevo antes de hacer commit, es gratis y mantiene tus commits coincidiendo con lo que pretendías guardar.
JunoLas tres áreas de Git El área de preparación es un archivo real, el índice en .git/index, y git commit construye su instantánea desde el índice, nunca directamente desde tu directorio de trabajo. Por eso dos archivos editados pueden aparecer ambos como sin preparar: el índice no ha sido informado sobre ninguno de ellos aún. Entender el índice de esta manera hace que cada comando de preparación se sienta menos como magia y más como leer y escribir un archivo.

Preparando cambios: git add

git add mueve un cambio de tu directorio de trabajo al área de preparación. Apúntalo al archivo cuyos cambios quieres 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 está preparada y lista. styles.css sigue listado bajo "not staged", exactamente donde lo quieres mientras el restyling está sin terminar. Ejecutar git add styles.css también lo prepararía; ejecutar git add . prepara cada archivo modificado en la carpeta actual a la vez, lo cual es conveniente una vez que confías en que todo en el disco está realmente 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 <file> (abreviatura de --patch) te guía a través de los cambios del archivo en pequeños fragmentos, llamados hunks, y te pide que decides 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 preparar ese hunk, n para dejarlo para después, y q para parar. Esto te da control del tamaño del commit incluso dentro de un solo archivo, que vale la pena usar cada vez que "la mitad de este archivo está hecho" describe tu situación.

git add hace dos cosas a la vez, y conocer ambas explica un gotcha que eventualmente encontrarás. Primero, escribe el contenido actual del archivo en un nuevo blob, un objeto que Git almacena permanentemente, identificado por un hash de la misma manera que los commits, que contiene el contenido exacto de un archivo en un momento. Segundo, actualiza el índice para apuntar a ese blob para ese archivo. Nada sobre add mira el archivo de nuevo después.

Es por eso que, si ejecutas git add src/index.js y luego sigues editando el mismo archivo antes de hacer commit, git status lo muestra como preparado y modificado al mismo tiempo: el índice aún apunta al blob de cuando ejecutaste add, mientras que tu directorio de trabajo ha avanzado. git commit solo ve el blob preparado, así que un segundo git add es lo que incorpora las ediciones más nuevas antes de guardar.

JunoPreparando cambiosgit add <file> mueve los cambios actuales de ese archivo al área de preparación, listo para el próximo commit. Los archivos que no has añadido todavía se quedan fuera, así que puedes terminar una cosa y dejar otra a mitad de edición. git add . prepara cada archivo modificado a la vez, útil una vez que todo está realmente listo.
JunoPreparando cambiosgit add <file> prepara un archivo completo, y git add -p <file> te permite prepararlo hunk por hunk cuando solo parte del archivo está lista. Esa segunda es la herramienta a usar en el momento en que "la mitad de este archivo está hecho" es verdad.
JunoPreparando cambiosgit add escribe el contenido actual de tu archivo en un blob y apunta el índice a él, no vigila el archivo después. Edita el archivo de nuevo antes de hacer commit y status lo muestra como tanto preparado como modificado, porque el índice aún sostiene el blob más antiguo. Re-ejecuta add para incorporar la edición más nueva. Este me costó diez minutos confundidos la primera vez que lo golpeé.

Guardando una instantánea: git commit

Una vez que el área de preparación contiene lo que quieres, git commit lo guarda como una instantánea permanente, con la bandera -m (abreviatura de "message") proporcionando la descripción que viaja con él:

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 src/index.js entró, porque eso es todo lo que preparaste. styles.css sigue sentado editado en tu directorio de trabajo, sin tocar, esperando por siempre que el restyling esté terminado. El id corto, 4f2a1c9, nombra este commit exacto, y el capítulo History cubre la lectura de la lista completa de commits de un proyecto como este.

Escribe mensajes de commit en modo imperativo, como una instrucción. "Fix temperature conversion" coincide con la redacción que usa Git mismo ("este commit va a...") y se lee limpiamente en herramientas que listan commits uno por línea. Para cualquier cosa más involucrada que una corrección de una línea, añade un cuerpo que explique por qué el cambio fue necesario. El diff ya muestra qué cambió, así que guarda el cuerpo para el razonamiento detrás de él:

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 tipográfico o un detalle perdido justo después de hacer commit, git commit --amend reemplaza tu commit más reciente con uno nuevo en lugar de añadir otro encima:

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

Solo enmienda un commit que no hayas compartido en ningún lado. Una vez que lo hayas empujado (enviado a una copia compartida del proyecto), enmendar reescribe el historial que otras personas pueden tener ya, y Undoing things cubre las formas seguras de corregir un commit después de ese punto.

Un commit en sí es un pequeño objeto con tres partes: un puntero a un árbol, el autor y mensaje, y un puntero a su commit padre. El árbol es una instantánea de la estructura de directorios de todo tu proyecto en ese momento: por cada archivo y carpeta, registra un nombre y un puntero a un blob (contenido de un archivo) u otro árbol (una subcarpeta). Ejecutar git add escribe blobs y actualiza el índice un archivo a la vez; 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 al exacto mismo blob, incluso sentados en carpetas diferentes, así que un commit nunca almacena esos bytes dos veces. Esa estructura también hace que comparar dos commits sea eficiente: Git camina sus dos árboles lado a lado y solo abre los blobs cuyos punteros realmente difieren, saltando cada archivo que se mantuvo igual.

JunoGuardando un commitgit commit -m "message" guarda lo que está actualmente preparado como una instantánea permanente, con el mensaje que le das. Cualquier cosa que no hayas preparado se queda fuera y sigue siendo editable. Mantén el mensaje corto y claro sobre lo que el commit hace, el futuro tú lo leerá de vuelta más a menudo de lo que esperas.
JunoGuardando un commit Escribe mensajes de commit como una instrucción, "Fix" en lugar de "Fixed", y añade un cuerpo cada vez que el diff solo dejaría a alguien adivinando por qué hiciste el cambio. git commit --amend reemplaza tu último commit en lugar de apilar uno nuevo, lo cual es perfecto para un error tipográfico en el mensaje, mientras no hayas empujado ese commit a ningún lado.
JunoGuardando un commit Un commit apunta a un árbol, y un árbol mapea nombres a blobs y árboles adicionales, una instantánea anidada completa del proyecto construida a partir de lo que el índice tenía al momento del commit. add escribe los blobs y commit convierte el índice en un árbol terminado, e el contenido idéntico en cualquier lugar del proyecto comparte un blob en lugar de ser almacenado dos veces. Visualiza esa forma y dos cosas dejan de sentirse como magia: por qué nada se duplica, y por qué comparar dos commits significa caminar dos árboles lado a lado en lugar de releer cada archivo.