Deshaciendo Cambios en Git: Reset, Restore y Revert Explicados

¿Confundido con `git reset`, `git restore` y `git revert`? Esta guía aclara sus diferencias y proporciona ejemplos prácticos. Aprende cómo descartar cambios de forma segura, desindexar archivos y reescribir o deshacer commits de forma segura en tu historial de Git. Domina estos comandos esenciales para un control de versiones efectivo y una línea de tiempo de proyecto más limpia.

49 vistas

Deshacer Cambios en Git: reset, restore, revert Explicados

Git es un potente sistema de control de versiones que te permite rastrear cambios en tu base de código. Sin embargo, los errores ocurren y podrías necesitar deshacer cambios. Git proporciona varios comandos para ayudarte con esto, pero funcionan de manera muy diferente y afectan a tu repositorio de formas distintas. Comprender los matices entre git reset, git restore y git revert es crucial para gestionar eficazmente el historial de tu proyecto y corregir errores sin consecuencias no deseadas.

Este artículo desmitificará estos tres comandos, explicando su propósito, cómo funcionan y cuándo usar cada uno. Aprovecharemos la documentación oficial de Git y los patrones de uso comunes para proporcionar explicaciones claras y ejemplos prácticos. Al final de esta guía, estarás equipado para deshacer cambios con confianza, gestionar tus archivos preparados (staged) y mantener un historial de commits limpio y coherente.

Comprendiendo los Conceptos Centrales

Antes de sumergirnos en los comandos, es importante captar algunos conceptos de Git:

  • Directorio de Trabajo (Working Directory): Los archivos que estás editando actualmente.
  • Área de Preparación (Staging Area / Index): Un área de espera donde preparas los cambios antes de confirmarlos (commit). git add mueve los cambios del directorio de trabajo al área de preparación.
  • Historial de Commits (Commit History): Una secuencia de instantáneas de tu proyecto a lo largo del tiempo, representadas por commits.
  • HEAD: Un puntero al commit más reciente en tu rama actual.

Estos tres comandos interactúan principalmente con estas áreas para modificar o descartar cambios.

git restore: Para Descartar Cambios en el Directorio de Trabajo y el Área de Preparación

El comando git restore, introducido más recientemente en Git, está diseñado para la tarea sencilla de deshacer cambios en tu directorio de trabajo o para quitar archivos del área de preparación. Generalmente se considera más seguro e intuitivo para estas operaciones específicas que git reset.

Quitar Archivos del Área de Preparación (Unstaging Files)

Si has preparado accidentalmente un archivo usando git add y quieres quitarlo del área de preparación, git restore es el comando a usar. Mueve los cambios de vuelta desde el área de preparación a tu directorio de trabajo, pero no descarta las modificaciones en sí.

  • Quitar un archivo específico del área de preparación:
    bash git restore <archivo>
    Este comando toma la versión del archivo del índice (área de preparación) y la devuelve al índice. Esencialmente, elimina el archivo del área de preparación para el próximo commit, pero los cambios permanecen en tu directorio de trabajo.

  • Quitar todos los archivos del área de preparación:
    Si bien no existe un git restore . directo para quitar todos los archivos del área de preparación de la misma manera que git reset puede hacerlo, típicamente aplicarías git restore a archivos individuales o lo usarías en conjunto con otros comandos si fuera necesario. Sin embargo, el caso de uso más común es quitar archivos específicos del área de preparación.

Descartar Cambios en el Directorio de Trabajo

git restore también se puede usar para descartar todos los cambios no preparados (unstaged) en tu directorio de trabajo para un archivo específico, revirtiéndolo a la versión en el área de preparación (índice) o al estado confirmado (committed) más reciente.

  • Descartar cambios no preparados en un archivo:
    bash git restore <archivo>
    (Nota: Este comando puede tener dos significados dependiendo del contexto. Cuando se usa sin --staged, se dirige principalmente al árbol de trabajo. Si el archivo está preparado, lo quita del área de preparación. Si el archivo está modificado en el árbol de trabajo y no está preparado, revierte el archivo del árbol de trabajo para que coincida con el índice.)

  • Descartar cambios tanto preparados como no preparados para un archivo (revertir a HEAD):
    Para descartar completamente todos los cambios (tanto preparados como no preparados) para un archivo y revertirlo al estado en el que estaba en el commit HEAD:
    bash git restore --staged --worktree <archivo>
    Este es un comando potente que efectivamente restablece el archivo a su último estado confirmado.

Restaurar un Archivo desde un Commit Específico

git restore también puede recuperar la versión de un archivo específico de un commit pasado sin alterar el historial de tu rama.

git restore <archivo> --source <commit>

Reemplaza <commit> con el hash del commit o una referencia simbólica como HEAD~1.

git reset: Reescribir el Historial

git reset es un comando más potente que puede modificar el historial de tu branch moviendo el puntero HEAD de la rama actual. También puede afectar al área de preparación y al directorio de trabajo, dependiendo del modo utilizado.

Comprendiendo los Modos (--soft, --mixed, --hard)

git reset tiene tres modos principales:

  1. --soft: Mueve HEAD al commit especificado pero deja tu área de preparación y directorio de trabajo sin cambios. Los cambios de los commits restablecidos aparecen como cambios preparados (staged).
    bash git reset --soft HEAD^ # Mueve HEAD un commit hacia atrás, los cambios del commit deshecho están preparados

  2. --mixed (por defecto): Mueve HEAD y restablece el área de preparación para que coincida con el commit especificado. Los cambios de los commits restablecidos están presentes en tu directorio de trabajo pero no están preparados.
    bash git reset HEAD^ # Equivalente a git reset --mixed HEAD^ # Mueve HEAD un commit hacia atrás, los cambios del commit deshecho no están preparados

  3. --hard: Mueve HEAD y restablece tanto el área de preparación como el directorio de trabajo para que coincidan con el commit especificado. Esto descarta todos los cambios de los commits que se están restableciendo y cualquier cambio no confirmado posterior en el directorio de trabajo. Úsese con extrema precaución.
    bash git reset --hard HEAD~ # Descarta el último commit Y todos los cambios desde entonces en el directorio de trabajo y el área de preparación

Casos de Uso para git reset:

  • Quitar archivos del área de preparación: git reset <archivo> es un atajo para git restore --staged <archivo>, eliminando un archivo del área de preparación sin afectar el directorio de trabajo.
  • Quitar todos los archivos preparados: git reset (sin argumentos o especificando HEAD) quita todos los cambios actualmente preparados, moviéndolos de vuelta al directorio de trabajo (equivalente a git restore --staged .).
  • Deshacer el último commit: git reset HEAD^ (o git reset --soft HEAD^) se usa comúnmente para modificar el último commit. Los cambios del commit anterior ahora están preparados, listos para ser confirmados nuevamente con modificaciones o un nuevo mensaje.
  • Descartar todos los cambios locales: git reset --hard se usa para descartar completamente todas las modificaciones locales (preparadas y no preparadas) y revertir el repositorio a un commit específico. Esta es una operación destructiva.

Restablecer un Commit Antiguo

Si necesitas deshacer cambios de un commit que no es el más reciente, se puede usar git reset. Por ejemplo, para restablecer a un commit antes de uno problemático:

# Ejemplo: Deshacer los últimos 2 commits, manteniendo los cambios sin preparar
git reset --mixed HEAD~2

Advertencia: git reset reescribe el historial. Si ya has subido (push) los commits que estás restableciendo, esto puede causar problemas significativos para los colaboradores. Generalmente es seguro restablecer commits que solo existen en tu repositorio local.

git revert: Creando un Nuevo Commit para Deshacer Cambios

git revert es la forma más segura de deshacer cambios en un historial compartido o publicado. En lugar de reescribir el historial, crea un nuevo commit que introduce los cambios inversos de un commit anterior.

Cómo Funciona

Cuando ejecutas git revert <commit>, Git analiza el commit especificado, calcula los cambios opuestos y los aplica a tu directorio de trabajo y área de preparación actuales. Luego te pide crear un nuevo commit con un mensaje por defecto que indica qué commit se está revirtiendo.

  • Revertir un commit específico:
    bash git revert <hash-del-commit>
    Esto creará un nuevo commit que deshace los cambios introducidos por <hash-del-commit>. Si hay conflictos de fusión (merge conflicts), Git se pausará y requerirá que los resuelvas antes de hacer el commit.

  • Revertir Múltiples Commits:
    Puedes revertir un rango de commits:
    bash # Revertir commits desde HEAD~3 hasta (pero sin incluir) HEAD git revert HEAD~3..HEAD
    Git intentará crear un commit de reversión por cada commit especificado. Si surgen conflictos durante el proceso, necesitarás resolverlos para cada reversión.

Ventajas de git revert:

  • Preserva el Historial: No altera los commits existentes, lo que lo hace seguro para ramas públicas o compartidas.
  • Rastro de Auditoría Claro: El commit de reversión indica explícitamente qué se deshizo y por qué.
  • Maneja Fusiones con Gracia: Git a menudo puede revertir commits de fusión automáticamente, aunque puede ser necesaria una intervención manual para escenarios complejos.

Cuándo Usar git revert:

  • Cuando necesites deshacer cambios en una rama que ya ha sido subida (push) a un repositorio remoto.
  • Cuando quieras mantener un registro claro e inmutable de todos los cambios, incluidas las correcciones.
  • Al deshacer un commit de fusión (merge commit).

Eligiendo el Comando Correcto

Aquí tienes una guía sencilla para ayudarte a decidir:

  • Para quitar un archivo del área de preparación: Usa git restore <archivo> o git reset <archivo>.
  • Para descartar cambios no preparados en tu directorio de trabajo para un archivo: Usa git restore <archivo>.
  • Para descartar todos los cambios (preparados y no preparados) para un archivo: Usa git restore --staged --worktree <archivo>.
  • Para deshacer el último commit y mantener los cambios preparados (para modificación): Usa git reset --soft HEAD^.
  • Para deshacer el último commit y mantener los cambios sin preparar: Usa git reset HEAD^.
  • Para descartar completamente el último commit y todos los cambios subsiguientes (destructivo): Usa git reset --hard HEAD^.
  • Para deshacer un commit en una rama compartida sin reescribir el historial: Usa git revert <hash-del-commit>.
  • Para descartar todos los cambios locales en todo el repositorio (destructivo): Usa git reset --hard.

Conclusión

Dominar git reset, git restore y git revert es fundamental para un uso eficaz de Git. git restore es tu opción principal para descartar cambios de forma segura en el directorio de trabajo y el área de preparación. git reset ofrece potentes capacidades de reescritura de historial, que es mejor usar en commits locales no subidos. git revert proporciona un método seguro y que preserva el historial para deshacer cambios, especialmente crucial en entornos colaborativos. Al comprender sus comportamientos distintos y elegir el comando apropiado, puedes gestionar con confianza la evolución de tu proyecto y corregir cualquier error en el camino.