Recuperar commits perdidos con Git Reflog
Recupera commits perdidos de Git con reflog: usa git reflog para hallar commits huérfanos, deshacer resets y rebases, y restaurar ramas.
Para recuperar un commit perdido, ejecuta git reflog, copia el hash del commit que quieras y restáuralo con git checkout -b recovered <hash>.
Querías escribir HEAD~1, escribiste HEAD~2 y viste cómo tres horas de trabajo desaparecían de git log. Esos pocos segundos entre pulsar enter y recordar que existe el reflog son los peores de Git. Cuando ejecutas git reset --hard, arruinas un rebase o eliminas una rama, tus commits casi nunca se destruyen: simplemente quedan huérfanos, y sus hashes siguen registrados en tu reflog. Esta guía te ofrece la receta de recuperación rápida para los tres escenarios que provocan el pánico, y después los límites estrictos que conviene conocer para no perder el trabajo por segunda vez. Todos los comandos que aparecen a continuación se pueden copiar y pegar.
Puntos clave
git reflogregistra cada movimiento deHEADen tu repositorio local (commit, checkout, reset, rebase, merge), por lo que un commit «perdido» suele estar a ungit reset --hard <hash>de volver.- La recuperación más segura es
git checkout -b recovered <hash>: reconstruye el commit en una rama nueva e inspecciónalo antes de tocar tu rama real. - El reflog no puede recuperar cambios no commiteados del directorio de trabajo, porque Git nunca los registró en una referencia.
- Por defecto, Git conserva las entradas de reflog alcanzables durante 90 días y las inalcanzables durante 30, así que recupera cuanto antes y evita ejecutar
git gchasta que tengas tus commits de vuelta. - El reflog es estrictamente local y nunca se envía al remoto, por lo que puede rescatar tu propio trabajo perdido, pero no los commits sin pushear de un compañero de equipo.
¿Cómo funciona el reflog de Git?
El reflog de Git es un registro local de todas las posiciones que han ocupado las puntas de tus ramas y otras referencias. Cada commit, checkout, reset, rebase y merge mueve HEAD y queda registrado. Así que cuando un commit se «pierde» (es decir, cuando ninguna rama o etiqueta apunta ya a él), solo está huérfano, no eliminado, y su hash sigue en el reflog esperando a ser reconectado.
Ejecuta el comando sin argumentos para ver el historial de HEAD:
git reflog
b58145c HEAD@{0}: reset: moving to HEAD~2
dd7c37e HEAD@{1}: commit: one more commit
b5b3286 HEAD@{2}: checkout: moving from main to feature
b58145c HEAD@{3}: commit: very important commit
Lee cada línea así: el hash corto, el índice HEAD@{n} (cuántos movimientos hacia atrás se sitúa esa entrada) y una descripción de la operación. En la salida anterior, HEAD@{0} es el reset destructivo que acaba de ocurrir, y HEAD@{1} (dd7c37e) es el commit del que se alejó, el que quieres recuperar. Internamente, git reflog show ejecuta lo mismo que git log -g --abbrev-commit --pretty=oneline, y acepta cualquier referencia, por lo que git reflog show main te da el historial de una rama concreta.
Discover how at OpenReplay.com.
Recuperarse de un hard reset accidental
Un git reset --hard mueve el puntero de tu rama, pero deja el commit antiguo intacto en el almacén de objetos. Encuéntralo en el reflog y vuelve a apuntar a él. El commit justo anterior al reset es el que perdiste, normalmente HEAD@{1}.
git reflog
# HEAD@{0}: reset: moving to HEAD~2
# HEAD@{1}: commit: one more commit <-- this hash
git checkout -b recovered dd7c37e
La recuperación más segura consiste en inspeccionar antes de sobrescribir: ejecuta git checkout -b recovered <hash> para reconstruir el commit en una rama nueva, verifícalo con git log y solo entonces decide si mueves tu rama real. Cuando estés seguro, puedes desplazar la rama directamente:
git reset --hard dd7c37e # or: git reset --hard HEAD@{1}
Advertencia de doble riesgo: git reset --hard descarta cualquier cambio actual no commiteado de tu árbol de trabajo. Si tu árbol está sucio, usa git stash o la vía de la rama nueva primero. De lo contrario, el comando de recuperación provoca una segunda pérdida. Inmediatamente después de un reset también puedes usar git reset --hard ORIG_HEAD, ya que git reset registra la punta anterior de la rama en ORIG_HEAD antes de moverse.
Deshacer un rebase fallido
Un rebase reescribe el historial, y un conflicto mal resuelto puede descartar commits de forma silenciosa. El reflog conserva la punta previa al rebase. Después de un rebase fallido, git reset --hard ORIG_HEAD devuelve tu rama al punto de partida. Pero ORIG_HEAD se sobrescribe con el siguiente reset, rebase o merge, así que si has ejecutado otro comando de ese tipo desde entonces, busca en su lugar la punta previa al rebase con git reflog.
git reflog
# HEAD@{0}: rebase (finish): returning to refs/heads/feature
# HEAD@{1}: rebase (pick): one more commit
# HEAD@{2}: rebase (start): checkout main
# HEAD@{3}: checkout: moving from main to feature <-- pre-rebase tip
git reset --hard HEAD@{3}
Busca la entrada rebase (start); la línea inmediatamente anterior es el estado de tu rama antes del rebase. Si solo quieres recuperar commits concretos que el rebase descartó, en lugar de deshacerlo todo, toma sus hashes del reflog y reprodúcelos:
git cherry-pick <hash>
Recuperar una rama eliminada
Eliminar una rama borra la referencia, no los commits. Su último commit sigue apareciendo en el reflog de HEAD (de la última vez que hiciste checkout de ella), así que busca ese hash y vuelve a crear la rama en él.
git reflog
# HEAD@{0}: checkout: moving from example-branch to main
# HEAD@{1}: commit: very important commit <-- last commit on deleted branch
git checkout -b example-branch b5b3286 # or HEAD@{1}
git checkout -b <branch> <hash> recrea la rama apuntando al commit recuperado, con su historial intacto. Si recuerdas parte de un mensaje de commit pero no el hash, git log --oneline -g --grep='<fragment>' lo busca en el reflog. (git switch -c es el equivalente moderno de checkout -b; ambos funcionan, y checkout no está obsoleto).
¿Qué no puede recuperar el reflog de Git?
El reflog puede recuperar cualquier cosa que haya movido HEAD o la punta de una rama (commits, resets, rebases, ramas eliminadas), pero no puede recuperar cambios no commiteados del directorio de trabajo, porque Git nunca los registró en una referencia. Si un cambio nunca se commiteó, no hay actualización de referencia a la que el reflog pueda apuntar, así que buscarlo en el reflog no dará ningún resultado.
| El reflog SÍ puede recuperar | El reflog NO puede recuperar |
|---|---|
Commits huérfanos por reset --hard | Ediciones no commiteadas en el árbol de trabajo |
| Commits descartados por un rebase fallido | Cambios en el área de staging sin commitear |
| Ramas locales eliminadas (recientes) | Commits sin pushear de un compañero de equipo |
| Tu vista local de un remoto con force-push | Entradas ya expiradas y recogidas por gc |
Hay tres restricciones más que condicionan la recuperación:
- Es local y temporal. El reflog vive únicamente en tu directorio
.gity nunca se envía al remoto, por lo que rescata tu propio trabajo, pero no los commits sin pushear de un compañero. - Las entradas expiran. Por defecto, Git conserva las entradas de reflog alcanzables durante 90 días y las inalcanzables durante 30, mediante
gc.reflogExpireygc.reflogExpireUnreachable. Los commits huérfanos sobreviven hasta que expiran y se ejecuta una pasada de recolección de basura, así que recupéralos cuanto antes y evita ejecutargit gchasta que hayas terminado. - Recupera primero en una rama nueva. Haz de
git checkout -b recovered <hash>tu movimiento por defecto para poder verificar antes de alterar una rama compartida. Y escribe mensajes de commit descriptivos: el reflog los muestra, así que «Fix login bug» es mucho más fácil de localizar que uno vago.
Extra (después de un force-push): el servidor remoto no tiene un reflog que puedas leer, pero tu referencia de seguimiento local sí. Para recuperar commits eliminados de un remoto por un force-push, consulta tu registro local con git reflog show origin/<branch>. La entrada inmediatamente anterior al force-push contiene la punta antigua.
Conclusión
El reflog es la red de seguridad local de Git, y convierte casi cualquier momento de «he perdido mi trabajo» en un arreglo de dos comandos: lee el registro y vuelve a apuntar al hash. Lo único que no puede rescatar es el trabajo que nunca commiteaste, por lo que la lección duradera es commitear pronto y con frecuencia, lo que garantiza que siempre haya una entrada de reflog que encontrar. La próxima vez que la terminal te dé un vuelco al estómago, ejecuta git reflog antes que nada.
Preguntas frecuentes
¿Cuál es la diferencia entre git reflog y git log?
git log muestra el historial de commits alcanzable desde la punta de una rama, siguiendo los punteros a los padres, por lo que los commits huérfanos nunca aparecen en él. git reflog muestra todas las posiciones que HEAD o una referencia han ocupado en tu repositorio local —commits, checkouts, resets, rebases y merges—, incluidos los commits a los que ya no apunta ninguna rama. Por eso el reflog puede encontrar un commit después de un hard reset cuando git log no puede: el commit está huérfano, pero su actualización de referencia sigue registrada.
¿Funciona git reflog después de clonar un repositorio?
No. El reflog se almacena únicamente en tu directorio .git local y nunca se envía ni se descarga, por lo que un clon nuevo empieza con un reflog vacío que cubre solo las acciones realizadas desde que clonaste. No puede mostrar movimientos de HEAD anteriores al clon ni revelar el historial local de un compañero de equipo. El reflog rescata tu propio trabajo perdido en tu propio repositorio; no es una herramienta de recuperación compartida ni respaldada por el remoto.
¿Puedo recuperar un commit después de que las entradas de git reflog hayan expirado?
No a través del reflog en sí. Por defecto, las entradas alcanzables expiran a los 90 días y las inalcanzables a los 30, y una vez que una pasada de recolección de basura elimina los objetos huérfanos, estos desaparecen de verdad. Antes de eso, a veces aún puedes encontrar el hash con git fsck --lost-found, que lista los commits colgantes (dangling) del almacén de objetos. Lo fiable es recuperar cuanto antes y evitar ejecutar git gc hasta que tus commits estén de vuelta.
¿Cómo encuentro un commit perdido si no conozco su hash?
Busca directamente en el reflog. Ejecuta git log --oneline -g --grep='fragment' para filtrar las entradas del reflog por parte de un mensaje de commit, o git reflog --date=relative para revisar las entradas por fecha y localizar la operación que quieres deshacer. Para commits huérfanos sin entrada en el reflog, git fsck --lost-found lista los commits colgantes que puedes inspeccionar con git show antes de recuperarlos en una rama nueva con git checkout -b recovered <hash>.