Git ofrece varias formas de deshacer cambios, y la diferencia entre ellas —cuál es "segura" y cuál reescribe el historial— es una de las fuentes más comunes de confusión. Este capítulo las recorre de menor a mayor alcance.
commit --amendgit reset: mover HEAD y decidir hasta dóndegit revert: deshacer sin reescribir historiagit restore: descartar cambios localescommit --amend ¶git commit --amend
En vez de crear un commit nuevo, reemplaza el commit actual
(HEAD) por uno nuevo que combina lo que había en el commit
anterior con lo que esté preparado en el índice en este momento.
Es decir: --amend crea un objeto commit distinto —con un hash
distinto— y mueve la rama actual para que apunte a ese nuevo commit
en vez de al anterior. El commit original no se destruye
inmediatamente: queda huérfano en la base de datos de objetos hasta
que el recolector de basura lo elimine (see Packfiles), pero
ninguna rama lo señala ya.
Usos típicos:
git commit --amend -m "Mensaje corregido"
git add archivo-olvidado.txt git commit --amend --no-edit
(--no-edit mantiene el mensaje original sin abrir el editor).
Advertencia: como --amend sustituye el commit por
uno nuevo, si ese commit ya fue compartido con otras personas
(push a un repositorio remoto), modificarlo reescribe
historia que otros ya pueden tener descargada. Esto se retoma en
Reescribir la historia.
git reset: mover HEAD y decidir hasta dónde ¶git reset mueve la rama actual (y, por lo tanto, HEAD)
a otro commit, y opcionalmente ajusta el índice y el directorio de
trabajo para que coincidan. Tiene tres modos, que se diferencian por
hasta qué área llega el retroceso:
git reset --soft <commit>Mueve HEAD (y la rama) al commit indicado. No toca
el índice ni el directorio de trabajo. El resultado es que todos los
cambios de los commits "deshechos" quedan preparados en el índice,
como si se hubiera hecho git add de todo pero sin confirmar
el commit. Es útil para combinar varios commits en uno: retroceder
con --soft y volver a hacer commit crea un único
commit con todos esos cambios juntos.
git reset --mixed <commit>El modo por defecto (git reset sin opciones equivale a
--mixed). Mueve HEAD y además vacía el índice hasta
igualarlo con el commit indicado, pero deja el directorio de trabajo
intacto. El resultado: los cambios quedan en el directorio de
trabajo como modificaciones sin preparar, listas para volver a
revisarse y prepararse selectivamente.
git reset --hard <commit>Mueve HEAD, vacía el índice, y además sobrescribe el
directorio de trabajo para que coincida exactamente con el commit
indicado. Este modo descarta cambios de forma permanente:
cualquier modificación sin confirmar en el directorio de trabajo se
pierde. Es el más usado y también el más peligroso.
índice directorio de trabajo --soft conservado conservado --mixed reiniciado conservado --hard reiniciado reiniciado
Una forma práctica de decidirlo: preguntarse qué se quiere hacer con
los cambios de los commits que se están "deshaciendo". Si se quieren
conservar preparados para un nuevo commit, --soft. Si se
quieren conservar pero revisarlos de nuevo antes de decidir qué
preparar, --mixed. Si directamente no importan y deben
desaparecer, --hard.
git reset también puede aplicarse a un archivo específico en
vez de mover HEAD:
git reset HEAD archivo.txt
Esto quita el archivo del índice (lo "despreparara"), sin mover
HEAD ni tocar el directorio de trabajo. Es equivalente al más
moderno git restore --staged archivo.txt.
git revert: deshacer sin reescribir historia ¶A diferencia de reset, que mueve la rama hacia atrás y hace
desaparecer commits del historial visible, git revert crea un
nuevo commit cuyo contenido es el opuesto exacto de un
commit anterior, dejando intacto todo lo que había en medio.
git revert a1b2c3d
Esto es seguro para commits ya compartidos: no reescribe nada, solo añade un commit nuevo que anula los efectos de otro. Es la forma recomendada de deshacer un cambio que ya fue publicado a un repositorio remoto y que otras personas pueden tener descargado.
git restore: descartar cambios locales ¶git restore (comando más reciente, pensado para separar estas
tareas de las de checkout) trabaja sobre el directorio de
trabajo o el índice, sin mover HEAD.
git restore archivo.txtDescarta los cambios sin preparar de un archivo, devolviéndolo a como estaba en el índice (o en el último commit, si no había nada preparado).
git restore --staged archivo.txtQuita el archivo del índice, sin tocar el directorio de trabajo.
git restore --source=HEAD~2 archivo.txtRestaura el contenido del archivo tal como estaba dos commits atrás, tanto en el directorio de trabajo como opcionalmente en el índice.
git commit --amend
git reset --soft HEAD~3 seguido de git commit
git reset --mixed HEAD~N (o simplemente git reset HEAD~N)
git reset --hard <commit>
git revert <commit>
git restore archivo