{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Escribir commits para tu yo del futuro\n",
    "\n",
    "Qué poner en el mensaje y cómo arreglar el último cuando sale mal\n",
    "\n",
    "Cuaderno de soluciones del capítulo 5 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/hacer-commits/\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": [
    "## El mensaje es la mitad del commit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El commit guarda el qué. El mensaje guarda el **por qué**, y esa\n",
    "es la parte que ningún programa puede reconstruir después.\n",
    "\n",
    "Armo el repositorio de la tienda y hago dos commits, uno con un mensaje malo\n",
    "y otro con uno bueno, para que veas la diferencia en la historia:"
   ]
  },
  {
   "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 \"cambios\"\n",
    "printf 'Arequipa,890\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se agregan las ventas de Arequipa del cierre de mes\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dentro de seis meses el primero no te dice nada. El segundo te dice qué\n",
    "buscar sin abrir un archivo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La regla que uso"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres cosas y ya está:\n",
    "\n",
    "- **Qué pasó, no qué tocaste.** \"Se corrige el IGV del reporte\",\n",
    "no \"modificado reporte.py\".\n",
    "\n",
    "- **Una idea por commit.** Si tienes que escribir \"y\" en el\n",
    "mensaje, probablemente son dos commits.\n",
    "\n",
    "- **Que se entienda solo.** Sin \"lo de ayer\" ni \"el arreglo que\n",
    "hablamos\", porque en seis meses eso no existe."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cuando el mensaje no cabe en una línea"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para lo complicado, un commit puede tener título y explicación. Se pasan dos\n",
    "`-m` y el segundo es el cuerpo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Trujillo,1450\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se agregan las ventas de Trujillo\" \\\n",
    "  -m \"Llegaron por WhatsApp del jefe de bodega y no por el sistema, asi que el monto esta sin IGV.\"\n",
    "git log -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El título es lo que se lee en la lista; el cuerpo lo lee quien necesita el\n",
    "detalle. Ahí es donde se escribe lo que no se puede deducir mirando el código 📝"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Arreglar el último commit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Te equivocaste en el mensaje o se te olvidó un archivo. Mientras\n",
    "**no lo hayas compartido**, el último commit se puede rehacer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git commit -q --amend -m \"Se agregan las ventas de Trujillo del cierre de mes\"\n",
    "git log --oneline -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fíjate en algo importante: **el hash cambió**. No corrigió el\n",
    "commit, hizo uno nuevo y tiró el viejo. Por eso la regla del \"mientras no lo\n",
    "hayas compartido\", que retomamos en el capítulo 10 cuando entren\n",
    "otras personas al proyecto."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El commit sin mensaje"
   ]
  },
  {
   "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",
    "printf 'Cusco,760\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -m \"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "Aborting commit due to empty commit message.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Git se niega. Es de las pocas veces que un programa te obliga a explicarte, y\n",
    "la verdad es que hace bien 🙃"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Vas a cerrar el día y guardas todo lo que tocaste desde la mañana en un solo commit, que es lo cómodo.\n",
    "\n",
    "```\n",
    "$ git add .\n",
    "$ git commit -m \"Avances del dia\"\n",
    "\n",
    "[main 5d9c2f1] Avances del dia\n",
    " 9 files changed, 340 insertions(+), 88 deletions(-)\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Ese commit no se puede deshacer nunca 🧨\n",
    "\n",
    "Ahí dentro está el arreglo del IGV, el reporte nuevo, dos pruebas y un cambio de configuración. El día que el IGV salga mal y quieras volver atrás, te llevas por delante las otras ocho cosas.\n",
    "\n",
    "Un commit es la unidad más chica que puedes deshacer. Si metes nueve cosas, la unidad más chica pasa a ser las nueve juntas, y ahí es donde **Git deja de servirte y pasa a ser un trámite**.\n",
    "\n",
    "Cuesta lo mismo hacer cuatro commits que uno: es el mismo trabajo repartido en cuatro `add`. Y el día que lo necesites vale oro."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "¿Cuál de estos mensajes sirve dentro de seis meses?\n",
    "\n",
    "a) actualización\n",
    "\n",
    "b) Se corrige el IGV mal calculado en el reporte de ventas\n",
    "\n",
    "c) arreglos varios\n",
    "\n",
    "d) cambios en ventas.csv\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* Ese mensaje no dice qué se actualizó ni por qué. Es el equivalente a no escribir nada, pero con más letras.\n",
    "\n",
    "*c)* La palabra varios es la que avisa de que ahí dentro hay más de una cosa. Si son varios, son varios commits.\n",
    "\n",
    "*d)* Que cambió ese archivo ya lo sabe Git, lo tiene guardado. Lo que Git no puede saber es por qué lo cambiaste.\n",
    "\n",
    "El mensaje guarda el porqué, que es lo único que no se puede reconstruir después 🧠"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Un commit con título y cuerpo\n",
    "\n",
    "Agrega el canal de cada venta y explica en el cuerpo de\n",
    "dónde salió el dato."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'canal\\nbodega\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Se agrega el catalogo de canales\" \\\n",
    "  -m \"Por ahora solo bodega. Minimarket y horeca entran cuando el area comercial confirme los nombres.\"\n",
    "git log -1 --format=\"%s%n%n%b\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Se agrega el catalogo de canales\n",
    "\n",
    "Por ahora solo bodega. Minimarket y horeca entran cuando el area comercial confirme los nombres.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El `%s` es el título y el `%b` el cuerpo. Poder pedir\n",
    "cada parte por separado es lo que hace útil la separación."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Corrige el mensaje del último\n",
    "\n",
    "Cámbiale el título al commit que acabas de hacer y\n",
    "comprueba que el hash es otro."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git commit -q --amend -m \"Se agrega el catalogo de canales de venta\"\n",
    "git log --oneline -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "4b9867f Se agrega el catalogo de canales de venta\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mensaje nuevo, hash nuevo. Es un commit distinto que reemplazó al anterior."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Mete un archivo que se te olvidó\n",
    "\n",
    "`--amend` no sirve solo para el mensaje. Agrega\n",
    "un archivo al último commit sin crear uno nuevo."
   ]
  },
  {
   "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 commit -q --amend --no-edit\n",
    "git show --stat --oneline HEAD"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "0e690f1 Se agrega el catalogo de canales de venta\n",
    " canales.csv  | 2 ++\n",
    " clientes.csv | 2 ++\n",
    " ventas.csv   | 1 +\n",
    " 3 files changed, 5 insertions(+)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El `--no-edit` significa \"no me preguntes el mensaje, deja el que\n",
    "había\". Ahora el commit lleva los dos archivos 📎"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Cuenta cuántos commits llevas\n",
    "\n",
    "Pide el número total de la historia."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git rev-list --count HEAD"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "4\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`rev-list` lista los commits sin adornos, y con `--count`\n",
    "los cuenta. Sirve para ver de un vistazo si un proyecto está vivo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Busca un commit por su mensaje\n",
    "\n",
    "Encuentra los commits que hablen de Arequipa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline --grep=\"Arequipa\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "92c3025 Se agregan las ventas de Arequipa del cierre de mes\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí se cobra todo el esfuerzo de escribir bien los mensajes: si dijeran\n",
    "\"cambios\", esta búsqueda no encontraría nada 🎯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Los mensajes a tu manera\n",
    "\n",
    "Muestra la historia con el formato que tú elijas: hash\n",
    "corto, mensaje y hace cuánto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --format=\"%h  %s\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "0e690f1  Se agrega el catalogo de canales de venta\n",
    "f2d11f7  Se agregan las ventas de Trujillo del cierre de mes\n",
    "92c3025  Se agregan las ventas de Arequipa del cierre de mes\n",
    "edf24eb  cambios\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los códigos con porcentaje son el idioma de `git log`. Con\n",
    "`%h`, `%s`, `%an` y `%ar` te alcanza\n",
    "para casi todo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. El commit que Git no deja hacer\n",
    "\n",
    "Intenta guardar con un mensaje vacío."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Piura,540\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -m \"   \""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Aborting commit due to empty commit message.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ni con espacios. Git los recorta y se sigue negando, que es exactamente lo\n",
    "que uno necesita a las once de la noche 😅"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**El mensaje lo escribes para quien va a leerlo dentro de seis meses,\n",
    "y esa persona eres tú.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Qué es un commit en Git?Una foto de tus archivos en un momento, con un mensaje que dice qué cambió y por qué. La historia del proyecto es la fila de esas fotos.\n",
    "\n",
    "¿Cómo se hace un commit?Primero git add para elegir qué entra, después git commit con el mensaje. Son dos pasos a propósito: no todo lo que tocaste tiene que entrar junto.\n",
    "\n",
    "¿Cómo se escribe un buen mensaje de commit?Que diga qué cambia y por qué, no qué archivos tocaste. \"Arregla el total que salía sin IGV\" sirve; \"cambios\" no sirve para nada.\n",
    "\n",
    "¿Cómo deshago el último commit?Depende de si ya lo subiste. Si no, git reset lo deshace. Si sí, git revert crea un commit que lo anula, que es lo correcto porque no reescribe historia que ya vio otra gente."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 5 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/hacer-commits/\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
}
