{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Leer la historia sin abrir un archivo\n",
    "\n",
    "Buscar en el pasado del proyecto y saber quién escribió cada línea\n",
    "\n",
    "Cuaderno de soluciones del capítulo 6 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/leer-la-historia/\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": [
    "## Preparo una historia para husmear"
   ]
  },
  {
   "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",
    "printf 'bodega,ciudad\\nBodega Sol,Lima\\n' > clientes.csv\n",
    "git add clientes.csv\n",
    "git commit -q -m \"Se agrega el catalogo de clientes\"\n",
    "printf 'Trujillo,1450\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se agregan las ventas de Trujillo\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Filtrar, que es de lo que se trata"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un proyecto real tiene miles de commits, así que el log casi nunca se lee\n",
    "entero. Se filtra.\n",
    "\n",
    "Por lo que dice el mensaje:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline --grep=\"ventas\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Por el archivo que tocaron:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline -- clientes.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese guion doble separa los commits de los archivos. Sin él, Git no sabe si\n",
    "`clientes.csv` es una rama o un archivo 📁\n",
    "\n",
    "Y los últimos, que es lo que más se usa:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline -2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ver un momento por dentro"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`git show` abre un commit y te enseña qué cambió en él:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git show --stat --oneline HEAD~1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El `--stat` resume; sin él te muestra las líneas una por una. Y\n",
    "`HEAD~1` es \"uno antes del actual\", que ya usamos en el capítulo\n",
    "1."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quién escribió esta línea"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este es el comando que más me sorprendió cuando lo descubrí:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git blame ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada línea del archivo, con el commit del que salió y quién la escribió.\n",
    "Cuando entras a un proyecto que no conoces y encuentras algo raro, esto te lleva\n",
    "directo al momento en que se puso ahí, y al mensaje que lo explica 🔦"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error de pedir 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 show inventado123"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "fatal: ambiguous argument 'inventado123': 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": [
    "*Ambiguous* porque Git no sabe si le pediste un commit o un archivo, y\n",
    "no encontró ninguno de los dos. De ahí sale el consejo del guion doble que te\n",
    "da en el propio mensaje."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Te toca revisar por qué el reporte de ventas de Lima salió mal el mes pasado. Miras el log del proyecto y ves solo tres commits en todo el mes, así que concluyes que casi nadie tocó nada.\n",
    "\n",
    "```\n",
    "$ git log --oneline\n",
    "\n",
    "4f2a91c Se corrige el reporte\n",
    "8c1b03e Ajustes\n",
    "2d9e77a Actualizacion mensual\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Estás viendo **una sola rama** 🌳\n",
    "\n",
    "`git log` a secas muestra la historia de donde estás parada, no la del proyecto. Los otros cuarenta commits del mes pueden estar en ramas que todavía no se juntaron con esta, o en el servidor sin que te los hayas traído.\n",
    "\n",
    "Se ve todo con `git log --all --oneline --graph`, que dibuja las ramas y no se salta ninguna.\n",
    "\n",
    "Y hay un segundo motivo para no fiarse: si nadie se trajo lo último del servidor, tu copia está vieja aunque el log se vea completo. Eso lo arreglamos en el capítulo de GitHub."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Quieres saber en qué commit se rompió una línea concreta de un archivo. ¿Con qué empiezas?\n",
    "\n",
    "a) `git log` y vas abriendo commits uno por uno\n",
    "\n",
    "b) `git blame` sobre ese archivo\n",
    "\n",
    "c) `git status`\n",
    "\n",
    "d) Abrir el archivo y buscar a ojo\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* Funciona, pero puede llevarte cien commits. Hay un comando que te lleva directo a la línea.\n",
    "\n",
    "*c)* El estado te dice qué hay pendiente ahora mismo, no tiene nada que ver con la historia.\n",
    "\n",
    "*d)* Eso te dice qué dice la línea hoy, que ya lo sabes. Lo que buscas es cuándo se puso así y por qué.\n",
    "\n",
    "blame no es para culpar, es para encontrar el mensaje que explica esa línea 🔍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Los commits de un solo archivo\n",
    "\n",
    "Pide la historia de `ventas.csv` nada más."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline -- ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "5ff7017 Se agregan las ventas de Trujillo\n",
    "571877b Se agregan las ventas de Arequipa\n",
    "54da6e2 Primeras ventas de Lima\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres de los cuatro commits tocaron ese archivo. Filtrar por archivo es lo\n",
    "primero que hago cuando algo falla en un sitio concreto."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Qué entró exactamente en el último commit\n",
    "\n",
    "Muestra las líneas que cambiaron, no el resumen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git show HEAD -- ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "commit 5ff7017a42f7ff951f3f95007caca474c3e95188\n",
    "Author: Miss Yera <hola@missyera.com>\n",
    "Date:   Thu Jan 15 09:00:00 2026 -0500\n",
    "\n",
    "    Se agregan las ventas de Trujillo\n",
    "\n",
    "diff --git a/ventas.csv b/ventas.csv\n",
    "index 0c80877..2961128 100644\n",
    "--- a/ventas.csv\n",
    "+++ b/ventas.csv\n",
    "@@ -1,3 +1,4 @@\n",
    " ciudad,monto\n",
    " Lima,1200\n",
    " Arequipa,890\n",
    "+Trujillo,1450\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La línea con `+` es lo que entró. Así se lee un cambio sin abrir\n",
    "el archivo ni compararlo con nada 👀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. La historia con su dibujo\n",
    "\n",
    "Pide el log con el gráfico de ramas, aunque todavía haya\n",
    "una sola."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --all --oneline --graph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "* 5ff7017 Se agregan las ventas de Trujillo\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 asteriscos son los commits. Cuando aparezcan ramas en el capítulo\n",
    "10, este dibujo se vuelve mucho más interesante."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Cuántas líneas cambió cada commit\n",
    "\n",
    "Pide la historia con el resumen de líneas de cada uno."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline --stat -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "5ff7017 Se agregan las ventas de Trujillo\n",
    " ventas.csv | 1 +\n",
    " 1 file changed, 1 insertion(+)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un commit que toca veinte archivos y trescientas líneas es una señal de\n",
    "alarma: casi siempre son varias cosas metidas juntas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Busca por lo que NO dice\n",
    "\n",
    "Encuentra los commits cuyo mensaje no mencione ventas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline --invert-grep --grep=\"ventas\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "e4eb86b Se agrega el catalogo de clientes\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El `--invert-grep` da la vuelta al filtro. Suena raro y sirve\n",
    "muchísimo para encontrar lo que se escapó de una convención."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Quién escribió el catálogo de clientes\n",
    "\n",
    "Pide el blame del CSV de clientes con el formato corto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git blame --line-porcelain clientes.csv | grep \"^author \" | sort -u"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "author Miss Yera\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En un proyecto con varias personas esto te dice a quién preguntarle. En uno\n",
    "tuyo te dice si fue tu yo de hace un año 😄"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Pide un archivo que no está en ese commit\n",
    "\n",
    "Intenta ver `clientes.csv` en el primer commit,\n",
    "cuando todavía no existía."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git show HEAD~3:clientes.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "fatal: path 'clientes.csv' exists on disk, but not in 'HEAD~3'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El mensaje es exacto: el archivo no existe **en ese momento**.\n",
    "Y eso es información: te acabas de enterar de que se creó después."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**La historia se filtra, no se lee entera. Y blame no es para culpar,\n",
    "es para encontrar el porqué.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 6 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/leer-la-historia/\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
}
