{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Recuperar lo que creías perdido\n",
    "\n",
    "El reflog, que es la red de seguridad que nadie te contó\n",
    "\n",
    "Cuaderno de soluciones del capítulo 25 de **Git desde cero**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/git-desde-cero/recuperar-lo-perdido/\n",
    "\n",
    "Este es el cuaderno de **soluciones**. Trae el código de cada ejercicio, la\n",
    "explicación de la trampa y la respuesta del quiz. Si vienes del cuaderno de\n",
    "práctica sin haberlo intentado, vuelve 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar\n",
    "\n",
    "Este capítulo son comandos de terminal, no Python. La celda de abajo baja el\n",
    "ayudante que los ejecuta y que **recuerda en qué carpeta quedaste**, que es lo\n",
    "que hace falta para que un `cd` de una celda siga valiendo en la siguiente.\n",
    "\n",
    "A partir de ahí, cada celda de comandos empieza por `%%consola`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# El ayudante de los cuadernos. Trae la corrección de los ejercicios y, en los\n",
    "# capítulos de consola, la celda mágica que ejecuta los comandos. Se baja en\n",
    "# vez de venir pegado aquí para que siempre sea el último.\n",
    "urllib.request.urlretrieve(\n",
    "    \"https://missyera.com/static/cuadernos/revisa.py\", \"revisa.py\")\n",
    "import revisa\n",
    "revisa.carga({}, lenguaje=\"bash\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este es el capítulo que hace que se te quite el miedo, y por eso lo pongo\n",
    "antes que nada de la parte que sigue. Casi todo lo que crees que perdiste está\n",
    "ahí 🧯\n",
    "\n",
    "La frase que me han dicho más veces es \"borré tres días de trabajo\". Y casi\n",
    "siempre la respuesta cabe en una línea."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El log y el reflog no son lo mismo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "|  | `git log` | `git reflog` |\n",
    "|---|---|---|\n",
    "| Qué muestra | La historia oficial | Cada vez que te moviste |\n",
    "| Incluye lo que \"borraste\" | No | Sí |\n",
    "| Se comparte | Sí | No, es de tu máquina |\n",
    "| Cuánto dura | Para siempre | 90 días por defecto |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso de que el reflog no se comparte importa: es tuyo y de tu carpeta. Si\n",
    "clonas de nuevo el proyecto, el reflog no viene 🧳"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perder algo a propósito"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Armamos un proyecto con historia y lo rompemos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "mkdir ventas-miss-yera\n",
    "cd ventas-miss-yera\n",
    "git init -q\n",
    "printf 'ciudad,monto\\nLima,1200\\n' > ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Primeras ventas de Lima\"\n",
    "printf 'Arequipa,890\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Entran las ventas de Arequipa\"\n",
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Entra el reporte por canal\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres commits. Y ahora el desastre clásico:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --hard HEAD~2\n",
    "git log --oneline\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un commit, un archivo, y el reporte por canal desapareció. Este es el\n",
    "momento del \"se me fue el trabajo de dos días\" 😰"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El reflog"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reflog"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está todo. Cada línea es un movimiento, con su hash y qué lo causó. El\n",
    "`HEAD@{1}` es \"donde estabas justo antes\", `HEAD@{2}` el\n",
    "anterior, y así 🧭"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Volver"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --hard HEAD@{1}\n",
    "git log --oneline\n",
    "ls\n",
    "cat canales.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los tres commits de vuelta y el archivo también. Eso es todo el\n",
    "capítulo 🎉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Volver a un hash concreto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuando hay muchos movimientos, es más seguro copiar el hash de la línea que\n",
    "quieres que contar posiciones:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --hard HEAD~1\n",
    "git log --oneline\n",
    "git reflog --format=\"%h %gs\" | head -4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese hash de la izquierda se copia y se usa directamente. No hay que\n",
    "acordarse de nada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Una rama borrada también vuelve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c reporte-por-ciudad\n",
    "printf 'Cusco,760\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Entran las ventas de Cusco\"\n",
    "git switch -q -\n",
    "git branch -D reporte-por-ciudad\n",
    "git branch\n",
    "git reflog --format=\"%h %gs\" | head -3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La rama ya no está en la lista, pero su commit sigue en el reflog. Se\n",
    "recupera creando una rama nueva en ese punto:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c reporte-recuperado HEAD@{1}\n",
    "git log --oneline -2\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cusco volvió. Borrar una rama nunca borra sus commits, solo quita el\n",
    "nombre 🏷️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo único que no vuelve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lo que nunca fue commit. Si editaste un archivo y no hiciste\n",
    "`git add` ni `commit`, Git no tiene copia y no hay nada\n",
    "que recuperar.\n",
    "\n",
    "De ahí la costumbre que me ahorra sustos: **antes de cualquier cosa\n",
    "que suene peligrosa, un commit**, aunque el mensaje diga \"a medias\". Se\n",
    "puede limpiar después 🧷"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "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.\n",
    "\n",
    "```\n",
    "$ git reset --hard HEAD~3\n",
    "HEAD is now at 3c1f80a Primeras ventas de Lima\n",
    "\n",
    "$ git log --oneline\n",
    "3c1f80a Primeras ventas de Lima\n",
    "\n",
    "# y los tres commits del reporte por canal?\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Están ahí, pero no en el `log` 😅\n",
    "\n",
    "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`.\n",
    "\n",
    "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.\n",
    "\n",
    "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**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Hiciste `git reset --hard` y tu `log` perdió tres commits. ¿Qué haces?\n",
    "\n",
    "a) `git reflog` y vuelvo al punto anterior\n",
    "\n",
    "b) Clono el proyecto otra vez desde GitHub\n",
    "\n",
    "c) Los vuelvo a escribir, no queda otra\n",
    "\n",
    "d) Busco los archivos en la papelera del sistema\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Si esos commits nunca se subieron, no están allá. Y el reflog no viaja en el clon.\n",
    "\n",
    "*c)* Sí queda: los commits siguen en la carpeta .git aunque el log ya no los muestre.\n",
    "\n",
    "*d)* Git no usa la papelera. Su copia está dentro del propio repositorio.\n",
    "\n",
    "El reset mueve la etiqueta; los commits siguen ahí 🧯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Construye algo que perder\n",
    "\n",
    "Tres commits en el catálogo de bodegas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ..\n",
    "mkdir catalogo\n",
    "cd catalogo\n",
    "git init -q\n",
    "printf 'bodega,ciudad\\nBodega Inti,Cusco\\n' > bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"Catalogo inicial de bodegas\"\n",
    "printf 'Minimarket Sol,Trujillo\\n' >> bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"Entra el minimarket de Trujillo\"\n",
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Entra el reporte por canal\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "9f37666 Entra el reporte por canal\n",
    "4cb519a Entra el minimarket de Trujillo\n",
    "a9874ae Catalogo inicial de bodegas\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres commits y dos archivos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Rómpelo\n",
    "\n",
    "Un reset duro de dos commits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --hard HEAD~2\n",
    "git log --oneline\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "HEAD is now at a9874ae Catalogo inicial de bodegas\n",
    "a9874ae Catalogo inicial de bodegas\n",
    "bodegas.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Uno solo y el CSV de canales desapareció. Tranqui 🧘‍♀️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Mira el reflog\n",
    "\n",
    "Todos tus movimientos, incluidos los que el log ya no\n",
    "muestra."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reflog"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "a9874ae HEAD@{0}: reset: moving to HEAD~2\n",
    "9f37666 HEAD@{1}: commit: Entra el reporte por canal\n",
    "4cb519a HEAD@{2}: commit: Entra el minimarket de Trujillo\n",
    "a9874ae HEAD@{3}: commit (initial): Catalogo inicial de bodegas\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El `HEAD@{1}` es donde estabas justo antes del reset."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Vuelve\n",
    "\n",
    "Recupera los tres commits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --hard HEAD@{1}\n",
    "git log --oneline\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "HEAD is now at 9f37666 Entra el reporte por canal\n",
    "9f37666 Entra el reporte por canal\n",
    "4cb519a Entra el minimarket de Trujillo\n",
    "a9874ae Catalogo inicial de bodegas\n",
    "bodegas.csv\n",
    "canales.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Todo de vuelta, incluidos los archivos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Borra una rama y recupérala\n",
    "\n",
    "Las ramas borradas también vuelven."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c precios-por-canal\n",
    "printf 'Horeca,3100\\n' >> canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Entra el canal horeca\"\n",
    "git switch -q -\n",
    "git branch -D precios-por-canal\n",
    "git switch -q -c precios-recuperado HEAD@{1}\n",
    "cat canales.csv\n",
    "git branch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Deleted branch precios-por-canal (was 6a3cf7b).\n",
    "canal,monto\n",
    "Bodegas,4200\n",
    "Horeca,3100\n",
    "  main\n",
    "* precios-recuperado\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El canal horeca estaba esperando en el reflog 🌿"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Mira el reflog de una rama concreta\n",
    "\n",
    "El reflog también existe por rama, no solo para\n",
    "`HEAD`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q main\n",
    "git reflog show main --format=\"%h %gs\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "9f37666 reset: moving to HEAD@{1}\n",
    "a9874ae reset: moving to HEAD~2\n",
    "9f37666 commit: Entra el reporte por canal\n",
    "4cb519a commit: Entra el minimarket de Trujillo\n",
    "a9874ae commit (initial): Catalogo inicial de bodegas\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Útil cuando la que se movió de más fue una rama y no tú."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Pide un punto del reflog que no existe\n",
    "\n",
    "Prueba a pedir un movimiento muy anterior en un\n",
    "repositorio recién creado."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git show HEAD@{50}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "fatal: log for 'HEAD' only has 10 entries\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Git te dice cuántos movimientos hay de verdad en vez de inventarse uno 🛑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Casi nada se pierde: el reflog guarda 90 días de movimientos. Lo\n",
    "único irrecuperable es lo que nunca fue commit.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 25 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/recuperar-lo-perdido/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
