■ espacio · carril de prosa
Deshacer el último commit conservando los cambios: git reset --soft HEAD~1 (staged) o git reset HEAD~1 (unstaged).
¿Ya empujado? git revert HEAD — un commit nuevo que lo deshace, seguro en ramas compartidas.
reset --hard es la única línea que borra trabajo — y git reflog aún lo sabe, durante ~90 días.
LOS TRES CARRILES
Un commit de más, tres salidas. El flag decide dónde aterrizan tus cambios — sigue los carriles:
fig — el único carril que borra trabajo es --hard; el reflog es la cuneta que corre bajo los tres
CONSERVAR LOS CAMBIOS: LOS DOS COMANDOS
La situación más común: commitaste, viste una errata, faltaba un archivo, o te diste cuenta de que el cambio va en otro commit. Nada empujado aún. Deshaz el commit y devuelve todo a su sitio:
# commit fuera — los cambios vuelven al área de staging
git reset --soft HEAD~1
# los cambios vuelven al árbol de trabajo (unstaged) en su lugar
git reset HEAD~1
HEAD~1 significa "un commit antes de donde apunta HEAD ahora". Tras cualquiera de las dos órdenes tus archivos están intactos en disco — solo se movió el puntero de rama. Comprueba con git status: with --soft los cambios están staged, listos para volver a committear con las correcciones incluidas; sin flag quedan unstaged, así que puedes editar libremente primero.
verifica: git status → staged (--soft) o unstaged (por defecto) · git log --oneline -1 → el commit anterior
Un hábito más seguro cuando solo quieres añadir archivos al último commit es no deshacerlo en absoluto:
git add forgotten-file.txt
git commit --amend --no-edit
--amend sustituye el último commit in situ (aquí sin cambiar el mensaje). Ojo: enmendar un commit empujado reescribe la historia — más abajo.
LOS TRES FLAGS, LADO A LADO
Esta es la parte que merece memorizarse, porque el flag decide dónde acaban tus cambios — y si pueden perderse:
| Flag | ¿Commit deshecho? | Cambios en disco | ¿Staged? | Uso típico |
|---|---|---|---|---|
--soft | Sí | Conservados | Yes | Volver a committear con arreglos menores |
--mixed (por defecto) | Sí | Conservados | No | Reagrupar cambios, re-stagear selectivamente |
--hard | Sí | Borrados | — | Tirar el trabajo entero |
git revert | No (commit nuevo) | Conservados | — | Deshacer un commit ya empujado |
--hard es el único peligroso: descarta el commit y los cambios. Antes de cualquier hard reset, guarda en stash o rama lo que tengas:
git branch backup-before-reset # seguro barato
git reset --hard HEAD~1 # commit + cambios fuera
¿Ya corriste la versión peligrosa? No todo está perdido — git reflog recuerda por dónde anduvo HEAD. El reflog conserva los commits huérfanos unos 90 días por defecto, así que "hice un hard-reset por error" casi siempre tiene remedio si actúas antes del garbage collection:
git reflog # o: git reset --hard <hash>
git reset --hard HEAD@{1} # or: git reset --hard <hash>
verifica: git reflog → el commit descartado está en HEAD@{1}
¿YA EMPUJADO?
El historial compartido recibe un commit nuevo, nunca una reescritura. Si el commit está en una rama compartida — cualquier cosa que no sea tu propia rama de feature — no reescribas la historia. git revert calcula el cambio inverso y lo committea. Todo el que haga pull simplemente recibe un commit nuevo que elimina los cambios del viejo. Ni force-push, ni compañeros rotos:
git revert HEAD
git push
Undoing a run of commits is the same move over a range: Deshacer una tanda de commits es el mismo movimiento sobre un rango: . The alternative — — solo es aceptable en una rama sobre la que nadie más construye: — is only acceptable on a branch nobody else builds on: --force-with-lease rechaza el push si el remoto se movió desde tu último fetch; un --force a secas sobreescribe lo que haya aparecido. Forzar el push de ramas compartidas es como los equipos pierden commits y los logs de CI dejan misteriosamente de cuadrar con el checkout de nadie.
verifica: git log --oneline -2 → Revert "…" queda encima del commit deshecho
BUEN TRABAJO, DIRECCIÓN EQUIVOCADA
A veces el commit está bien — rama equivocada, o demasiado pronto. En vez de deshacerlo, muévelo. Apara el commit entero en una rama nueva, y luego limpia tu
git branch stash-commit # apara el commit en una rama nueva
git reset --hard HEAD~1 # luego limpia tu rama actual
git cherry-pick <hash> # estando en la rama destino
Entre reset --soft, cherry-pick and revert, no hay commit que no puedas reubicar o anular — el truco es elegir "mover" contra "deshacer" antes de estirar la mano hacia un flag.
LA PALABRA ES EL COMANDO
Media confusión es vocabulario: seis verbos que la gente teclea en el buscador, tres comandos que existen de verdad. Empareja la palabra de tu cabeza con el flag que lo hace:
| La palabra que tecleaste | Lo que quieres | El comando | ¿Cambios conservados? |
|---|---|---|---|
| undo / uncommit | commit fuera, trabajo de vuelta | git reset --soft HEAD~1 | sí — staged |
| undo, unstaged | commit fuera, trabajo de vuelta suelto | git reset HEAD~1 | sí — mixed |
| remove / drop | commit y ediciones fuera | git reset --hard HEAD~1 | no — reflog ~90 días |
| revert / cancel, empujado | deshacer público, historia intacta | git revert HEAD | sí — commit nuevo |
Los verbos no son sinónimos en Git: revert jamás toca el pasado, reset lo reescribe. Di la palabra equivocada a un compañero y recibirás el comando equivocado. Toda la decisión, como checklist:
- Sin empujar, corregir y volver a committear →
git reset --soft HEAD~1 - Sin empujar, re-stagear selectivamente →
git reset HEAD~1(mixed) - Quiero que los cambios desaparezcan del todo →
git reset --hard HEAD~1(el reflog lo sabe, si te arrepientes) - Ya empujado a una rama compartida →
git revert HEAD - El commit pertenece a otra rama →
cherry-pick, no lo deshagas
¿Quieres que decidan por ti? El generador git-undo hace estas preguntas e imprime el comando. Un último consejo operativo: si un commit malo llegó de verdad a un servidor — un hook de despliegue torcido, un runner de CI desbocado tras un force-push — el siguiente sitio donde mirar son los logs de la máquina, no Git: en cualquier caja systemd, journalctl -u <service> -n 100 shows you exactly what ran and when. And when Git refuses to run at all — fatal: not a git repository — that is a different problem from undoing anything.
fin de la página de deshacer · nada descargado · todo local