Git reset vs git revert: cuál usar y cuándo
Git reset vs git revert: cuándo usar cada uno según si el commit ya está compartido, con ejemplos de historial y cómo evitar reescribir ramas públicas.
Foto de Markus Spiske en Unsplash
Si ya cometiste un error en Git, la pregunta clave es si alguien más tiene ese commit. En la comparación de git reset vs git revert la regla básica es esta: si el commit solo existe en tu computadora, git reset te permite reescribir la historia local; si ya está en un repositorio compartido, usa git revert, que deshace los cambios creando un commit nuevo.
Esa respuesta resuelve la mayoría de los casos, pero conviene entender qué cambia en el historial con cada comando. Aquí te explico cómo funciona cada uno, con ejemplos de git log y los riesgos de reescribir ramas públicas.
Qué hace cada comando
git revert: deshacer con un commit nuevo
git revert crea un commit que invierte los cambios de otro commit. Según la documentación oficial de git-revert, sirve para registrar commits nuevos que anulan el efecto de commits anteriores. El historial original no se toca: los commits viejos siguen ahí y el nuevo queda encima.
git revert 9b13e7d
Git abrirá tu editor para que escribas el mensaje del nuevo commit, que por defecto dice algo como Revert "Corrige enlace roto en el footer". Antes de ejecutarlo, tu árbol de trabajo debe estar limpio, es decir, sin cambios pendientes respecto a HEAD.
git reset: mover el puntero de la rama
git reset no crea un commit de deshacer. Lo que hace es mover el puntero de la rama actual hacia otro commit. Según el modo que uses, afecta a distintas zonas:
--soft: mueve HEAD. Los cambios de los commits eliminados quedan en el área de staging, listos para volver a confirmarse.--mixed(el modo por defecto): mueve HEAD y limpia el staging. Los cambios siguen en tus archivos, pero sin preparar.--hard: mueve HEAD, limpia el staging y descarta los cambios del directorio de trabajo. La documentación de git-reset advierte que este modo puede descartar trabajo; los cambios no confirmados que se pierden así generalmente no se recuperan.
La regla de decisión: ¿el commit ya es compartido?
La regla práctica es esta:
- Si el commit solo está en tu computadora, puedes usar
git resetpara limpiar el historial. - Si el commit ya está en un remoto (GitHub, GitLab, Bitbucket o cualquier servidor que otras personas usen), usa
git revert.
El problema es que a veces no sabes con certeza si alguien ya lo descargó. Puedes comprobarlo así:
git fetch origin
git branch -r --contains 9b13e7d
Si la salida muestra ramas como origin/main, el commit ya está en el remoto. Si no muestra nada, solo existe en tu repositorio local. El git fetch previo es importante: Git compara contra las referencias remotas que tiene guardadas, y si no las actualizas, la respuesta puede estar desfasada.
El matiz de la rama personal
No todo lo que está en un remoto implica el mismo riesgo. Una rama de funcionalidad que solo tú usas no es igual que main. Si después de subirla decides reescribirla, la opción más segura es --force-with-lease en lugar de --force. La documentación de git push explica que esta opción rechaza el envío si el remoto tiene cambios que tu copia local no conoce, así que no sobrescribes por accidente el trabajo de otra persona.
git reset --soft HEAD~1
git push --force-with-lease origin mi-rama
Esto solo es razonable si nadie más trabaja sobre esa rama. Si un compañero hizo commits encima, su copia quedará desincronizada y tendrá que recuperar el trabajo con cuidado. En ramas compartidas como main o develop, la regla es no reescribir: usa git revert.
Ejemplo de historial: antes y después
Los diagramas de git log --graph ayudan a ver la diferencia. Supongamos que el commit 9b13e7d contiene un error y que ya se subió al remoto.
Caso compartido: revert
Antes:
* 4f2a9c1 (HEAD -> main, origin/main) Agrega formulario de contacto
* 9b13e7d Corrige enlace roto en el footer
* 2c8d0aa Actualiza dependencias
Después de ejecutar git revert 9b13e7d:
* 7e5f310 (HEAD -> main) Revert "Corrige enlace roto en el footer"
* 4f2a9c1 (origin/main) Agrega formulario de contacto
* 9b13e7d Corrige enlace roto en el footer
* 2c8d0aa Actualiza dependencias
El commit 9b13e7d sigue en el historial, y el nuevo 7e5f310 anula sus cambios. Como solo agregaste un commit, puedes hacer un git push normal. Tus compañeros reciben un cambio hacia adelante y su historial sigue coincidiendo con el tuyo.
Caso local: reset
Ahora imagina que los dos últimos commits nunca salieron de tu computadora:
Antes:
* 4f2a9c1 (HEAD -> main) Agrega formulario de contacto
* 9b13e7d Corrige enlace roto en el footer
* 2c8d0aa (origin/main) Actualiza dependencias
Después de ejecutar git reset --soft HEAD~2:
* 2c8d0aa (HEAD -> main, origin/main) Actualiza dependencias
Los dos commits desaparecen de la rama, pero sus cambios quedan en el área de staging, así que puedes corregirlos y volver a confirmarlos. Si necesitas guardar cambios que aún no están listos, revisa nuestra guía sobre qué es git stash y cuándo usarlo antes de ejecutar un reset.
Cómo revertir un merge commit
Un merge commit tiene dos padres. Para revertirlo, Git necesita saber cuál línea quieres conservar. Con -m 1 le indicas que conserve la rama principal (el primer padre, normalmente la rama en la que hiciste el merge) y deshaga los cambios que trajo la otra:
git log --oneline --graph -n 5
git revert -m 1 a1b2c3d
Hay una trampa importante. Después de revertir un merge, si intentas volver a fusionar esa misma rama, Git puede considerar que sus cambios ya están integrados y no traerlos de nuevo. Para reincorporarlos, primero tienes que revertir el revert. Por eso, antes de deshacer un merge, conviene pensar si realmente quieres quitar esos cambios o solo corregir una parte.
Cómo revertir desde un pull request
En GitHub no siempre necesitas la terminal. Si un pull request ya fusionado causa problemas, puedes crear una reversión desde la interfaz. Según la documentación de GitHub sobre revertir un pull request, el resultado es un pull request nuevo que contiene el commit de reversión.
Este flujo tiene una ventaja para equipos: el cambio que deshace el trabajo también pasa por revisión antes de integrarse a la rama principal. Así no quedas con una reversión que nadie revisó.
Cómo recuperar un commit después de un reset equivocado
Si hiciste un reset y te diste cuenta tarde, el reflog suele salvarte. Según la guía de Atlassian sobre reescritura de historial, el registro de referencia guarda el estado anterior de las ramas y te permite volver a él.
- Ejecuta
git reflogy busca la línea anterior al reset. Por ejemplo:4f2a9c1 HEAD@{1}: commit: Agrega formulario de contacto. - Confirma que es el punto correcto con
git log --oneline -3 4f2a9c1. - Crea una rama de rescate para no perder nada más:
git branch rescate-formulario 4f2a9c1. - Si quieres que
mainvuelva a apuntar ahí, ejecutagit reset 4f2a9c1mientras estás en esa rama. Si prefieres revisar primero, puedes fusionar la rama de rescate.
Una advertencia: el reflog es local, así que solo sirve en tu computadora, y Git limpia entradas antiguas con el tiempo. Actúa pronto y no ejecutes git reset --hard sin antes guardar cambios pendientes.
Tabla comparativa
| git revert | git reset | |
|---|---|---|
| Qué hace | Crea un commit que invierte cambios anteriores | Mueve el puntero de la rama a otro commit |
| Historial | Conserva todos los commits | Quita commits de la rama (siguen en el reflog) |
| Ramas compartidas | Seguro | Riesgoso: obliga a hacer force push |
| Cambios sin confirmar | Pide árbol de trabajo limpio | --hard los descarta |
| Caso típico | Anular un commit ya publicado | Limpiar commits locales antes de compartir |
Si además te interesa entender cómo integrar cambios sin reescribir historial, revisa nuestra comparación de git rebase y git merge.
Preguntas frecuentes
¿git revert borra el commit original?
No. El commit original sigue en el historial. Git agrega un commit nuevo que invierte sus cambios, así que ambos quedan visibles.
¿Puedo usar git reset en una rama compartida?
Técnicamente sí, pero tendrás que hacer un push forzado y tus compañeros tendrán que sincronizar su copia, lo que puede causarles problemas. En ramas como main, usa git revert.
¿Qué es --force-with-lease y en qué se diferencia de --force?
--force sobrescribe el remoto sin revisar si alguien subió cambios nuevos. --force-with-lease solo envía si el remoto está donde tu copia local lo espera, lo que evita borrar trabajo ajeno sin querer.
¿Cómo deshago un commit que ya subí a GitHub?
Usa git revert sobre ese commit y después un git push normal. Si el cambio vino de un pull request fusionado, también puedes crear la reversión desde la interfaz de GitHub.
Conclusión
La diferencia entre git reset y git revert depende sobre todo de si el commit ya salió de tu máquina. Si es local, reset te ayuda a limpiar el historial. Si ya está compartido, revert es la opción segura porque conserva la historia y no obliga a nadie a reescribir su copia. Antes de ejecutar cualquiera de los dos, comprueba el estado de la rama y guarda tus cambios pendientes.