Undoing changes

Deshacer commits y cambios

En esta sección, abordaremos las estrategias y los comandos de Git que hay disponibles para deshacer acciones. Cabe destacar que Git no cuenta con un sistema tradicional para deshacer acciones como los que se encuentran en una aplicación de procesamiento de texto. Lo mejor es no relacionar las operaciones de Git con ningún modelo mental convencional al respecto. Además, Git tiene su propia nomenclatura para este tipo de operaciones, pero dejaremos este tema para otra ocasión. Esta nomenclatura incluye términos como “reset”, “revert”, “checkout” y “clean”, entre otros.

Una divertida metáfora consiste en pensar en Git como una utilidad de gestión de cronogramas. Las confirmaciones son instantáneas de un punto en el tiempo o puntos de interés a lo largo del cronograma del historial de un proyecto. Asimismo, se pueden gestionar varios cronogramas mediante el uso de ramas. Al deshacer una acción en Git, normalmente retrocedes en el tiempo o a otro cronograma en el que no se produjeron errores.

En este tutorial, se ofrecen todas las habilidades necesarias para trabajar con las revisiones previas de un proyecto de software. En primer lugar, se muestra cómo explorar las confirmaciones antiguas. A continuación, se explica la diferencia que existe entre revertir confirmaciones públicas en el historial del proyecto y restablecer cambios no publicados en la máquina local.

Encontrar lo que se ha perdido: revisar confirmaciones antiguas

La idea que se esconde tras cualquier sistema de control de versiones es almacenar copias “seguras” de un proyecto para no tener que preocuparte nunca sobre si se producen daños irreparables en tu base de código. Una vez que has creado un historial de confirmaciones del proyecto, puedes revisar y volver a consultar cualquier confirmación en el historial. Una de las mejores utilidades para revisar el historial de un repositorio de Git es el comando git log. En el ejemplo que se muestra a continuación, hemos utilizado el comando git log para pasar una lista de las confirmaciones más recientes a una popular biblioteca de gráficos de código abierto.

git log --oneline
e2f9a78fe Replaced FlyControls with OrbitControls
d35ce0178 Editor: Shortcuts panel Safari support.
9dbe8d0cf Editor: Sidebar.Controls to Sidebar.Settings.Shortcuts. Clean up.
05c5288fc Merge pull request #12612 from TyLindberg/editor-controls-panel
0d8b6e74b Merge pull request #12805 from harto/patch-1
23b20c22e Merge pull request #12801 from gam0022/improve-raymarching-example-v2
fe78029f1 Fix typo in documentation
7ce43c448 Merge pull request #12794 from WestLangley/dev-x
17452bb93 Merge pull request #12778 from OndrejSpanel/unitTestFixes
b5c1b5c70 Merge pull request #12799 from dhritzkiv/patch-21
1b48ff4d2 Updated builds.
88adbcdf6 WebVRManager: Clean up.
2720fbb08 Merge pull request #12803 from dmarcos/parentPoseObject
9ed629301 Check parent of poseObject instead of camera
219f3eb13 Update GLTFLoader.js
15f13bb3c Update GLTFLoader.js
6d9c22a3b Update uniforms only when onWindowResize
881b25b58 Update ProjectionMatrix on change aspect

Cada confirmación tiene un hash SHA-1 de identificación único. Estos ID se emplean para desplazarse por el cronograma confirmado y revisar las confirmaciones. De manera predeterminada, git log solo muestra las confirmaciones de la rama seleccionada en ese momento. Es perfectamente posible que la confirmación que buscas se encuentre en otra rama. Para ver todas las confirmaciones de todas las ramas, puedes ejecutar git log --branches=*. El comando git branch se utiliza para visualizar y visitar otras ramas. Al invocar el comando, git branch -a devolverá una lista con los nombres de todas las ramas conocidas. Después, puede registrarse uno de los nombres de estas ramas mediante el comando git log <branch_name>.

Si encuentras una referencia de confirmación al punto del historial que quieres visitar, puedes utilizar el comando git checkout para visitar dicha confirmación. Git checkout es una forma sencilla de “cargar” cualquiera de estas instantáneas guardadas en tu máquina de desarrollo. Durante el curso normal del desarrollo, el HEAD generalmente apunta a la rama master u otra rama local, pero cuando extraes una confirmación anterior, el HEAD ya no apunta a una rama: apunta directamente a la confirmación. Este estado recibe el nombre de “HEAD desasociado” (detached HEAD) y se puede representar de la siguiente manera:

Git Tutorial: Checking out a previous commit

Extraer un archivo antiguo no mueve el puntero HEAD. Este permanece en la misma rama y en la misma confirmación, lo que evita un estado del tipo “HEAD desasociado”. A continuación, puedes hacer una confirmación de la versión antigua de un archivo en una nueva instantánea como lo harías con cualquier otro cambio. Así que, en efecto, este uso del comando git checkout en un archivo sirve para revertir a una versión antigua de un archivo individual. Para obtener más información sobre estos dos modos, visita la página acerca del comando git checkout.

Visualización de una versión antigua

En este ejemplo, se asume que has comenzado a desarrollar un disparatado experimento, pero no sabes con seguridad si quieres conservarlo o no. Para decidirte, quieres echar un vistazo al estado del proyecto antes de empezar el experimento. Primero, tendrás que encontrar el ID de la revisión que deseas ver.

git log --oneline

Pongamos que el historial del proyecto presenta un aspecto similar al siguiente:

b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

Puedes usar git checkout para ver la confirmación “Make some import changes to hello.txt” de la siguiente manera:

git checkout a1e8fb5

De este modo, tu directorio de trabajo coincidirá con el estado exacto de la confirmación a1e8fb5. Puedes echar un vistazo a los archivos, compilar el proyecto, realizar pruebas e incluso editar archivos sin preocuparte de perder el estado actual del proyecto. Nada de lo que hagas aquí se guardará en tu repositorio. Para continuar con el desarrollo, debes volver al estado “actual” del proyecto:

git checkout master

En este caso, se asume que estás llevando a cabo el desarrollo en la rama master predeterminada. Una vez que vuelvas a la rama master, puedes utilizar git revert o git reset para deshacer los cambios no deseados.

Deshacer una instantánea confirmada

Técnicamente, hay varias estrategias diferentes para deshacer una confirmación. En los siguientes ejemplos, se presupondrá que tenemos un historial de confirmaciones como el siguiente:

git log --oneline
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

Nos centraremos en deshacer la confirmación 872fa7e Try something crazy. Tal vez porque la cosa se fue un poco de las manos.

Deshacer una confirmación con git checkout

Al utilizar el comando git checkout, podemos comprobar la confirmación anterior, a1e8fb5, poniendo el repositorio en un estado anterior a que tuviera lugar la locura. Comprobar una confirmación específica pondrá el repositorio en un estado “HEAD desasociado”. Esto significa que ya no estás trabajando en ninguna rama. En un estado desasociado, cualquier nueva confirmación que hagas quedará huérfana cuando vuelvas a cambiar las ramas a una rama establecida. Las confirmaciones huérfanas están listas para que el recolector de basura de Git las elimine. El recolector de basura se ejecuta en un intervalo configurado y destruye de forma permanente las confirmaciones huérfanas. Para evitar que se recojan como basura confirmaciones huérfanas, es preciso asegurarse de que se está en una rama.

A partir del estado HEAD desasociado, podemos ejecutar git checkout -b new_branch_without_crazy_commit. De este modo, se creará una nueva rama llamada new_branch_without_crazy_commit y se cambiará a ese estado. El repositorio está ahora en un nuevo cronograma del historial en el que la confirmación 872fa7e ya no existe. Llegados a este punto, podemos continuar trabajando en esta nueva rama en la que la confirmación 872fa7e ya no existe y considerarla desecha. Desafortunadamente, si necesitas la rama anterior (quizás era tu rama master), esta estrategia para deshacer la acción no resulta adecuada. Veamos algunas otras estrategias al respecto. Para obtener más información y ejemplos, consulta nuestro análisis en profundidad sobre git checkout.

Deshacer una confirmación pública con git revert

Supongamos que volvemos a nuestro ejemplo original del historial de confirmaciones. El historial que incluye la confirmación 872fa7e. Esta vez vamos a probar a deshacer una acción con el comando revert. Si ejecutamos git revert HEAD, Git creará una nueva confirmación con lo opuesto a la última confirmación. De este modo, se añade una nueva confirmación al historial de la rama actual, que se asemejará a lo siguiente:

git log --oneline
e2f9a78 Revert "Try something crazy"
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

En este punto, técnicamente hemos vuelto a deshacer la confirmación 872fa7e. Aunque 872fa7e sigue encontrándose en el historial, la nueva confirmación e2f9a78 es lo opuesto a los cambios de 872fa7e. A diferencia de nuestra estrategia de extracción anterior, podemos seguir usando la misma rama. Esta solución permite deshacer una acción satisfactoriamente. Se trata del método ideal para trabajar con repositorios públicos compartidos. Si debes mantener un historial de Git organizado y simple, es posible que esta estrategia no resulte adecuada.

Deshacer una confirmación con git reset

En esta estrategia para deshacer una acción, continuaremos con nuestro ejemplo práctico. Git reset es un extenso comando con diversos usos y funciones. Si invocamos git reset --hard a1e8fb5, el historial de confirmaciones se restablece a esa confirmación específica. Al examinar el historial de confirmaciones con git log, tendrá el siguiente aspecto:

git log --oneline
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

La salida del registro muestra que las confirmaciones e2f9a78 y 872fa7e ya no se encuentran en el historial de confirmaciones. Llegados a este punto, podemos seguir trabajando y creando nuevas confirmaciones como si las confirmaciones con el texto “crazy” nunca hubieran existido. Este método para deshacer cambios es el que más limpio deja el historial. Realizar un restablecimiento va muy bien para los cambios locales, pero complica más el trabajo con un repositorio remoto compartido. Si tenemos un repositorio remoto compartido con la confirmación 872fa7e e intentamos ejecutar el comando git push en una rama donde hemos restablecido el historial, Git lo detectará y arrojará un error. Git asumirá que la rama enviada está desactualizada debido a que le faltan confirmaciones. En estos casos, el comando git revert debería ser el método preferido para deshacer acciones.

Deshacer la última confirmación

En la sección anterior, hemos analizado distintas estrategias para deshacer las confirmaciones. Todas estas estrategias también son aplicables a la confirmación más reciente. En algunos casos, sin embargo, es posible que no resulte necesario eliminar o restablecer la última confirmación. Tal vez solo se hizo precipitadamente. En este caso, puedes modificar la confirmación más reciente. Una vez que hayas realizado más cambios en el directorio de trabajo y los hayas preparado para la confirmación mediante el uso del comando git add, puedes ejecutar git commit --amend. De esta forma, Git abrirá el editor de sistema configurado y te permitirá modificar el último mensaje de confirmación. Los nuevos cambios se añadirán a la confirmación corregida.

Deshacer cambios no confirmados

Antes de que los cambios se confirmen en el historial del repositorio, residen en el índice del entorno de ensayo y el directorio de trabajo. Es posible que tengas que deshacer cambios dentro de estas dos áreas. El índice del entorno de ensayo y el directorio de trabajo son mecanismos internos de gestión del estado de Git. Para obtener información más detallada acerca de cómo funcionan estos dos mecanismos, visita la página sobre el comando git reset, donde se exploran a fondo.

El directorio de trabajo

El directorio de trabajo se sincroniza generalmente con el sistema de archivos local. Para deshacer cambios en el directorio de trabajo, puedes editar los archivos como de costumbre utilizando tu editor favorito. Git dispone de un par de utilidades que ayudan a gestionar el directorio de trabajo. Por un lado, está el comando git clean, una práctica utilidad para deshacer cambios en el directorio de trabajo. Por otro lado, el comando git reset puede invocarse con las opciones --mixed o --hard y restablecerá el directorio de trabajo.

Índice del entorno de ensayo

El comando git add se emplea para añadir cambios en el índice del entorno de ensayo. Git reset se utiliza principalmente para deshacer los cambios del índice del entorno de ensayo. Un comando reset con la opción --mixed devolverá los cambios pendientes del índice del entorno de ensayo al directorio de trabajo.

Deshacer cambios públicos

Cuando se trabaja en un equipo con repositorios remotos, se debe prestar especial atención al deshacer cambios. Por lo general, git reset debería considerarse como un método local para deshacer acciones. El comando reset debería emplearse al deshacer cambios en una rama privada. De este modo, se aísla de forma segura la eliminación de las confirmaciones de otras ramas que otros desarrolladores puedan estar usando. Los problemas surgen cuando se ejecuta un reset en una rama compartida y esa rama se envía de manera remota con git push. En este escenario, Git bloqueará el envío objetando que la rama enviada está desactualizada con respecto a la rama remota, ya que faltan confirmaciones.

El método preferido para deshacer un historial compartido es git revert. Un revert es más seguro que un reset porque no eliminará ninguna confirmación de un historial compartido. Un revert conservará las confirmaciones que quieras deshacer y creará una nueva confirmación que invierta la confirmación no deseada. Este método es más seguro para la colaboración remota compartida, porque un desarrollador remoto puede incorporar los cambios de la rama y recibir la nueva confirmación revertida que deshace la confirmación no deseada.

Resumen

Hemos analizado muchas estrategias de alto nivel para deshacer acciones en Git. Es importante recordar que hay más de una forma de deshacer acciones en un proyecto de Git. En la mayor parte de esta página, se han tocado temas más profundos que se explican con más detalle en las páginas específicas de los comandos de Git correspondientes. Las herramientas más utilizadas para deshacer acciones son git checkout, git revert y git reset. Estos son algunos puntos clave que debes recordar:

  • Una vez confirmados los cambios, por lo general son permanentes.
  • Utiliza git checkout para desplazarte por el historial de confirmaciones y consultarlo.
  • Git revert es la mejor herramienta para deshacer cambios públicos compartidos.
  • El comando git reset es más adecuado para deshacer cambios privados locales.

Además de los comandos principales para deshacer acciones, hemos echado un vistazo a otras utilidades de Git: git log para encontrar confirmaciones perdidas, git clean para deshacer cambios no confirmados y git add para modificar el índice del entorno de ensayo.

Cada uno de estos comandos tiene su propia documentación detallada. Para obtener más información sobre un comando concreto de los que hemos mencionado aquí, visita los enlaces correspondientes.