git revert contra git reset — una pregunta decide
Ambos hacen que un commit pasado parezca que nunca existió. Uno calcula el parche inverso y lo commitea — la historia crece; el otro mueve el puntero de rama — la historia se encoge. Una sola pregunta decide: ¿alguien más ha hecho pull?
■ espacio · carril prosa
¿Alguien más ha hecho pull del commit? Sí → git revert <sha>. No → git reset, cualquier modo.
--soft conserva los cambios staged, --mixed (por defecto) unstaged, --hard los borra — la única trituradora.
¿Un archivo estropeado? git restore <file> — deja la rama en paz.
LA DEMO DEL REPO DESECHABLE
Dos minutos en un repo de desecho, y la diferencia ya no es una definición — es un log que has visto:
construir un repo de dos commits — 2 commits · el error está committeado, el árbol está limpio
git init /tmp/revert-vs-reset && cd /tmp/revert-vs-reset
echo one > file.txt && git add . && git commit -m "one"
echo two > file.txt && git add . && git commit -m "two"
revert — la historia crece
git revert --no-edit HEAD
git log --oneline
# 1a2b3c4 (HEAD -> main) Revert "two" ← the cancellation
# 5d6e7f8 two ← still in the log
# 9f8e7d6 one
reset — la historia se encoge
git reset --hard HEAD~1
git log --oneline
# 9f8e7d6 one ← "two" is unreachable
verify: tras el revert git log --oneline → 3 commits · tras el reset → 1 commit — la asimetría es toda la respuesta
LOS TRES MODOS DE RESET
Los modos controlan dónde sobreviven tus cambios después de que se mueva el puntero — y solo una fila de esta tabla borra trabajo:
| Modo | Puntero de rama | Área de staging | Árbol de trabajo | Tus cambios |
|---|---|---|---|---|
--soft | retrocede | conservados staged | intactos | totalmente conservados |
--mixed (por defecto) | retrocede | unstaged | intactos | conservados, unstaged |
--hard | retrocede | reset | reset | borrados |
git reset --soft HEAD~1— «Me comprometí demasiado pronto.» Todo vuelve a staged, listo para recommittear.git reset HEAD~1(mixed) — «Hice staging de los archivos equivocados.» Los cambios sobreviven en el árbol de trabajo, nada staged. Emparéjalo con un clean si también quieres fuera los archivos perdidos.git reset --hard HEAD~1— «Este commit y mis cambios sin committear son basura.» Git no preguntará dos veces; no hay deshacer para la parte del árbol de trabajo, salvo que primero la hubieras stasheado o committeado.
verify: git reset --soft HEAD~1 → git status muestra el cambio «to be committed» · --mixed → «changes not staged»
revert no tiene modos porque nunca destruye nada — solo añade un commit compensatorio.
LA ÚNICA PREGUNTA
¿Alguien más ha hecho pull de este commit? Si sí, reset queda fuera de la mesa. Resetear una rama sobre la que otros basaron trabajo reescribe la historia compartida: su próximo pull encuentra ramas divergentes, y el «arreglo» suele ser un force-push que convierte tu error en problema de todos. Revert añade un commit normal, así que un simple git pull se fusiona limpio para todos — por eso revert es la respuesta por defecto en main y en los pull requests de GitHub, donde el botón «Revert» existe precisamente porque reescribir la historia de una PR fusionada no es una opción.
Reset es para la ventana antes de compartir: el commit de hace treinta segundos, el error de staging, la rama local de experimentos. Si la única copia del trabajo vive en tu máquina, eres libre de reorganizar la historia — ese es todo el punto de la ventana pre-push. Caso intermedio: empujaste a tu propia rama feature y nadie construye sobre ella — un force-push tras el reset es socialmente aceptable ahí, pero revert sigue costando menos coordinación. Reserva los force-push para ramas que posees en solitario.
Revert también es más seguro estructuralmente, no solo por convención: el revert produce un commit ordinario — la CI corre sobre él, el diff es revisable, y git log le explica al tú-del-futuro por qué desapareció el cambio. Reset descarta en silencio el estado intermedio; nadie que revise después puede ver qué se deshizo ni cuándo.
DOS FILOS DEL REVERT
- Revertir un commit de fusión exige
git revert -m 1 <sha>— una fusión tiene dos padres, así que «deshacerla» es ambiguo;-m 1conserva la línea donde fusionaste. Sin él, git se niega. Trampa posterior: re-fusionar la misma rama después le parece ya-aplicado a git, así que volver a aterrizar ese trabajo significa revertir el revert. - Revert no es una máquina del tiempo — deshace el diff de un solo commit. Si commits posteriores tocaron las mismas líneas, quizá tengas que resolver conflictos: git te está diciendo que el deshacer va enredado — información útil, no un fallo.
DÓNDE ENTRA RESTORE
git restore (y git switch, su pareja) llegaron en git 2.23 para tomar los trabajos que reset hacía mal por sobrecarga de nombres:
| Tarea | Modo antiguo | Modo claro |
|---|---|---|
| Descartar los cambios del árbol de trabajo de un archivo | git checkout -- file / git reset --hard | git restore file |
| Quitar del staging un archivo | git reset HEAD file | git restore --staged file |
| Mover la rama a otro commit | git reset <sha> | git reset <sha> (sin reemplazo — esto se queda) |
La división moderna: restore arregla archivos, reset mueve ramas, revert deshace commits publicados. Son un solo modelo mental repartido en tres comandos. Las formas antiguas de git checkout siguen funcionando en todas partes; las nuevas solo evitan que mandes una rama entera a la trituradora cuando querías quitar del staging un archivo.
EL RESUMEN
- El commit es público (empujado, otros hicieron pull) →
git revert <sha>. - El commit es solo local, quieres rehacerlo →
git reset --softo--mixedy recommittear. - Solo local, quitarlo junto con el desorden sin committear →
git reset --hard, tras una mirada honesta a lo demás que contiene el árbol de trabajo. - Un archivo quedó estropeado →
git restore <file>y deja la rama en paz.
verify: git reflog → HEAD@{n} lista la posición previa al reset — git reset --hard HEAD@{1} retrocede hacia ella
La única entrada realmente peligrosa de esa lista es --hard — todo lo demás se revive con git reflog — que recuerda los commits sueltos durante ~90 días. Las pérdidas permanentes en git son estrechas y casi siempre exigen que lo teclees explícitamente; el hábito que vale la pena construir es pausar medio segundo antes de --hard, no eludir por completo las herramientas afiladas de git. Y si todo el menú de deshacer te parece ceremonia, la crítica es justa — jujutsu, el VCS compatible con git, rediseña exactamente esta parte.
fin de la pareja deshacer · nada descargado · todo local