{
 "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 práctica 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",
    "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: \"djEuMAphOTg3NGFlIChIRUFEIC0+IG1haW4sIHRhZzogdjEuMCkgQ2F0YWxvZ28gaW5pY2lhbCBkZSBib2RlZ2Fz\",\n",
    "    2: \"dGFnIHYxLjAKVGFnZ2VyOiBNaXNzIFllcmEgPGhvbGFAbWlzc3llcmEuY29tPgpEYXRlOiAgIFRodSBKYW4gMTUgMDk6MDA6MDAgMjAyNiAtMDUwMAoKUHJpbWVyYSB2ZXJzaW9uIGRlbCBjYXRhbG9nbwoKY29tbWl0IGE5ODc0YWU1YWRlOWViZmY0ODBlYzlmMjk2MjliMWI2NTE1NzM4NDkKQXV0aG9yOiBNaXNzIFllcmEgPGhvbGFAbWlzc3llcmEuY29tPgpEYXRlOiAgIFRodSBKYW4gMTUgMDk6MDA6MDAgMjAyNiAtMDUwMAoKICAgIENhdGFsb2dvIGluaWNpYWwgZGUgYm9kZWdhcwoKIGJvZGVnYXMuY3N2IHwgMiArKwogMSBmaWxlIGNoYW5nZWQsIDIgaW5zZXJ0aW9ucygrKQ==\",\n",
    "    3: \"djEuMAp2MS4xCjRjYjUxOWEgKEhFQUQgLT4gbWFpbiwgdGFnOiB2MS4xKSBFbnRyYSBlbCBtaW5pbWFya2V0IGRlIFRydWppbGxvCmE5ODc0YWUgKHRhZzogdjEuMCkgQ2F0YWxvZ28gaW5pY2lhbCBkZSBib2RlZ2Fz\",\n",
    "    4: \"djEuMS0xLWdiOWY3MjVk\",\n",
    "    5: \"Ym9kZWdhLGNpdWRhZApCb2RlZ2EgSW50aSxDdXNjbwpib2RlZ2EsY2l1ZGFkCkJvZGVnYSBJbnRpLEN1c2NvCk1pbmltYXJrZXQgU29sLFRydWppbGxvCk1heW9yaXN0YSBOb3J0ZSxQaXVyYQ==\",\n",
    "    6: \"KHZhY2lvKQo0MTlhYjViN2EwYTMwMTMyNTVlOTA0NDFlZGM1ZWFhMGNmZjBjMDA5CXJlZnMvdGFncy92MS4wCmE5ODc0YWU1YWRlOWViZmY0ODBlYzlmMjk2MjliMWI2NTE1NzM4NDkJcmVmcy90YWdzL3YxLjBee30KNWE3ZTY2NjYwZTQ1YmUzZDg3ZjM0YzQ2YTM2YjE3MDQ3NDI4YzY3MAlyZWZzL3RhZ3MvdjEuMQo0Y2I1MTlhZjY2OGIzNTQ1OTk0OWI1NmY1ODIzZDhkYzBjNGI4NjhhCXJlZnMvdGFncy92MS4xXnt9\",\n",
    "    7: \"ZmF0YWw6IHRhZyAndjEuMCcgYWxyZWFkeSBleGlzdHM=\",\n",
    "}, 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?** 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",
    "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ó"
   ]
  },
  {
   "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": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "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": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "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": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "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": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "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": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "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": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "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": [
    "%%revisa 7\n",
    "# tu turno"
   ]
  },
  {
   "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
}
