Portada del artículo: Git reset: soft, mixed y hard, y cómo deshacer un reset --hard

Git reset: soft, mixed y hard, y cómo deshacer un reset --hard

git reset es de esos comandos que se aprenden a golpes. Lo usas una vez, borra más de lo que esperabas y de ahí en adelante lo miras con respeto.

Esta guía te lo explica con palabras simples y con comandos probados en un repositorio de prueba con Git 2.53.0. Los hashes aparecen recortados a 7 caracteres y los mensajes de Git, en inglés. Si apenas empiezas, pasa antes por la primera parte de la guía de Git.

En corto

  • Qué hace: git reset mueve tu rama (y con ella HEAD) a otro commit.
  • --soft: deshace el commit y deja todo preparado en el staging.
  • --mixed (el modo por defecto): deshace el commit y vacía el staging. Tus archivos quedan intactos.
  • --hard: deshace el commit, vacía el staging y sobrescribe tus archivos.
  • “git reset -1” no existe. Lo que quieres escribir es git reset HEAD~1.
  • Si te arrepientes de un --hard: git reflog muestra el commit y git reset --hard <hash> lo trae de vuelta. Los cambios que nunca guardaste con commit no se recuperan.
  • Si ya hiciste push: no uses reset. Usa git revert.

¿Qué hace git reset?

Git maneja tu trabajo en tres lugares. Verlos así hace que todo lo demás tenga sentido:

  1. El historial: los commits. La rama y HEAD apuntan al último.
  2. El staging: lo que dejaste preparado con git add para el próximo commit.
  3. Tus archivos: lo que ves en la carpeta del proyecto (el working tree, el árbol de trabajo).

git reset <commit> hace siempre lo mismo en el primer lugar: mueve la rama a ese commit. Lo que cambia entre un modo y otro es hasta dónde llega después. Según la documentación oficial, si no indicas modo, Git usa --mixed.

git reset —soft, —mixed y —hard: la diferencia

Tabla 1: Qué toca cada modo de git reset

ModoMueve la ramaStagingTus archivos
--softSíSe conservaSe conservan
--mixed (por defecto)SíSe vacíaSe conservan
--hardSíSe vacíaSe sobrescriben: cambios perdidos

Cómo leerla: cada columna dice qué le pasa a esa parte de tu trabajo. Solo --hard toca tus archivos.

Los bloques bash son lo que escribes en la terminal, dentro de la carpeta de un repositorio. Están en sintaxis bash: en Windows úsalos en Git Bash o WSL. Los bloques text son la salida que debería aparecer.

Partimos de un repositorio con tres commits:

git log --oneline
0f807bc Commit tres
e9a9ad9 Commit dos
8a73f8f Commit uno

Cada ejemplo parte de cero desde estos tres commits. Si los repites uno tras otro en el mismo repositorio, tus hashes, tu git status y tu reflog se verán distintos; para que coincidan, crea el repositorio de nuevo antes de cada ejemplo.

git reset —soft HEAD~1

Deshace el commit, pero deja sus cambios preparados, como si estuvieras a punto de hacer git commit otra vez:

git reset --soft HEAD~1
git log --oneline
git status --short
e9a9ad9 Commit dos
8a73f8f Commit uno
A  tres.txt

La A en la primera columna significa que tres.txt sigue en el staging. Sirve para rehacer el último commit (por ejemplo, para corregir el mensaje) o para unir varios en uno con git reset --soft HEAD~3 y un solo git commit.

git reset HEAD~1 (—mixed)

Sin ningún modo, el commit se deshace y el staging se vacía, pero tus archivos siguen en la carpeta:

git reset HEAD~1
git status --short
?? tres.txt

Ahora tres.txt aparece como archivo sin seguimiento (??). Sigue en tu carpeta, solo que Git ya no lo tiene preparado. Sirve para volver a elegir qué entra en el commit. Y responde una duda frecuente: git reset HEAD~1 y git reset --mixed HEAD~1 son exactamente lo mismo.

git reset —hard HEAD~1

git reset --hard HEAD~1
ls
HEAD is now at e9a9ad9 Commit dos
dos.txt
uno.txt

El commit se fue, el staging quedó vacío y tres.txt ya no está en la carpeta. La documentación lo dice así: --hard sobrescribe los archivos con la versión del commit y elimina los archivos rastreados que no están en él. Úsalo cuando de verdad quieras descartar todo. El resto del tiempo, --soft o --mixed son más amables.

git reset HEAD~1, HEAD^ y un hash: a dónde apuntas

HEAD es el commit en el que estás parado. El segundo argumento de git reset le dice a qué commit moverte. Hay tres formas de nombrarlo:

for r in HEAD^ HEAD~1 HEAD~2; do echo -n "$r -> "; git rev-parse --short $r; done
HEAD^ -> e9a9ad9
HEAD~1 -> e9a9ad9
HEAD~2 -> 8a73f8f

Este for es de bash. En Windows ejecútalo en Git Bash o WSL, o escribe git rev-parse --short HEAD~1 y cambia el valor a mano.

  • HEAD~1 es el commit anterior al actual. HEAD~2 retrocede dos, HEAD~3 tres, y así.
  • HEAD^ apunta al mismo sitio que HEAD~1: el padre directo del commit actual. La diferencia aparece solo en commits de merge, que tienen dos padres. Ahí HEAD^ es el primero y HEAD^2 es el segundo (lo explica gitrevisions).
  • Un hash que saques de git log --oneline. Sirve cuando quieres volver a un punto concreto y no quieres contar commits:
git reset --hard 8a73f8f
HEAD is now at 8a73f8f Commit uno

¿Por qué “git reset -1” y “git reset HEAD -1” dan error?

Porque -1 no es una forma válida de decir “un commit atrás”. Para Git, un guion seguido de algo es una opción, y -1 no es ninguna. Esto fue lo que respondió:

git reset -1
error: unknown switch `1'
usage: git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]

Con git reset HEAD -1 y con git reset --soft -1 pasa lo mismo. Lo que casi siempre se quiere decir es esto:

  • git reset -1 o git reset HEAD -1 → git reset HEAD~1
  • git reset soft -1 → git reset --soft HEAD~1
  • git reset hard HEAD → git reset --hard HEAD

Fíjate en dos detalles. El modo lleva dos guiones (--soft, no soft). Sin ellos, Git cree que soft o hard es el nombre de un commit y falla:

fatal: ambiguous argument 'hard': unknown revision or path not in the working tree.

Y “un commit atrás” se escribe con la tilde: HEAD~1.

Otra búsqueda parecida es “git rollback”. No existe. Si lo escribes, Git responde git: 'rollback' is not a git command. Lo que la gente suele querer con esa palabra es git reset (si el commit es solo tuyo) o git revert (si ya lo compartiste).

git reset de un solo archivo frente a git restore

git reset también acepta nombres de archivo. En ese caso no mueve la rama: solo saca el archivo del staging. Partimos de uno.txt modificado y agregado con git add:

git status --short
git reset uno.txt
git status --short
M  uno.txt
Unstaged changes after reset:
M	uno.txt
 M uno.txt

El cambio sigue en el archivo (mira la M en la segunda columna), pero ya no está preparado. git restore --staged uno.txt hace lo mismo. Para descartar el cambio del archivo:

git restore uno.txt
git status --short
cat uno.txt
uno

El árbol quedó limpio (git status --short no imprimió nada) y uno.txt volvió a su contenido del último commit. Este paso no se puede deshacer, porque ese cambio nunca estuvo en un commit.

Dos reglas más. git restore existe desde Git 2.23. Y git reset --hard no acepta archivos:

fatal: Cannot do hard reset with paths.

Tabla 2: Reset frente a restore

Quiero…ComandoQué pasa con mi cambio
Retroceder commitsgit reset --soft, --mixed o --hardDepende del modo
Sacar un archivo del staginggit restore --staged archivo (o git reset archivo)Se conserva en el archivo
Descartar los cambios de un archivogit restore archivoSe pierde
Dejar un archivo como en el último commitgit restore --source=HEAD --staged --worktree archivoSe pierde

La regla corta: reset es para mover commits; restore es para archivos.

¿Se puede deshacer un git reset —hard?

Depende de qué perdiste.

Si lo que perdiste eran commits, sí. Git guarda en el reflog un registro de los lugares donde ha estado HEAD, incluso los que ya no aparecen en git log. Después del reset --hard HEAD~1 de arriba:

git reflog
e9a9ad9 HEAD@{0}: reset: moving to HEAD~1
0f807bc HEAD@{1}: commit: Commit tres
e9a9ad9 HEAD@{2}: commit: Commit dos
8a73f8f HEAD@{3}: commit (initial): Commit uno

El commit 0f807bc sigue ahí. Para traerlo de vuelta usa su hash o su posición en el reflog:

git reset --hard HEAD@{1}
ls
HEAD is now at 0f807bc Commit tres
dos.txt
tres.txt
uno.txt

tres.txt regresó. Según la documentación de reflog, por defecto las entradas se conservan 90 días (gc.reflogExpire), y las que ya no son alcanzables desde la rama, 30 días (gc.reflogExpireUnreachable). En PowerShell, HEAD@{1} necesita comillas: "HEAD@{1}".

Ahora la parte incómoda. El reflog recuerda commits, no cambios sueltos. Con un archivo modificado sin commit y uno nuevo en el staging:

git status --short
git reset --hard HEAD
git status --short
cat uno.txt
A  nuevo.txt
 M uno.txt
HEAD is now at 0f807bc Commit tres
uno

La línea agregada a uno.txt desapareció y nuevo.txt también. Ni el reflog los registra.

Tabla 3: Qué puedes recuperar después de un reset —hard

Lo que perdiste¿Se recupera?Cómo
Commits que “desaparecieron”Sígit reflog y git reset --hard <hash>
Cambios modificados que nunca guardaste con commit ni con git addNoGit no tiene registro de ellos
Archivos nuevos que alcanzaste a pasar por git addA vecesGit guarda su contenido como objeto huérfano; git fsck --lost-found lo lista

El último caso es un rescate de último recurso: sin nombre de archivo y sujeto a que Git limpie esos objetos. La conclusión práctica es una sola: haz commit antes de arriesgar. Un commit “feo” que luego deshaces con --soft es mejor que un trabajo que no existe en ningún lado.

Un atajo útil: justo después de un reset, Git deja guardado el punto de partida en ORIG_HEAD. Si acabas de equivocarte, git reset --hard ORIG_HEAD te devuelve ahí.

¿Cuándo NO usar git reset?

Cuando el commit que quieres deshacer ya lo subiste a una rama que otras personas usan.

reset reescribe el historial. Si lo haces en un commit que ya hiciste push, tu rama queda con menos commits que el remoto y Git rechaza el siguiente push normal. Forzarlo con --force arregla tu lado, pero deja a tus compañeros con una copia del commit que tú borraste.

Para ese caso existe git revert: no borra nada, crea un commit nuevo que anula el anterior. La comparación completa, con el escenario del remoto probado paso a paso, está en Git reset vs git revert.

Tabla 4: Qué comando uso según mi situación

SituaciónComando
El commit solo está en mi máquina, quiero rehacerlogit reset --soft HEAD~1
El commit solo está en mi máquina, quiero reelegir qué entragit reset HEAD~1
El commit solo está en mi máquina y no lo quiero para nadagit reset --hard HEAD~1
El commit ya está en una rama compartidagit revert <hash>
Solo quiero sacar un archivo del staginggit restore --staged <archivo>
Me arrepentí de un reset --hardgit reflog y git reset --hard <hash>

Para probarlo sin riesgo, crea una carpeta de juguete, haz tres commits y repite los ejemplos de esta guía. Romper algo ahí no le cuesta nada a nadie.

Fuentes

Consultadas el 27 de octubre de 2026.

RUTA DE LECTURA RECOMENDADA

Más allá del código: estrategia, escalabilidad e impacto

Lecturas clave para conectar la arquitectura técnica con el valor de negocio y rentabilidad.

EXPLORAR CATEGORÍA COMPLETA→