Saltar a contenido

A1.1 Deshacer: amend, reset y revert

Para quién es esto

Es material avanzado. Da por sabido hacer commits y mirar el historial de la unidad 1. Todas las sesiones de esta página son reales: se han ejecutado con git en un repositorio de prueba. Los números de commit (como 3f3a1c5) serán distintos en tu equipo, porque dependen de la fecha y de quién hace el commit.

Un repositorio de prueba

Todos los ejemplos parten de un repositorio con tres commits (una lista de la compra). Para reproducirlos:

mkdir tienda && cd tienda
git init
echo '# Tienda' > README.md && git add README.md && git commit -m 'Crea el proyecto'
echo pan > lista.txt && git add lista.txt && git commit -m 'Añade la lista'
echo leche >> lista.txt && git commit -am 'Añade leche'

La idea clave: un commit no se «edita»

Un commit es inmutable: su número (el hash) se calcula a partir de su contenido, su autor, su fecha y el commit anterior. Por eso, «corregir» un commit en realidad crea otro nuevo y deja el viejo sin usar. Casi todo lo de esta página consiste en elegir qué commits quedan en una rama, y no cambia las copias de los demás.

commit --amend: enmendar el último commit

El caso más común: el mensaje del último commit tiene una errata, o se te olvidó un archivo. --amend sustituye el último commit por uno nuevo.

Una errata en el mensaje:

$ echo huevos >> lista.txt
$ git commit -am 'Añade hueos'
[main bcb01de] Añade hueos
 1 file changed, 1 insertion(+)
$ git log --oneline
bcb01de Añade hueos
c7355aa Añade leche
c510b48 Añade la lista
d373f4a Crea el proyecto
$ git commit --amend -m 'Añade huevos'
[main 0948520] Añade huevos
 Date: Sat Mar 1 10:05:00 2025 +0000
 1 file changed, 1 insertion(+)
$ git log --oneline
0948520 Añade huevos
c7355aa Añade leche
c510b48 Añade la lista
d373f4a Crea el proyecto

Fíjate en que el commit cambia de número (Añade hueos y Añade huevos no comparten hash): es un commit distinto que ha reemplazado al anterior. El historial queda limpio, con un solo commit.

Un archivo olvidado: se añade al área de preparación y se enmienda sin tocar el mensaje (--no-edit):

$ echo 'Notas de la tienda' > notas.txt
$ git commit --amend --no-edit
[main 2d3e197] Añade leche
 Date: Sat Mar 1 10:03:00 2025 +0000
 1 file changed, 1 insertion(+)
$ git status -s
?? notas.txt

Solo con commits que no has publicado

Enmendar cambia el número del commit. Si ya lo habías subido (push) y otra persona lo había descargado, ahora tenéis dos versiones distintas del mismo commit y los conflictos están asegurados. Se ve con detalle en la página siguiente.

reset: mover la rama hacia atrás

git reset <commit> mueve la rama a otro commit (normalmente uno anterior), y qué pasa con tus cambios depende de una opción. Son tres modos y conviene distinguirlos bien. Se parte siempre del mismo estado: tres commits y se hace un cuarto (Añade huevos).

Modo Mueve la rama Área de preparación (staging) Archivos de tu carpeta
--soft Sí Se conserva (los cambios siguen preparados) Se conservan
--mixed (el de por defecto) Sí Se vacía (los cambios dejan de estar preparados) Se conservan
--hard Sí Se vacía Se pierden: vuelven al estado de ese commit

Un cuarto commit, y después se deshace con cada modo.

--soft: el commit desaparece, pero sus cambios quedan preparados, listos para volver a hacer commit (por ejemplo, con otro mensaje o juntos con otro):

$ echo huevos >> lista.txt && git commit -qam 'Añade huevos'
$ git reset --soft HEAD~1
$ git status -s
M  lista.txt
$ git log --oneline
c7355aa Añade leche
c510b48 Añade la lista
d373f4a Crea el proyecto

La M en la primera columna significa «preparado»: el cambio de lista.txt sigue ahí, preparado.

--mixed (lo que haces si no pones nada): los cambios se conservan, pero ya no están preparados:

$ echo huevos >> lista.txt && git commit -qam 'Añade huevos'
$ git reset HEAD~1
Unstaged changes after reset:
M   lista.txt
$ git status -s
 M lista.txt
$ git log --oneline
c7355aa Añade leche
c510b48 Añade la lista
d373f4a Crea el proyecto

Ahora el M está en la segunda columna: el archivo está modificado, pero sin preparar (hay que volver a hacer git add).

--hard: se pierde todo, también los cambios de tu carpeta:

$ echo huevos >> lista.txt && git commit -qam 'Añade huevos'
$ git reset --hard HEAD~1
HEAD is now at c7355aa Añade leche
$ git status -s
$ cat lista.txt
pan
leche
$ git log --oneline
c7355aa Añade leche
c510b48 Añade la lista
d373f4a Crea el proyecto

El git status -s sale vacío y el archivo ha vuelto a su contenido anterior (sin huevos). Es el modo más peligroso, porque los cambios sin commit no se pueden recuperar. Los commits sí, como se verá más abajo con reflog.

HEAD~1

HEAD es el commit en el que estás y HEAD~1 es el anterior; HEAD~2, el anterior a ese. Es la forma de decir «uno atrás» sin copiar números.

revert: deshacer sin reescribir

reset reescribe la historia (hace como si algo nunca hubiera pasado). git revert hace lo contrario: añade un commit nuevo que deshace los cambios de otro. La historia no se toca: queda constancia del error y de su corrección.

$ git revert HEAD --no-edit
[main 8c72e87] Revert "Añade leche"
 Date: Sat Mar 1 10:04:00 2025 +0000
 1 file changed, 1 deletion(-)
$ git log --oneline
8c72e87 Revert "Añade leche"
c7355aa Añade leche
c510b48 Añade la lista
d373f4a Crea el proyecto
$ cat lista.txt
pan

Se ha creado un commit Revert "Añade leche" y lista.txt ha vuelto a lo que era, sin borrar nada del historial.

reset revert
¿Cambia la historia? Sí: quita commits No: añade uno
¿Seguro con commits ya publicados? No Sí
¿Cuándo? En tu rama local, antes de publicar Para deshacer algo que ya está subido

¿Qué uso?

Quiero... Comando
Corregir el mensaje del último commit git commit --amend -m "..."
Añadir algo olvidado al último commit git add ... y git commit --amend --no-edit
Quitar el último commit conservando los cambios preparados git reset --soft HEAD~1
Quitar el último commit conservando los cambios sin preparar git reset HEAD~1
Quitar el último commit y tirar sus cambios git reset --hard HEAD~1
Deshacer un commit ya publicado git revert <commit>

Errores frecuentes

Error Cómo evitarlo
Usar reset --hard con cambios sin guardar Antes, git status: lo que no está en un commit no tiene vuelta atrás
Enmendar un commit ya subido Si ya está publicado, usa revert
Confundir reset (mueve la rama) con restore (recupera archivos) reset es sobre commits; git restore archivo es sobre archivos
Creer que revert borra el commit original Lo deja y añade su inverso

Para practicar

Los ejercicios GA1.1 a GA1.3 de GA1 · Ejercicios practican amend, reset --soft y revert.