{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deshacer sin romper nada\n",
    "\n",
    "Restore, revert y reset, que suenan igual y hacen cosas muy distintas\n",
    "\n",
    "Cuaderno de soluciones del capítulo 7 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/deshacer/\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": [
    "## La tabla que quiero que te lleves"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de nada, esto:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Quiero | Comando | ¿Destruye? |\n",
    "|---|---|---|\n",
    "| Tirar lo que edité y no guardé | `git restore archivo` | Sí, eso |\n",
    "| Sacar algo de la foto | `git restore --staged archivo` | No |\n",
    "| Anular un commit ya compartido | `git revert hash` | No |\n",
    "| Deshacer el último commit y quedarme los cambios | `git reset --soft HEAD~1` | No |\n",
    "| Borrar el último commit y todo lo que traía | `git reset --hard HEAD~1` | Sí, todo |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Guárdala. Yo la tuve pegada en la pantalla meses 📌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preparo el terreno"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "mkdir tienda\n",
    "cd tienda\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 \"Se agregan las ventas de Arequipa\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Revert, el que se puede usar sin miedo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`revert` no borra el commit: crea uno nuevo que hace justo lo\n",
    "contrario. La historia crece en vez de encogerse."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git revert --no-edit HEAD\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres commits, no uno. El de Arequipa sigue ahí y encima hay otro que lo\n",
    "deshace. Y el archivo quedó como estaba antes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este es **el único que se puede usar cuando el commit ya lo tiene otra\n",
    "persona**, porque no cambia nada de lo que ya estaba. Los demás\n",
    "reescriben, y eso se cuenta en el capítulo 10."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reset soft, deshacer el commit y quedarte lo hecho"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --soft HEAD~1\n",
    "git log --oneline\n",
    "git status --short"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El commit del revert desapareció de la historia, pero su contenido se quedó\n",
    "en la foto, listo para volver a guardarse. Es lo que uso cuando hice un commit\n",
    "antes de tiempo o con el mensaje mal."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reset hard, el que sí borra"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --hard HEAD\n",
    "printf 'Cusco,760\\n' >> ventas.csv\n",
    "git reset --hard HEAD\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cusco no está. No se fue a ningún sitio recuperable: **nunca llegó a\n",
    "un commit**, así que Git no tiene de dónde sacarlo.\n",
    "\n",
    "Este es el único comando de todo el libro que destruye trabajo de verdad, y\n",
    "por eso el capítulo 8 existe: hay una forma de guardar lo\n",
    "que llevas antes de limpiar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error de anular un commit que no existe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito**, y de eso va. La celda te muestra la queja igual que te la mostraría tu terminal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git revert --no-edit abc1234"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "fatal: bad revision 'abc1234'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Bad revision*: ese hash no está en esta historia. Suele pasar cuando\n",
    "copias un hash de otra rama o de un mensaje viejo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Llevas dos horas con el análisis de las ventas por canal, se te enreda todo y decides empezar de nuevo desde el último commit.\n",
    "\n",
    "```\n",
    "$ git reset --hard HEAD\n",
    "\n",
    "HEAD is now at 8a2b19c Ventas por canal de Lima\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Esas dos horas ya no existen 💀\n",
    "\n",
    "`reset --hard` es el único comando de este libro que borra trabajo **sin posibilidad de recuperarlo**. Lo que nunca llegó a un commit no está en ningún lado, así que Git tampoco lo puede devolver.\n",
    "\n",
    "Lo peligroso es que se parece a los otros: `restore`, `revert` y `reset` suenan igual de inocentes y hacen cosas muy distintas. Por eso este capítulo trae la tabla de cuál usar cuándo, y por eso conviene leerla antes de necesitarla.\n",
    "\n",
    "La red de seguridad se pone en un segundo: `git stash` guarda lo que llevas antes de limpiar. Después decides si lo recuperas o no."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Subiste un commit que ya se llevaron dos compañeras y hay que anularlo. ¿Qué usas?\n",
    "\n",
    "a) `git reset --hard`\n",
    "\n",
    "b) `git revert`\n",
    "\n",
    "c) `git restore`\n",
    "\n",
    "d) Borrar el archivo y hacer un commit nuevo\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* Eso reescribe la historia que ellas ya tienen, y cuando vuelvan a sincronizar se arma un lío difícil de explicar.\n",
    "\n",
    "*c)* restore trabaja con archivos sin guardar, no con commits. Aquí el commit ya existe y ya viajó.\n",
    "\n",
    "*d)* Funciona a medias y deja la historia confusa. revert hace exactamente eso pero dejando escrito qué commit está anulando.\n",
    "\n",
    "revert es el único que se puede usar sobre algo que ya salió de tu computadora 🤝"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Tira un cambio que no querías\n",
    "\n",
    "Escribe un monto mal en el CSV y descártalo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Piura,99999999\\n' >> ventas.csv\n",
    "git restore ventas.csv\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ciudad,monto\n",
    "Lima,1200\n",
    "Arequipa,890\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El archivo volvió a lo último guardado. Es lo que quieres nueve de cada diez\n",
    "veces que dices \"deshacer\" 🔙"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Saca algo de la foto sin perderlo\n",
    "\n",
    "Agrega un archivo de clientes, mételo en la foto y después\n",
    "sácalo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'bodega,ciudad\\nBodega Sol,Lima\\n' > clientes.csv\n",
    "git add clientes.csv\n",
    "git restore --staged clientes.csv\n",
    "git status --short"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "?? clientes.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las dos interrogaciones dicen que Git volvió a no conocerlo, y el archivo\n",
    "sigue en tu carpeta. Ese `--staged` es la diferencia entre sacar de\n",
    "la foto y borrar 🙌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Un commit y su anulación\n",
    "\n",
    "Guarda el archivo de clientes y después anúlalo con revert."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git add clientes.csv\n",
    "git commit -q -m \"Se agrega el catalogo de clientes\"\n",
    "git revert --no-edit HEAD\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "[main 5972d6a] Revert \"Se agrega el catalogo de clientes\"\n",
    " Date: Thu Jan 15 09:00:00 2026 -0500\n",
    " 1 file changed, 2 deletions(-)\n",
    " delete mode 100644 clientes.csv\n",
    "5972d6a Revert \"Se agrega el catalogo de clientes\"\n",
    "e4eb86b Se agrega el catalogo de clientes\n",
    "571877b Se agregan las ventas de Arequipa\n",
    "54da6e2 Primeras ventas de Lima\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los dos commits en la historia: el que puso el archivo y el que lo quitó.\n",
    "Nadie borró nada y se puede reconstruir todo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Comprueba que el archivo se fue\n",
    "\n",
    "Mira qué hay en la carpeta después del revert."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ventas.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`clientes.csv` ya no está, porque el revert deshizo lo que el\n",
    "commit había hecho. Y sigue estando en la historia, a un `git show`\n",
    "de distancia."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Deshaz el commit pero quédate lo hecho\n",
    "\n",
    "Usa `--soft` sobre el revert y mira dónde quedan\n",
    "las cosas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --soft HEAD~1\n",
    "git log --oneline\n",
    "git status --short"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "e4eb86b Se agrega el catalogo de clientes\n",
    "571877b Se agregan las ventas de Arequipa\n",
    "54da6e2 Primeras ventas de Lima\n",
    "D  clientes.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La `D` en la primera columna significa que el borrado está en la\n",
    "foto, listo para guardarse otra vez. El commit se fue; su contenido no."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Vuelve al último commit y limpia\n",
    "\n",
    "Descarta todo lo que hay pendiente con un reset duro."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git reset --hard HEAD\n",
    "git status --short\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "HEAD is now at e4eb86b Se agrega el catalogo de clientes\n",
    "clientes.csv\n",
    "ventas.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Estado limpio y el archivo de clientes de vuelta, porque el commit que lo\n",
    "creaba volvió a ser el último. Aquí `--hard` no destruyó nada\n",
    "porque no había nada sin guardar ✅"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Un hash inventado\n",
    "\n",
    "Intenta anular un commit que no existe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git revert --no-edit 0000000"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "fatal: bad revision '0000000'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de pelearte con un comando, comprueba que el hash existe con\n",
    "`git log --oneline`. La mitad de los errores raros de Git son un\n",
    "hash mal copiado 📋"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**revert nunca destruye, reset --hard siempre puede. Si dudas, usa\n",
    "revert.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 7 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/deshacer/\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
}
