Git · 06

Deshacer cambios sin miedo

Casi todo en Git se puede deshacer. La clave es saber dónde está lo que querés deshacer (sin preparar, preparado, commiteado o ya subido) y elegir el comando adecuado. Esta página empieza con una tabla para decidir.

Términos de esta página

Descartar
Tirar cambios que no commiteaste. Lo descartado que nunca estuvo en un commit no se puede recuperar.
HEAD~1
“El commit anterior a donde estoy”. HEAD~2 es dos atrás, y así. También se puede usar un hash: a1f3c9e.
restore
Devuelve archivos a como estaban en el último commit (o en el que indiques). No toca el historial.
reset
Mueve la rama hacia atrás, a un commit anterior. Según el modo, los cambios de los commits “sacados” quedan preparados, sin preparar o se pierden.
revert
Crea un commit nuevo que hace lo contrario de otro. No borra nada del historial: es la forma segura de deshacer algo ya subido.
amend
Reemplazar el último commit por uno corregido.
Reescribir el historial
Cambiar o quitar commits que ya existen (amend, reset, rebase). Está bien en commits locales; es problemático en commits ya subidos que otros usan.
reflog
Un registro local de todos los lugares por donde pasó HEAD. Permite recuperar commits que parecen perdidos.
1

¿Qué comando uso?

Tabla de decisión

Quiero…Comando¿Se pierde algo?¿Sirve si ya hice push?
Descartar los cambios de un archivogit restore archivoSí, esos cambios— (no hay commit)
Sacar un archivo del área de preparacióngit restore --staged archivoNo—
Corregir el mensaje del último commit o sumarle un archivogit commit --amendNoNo
Deshacer el último commit pero conservar los cambiosgit reset --soft HEAD~1NoNo
Volver la rama a un commit y tirar todo lo posteriorgit reset --hard <hash>SíNo
Deshacer un commit que ya está en GitHubgit revert <hash>No (queda en el historial)Sí
Recuperar un archivo como estaba en un commit viejogit restore --source <hash> archivoLos cambios actuales de ese archivoSí
Recuperar un commit que “se perdió” tras un resetgit reflog + git reset --hard <hash>No—
Borrar archivos nuevos que no están en Gitgit clean -n y luego -fSí—
✓

Ante la duda, antes de algo destructivo, hacé una rama de respaldo: git branch respaldo. Si algo sale mal, tus commits siguen ahí.

2

Cambios que todavía no commiteaste

Descartar modificaciones

git restore app.js
Devuelve app.js a como estaba en el último commit. Lo que habías cambiado se pierde.
git restore .
Lo mismo para todos los archivos modificados de la carpeta actual y sus subcarpetas: quedan como en el último commit. No toca los archivos nuevos que Git todavía no sigue (para esos, git clean).
✕

Lo descartado con restore no pasa por ninguna papelera. Si no estás seguro, primero git stash (lo guarda) en vez de descartarlo.

Despreparar y limpiar

git restore --staged app.js
Deshace un git add: el archivo deja de estar preparado, pero tus cambios siguen en el archivo.
git clean -n
Muestra qué archivos sin seguimiento (nuevos, que Git no conoce) borraría. Solo muestra.
git clean -f
Los borra de verdad. Con -d incluye carpetas. Los ignorados por .gitignore no se tocan (salvo con -x).
3

Arreglar el último commit

Solo si todavía no hiciste push de ese commit.

Me equivoqué en el mensaje

git commit --amend -m "Mensaje correcto"
Reemplaza el último commit por uno nuevo con el mismo contenido y el mensaje corregido. Tiene otro hash: por eso solo se hace si todavía no lo subiste con push.

Me olvidé un archivo

git add archivo-olvidado.js
git commit --amend --no-edit
Prepara el archivo que faltaba y lo suma al último commit en vez de crear uno nuevo. --no-edit conserva el mensaje que ya tenía. Igual que antes: solo si no hiciste push.

Quiero “descommitear”

git reset --soft HEAD~1
Saca el último commit del historial, pero sus cambios quedan preparados, listos para commitear de nuevo (por ejemplo, partidos en dos commits).
!

¿Ya lo subiste? Entonces no reescribas: usá git revert.

4

reset y sus tres modos

git reset <commit> mueve la rama actual hacia atrás. Los commits que quedan “después” dejan de estar en la rama. La pregunta es qué pasa con los cambios que tenían esos commits: eso lo decide el modo.

git reset HEAD~1 (main estaba en C, pasa a B) A B C main sale de la rama --soft los cambios de C quedan preparados nada se pierde --mixed quedan en tus archivos, sin preparar (modo por defecto) --hard archivos iguales a B: los cambios se pierden (también lo no commiteado) En los tres casos C sigue existiendo un tiempo y se puede recuperar con git reflog.
git reset --soft HEAD~1
Deshace el último commit (HEAD~1 = “uno antes del actual”; HEAD~3, tres) pero deja sus cambios preparados, listos para volver a commitear. Sirve para juntar varios commits en uno o rehacerlo con otro mensaje.
git reset HEAD~1
Modo mixto (el que se usa si no ponés --soft ni --hard): deshace el commit y deja sus cambios en los archivos, pero sin preparar. Así podés elegir de nuevo con git add qué va en cada commit.
git reset --hard origin/main
Tirar todo lo local y dejar la rama exactamente como en GitHub (después de git fetch). ⚠ Se pierden los commits locales no subidos y los cambios sin commitear.
✕

--hard es el único comando habitual que borra trabajo sin commitear de forma irrecuperable. Corré git status antes.

5

revert: deshacer algo que ya subiste

Si el commit ya está en GitHub, no lo borres: otras personas (o Netlify) pueden tenerlo. git revert crea un commit nuevo que aplica lo contrario: si el original agregó una línea, el revert la quita. El historial queda completo y se ve qué se deshizo y cuándo.

git log --oneline
Buscá en la lista el hash del commit que querés deshacer (la columna de la izquierda). Alcanza con copiar esos 7 caracteres.
git revert a1f3c9e
Crea el commit inverso. Se abre el editor con el mensaje “Revert …”: guardá y cerrá.
git push
El commit inverso es un commit más: se sube con git push como cualquier otro, sin reescribir el historial. Por eso revert es la forma segura de deshacer algo que ya está en GitHub.
  • Si el revert choca con cambios posteriores, aparece un conflicto: se resuelve igual que en un merge y se sigue con git revert --continue.
  • git revert HEAD deshace el último commit.
  • Para revertir un commit de merge hay que indicar qué lado conservar: git revert -m 1 <hash> (el lado 1 es la rama en la que estabas al hacer el merge).
6

Recuperar algo “perdido” con reflog

Hiciste un reset --hard de más, borraste una rama o un rebase salió mal. Mientras los cambios hayan estado commiteados, casi seguro se pueden recuperar: Git guarda en el reflog cada lugar por donde pasó HEAD durante los últimos meses.

git reflog
El reflog es un registro local de cada vez que HEAD se movió: commits, resets, cambios de rama, merges. Lista los movimientos del más nuevo al más viejo, con el hash de cada punto. Permite encontrar commits que ya no aparecen en git log. Se conserva unos 90 días.
b7e10c2 HEAD@{0}: reset: moving to HEAD~1
9f2d4aa HEAD@{1}: commit: Agrega la ruta /health
b7e10c2 HEAD@{2}: commit: Corrige el puerto

Acá, el commit “Agrega la ruta /health” (9f2d4aa) es el que se perdió con el reset.

git reset --hard 9f2d4aa
Mueve la rama actual a ese commit y deja los archivos exactamente como estaban en él. ⚠ Los cambios sin commitear que tengas en ese momento se pierden, así que hacé git status antes.
git branch rescate 9f2d4aa
Alternativa más prudente: crea una rama apuntando a ese commit, sin mover la actual.
i

El reflog es solo local: no se sube a GitHub ni existe en otro clon.

7

Trabajar con versiones viejas

Recuperar un archivo de un commit

git restore --source a1f3c9e app.js
Deja app.js como estaba en ese commit (el resto del proyecto no cambia). Después lo commiteás si te sirve.
git show a1f3c9e:app.js
Solo muestra el contenido de ese archivo en ese commit, sin tocar nada.

Mirar el proyecto entero en el pasado

git switch --detach a1f3c9e
Pone toda la carpeta como estaba en ese commit, en modo “detached HEAD” (sin rama). Sirve para probar algo viejo.
git switch main
Volver al presente. Si en el modo detached hiciste commits que querés conservar, antes creá una rama: git switch -c nombre.

No encontré resultados.