Capítulo 25 de 33 10 secciones 7 min

Recuperar lo que creías perdido

El reflog, que es la red de seguridad que nadie te contó

<strong><code>git reflog</code>.</strong> Git anota cada vez que te mueves (commits, cambios de rama, resets, fusiones) y guarda esa lista aunque los commits ya no aparezcan en el <code>log</code>. Con ella se vuelve a cualquier punto de los últimos 90 días. Lo único que Git no puede recuperar es lo que nunca llegó a ser commit, y por eso la costumbre más barata es hacer un commit antes de cualquier operación que suene peligrosa.

Este es el capítulo que hace que se te quite el miedo, y por eso lo pongo antes que nada de la parte que sigue. Casi todo lo que crees que perdiste está ahí 🧯

La frase que me han dicho más veces es "borré tres días de trabajo". Y casi siempre la respuesta cabe en una línea.

El log y el reflog no son lo mismo

git loggit reflog
Qué muestraLa historia oficialCada vez que te moviste
Incluye lo que "borraste"No
Se comparteNo, es de tu máquina
Cuánto duraPara siempre90 días por defecto

Eso de que el reflog no se comparte importa: es tuyo y de tu carpeta. Si clonas de nuevo el proyecto, el reflog no viene 🧳

Perder algo a propósito

Armamos un proyecto con historia y lo rompemos:

mkdir ventas-miss-yera
cd ventas-miss-yera
git init -q
printf 'ciudad,monto\nLima,1200\n' > ventas.csv
git add ventas.csv
git commit -q -m "Primeras ventas de Lima"
printf 'Arequipa,890\n' >> ventas.csv
git add ventas.csv
git commit -q -m "Entran las ventas de Arequipa"
printf 'canal,monto\nBodegas,4200\n' > canales.csv
git add canales.csv
git commit -q -m "Entra el reporte por canal"
git log --oneline
ac55194 Entra el reporte por canal
508ad4d Entran las ventas de Arequipa
54da6e2 Primeras ventas de Lima

Tres commits. Y ahora el desastre clásico:

git reset --hard HEAD~2
git log --oneline
ls
HEAD is now at 54da6e2 Primeras ventas de Lima
54da6e2 Primeras ventas de Lima
ventas.csv

Un commit, un archivo, y el reporte por canal desapareció. Este es el momento del "se me fue el trabajo de dos días" 😰

El reflog

git reflog
54da6e2 HEAD@{0}: reset: moving to HEAD~2
ac55194 HEAD@{1}: commit: Entra el reporte por canal
508ad4d HEAD@{2}: commit: Entran las ventas de Arequipa
54da6e2 HEAD@{3}: commit (initial): Primeras ventas de Lima

Ahí está todo. Cada línea es un movimiento, con su hash y qué lo causó. El HEAD@{1} es "donde estabas justo antes", HEAD@{2} el anterior, y así 🧭

Volver

git reset --hard HEAD@{1}
git log --oneline
ls
cat canales.csv
HEAD is now at ac55194 Entra el reporte por canal
ac55194 Entra el reporte por canal
508ad4d Entran las ventas de Arequipa
54da6e2 Primeras ventas de Lima
canales.csv
ventas.csv
canal,monto
Bodegas,4200

Los tres commits de vuelta y el archivo también. Eso es todo el capítulo 🎉

Volver a un hash concreto

Cuando hay muchos movimientos, es más seguro copiar el hash de la línea que quieres que contar posiciones:

git reset --hard HEAD~1
git log --oneline
git reflog --format="%h %gs" | head -4
HEAD is now at 508ad4d Entran las ventas de Arequipa
508ad4d Entran las ventas de Arequipa
54da6e2 Primeras ventas de Lima
508ad4d reset: moving to HEAD~1
ac55194 reset: moving to HEAD@{1}
54da6e2 reset: moving to HEAD~2
ac55194 commit: Entra el reporte por canal

Ese hash de la izquierda se copia y se usa directamente. No hay que acordarse de nada.

Una rama borrada también vuelve

git switch -q -c reporte-por-ciudad
printf 'Cusco,760\n' >> ventas.csv
git add ventas.csv
git commit -q -m "Entran las ventas de Cusco"
git switch -q -
git branch -D reporte-por-ciudad
git branch
git reflog --format="%h %gs" | head -3
Deleted branch reporte-por-ciudad (was 721b81f).
* main
508ad4d checkout: moving from reporte-por-ciudad to main
721b81f commit: Entran las ventas de Cusco
508ad4d checkout: moving from main to reporte-por-ciudad

La rama ya no está en la lista, pero su commit sigue en el reflog. Se recupera creando una rama nueva en ese punto:

git switch -q -c reporte-recuperado HEAD@{1}
git log --oneline -2
cat ventas.csv
721b81f Entran las ventas de Cusco
508ad4d Entran las ventas de Arequipa
ciudad,monto
Lima,1200
Arequipa,890
Cusco,760

Cusco volvió. Borrar una rama nunca borra sus commits, solo quita el nombre 🏷️

Lo único que no vuelve

Lo que nunca fue commit. Si editaste un archivo y no hiciste git add ni commit, Git no tiene copia y no hay nada que recuperar.

De ahí la costumbre que me ahorra sustos: antes de cualquier cosa que suene peligrosa, un commit, aunque el mensaje diga "a medias". Se puede limpiar después 🧷

La trampa

Te equivocaste de rama y guardaste tres commits donde no era. Buscas cómo deshacer, encuentras un reset --hard en un foro y lo pegas.

$ git reset --hard HEAD~3
HEAD is now at 3c1f80a Primeras ventas de Lima

$ git log --oneline
3c1f80a Primeras ventas de Lima

# y los tres commits del reporte por canal?
Qué está mal

Están ahí, pero no en el log 😅

Lo que hizo el reset fue mover la etiqueta de dónde estás, no borrar los commits. Siguen dentro de la carpeta .git, esperando, y se ven con git reflog.

El susto de "perdí tres días" casi siempre es esto: el trabajo está y lo que se perdió es el camino para llegar. El reflog es ese camino.

Lo que sí se pierde de verdad es lo que nunca llegó a ser commit. Por eso la costumbre más barata de todas: commit antes de cualquier cosa que suene peligrosa.

Comprueba que se entendió

Comprueba que lo tienes

Hiciste git reset --hard y tu log perdió tres commits. ¿Qué haces?

  • git reflog y vuelvo al punto anterior
  • Clono el proyecto otra vez desde GitHub
  • Los vuelvo a escribir, no queda otra
  • Busco los archivos en la papelera del sistema

Ejercicios

1. Construye algo que perder

Tres commits en el catálogo de bodegas.

cd ..
mkdir catalogo
cd catalogo
git init -q
printf 'bodega,ciudad\nBodega Inti,Cusco\n' > bodegas.csv
git add bodegas.csv
git commit -q -m "Catalogo inicial de bodegas"
printf 'Minimarket Sol,Trujillo\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "Entra el minimarket de Trujillo"
printf 'canal,monto\nBodegas,4200\n' > canales.csv
git add canales.csv
git commit -q -m "Entra el reporte por canal"
git log --oneline
9f37666 Entra el reporte por canal
4cb519a Entra el minimarket de Trujillo
a9874ae Catalogo inicial de bodegas

Tres commits y dos archivos.

2. Rómpelo

Un reset duro de dos commits.

git reset --hard HEAD~2
git log --oneline
ls
HEAD is now at a9874ae Catalogo inicial de bodegas
a9874ae Catalogo inicial de bodegas
bodegas.csv

Uno solo y el CSV de canales desapareció. Tranqui 🧘‍♀️

3. Mira el reflog

Todos tus movimientos, incluidos los que el log ya no muestra.

git reflog
a9874ae HEAD@{0}: reset: moving to HEAD~2
9f37666 HEAD@{1}: commit: Entra el reporte por canal
4cb519a HEAD@{2}: commit: Entra el minimarket de Trujillo
a9874ae HEAD@{3}: commit (initial): Catalogo inicial de bodegas

El HEAD@{1} es donde estabas justo antes del reset.

4. Vuelve

Recupera los tres commits.

git reset --hard HEAD@{1}
git log --oneline
ls
HEAD is now at 9f37666 Entra el reporte por canal
9f37666 Entra el reporte por canal
4cb519a Entra el minimarket de Trujillo
a9874ae Catalogo inicial de bodegas
bodegas.csv
canales.csv

Todo de vuelta, incluidos los archivos.

5. Borra una rama y recupérala

Las ramas borradas también vuelven.

git switch -q -c precios-por-canal
printf 'Horeca,3100\n' >> canales.csv
git add canales.csv
git commit -q -m "Entra el canal horeca"
git switch -q -
git branch -D precios-por-canal
git switch -q -c precios-recuperado HEAD@{1}
cat canales.csv
git branch
Deleted branch precios-por-canal (was 6a3cf7b).
canal,monto
Bodegas,4200
Horeca,3100
  main
* precios-recuperado

El canal horeca estaba esperando en el reflog 🌿

6. Mira el reflog de una rama concreta

El reflog también existe por rama, no solo para HEAD.

git switch -q main
git reflog show main --format="%h %gs"
9f37666 reset: moving to HEAD@{1}
a9874ae reset: moving to HEAD~2
9f37666 commit: Entra el reporte por canal
4cb519a commit: Entra el minimarket de Trujillo
a9874ae commit (initial): Catalogo inicial de bodegas

Útil cuando la que se movió de más fue una rama y no tú.

7. Pide un punto del reflog que no existe

Prueba a pedir un movimiento muy anterior en un repositorio recién creado.

git show HEAD@{50}
fatal: log for 'HEAD' only has 10 entries

Git te dice cuántos movimientos hay de verdad en vez de inventarse uno 🛑

Lo que te llevas

Casi nada se pierde: el reflog guarda 90 días de movimientos. Lo único irrecuperable es lo que nunca fue commit.

¿Tienes alguna duda o consulta?