{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Qué hay dentro de la carpeta oculta\n",
    "\n",
    "Cómo Git guarda todo, para que deje de parecer magia\n",
    "\n",
    "Cuaderno de práctica del capítulo 29 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/dentro-de-git/\n",
    "\n",
    "Los ejercicios están al final y traen una celda vacía debajo de cada uno. Las\n",
    "respuestas viven en el cuaderno de soluciones, y merece la pena pelearse un\n",
    "rato antes de abrirlo 💛"
   ]
  },
  {
   "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({\n",
    "    1: \"Q09NTUlUX0VESVRNU0cKSEVBRApicmFuY2hlcwpjb25maWcKZGVzY3JpcHRpb24KaG9va3MKaW5kZXgKaW5mbwpsb2dzCm9iamVjdHMKcmVmcw==\",\n",
    "    2: \"cmVmOiByZWZzL2hlYWRzL21haW4KYTk4NzRhZTVhZGU5ZWJmZjQ4MGVjOWYyOTYyOWIxYjY1MTU3Mzg0OQphOTg3NGFlIENhdGFsb2dvIGluaWNpYWwgZGUgYm9kZWdhcw==\",\n",
    "    3: \"YTc1ZWUyZjljNzU3NWVhZGUxNjhjMTE4NTg4NTE2OTE4YTdiNzY0ZQphNzVlZTJmOWM3NTc1ZWFkZTE2OGMxMTg1ODg1MTY5MThhN2I3NjRlCmZmNzY5NDM1YWU4NDhkMTkyMGI0M2M2ZWZiZjM2YWYzMzFhNzIwYWI=\",\n",
    "    4: \"Y29tbWl0CnRyZWUgNGZkODljODBjZGVkMjk2NTVhZmVlZTkxYjFmYTgzYzgwZDk3ODUzZAphdXRob3IgTWlzcyBZZXJhIDxob2xhQG1pc3N5ZXJhLmNvbT4gMTc2ODQ4NTYwMCAtMDUwMApjb21taXR0ZXIgTWlzcyBZZXJhIDxob2xhQG1pc3N5ZXJhLmNvbT4gMTc2ODQ4NTYwMCAtMDUwMAoKQ2F0YWxvZ28gaW5pY2lhbCBkZSBib2RlZ2Fz\",\n",
    "    5: \"MTAwNjQ0IGJsb2IgOWZlOTYwNTkyZjcxNGQyOTUyMjhhN2Q2MWU4ODZiNjMxYjZkOGMxMglib2RlZ2FzLmNzdgpib2RlZ2EsY2l1ZGFkCkJvZGVnYSBJbnRpLEN1c2Nv\",\n",
    "    6: \"Y291bnQ6IDYKc2l6ZTogMjQKaW4tcGFjazogMApwYWNrczogMApzaXplLXBhY2s6IDAKcHJ1bmUtcGFja2FibGU6IDAKZ2FyYmFnZTogMApzaXplLWdhcmJhZ2U6IDAKY291bnQ6IDAKc2l6ZTogMAppbi1wYWNrOiA2CnBhY2tzOiAxCnNpemUtcGFjazogMQpwcnVuZS1wYWNrYWJsZTogMApnYXJiYWdlOiAwCnNpemUtZ2FyYmFnZTogMA==\",\n",
    "    7: \"ZmF0YWw6IE5vdCBhIHZhbGlkIG9iamVjdCBuYW1lIDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE=\",\n",
    "}, lenguaje=\"bash\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Llevas veintiocho capítulos usando Git. Este es el capítulo donde miramos\n",
    "debajo, no porque haga falta, sino porque lo que se entiende deja de dar\n",
    "miedo 🔦"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La carpeta"
   ]
  },
  {
   "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",
    "ls -a\n",
    "ls .git"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso es todo tu repositorio. Si copias esa carpeta a otro sitio, te llevas la\n",
    "historia completa; si la borras, tu proyecto vuelve a ser una carpeta normal.\n",
    "Es lo que pasó en la trampa de la portada 💀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Una rama es un archivo de texto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esto es lo que más sorprende:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cat .git/HEAD\n",
    "cat .git/refs/heads/main\n",
    "git log --oneline -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`HEAD` dice en qué rama estás. La rama es un archivo con\n",
    "**un hash y nada más**. Por eso crear una rama es instantáneo\n",
    "aunque el proyecto pese gigas: se escribe un archivo de 41 caracteres 🪶"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c reporte-por-canal\n",
    "cat .git/HEAD\n",
    "ls .git/refs/heads\n",
    "git switch -q main\n",
    "cat .git/HEAD"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El hash sale del contenido"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esta es la idea de la que cuelga todo lo demás. El nombre de cada cosa\n",
    "guardada se calcula a partir de lo que contiene:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'ciudad,monto\\nLima,1200\\n' | git hash-object --stdin\n",
    "printf 'ciudad,monto\\nLima,1200\\n' | git hash-object --stdin\n",
    "printf 'ciudad,monto\\nLima,1290\\n' | git hash-object --stdin"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El mismo contenido da el mismo nombre, siempre y en cualquier máquina del\n",
    "mundo. Cambia un dígito y el nombre es otro completamente distinto 🧬\n",
    "\n",
    "De ahí salen dos cosas importantes: que Git guarde una sola vez un archivo\n",
    "repetido en veinte carpetas, y que no se pueda cambiar el pasado sin que se\n",
    "note, porque cambiar un commit viejo cambiaría su hash y el de todos los que\n",
    "vienen detrás 🔗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Los tres tipos de cosa que guarda"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Se llama | Guarda |\n",
    "|---|---|\n",
    "| `blob` | El contenido de un archivo, sin su nombre |\n",
    "| `tree` | Una carpeta: qué nombres hay y a qué blob apunta cada uno |\n",
    "| `commit` | Un tree, su padre, el autor, la fecha y el mensaje |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git cat-file -t HEAD\n",
    "git cat-file -p HEAD"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está un commit entero, por dentro. Un `tree`, un autor, un\n",
    "committer y el mensaje. Nada más 📄"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git cat-file -p HEAD^{tree}\n",
    "git cat-file -p $(git rev-parse HEAD:ventas.csv)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El árbol dice \"aquí hay un archivo llamado `ventas.csv` cuyo\n",
    "contenido es este blob\", y el blob es el contenido. Nombre y contenido\n",
    "separados: por eso renombrar un archivo no duplica nada 🪄"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Por qué el primer commit no tiene padre"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Arequipa,890\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Entran las ventas de Arequipa\"\n",
    "git cat-file -p HEAD | head -3\n",
    "git cat-file -p HEAD~1 | head -3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El segundo commit tiene una línea `parent` y el primero no. Esa\n",
    "cadena de padres **es** la historia: no hay una lista guardada en\n",
    "ningún lado, cada commit apunta al anterior 🔗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cuando pesa de más"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git count-objects -v\n",
    "git gc -q\n",
    "git count-objects -v"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`gc` agrupa y comprime los objetos sueltos. Git lo corre solo de\n",
    "vez en cuando, y es lo que hay que usar cuando la carpeta pesa, en vez de\n",
    "borrar nada a mano 🧹"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Tu repositorio pesa mucho, entras a la carpeta `.git` a ver qué hay y borras lo que te parece basura.\n",
    "\n",
    "```\n",
    "$ du -sh .git\n",
    "48M\t.git\n",
    "\n",
    "$ rm -rf .git/objects/pack\n",
    "$ git status\n",
    "\n",
    "error: object file .git/objects/pack/pack-3c1f80a.pack is empty\n",
    "fatal: loose object 8a2b19c is corrupt\n",
    "```\n",
    "\n",
    "**¿Qué está mal?** La respuesta está en el cuaderno de soluciones. Míralo tú primero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Creas una rama nueva en un proyecto de 3 GB. ¿Qué escribe Git en el disco?\n",
    "\n",
    "a) Un archivo de texto con un hash dentro\n",
    "\n",
    "b) Una copia del proyecto entero\n",
    "\n",
    "c) Nada, la rama solo existe en memoria\n",
    "\n",
    "d) Una copia de los archivos que cambiaron"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Mira dentro de la carpeta\n",
    "\n",
    "Crea el catálogo y lista qué hay en `.git`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Lee la rama y HEAD\n",
    "\n",
    "Los dos son archivos de texto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Comprueba que el hash sale del contenido\n",
    "\n",
    "El mismo texto da siempre el mismo nombre."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Abre un commit por dentro\n",
    "\n",
    "Qué guarda exactamente un commit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Baja hasta el contenido del archivo\n",
    "\n",
    "Del commit al árbol, y del árbol al contenido."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Cuenta y compacta\n",
    "\n",
    "Cuántos objetos hay sueltos y qué pasa al compactar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Pide un objeto que no existe\n",
    "\n",
    "Un hash inventado."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 7\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Una rama es un archivo con un hash. El hash sale del contenido. Y\n",
    "dentro de `.git` se mira, no se toca.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 29 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/dentro-de-git/\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
}
