{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Git es el cinturón de seguridad cuando trabajas con IA\n",
    "\n",
    "Por qué esto importa más ahora que hace cinco años\n",
    "\n",
    "Cuaderno de soluciones del capítulo 30 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/git-con-ia/\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 libro existe por una razón muy concreta, y es de 2026: hay muchísima\n",
    "gente que llegó a GitHub sin haber programado nunca, empujada por la IA. Buscan\n",
    "Claude Code, buscan skills, buscan proyectos, y todo eso vive en repositorios 🤖\n",
    "\n",
    "Y resulta que Git, que era una herramienta de programadores, se volvió lo\n",
    "que más falta hace cuando trabajas con IA. Por una razón simple: los cambios ya\n",
    "no los escribes tú de a uno, llegan de golpe."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El problema nuevo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuando escribes tú, vas viendo. Cuando le pides a una IA que reorganice el\n",
    "proyecto, en un minuto hay seis archivos cambiados, uno borrado y una carpeta\n",
    "nueva. Si eso no funciona, ¿qué parte falló? 🤔\n",
    "\n",
    "Git contesta esa pregunta, pero solo si hiciste una cosa antes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La costumbre que lo cambia todo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "mkdir analisis-ventas\n",
    "cd analisis-ventas\n",
    "git init -q\n",
    "printf 'ciudad,monto\\nLima,1200\\nArequipa,890\\n' > ventas.csv\n",
    "printf 'import csv\\n\\ntotal = 0\\n' > reporte.py\n",
    "git add .\n",
    "git commit -q -m \"Primera version del reporte de ventas\"\n",
    "git status --short\n",
    "echo \"todo guardado, ahora si puedo pedir cambios\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Diez segundos. Y con eso ya tienes un punto de retorno 🧷"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ahora llega el cambio grande"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Simulamos lo que haría una herramienta de IA: reescribe el script, agrega un\n",
    "archivo y toca los datos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'import csv\\n\\ndef total_por_ciudad(ruta):\\n    with open(ruta) as f:\\n        filas = list(csv.DictReader(f))\\n    return {f[\"ciudad\"]: int(f[\"monto\"]) for f in filas}\\n' > reporte.py\n",
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "printf 'Cusco,760\\n' >> ventas.csv\n",
    "git status --short"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres archivos tocados. Sin el commit de antes, esto sería un montón; con\n",
    "él, es una lista de cosas que puedes revisar una por una 📋"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Mirar exactamente qué hizo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git diff --stat\n",
    "git diff ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese `diff` es la herramienta más importante de todo el capítulo.\n",
    "No estás confiando ni desconfiando: estás **mirando** 👀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Aceptar una parte y rechazar otra"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí es donde se paga el capítulo 4. No hace falta\n",
    "aceptar todo o nada:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git add reporte.py canales.csv\n",
    "git status --short\n",
    "git restore ventas.csv\n",
    "git status --short\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El código nuevo se queda, el cambio en los datos se fue. Cusco desapareció y\n",
    "el resto sigue 🎯"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git commit -q -m \"El reporte calcula el total por ciudad\n",
    "\n",
    "Escrito con ayuda de una herramienta de IA y revisado antes de entrar.\"\n",
    "git log --oneline\n",
    "git status --short"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deshacerlo entero"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y si el cambio no sirve, vuelves al punto de antes sin pensar:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'algo que la IA propuso y no sirve\\n' > reporte.py\n",
    "printf 'basura.txt\\n' > basura.txt\n",
    "git status --short\n",
    "git restore .\n",
    "git clean -qfd\n",
    "git status --short\n",
    "head -3 reporte.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`restore .` devuelve lo que Git conocía y `clean -fd`\n",
    "se lleva lo que no conocía. Los dos juntos son \"vuelve a como estaba\" 🔙"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tres reglas que uso"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **Commit antes de pedir.** Siempre, aunque el mensaje diga\n",
    "\"antes de tocar nada\".\n",
    "\n",
    "- **Revisar el diff antes de aceptar.** Quien firma el commit\n",
    "eres tú, y en una revisión de equipo vas a tener que explicarlo.\n",
    "\n",
    "- **Ramas para lo experimental.** Si vas a probar algo grande,\n",
    "una rama de 10 y tu `main` se queda intacta."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y una cosa sobre qué le das"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las herramientas de IA leen tu carpeta. Si ahí dentro hay un\n",
    "`.env` con claves o un CSV con datos de clientes, eso puede terminar\n",
    "saliendo de tu máquina 🔐\n",
    "\n",
    "El `.gitignore` del capítulo 9 no basta\n",
    "aquí, porque muchas herramientas leen archivos ignorados igual. Lo que sirve es\n",
    "no tener secretos sueltos en la carpeta del proyecto, y trabajar con datos de\n",
    "ejemplo cuando los de verdad son de un cliente."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Le pides a una herramienta de IA que reorganice el proyecto entero. Acepta todos los cambios de una y prueba.\n",
    "\n",
    "```\n",
    "$ git status --short\n",
    " M reporte.py\n",
    " M ventas.csv\n",
    " M canales.csv\n",
    " D total.py\n",
    " M README.md\n",
    "?? utils/\n",
    "\n",
    "# y ahora nada funciona. que cambio exactamente?\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Seis archivos tocados y ningún punto de retorno 😵\n",
    "\n",
    "Cuando el cambio lo escribes tú, vas viendo. Cuando lo escribe una IA, llega todo junto y en un minuto, y si no funcionaba desde antes ya no sabes qué parte lo rompió.\n",
    "\n",
    "Lo que convierte eso en algo manejable es un commit **antes** de pedirle nada. Con eso, `git diff` te muestra exactamente lo que hizo y `git restore` te devuelve el estado anterior entero.\n",
    "\n",
    "Es la costumbre más barata que conozco: diez segundos de commit para poder aceptar cambios grandes sin miedo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Vas a pedirle a una IA que reorganice tu proyecto. ¿Qué haces antes?\n",
    "\n",
    "a) Un commit, aunque sea con mensaje de \"antes de tocar\"\n",
    "\n",
    "b) Una copia de la carpeta en el escritorio\n",
    "\n",
    "c) Nada, si sale mal deshago con el editor\n",
    "\n",
    "d) Subo el proyecto a GitHub\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Funciona a lo bruto, pero después hay que comparar carpetas a mano en vez de leer un diff.\n",
    "\n",
    "*c)* Deshacer del editor no llega a los archivos que la herramienta creó o borró.\n",
    "\n",
    "*d)* Ayuda a tener copia, pero no te da el punto de comparación local que necesitas para revisar el cambio.\n",
    "\n",
    "Diez segundos de commit compran todo el capítulo 🧷"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Deja el punto de retorno\n",
    "\n",
    "Crea el proyecto del catálogo y guárdalo antes de pedir\n",
    "nada."
   ]
  },
  {
   "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",
    "printf 'import csv\\n' > reporte.py\n",
    "git add .\n",
    "git commit -q -m \"Primera version del catalogo de bodegas\"\n",
    "git status --short\n",
    "echo \"punto de retorno listo\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "punto de retorno listo\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con eso ya puedes aceptar cambios grandes sin miedo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Simula el cambio grande\n",
    "\n",
    "Tres archivos tocados de golpe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'import csv\\n\\ndef bodegas_por_ciudad(ruta):\\n    pass\\n' > reporte.py\n",
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "printf 'Minimarket Sol,Trujillo\\n' >> bodegas.csv\n",
    "git status --short"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    " M bodegas.csv\n",
    " M reporte.py\n",
    "?? canales.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos modificados y uno nuevo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Mira qué cambió de verdad\n",
    "\n",
    "El resumen y después el detalle de los datos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git diff --stat\n",
    "git diff bodegas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    " bodegas.csv | 1 +\n",
    " reporte.py  | 3 +++\n",
    " 2 files changed, 4 insertions(+)\n",
    "diff --git a/bodegas.csv b/bodegas.csv\n",
    "index 9fe9605..d3027d2 100644\n",
    "--- a/bodegas.csv\n",
    "+++ b/bodegas.csv\n",
    "@@ -1,2 +1,3 @@\n",
    " bodega,ciudad\n",
    " Bodega Inti,Cusco\n",
    "+Minimarket Sol,Trujillo\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una línea agregada al CSV. Ahora sí puedes decidir 👀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Acepta una parte\n",
    "\n",
    "Quédate con el código y descarta el cambio en los datos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git add reporte.py canales.csv\n",
    "git restore bodegas.csv\n",
    "git status --short\n",
    "cat bodegas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "A  canales.csv\n",
    "M  reporte.py\n",
    "bodega,ciudad\n",
    "Bodega Inti,Cusco\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El minimarket se fue y el código nuevo se queda 🎯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Guárdalo diciendo cómo se hizo\n",
    "\n",
    "El cuerpo del commit es buen sitio para dejarlo dicho."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git commit -q -m \"El reporte agrupa las bodegas por ciudad\n",
    "\n",
    "Escrito con ayuda de una herramienta de IA y revisado antes de entrar.\"\n",
    "git log -1 --format=\"%s%n%n%b\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "El reporte agrupa las bodegas por ciudad\n",
    "\n",
    "Escrito con ayuda de una herramienta de IA y revisado antes de entrar.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dentro de un año, esa línea explica por qué el estilo del archivo cambió."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Deshaz un cambio entero\n",
    "\n",
    "Vuelve al punto anterior, incluidos los archivos que no\n",
    "conocía."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'propuesta que no sirve\\n' > reporte.py\n",
    "printf 'temporal\\n' > borrame.txt\n",
    "git status --short\n",
    "git restore .\n",
    "git clean -qfd\n",
    "git status --short\n",
    "cat reporte.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    " M reporte.py\n",
    "?? borrame.txt\n",
    "import csv\n",
    "\n",
    "def bodegas_por_ciudad(ruta):\n",
    "    pass\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`restore` para lo conocido, `clean` para lo nuevo 🔙"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Pide el diff de un archivo que no existe\n",
    "\n",
    "Comprueba qué pasa cuando el nombre está mal escrito."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git diff bodegas-2019.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "fatal: ambiguous argument 'bodegas-2019.csv': unknown revision or path not in the working tree.\n",
    "Use '--' to separate paths from revisions, like this:\n",
    "'git <command> [<revision>...] -- [<file>...]'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Git no compara contra nada inventado, y ese error es la señal de que te\n",
    "equivocaste de nombre 🛑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Commit antes de pedir, diff antes de aceptar. Quien firma el commit\n",
    "eres tú.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 30 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/git-con-ia/\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
}
