{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Marcar versiones\n",
    "\n",
    "Etiquetas, releases y por qué una versión publicada no se mueve\n",
    "\n",
    "Cuaderno de soluciones del capítulo 24 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/marcar-versiones/\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": [
    "Un hash como `54da6e2` identifica un momento perfectamente, pero\n",
    "no se puede decir por teléfono. Las etiquetas son eso: ponerle a un commit un\n",
    "nombre que un humano pueda repetir 🏷️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué numero poner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La costumbre más extendida son tres números, `1.4.2`, y cada uno\n",
    "dice algo:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Número | Se sube cuando | Ejemplo |\n",
    "|---|---|---|\n",
    "| El primero | Algo cambia y rompe lo anterior | El reporte ya no acepta el CSV viejo |\n",
    "| El segundo | Hay algo nuevo que no rompe nada | Se agrega el corte por canal |\n",
    "| El tercero | Solo se arregló un error | Se corrige el total de Arequipa |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "No hay policía de versiones: si tu proyecto es tuyo, numera como quieras.\n",
    "Pero si otra gente lo usa, esta convención les dice de un vistazo si actualizar\n",
    "es seguro 🚦"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Poner la primera"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "mkdir reporte-ventas\n",
    "cd reporte-ventas\n",
    "git init -q\n",
    "printf 'ciudad,monto\\nLima,1200\\nArequipa,890\\n' > ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Primeras ventas de Lima y Arequipa\"\n",
    "git tag -a v1.0 -m \"Primera version del reporte por ciudad\"\n",
    "git tag\n",
    "git log --oneline --decorate -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese `-a` hace la etiqueta **anotada**, que guarda\n",
    "quién la puso, cuándo y por qué. Sin él sale una etiqueta ligera, que es solo un\n",
    "apodo. Para versiones que se publican, siempre anotada 📌"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git show v1.0 --stat"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Seguir trabajando y marcar otra"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Se agrega el corte por canal\"\n",
    "git tag -a v1.1 -m \"Entra el corte por canal\"\n",
    "printf 'Horeca,3100\\n' >> canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Entra el canal horeca\"\n",
    "git tag\n",
    "git log --oneline --decorate"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las etiquetas se quedan pegadas a su commit aunque la historia siga\n",
    "creciendo. Por eso sirven 📍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dónde estoy respecto a la última versión"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git describe --tags"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Léelo así: estás en la `v1.1`, más un commit, y el hash del que\n",
    "estás. Es lo que se pone en un pie de página o en un\n",
    "`--version` para saber exactamente qué está corriendo 🧭"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Volver a una versión para mirarla"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q --detach v1.0\n",
    "ls\n",
    "cat ventas.csv\n",
    "git switch -q -\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En `v1.0` todavía no existía `canales.csv`. Miraste el\n",
    "pasado y volviste, sin tocar nada 🕰️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Subirlas, que es el paso que se olvida"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`git push` a secas **no lleva las etiquetas**. Hay\n",
    "que pedirlo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ..\n",
    "git init -q --bare servidor.git\n",
    "cd reporte-ventas\n",
    "git remote add origin ../servidor.git\n",
    "git push -q -u origin main\n",
    "git ls-remote --tags origin\n",
    "echo \"(vacio: el push no se llevo las etiquetas)\"\n",
    "git push -q origin --tags\n",
    "git ls-remote --tags origin"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí están. Es el olvido más común del capítulo: la versión marcada en tu\n",
    "máquina y nadie más la ve 🙈"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué es un release entonces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un release es de GitHub, no de Git: es una etiqueta **más** una\n",
    "página con notas de la versión y archivos para descargar. Se crea desde\n",
    "*Releases > Draft a new release* eligiendo una etiqueta que ya\n",
    "exista.\n",
    "\n",
    "O sea que el orden es: etiqueta primero, release después. Y lo que hace\n",
    "valioso a un release son las notas: qué cambió, qué se rompe y qué hay que\n",
    "hacer para actualizar 📝"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Marcas la versión 1.0 del reporte, la anuncias, y descubres un error de dedo. Lo arreglas y mueves la etiqueta al commit nuevo.\n",
    "\n",
    "```\n",
    "$ git tag -f v1.0\n",
    "Updated tag 'v1.0' (was 3c1f80a)\n",
    "$ git push -f origin v1.0\n",
    "\n",
    "# y en la maquina de tu compañera, que ya la tenia:\n",
    "$ git describe --tags\n",
    "v1.0        # pero apuntando a otro commit\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Ahora hay dos versiones 1.0 distintas en el mundo 😵\n",
    "\n",
    "Una etiqueta es una promesa: \"v1.0 es exactamente este contenido\". Quien ya se la bajó no la vuelve a traer, porque Git supone que una etiqueta no cambia. O sea que su v1.0 y la tuya son cosas distintas con el mismo nombre.\n",
    "\n",
    "Cuando eso pasa, depurar es imposible: dos personas ejecutan \"la misma versión\" y obtienen resultados diferentes.\n",
    "\n",
    "La regla: **una etiqueta publicada no se mueve**. Si salió con un error, se saca una v1.0.1 y se sigue hacia adelante. Sale más barato que explicarle a cada uno cuál v1.0 tiene."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Publicaste la v1.0 y descubres un error. ¿Qué haces?\n",
    "\n",
    "a) Muevo la etiqueta v1.0 al commit arreglado\n",
    "\n",
    "b) Saco una v1.0.1 con el arreglo\n",
    "\n",
    "c) Borro la v1.0 y la vuelvo a crear\n",
    "\n",
    "d) Aviso por el chat de que la v1.0 cambió\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* Quien ya la bajó no la vuelve a traer, así que quedarían dos v1.0 distintas circulando.\n",
    "\n",
    "*c)* Es lo mismo que moverla, con un paso más. El problema sigue siendo que ya está en máquinas ajenas.\n",
    "\n",
    "*d)* Los avisos no llegan a todo el mundo, y las máquinas no leen el chat.\n",
    "\n",
    "Una etiqueta publicada es una promesa. Se avanza, no se reescribe 🏷️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Marca la primera versión\n",
    "\n",
    "Crea el proyecto del catálogo y ponle una etiqueta\n",
    "anotada."
   ]
  },
  {
   "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",
    "git add bodegas.csv\n",
    "git commit -q -m \"Catalogo inicial de bodegas\"\n",
    "git tag -a v1.0 -m \"Primera version del catalogo\"\n",
    "git tag\n",
    "git log --oneline --decorate -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "v1.0\n",
    "a9874ae (HEAD -> main, tag: v1.0) Catalogo inicial de bodegas\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La etiqueta aparece al lado del hash."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Mira qué guarda la etiqueta\n",
    "\n",
    "Una etiqueta anotada guarda autor, fecha y mensaje."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git show v1.0 --stat"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "tag v1.0\n",
    "Tagger: Miss Yera <hola@missyera.com>\n",
    "Date:   Thu Jan 15 09:00:00 2026 -0500\n",
    "\n",
    "Primera version del catalogo\n",
    "\n",
    "commit a9874ae5ade9ebff480ec9f29629b1b651573849\n",
    "Author: Miss Yera <hola@missyera.com>\n",
    "Date:   Thu Jan 15 09:00:00 2026 -0500\n",
    "\n",
    "    Catalogo inicial de bodegas\n",
    "\n",
    " bodegas.csv | 2 ++\n",
    " 1 file changed, 2 insertions(+)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Todo eso es lo que no tendrías con una etiqueta ligera."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Avanza y marca la siguiente\n",
    "\n",
    "Agrega los minimarkets y saca la v1.1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Minimarket Sol,Trujillo\\n' >> bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"Entra el minimarket de Trujillo\"\n",
    "git tag -a v1.1 -m \"Entran los minimarkets\"\n",
    "git tag\n",
    "git log --oneline --decorate"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "v1.0\n",
    "v1.1\n",
    "4cb519a (HEAD -> main, tag: v1.1) Entra el minimarket de Trujillo\n",
    "a9874ae (tag: v1.0) Catalogo inicial de bodegas\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos versiones marcadas y cada una en su commit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Averigua dónde estás\n",
    "\n",
    "Un commit después de la última versión."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Mayorista Norte,Piura\\n' >> bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"Entra el mayorista de Piura\"\n",
    "git describe --tags"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "v1.1-1-gb9f725d\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La versión, cuántos commits llevas encima y el hash 🧭"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Vuelve a mirar la v1.0\n",
    "\n",
    "Ve al pasado y regresa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q --detach v1.0\n",
    "cat bodegas.csv\n",
    "git switch -q -\n",
    "cat bodegas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "bodega,ciudad\n",
    "Bodega Inti,Cusco\n",
    "bodega,ciudad\n",
    "Bodega Inti,Cusco\n",
    "Minimarket Sol,Trujillo\n",
    "Mayorista Norte,Piura\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una sola bodega en la v1.0, tres al volver. Ese es el valor de marcar 🕰️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Súbelas al servidor\n",
    "\n",
    "Comprueba que el push normal no las lleva y mándalas\n",
    "aparte."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ..\n",
    "git init -q --bare catalogo-servidor.git\n",
    "cd catalogo\n",
    "git remote add origin ../catalogo-servidor.git\n",
    "git push -q -u origin main\n",
    "git ls-remote --tags origin\n",
    "echo \"(vacio)\"\n",
    "git push -q origin --tags\n",
    "git ls-remote --tags origin"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "(vacio)\n",
    "419ab5b7a0a3013255e90441edc5eaa0cff0c009\trefs/tags/v1.0\n",
    "a9874ae5ade9ebff480ec9f29629b1b651573849\trefs/tags/v1.0^{}\n",
    "5a7e66660e45be3d87f34c46a36b17047428c670\trefs/tags/v1.1\n",
    "4cb519af668b35459949b56f5823d8dc0c4b868a\trefs/tags/v1.1^{}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes nada, después las dos. Es el paso que más se olvida 🙈"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Intenta crear una etiqueta que ya existe\n",
    "\n",
    "Prueba a poner otra vez la v1.0."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git tag -a v1.0 -m \"Otra vez la primera\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "fatal: tag 'v1.0' already exists\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Git no deja pisar una etiqueta sin que se lo pidas a propósito, y esa\n",
    "negativa es justo la que protege a quien ya se la bajó 🛡️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Una etiqueta anotada por versión, se sube con\n",
    "`--tags`, y una vez publicada no se mueve.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 24 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/marcar-versiones/\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
}
