{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Publicar lo tuyo en GitHub\n",
    "\n",
    "Del repositorio de tu máquina a una dirección que puedes compartir\n",
    "\n",
    "Cuaderno de soluciones del capítulo 17 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/publicar-en-github/\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": [
    "Llevas once capítulos con un repositorio que solo existe en tu máquina. Hoy\n",
    "le damos dirección 🌎\n",
    "\n",
    "Y empiezo por la decisión que la gente pasa de largo, porque la pantalla de\n",
    "GitHub la pone chiquita y ya viene marcada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Privado o público, antes de nada"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "|  | Privado | Público |\n",
    "|---|---|---|\n",
    "| Quién lo ve | Tú y quien invites | Cualquiera, y Google |\n",
    "| Precio | Gratis, sin límite de repositorios | Gratis |\n",
    "| Para | Trabajo de clientes, datos, borradores | Portafolio, cosas que quieres compartir |\n",
    "| Se puede cambiar | Sí, en Settings | Sí, pero lo copiado no vuelve |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mi regla: **privado siempre, y público cuando decides que sí**.\n",
    "Pasar de privado a público es un clic; al revés no deshace nada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Los tres pasos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En GitHub, *New repository*, le pones nombre y\n",
    "**no marcas nada más**: ni README, ni licencia, ni gitignore. Si\n",
    "marcas algo, GitHub crea un commit allá que tú no tienes aquí, y el primer\n",
    "push sale rechazado. Es el tropiezo más común del capítulo.\n",
    "\n",
    "Después, en tu máquina, dos líneas. Para verlas ejecutadas usamos otra vez\n",
    "el servidor de práctica del capítulo 12:"
   ]
  },
  {
   "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\\nArequipa,890\\n' > ventas.csv\n",
    "printf '# Ventas Miss Yera\\n' > README.md\n",
    "git add .\n",
    "git commit -q -m \"Primeras ventas de Lima y Arequipa\"\n",
    "cd ..\n",
    "git init -q --bare servidor.git\n",
    "cd ventas-miss-yera\n",
    "git remote add origin ../servidor.git\n",
    "git push -u origin main"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con GitHub de verdad la única diferencia es la dirección:\n",
    "`git remote add origin git@github.com:tuusuario/ventas-miss-yera.git`.\n",
    "El resto es idéntico ✅"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué significa cada línea de la respuesta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Enumerating objects` es Git contando qué tiene que mandar.\n",
    "`Writing objects` es la subida. `[new branch] main -> main`\n",
    "es \"creé allá una rama main con lo de tu main\". Y\n",
    "`branch 'main' set up to track` es lo que hizo el `-u`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## De ahora en adelante"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Cusco,760\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Entran las ventas de Cusco\"\n",
    "git push"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A secas. Esa es la rutina del resto de tu vida con ese proyecto: trabajas,\n",
    "haces commits, y cuando quieres dejarlo a salvo, `git push` 🔁"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El rechazo que asusta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un día vas a ver esto, y no es que se haya roto nada. Vamos a provocarlo:\n",
    "alguien (o tú desde otra máquina) subió algo que tú no tienes."
   ]
  },
  {
   "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",
    "cd ..\n",
    "git clone -q servidor.git otra-maquina\n",
    "cd otra-maquina\n",
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Desde la otra maquina, entran los canales\"\n",
    "git push -q origin main\n",
    "cd ../ventas-miss-yera\n",
    "printf 'Piura,980\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Entran las ventas de Piura\"\n",
    "git push"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "To ../servidor.git\n",
    " ! [rejected]        main -> main (fetch first)\n",
    "error: failed to push some refs to '../servidor.git'\n",
    "hint: Updates were rejected because the remote contains work that you do not\n",
    "hint: have locally. This is usually caused by another repository pushing to\n",
    "hint: the same ref. If you want to integrate the remote changes, use\n",
    "hint: 'git pull' before pushing again.\n",
    "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Léelo entero, porque el propio Git te dice qué hacer: *\"Updates were\n",
    "rejected because the remote contains work that you do not have locally\"*.\n",
    "Traducido: **allá hay algo que aquí no está, y si subieras, lo\n",
    "borrarías**.\n",
    "\n",
    "La solución es traerlo primero:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git pull -q --no-rebase origin main\n",
    "git log --oneline\n",
    "cat ventas.csv\n",
    "git push -q origin main\n",
    "echo \"ahora si\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los canales y Piura conviven. Bajar antes de subir es la costumbre que evita el\n",
    "99% de los problemas de equipo 🤝"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Tu commit tenía un error de dedo en el mensaje, lo arreglas con `--amend` y al subir Git se queja. Le pones `--force` y listo.\n",
    "\n",
    "```\n",
    "$ git push --force origin main\n",
    "\n",
    "+ 8a2b19c...3c1f80a main -> main (forced update)\n",
    "\n",
    "# tres horas despues, en el chat del equipo:\n",
    "# \"oigan, se borraron mis dos commits de ayer\"\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "El `--force` no subió tu versión: **reemplazó la de allá** 💥\n",
    "\n",
    "Ese `+` del principio y ese \"forced update\" son la señal: le dijiste a GitHub \"lo que tengas ahí, bórralo, la verdad es la mía\". Y lo que había eran los commits que subió tu compañera mientras tú arreglabas el mensaje.\n",
    "\n",
    "Existe una versión con freno, `--force-with-lease`, que se niega si alguien subió algo desde la última vez que miraste. Hace lo mismo cuando es seguro y te salva cuando no.\n",
    "\n",
    "Y en una rama donde trabaja más gente, la respuesta casi siempre es no forzar nada: se arregla hacia adelante, con un commit más."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Tu `git push` sale rechazado con \"remote contains work that you do not have locally\". ¿Qué haces?\n",
    "\n",
    "a) `git push --force`\n",
    "\n",
    "b) `git pull` y después `git push`\n",
    "\n",
    "c) Clonar de nuevo y copiar mis archivos encima\n",
    "\n",
    "d) Esperar, seguro se arregla solo\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* Eso sube lo tuyo borrando lo de allá. Es exactamente lo que el mensaje te está pidiendo evitar.\n",
    "\n",
    "*c)* Funciona a la fuerza, pero pierdes tus commits y su historia. El pull hace lo mismo bien.\n",
    "\n",
    "*d)* No se arregla solo: allá hay commits que aquí no están y van a seguir estando.\n",
    "\n",
    "Bajar antes de subir. Siempre en ese orden ⬇️⬆️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Prepara el proyecto de canales\n",
    "\n",
    "Crea el repositorio con las ventas por canal y su primer\n",
    "commit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ..\n",
    "mkdir canales-venta\n",
    "cd canales-venta\n",
    "git init -q\n",
    "printf 'canal,monto\\nBodegas,4200\\nHoreca,3100\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Ventas por canal de bodegas y horeca\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "0c460fb Ventas por canal de bodegas y horeca\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Todavía no sabe que GitHub existe."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Conéctalo y súbelo\n",
    "\n",
    "Crea el repositorio vacío del otro lado, conéctalo y haz\n",
    "el primer push."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ..\n",
    "git init -q --bare canales-servidor.git\n",
    "cd canales-venta\n",
    "git remote add origin ../canales-servidor.git\n",
    "git push -u origin main"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "To ../canales-servidor.git\n",
    " * [new branch]      main -> main\n",
    "branch 'main' set up to track 'origin/main'.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El `-u` solo esta vez."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Sube un cambio sin escribir el destino\n",
    "\n",
    "Agrega los mayoristas y súbelo con `git push` a\n",
    "secas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'Mayoristas,5600\\n' >> canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Entra el canal mayorista\"\n",
    "git push"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "To ../canales-servidor.git\n",
    "   0c460fb..e91e683  main -> main\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa es la rutina: commit y push."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Comprueba qué hay del otro lado\n",
    "\n",
    "Pregúntale al servidor qué ramas tiene y con qué hash."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git ls-remote origin\n",
    "git log --oneline -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "e91e683ffa1ccaa8e49e108fd093ecb373068085\tHEAD\n",
    "e91e683ffa1ccaa8e49e108fd093ecb373068085\trefs/heads/main\n",
    "e91e683 Entra el canal mayorista\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El mismo hash aquí y allá."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Provoca el rechazo\n",
    "\n",
    "Sube algo desde otra copia y después intenta subir lo\n",
    "tuyo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ..\n",
    "git clone -q canales-servidor.git canales-otra\n",
    "cd canales-otra\n",
    "printf 'ciudad,monto\\nLima,1200\\n' > ciudades.csv\n",
    "git add ciudades.csv\n",
    "git commit -q -m \"Desde la otra copia, entran las ciudades\"\n",
    "git push -q origin main\n",
    "cd ../canales-venta\n",
    "printf 'Farmacias,1800\\n' >> canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Entra el canal farmacias\"\n",
    "git push"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "To ../canales-servidor.git\n",
    " ! [rejected]        main -> main (fetch first)\n",
    "error: failed to push some refs to '../canales-servidor.git'\n",
    "hint: Updates were rejected because the remote contains work that you do not\n",
    "hint: have locally. This is usually caused by another repository pushing to\n",
    "hint: the same ref. If you want to integrate the remote changes, use\n",
    "hint: 'git pull' before pushing again.\n",
    "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El rechazo no es un castigo: es Git impidiendo que borres el trabajo de\n",
    "alguien 🛡️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Resuélvelo bajando primero\n",
    "\n",
    "Trae lo de allá, comprueba que están los dos canales y\n",
    "sube."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git pull -q --no-rebase origin main\n",
    "ls\n",
    "git push -q origin main\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "canales.csv\n",
    "ciudades.csv\n",
    "7a33a8b Merge branch 'main' of ../canales-servidor\n",
    "8da6b0b Entra el canal farmacias\n",
    "dbf5d37 Desde la otra copia, entran las ciudades\n",
    "e91e683 Entra el canal mayorista\n",
    "0c460fb Ventas por canal de bodegas y horeca\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las ciudades y las farmacias, las dos cosas. Nadie perdió nada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Cambia la dirección del remoto\n",
    "\n",
    "Si mueves el proyecto de sitio, no hay que clonar de\n",
    "nuevo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cd ..\n",
    "git init -q --bare canales-nuevo.git\n",
    "cd canales-venta\n",
    "git remote set-url origin ../canales-nuevo.git\n",
    "git push -q -u origin main\n",
    "git remote -v"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "origin\t../canales-nuevo.git (fetch)\n",
    "origin\t../canales-nuevo.git (push)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`set-url` y listo. El repositorio ni se entera 🚚"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Crear vacío, conectar y subir. Y cuando el push sea rechazado, baja\n",
    "antes de subir en vez de forzar.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 17 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/publicar-en-github/\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
}
