Git sin pánico: cómo elegir entre restore, reset y revert cuando algo salió mal

Decide sin pánico: respuesta corta antes del árbol

Si el error está solo en tu copia de trabajo o en el área de staging, usa git restore. Si quieres mover la puntera de tu rama y reescribir historial local (aún no compartido), usa git reset. Si necesitas deshacer un commit ya compartido sin cambiar el historial, usa git revert para crear un commit inverso. Verifica siempre con git status y git log antes de ejecutar un comando destructivo.

Estas decisiones se apoyan en la sección «Undoing Things» del libro oficial de Git y en la documentación de git reset (en inglés). Consulta la sección Undoing Things del libro oficial de Git y la documentación oficial de git reset para definiciones precisas y alcances.

Tesis: un árbol de diagnóstico práctico para deshacer cambios en Git

El objetivo es decidir de forma segura entre restore, reset y revert según qué quieres deshacer y dónde está el cambio.

¿Qué quieres deshacer exactamente?
│
├─ A) Cambios en archivos sin commitear o en staging
│    ├─ ¿Solo quieres sacar archivos del staging? → git restore --staged <ruta>
│    └─ ¿Quieres descartar cambios en el directorio de trabajo? → git restore <ruta>
│
├─ B) Commits locales que aún NO compartiste (historial privado)
│    ├─ ¿Solo quieres deshacer el último commit conservando tus cambios en staging? → git reset --soft HEAD~1
│    ├─ ¿Quieres deshacer el último commit y pasar los cambios al directorio de trabajo? → git reset --mixed HEAD~1
│    └─ ¿Quieres descartar commits y cambios asociados por completo? → git reset --hard <ref>
│
└─ C) Commits YA compartidos (historial público)
     └─ ¿Quieres revertir sin reescribir historial? → git revert <commit> [--no-commit]

Regla de oro: evita git reset en ramas cuyo historial ya fue compartido, porque reescribe la historia. Para esos casos, prefiere git revert, que crea un nuevo commit que invierte cambios previos sin mover la puntera hacia atrás. Esta distinción está alineada con la guía oficial de “Undoing Things”.

Comparativa de acciones: qué toca cada comando

AcciónObjetivo principal¿Modifica historial?¿Modifica staging?¿Modifica directorio de trabajo?¿Crea commit nuevo?Uso seguro en historial públicoAlcance típico
git restoreRestaurar archivos o staging desde una referenciaNoSí (con –staged)Sí (sin –staged)NoArchivo(s)/ruta(s)
git reset –soft/–mixed/–hardMover HEAD y puntera de rama a otra referenciaSí (reescribe historia local)Depende del modoDepende del modoNoNo (evitar en historial compartido)Commit(s)/rama
git revertInvertir cambios de uno o más commitsNo reescribe; agrega un commit inversoNo (a menos que uses –no-commit para preparar)No (aplica al historial; tu árbol resultante refleja la inversión)Commit(s) específicos

Los efectos exactos de git reset sobre HEAD, index y working tree están definidos con detalle en la documentación oficial de git reset. La idea práctica: soft conserva cambios en staging, mixed los pasa al directorio de trabajo, y hard descarta ambos estados a la nueva referencia.

Piezas que debes entender (rápido)

  • Directorio de trabajo: tus archivos en el sistema de archivos. Cambios aquí aún no forman parte del historial.
  • Staging (index): instantánea intermedia. Lo que aquí pones es lo que se comprometerá en el siguiente commit. Si necesitas repaso, ve nuestra explicación de concepto de staging y commits en Git.
  • HEAD: referencia al commit actual. Mover HEAD con git reset cambia qué commit considera “presente” la rama.
  • Ramas: punteros con nombre a commits. Si esto es nuevo para ti, refuérzalo con nuestra guía de trabajo con ramas en Git.

Si necesitas bases sólidas desde cero, comienza por la guía de Git para principiantes.

Escenarios de recuperación guiados

1) Quitaste archivos al staging por error o quieres revertir el stage

Objetivo: sacar uno o varios archivos del área de staging, sin tocar el directorio de trabajo ni el historial.

# Saca un archivo del staging (lo deja modificado en el directorio de trabajo)
git restore --staged <ruta/archivo>

# Saca varios archivos
git restore --staged <ruta1> <ruta2>

Equivalente histórico: git reset HEAD <ruta>, documentado bajo git reset. Con git restore el propósito es explícito: solo tocar el index.

2) Descartar cambios locales en archivos no commiteados

Objetivo: volver un archivo al estado que tiene en HEAD o alguna otra referencia, sin alterar historial.

# Restaura desde HEAD (el último commit actual)
git restore <ruta/archivo>

# Restaura desde otra referencia (por ejemplo, un commit o rama específica)
git restore --source=<ref> -- <ruta/archivo>

Atención: descartar cambios en el directorio de trabajo es destructivo para esos cambios. Confirma con git diff antes de proceder.

3) Corregir el último commit local aún no compartido

Objetivo: quitar el último commit pero conservar los cambios para rehacerlo.

# Deja todo en staging, listo para un nuevo commit
git reset --soft HEAD~1

# Deja los cambios en el directorio de trabajo (staging limpio)
git reset --mixed HEAD~1

Estos usos están en línea con los modos de git reset explicados en su documentación. Evita empujar cambios reescritos si ya los compartiste.

4) Rehacer varios commits locales antes de compartir

Objetivo: mover la rama a un commit anterior y, opcionalmente, descartar cambios intermedios.

# Verifica primero dónde estás
git log --oneline --decorate --graph -n 10

# Crea una rama de respaldo por si te arrepientes
git branch respaldo-antes-reset

# Mueve la rama a un commit específico y descarta index + directorio de trabajo
# (Destructivo: perderás lo que no esté en la nueva referencia)
git reset --hard <hash-o-ref>

Si te arrepientes tras un --hard, suele poder recuperarse con git reflog para encontrar la referencia anterior y volver a moverte allí. La sección «Undoing Things» del libro oficial muestra cómo el reflog permite localizar estados previos de HEAD, y luego usar git reset --hard <hash> para regresar.

5) Revertir un commit ya compartido

Objetivo: deshacer el efecto de un commit específico sin reescribir historial, preservando un registro claro.

# Crea un commit inverso automáticamente
git revert <hash-del-commit>

# Prepara los cambios sin commitear aún (útil si quieres agrupar varios reverts)
git revert --no-commit <hash1> <hash2> ...
# Luego haces un único commit de todo lo revertido

Si aparecen conflictos, Git te pedirá resolverlos y continuar el revert. Esta ruta mantiene la historia lineal y comprensible para cualquiera que ya clonó o actualizó la rama.

Comandos listos para copiar según tu objetivo

Inspeccionar antes de actuar

git status -sb
git log --oneline --decorate --graph -n 15
git diff            # Cambios en el directorio de trabajo
git diff --staged   # Cambios que están en staging

Solo tocar staging o archivos sin tocar el historial

# Quitar del staging sin perder cambios locales
git restore --staged <archivo>

# Restaurar archivo desde HEAD (descarta cambios locales en ese archivo)
git restore <archivo>

Mover la puntera de la rama (historial local)

# Retroceder 1 commit, conservando cambios en staging
git reset --soft HEAD~1

# Retroceder 1 commit, pasando cambios al directorio de trabajo
git reset --mixed HEAD~1

# Ir a un commit y descartar index + directorio de trabajo (destructivo)
git reset --hard <hash>

Deshacer commits en historial público

# Revertir un commit compartido sin reescribir historia
git revert <hash>

Recuperación ante un reset equivocado

# Ver estados recientes de HEAD (reflog)
git reflog
# Elige el hash al que quieres volver
git reset --hard <hash-del-reflog>

El uso de git reflog para localizar y recuperar estados previos está descrito en la sección “Undoing Things”.

Buenas prácticas y límites de seguridad

  • Confirma tu objetivo: ¿archivo(s), staging o historial? Elegir el comando correcto depende de esto.
  • Inspecciona antes: usa git status, git log y git diff. Evita operar “a ciegas”.
  • Evita reset en ramas públicas: no reescribas historia ya compartida. Prefiere git revert para mantener un registro claro.
  • Crea un respaldo corto: una rama temporal antes de un reset ambicioso te ahorra sustos.
  • Lee la documentación oficial: las diferencias entre modos de reset están bien definidas en su documentación.
  • Reconoce las fronteras: git restore no cambia historia; git reset puede ser destructivo; git revert no “borra” un commit, lo contradice con otro.

Preguntas frecuentes (FAQ)

¿En qué se diferencia git revert de git reset para deshacer?

git revert crea un nuevo commit que invierte los cambios de un commit previo sin mover la puntera hacia atrás, lo cual es apropiado para ramas compartidas. git reset mueve la puntera y, según el modo, también el index y/o el directorio de trabajo; reescribe la historia local y no es adecuado para historial público. Esta distinción se alinea con la guía de “Undoing Things” y la documentación de git reset.

¿Puedo usar git reset en la rama principal que ya empujé?

Técnicamente es posible, pero no es recomendable porque reescribe historia que otros podrían estar usando. El enfoque seguro para deshacer en una rama pública es git revert, que mantiene la historia lineal y compartible.

¿git restore sustituye a otros comandos para archivos?

git restore está orientado a restaurar contenido de archivos y el área de staging desde una referencia, sin mover HEAD. Su ventaja es la claridad de intención. Para revertir cambios en historial, no es el comando indicado; allí entran git reset o git revert según el caso.

Hice git reset –hard por error. ¿Hay vuelta atrás?

A menudo sí: utiliza git reflog para ver a qué commits apuntó HEAD recientemente. Identifica el estado previo y vuelve con git reset --hard <hash>. Esta técnica está mostrada en la sección “Undoing Things”.

¿Qué pasa si git revert produce conflictos?

Detente, resuélvelos en los archivos implicados, marca las resoluciones con git add y continúa el proceso (según indique el mensaje de Git) para completar el revert con un commit. Los conflictos no invalidan el método; solo requieren intervención manual.

Pon a prueba el árbol en un repositorio descartable

CTA: Prueba el árbol con un repositorio descartable y documenta el estado antes de ejecutar un comando destructivo.

  1. Crea un repositorio temporal:
    mkdir sandbox-undo && cd sandbox-undo
    git init
    
  2. Genera commits de práctica:
    echo "uno" > a.txt
    git add a.txt && git commit -m "A"
    echo "dos" >> a.txt
    git commit -am "B"
    echo "tres" >> a.txt
    git commit -am "C"
    
  3. Captura el estado ANTES de deshacer:
    git status -sb > estado-antes.txt
    git log --oneline --decorate --graph -n 20 >> estado-antes.txt
    git diff >> estado-antes.txt
    
  4. Aplica un caso del árbol (por ejemplo, git reset --soft HEAD~1 o git revert <hash>).
  5. Documenta el estado DESPUÉS:
    git status -sb > estado-despues.txt
    git log --oneline --decorate --graph -n 20 >> estado-despues.txt
    git diff >> estado-despues.txt
    
  6. Compara los archivos estado-antes.txt y estado-despues.txt para verificar el efecto real del comando.

Repite con las demás ramas del árbol de diagnóstico hasta que tu intuición de “qué toca cada comando” se vuelva automática. Si necesitas reforzar conceptos, vuelve a nuestra explicación de staging y commits, a la guía de ramas y a la base de Git para principiantes. Y para definiciones exactas, consulta la sección oficial de “Undoing Things” y la página de git reset.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio