{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Proponer un cambio en el proyecto de otra persona\n",
    "\n",
    "Qué es un fork, qué es un pull request y por qué no da miedo\n",
    "\n",
    "Cuaderno de soluciones del capítulo 20 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/pull-request/\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": [
    "Aquí es donde Git deja de ser una herramienta personal. Un pull request es\n",
    "la forma en que millones de personas mejoran proyectos que no son suyos, y\n",
    "también es la forma en que trabaja cualquier equipo por dentro 🤝\n",
    "\n",
    "Y la palabra asusta más que la cosa. Traducida es: *\"te dejé unos\n",
    "cambios listos, ¿te los llevas?\"*."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fork y clone no son lo mismo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "|  | Fork | Clone |\n",
    "|---|---|---|\n",
    "| Dónde queda la copia | En tu cuenta de GitHub | En tu computadora |\n",
    "| Es de | GitHub, no de Git | Git |\n",
    "| Puedes subir cambios | Sí, es tuya | Solo si tienes permiso en el original |\n",
    "| Para qué | Proponer cambios en algo ajeno | Trabajar en tu máquina |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El orden real es: **fork primero, clone después**. Clonas tu\n",
    "copia, no la original 🍴"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Los cuatro pasos, con la parte de Git ejecutada"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El fork es un botón en GitHub. Todo lo demás es esto, y lo vamos a hacer con\n",
    "el servidor de práctica del capítulo 12: uno hace de\n",
    "proyecto original y otro de tu bifurcación."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "mkdir original\n",
    "cd original\n",
    "git init -q\n",
    "printf 'ciudad,monto\\nLima,1200\\nArequipa,890\\n' > ventas.csv\n",
    "printf '# Ventas por ciudad\\n' > README.md\n",
    "git add .\n",
    "git commit -q -m \"Primera version del reporte de ventas\"\n",
    "cd ..\n",
    "git clone -q --bare original original-servidor.git\n",
    "git clone -q --bare original mi-fork.git\n",
    "git clone -q mi-fork.git mi-copia\n",
    "cd mi-copia\n",
    "git remote add upstream ../original-servidor.git\n",
    "git remote -v"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos remotos y esto es la costumbre del mundo entero: `origin` es\n",
    "tu bifurcación, donde sí puedes subir, y `upstream` es el proyecto\n",
    "original, de donde traes las novedades 🔀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Una rama por propuesta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este es el paso que la gente se salta y el que más problemas causa después:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c agrega-cusco\n",
    "printf 'Cusco,760\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se agregan las ventas de Cusco\"\n",
    "git branch\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El nombre de la rama importa porque va a ser el título por defecto de tu\n",
    "propuesta. `agrega-cusco` se entiende; `cambios` o\n",
    "`arreglo2` no 🏷️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Subirla a tu bifurcación"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git push -q -u origin agrega-cusco\n",
    "git ls-remote --heads origin"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está tu rama en tu copia de GitHub. Al entrar a la web te va a salir\n",
    "sola una barra amarilla que dice *\"Compare & pull request\"*. Ese es\n",
    "el único clic obligatorio del capítulo 🖱️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué escribir en la propuesta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres cosas, cortas:\n",
    "\n",
    "- **Qué cambia**, en una frase.\n",
    "\n",
    "- **Por qué**, sobre todo si no es obvio.\n",
    "\n",
    "- **Cómo comprobarlo**, si hace falta ejecutar algo.\n",
    "\n",
    "Y si el proyecto tiene un archivo `CONTRIBUTING.md`, léelo antes.\n",
    "Ahí está lo que quien mantiene el proyecto ya se cansó de repetir."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ver exactamente lo que estás proponiendo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de abrir el pull request conviene mirar tu propio cambio con los ojos\n",
    "de quien lo va a revisar:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git diff main..agrega-cusco\n",
    "git log --oneline main..agrega-cusco"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso, exactamente eso, es lo que va a ver la otra persona. Si ahí aparece algo\n",
    "que no querías mandar, ahora es el momento 👀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cuando el original avanza mientras esperas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los pull requests tardan. Mientras tanto el proyecto sigue, y hay que traer\n",
    "lo nuevo para que tu propuesta no envejezca:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ../original\n",
    "printf 'Trujillo,1450\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Entran las ventas de Trujillo\"\n",
    "git push -q ../original-servidor.git main\n",
    "cd ../mi-copia\n",
    "git fetch -q upstream\n",
    "git switch -q main\n",
    "git merge -q upstream/main\n",
    "cat ventas.csv\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`fetch` trae sin mezclar, `merge` mezcla. Tu\n",
    "`main` vuelve a estar al día con el original 🔄"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Quieres proponer un arreglo en un proyecto ajeno. Lo bifurcas, clonas, arreglas el CSV de ventas ahí mismo y abres el pull request.\n",
    "\n",
    "```\n",
    "$ git branch\n",
    "* main\n",
    "\n",
    "$ git commit -am \"Se corrige el monto de Lima\"\n",
    "$ git push origin main\n",
    "\n",
    "# y al dia siguiente, otro arreglo distinto...\n",
    "$ git commit -am \"Se agrega Chiclayo\"\n",
    "$ git push origin main\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Los dos cambios están dentro del mismo pull request 🙃\n",
    "\n",
    "Un pull request no es una lista de commits que tú eliges: es **todo lo que tenga esa rama**. Si trabajas en `main`, cada cosa nueva que subas se mete sola en la propuesta abierta, aunque no tenga nada que ver.\n",
    "\n",
    "Quien revisa se encuentra un arreglo de un monto mezclado con una ciudad nueva y no puede aceptar uno sin el otro. Casi siempre acaba pidiendo que lo separes, o sea que el atajo costó más tiempo.\n",
    "\n",
    "La costumbre: **una rama por propuesta**, con nombre de lo que hace, y `main` se queda limpia para poder empezar la siguiente desde ahí."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Vas a proponer un arreglo en un proyecto que no es tuyo. ¿Dónde haces el commit?\n",
    "\n",
    "a) En `main` de mi bifurcación\n",
    "\n",
    "b) En una rama nueva de mi bifurcación\n",
    "\n",
    "c) En `main` del proyecto original\n",
    "\n",
    "d) Da igual, el pull request los separa\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* Todo lo que subas después a main se mete solo en la propuesta abierta, aunque no tenga relación.\n",
    "\n",
    "*c)* No tienes permiso para subir ahí, y ese es justo el motivo por el que existe el fork.\n",
    "\n",
    "*d)* No los separa: un pull request lleva todo lo que tenga esa rama.\n",
    "\n",
    "Una rama por propuesta, y `main` siempre limpia 🌿"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Monta el original y tu bifurcación\n",
    "\n",
    "Crea el proyecto del catálogo de bodegas, publícalo y haz\n",
    "tu copia."
   ]
  },
  {
   "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",
    "cd ..\n",
    "git clone -q --bare catalogo catalogo-servidor.git\n",
    "git clone -q --bare catalogo fork-bodegas.git\n",
    "git clone -q fork-bodegas.git trabajo\n",
    "cd trabajo\n",
    "git remote add upstream ../catalogo-servidor.git\n",
    "git remote -v"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "origin\t/home/miss-yera/fork-bodegas.git (fetch)\n",
    "origin\t/home/miss-yera/fork-bodegas.git (push)\n",
    "upstream\t../catalogo-servidor.git (fetch)\n",
    "upstream\t../catalogo-servidor.git (push)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`origin` es tuyo, `upstream` es de la otra persona."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Crea la rama de tu propuesta\n",
    "\n",
    "Una rama con nombre de lo que hace."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c agrega-minimarkets\n",
    "printf 'Minimarket Sol,Trujillo\\n' >> bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"Se agrega el minimarket de Trujillo\"\n",
    "git branch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "* agrega-minimarkets\n",
    "  main\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El asterisco marca dónde estás parada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Mira lo que vas a proponer\n",
    "\n",
    "El diff contra `main` es exactamente lo que\n",
    "verá quien revise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git diff main..agrega-minimarkets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "diff --git a/bodegas.csv b/bodegas.csv\n",
    "index 9fe9605..d3027d2 100644\n",
    "--- a/bodegas.csv\n",
    "+++ b/bodegas.csv\n",
    "@@ -1,2 +1,3 @@\n",
    " bodega,ciudad\n",
    " Bodega Inti,Cusco\n",
    "+Minimarket Sol,Trujillo\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una línea agregada y nada más. Así se ve una propuesta limpia ✨"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Súbela a tu bifurcación\n",
    "\n",
    "Manda la rama a `origin`, que es tu copia."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git push -q -u origin agrega-minimarkets\n",
    "git ls-remote --heads origin"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "a9a0491c4de4c36c7b7cc8adea3385832f96b850\trefs/heads/agrega-minimarkets\n",
    "a9874ae5ade9ebff480ec9f29629b1b651573849\trefs/heads/main\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos ramas allá: la original y la tuya."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Trae lo nuevo del original\n",
    "\n",
    "El proyecto avanzó mientras esperabas. Ponte al día."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ../catalogo\n",
    "printf 'ciudad,monto\\nLima,1200\\n' > ciudades.csv\n",
    "git add ciudades.csv\n",
    "git commit -q -m \"Entra el corte por ciudad\"\n",
    "git push -q ../catalogo-servidor.git main\n",
    "cd ../trabajo\n",
    "git fetch -q upstream\n",
    "git switch -q main\n",
    "git merge -q upstream/main\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "bodegas.csv\n",
    "ciudades.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El archivo nuevo del original ya está en tu `main`, y tu rama\n",
    "sigue intacta."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Actualiza tu propuesta\n",
    "\n",
    "Lleva lo nuevo a tu rama para que la propuesta no envejezca."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q agrega-minimarkets\n",
    "git merge -q main\n",
    "ls\n",
    "cat bodegas.csv\n",
    "git log --oneline --topo-order -3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "bodegas.csv\n",
    "ciudades.csv\n",
    "bodega,ciudad\n",
    "Bodega Inti,Cusco\n",
    "Minimarket Sol,Trujillo\n",
    "bc33316 Merge branch 'main' into agrega-minimarkets\n",
    "b648c87 Entra el corte por ciudad\n",
    "a9a0491 Se agrega el minimarket de Trujillo\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El corte por ciudad y tu minimarket conviven. La propuesta sigue siendo\n",
    "tuya y está al día."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Intenta subir al proyecto original\n",
    "\n",
    "Prueba a mandar tu rama a una dirección que no existe,\n",
    "que es lo que pasa cuando escribes mal el remoto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git push otro-remoto agrega-minimarkets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "fatal: 'otro-remoto' does not appear to be a git repository\n",
    "fatal: Could not read from remote repository.\n",
    "\n",
    "Please make sure you have the correct access rights\n",
    "and the repository exists.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Git no inventa destinos: si el apodo no está configurado, se planta 🛑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Fork, clone, rama, push y un clic. Y una rama por propuesta,\n",
    "siempre.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 20 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/pull-request/\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
}
