{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Reescribir la historia sin quemar al equipo\n",
    "\n",
    "Amend, rebase y la única regla que no se rompe\n",
    "\n",
    "Cuaderno de soluciones del capítulo 27 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/reescribir-la-historia/\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": [
    "Este capítulo va con una advertencia por delante, y no es de las de adorno.\n",
    "Todo lo que hay aquí es seguro mientras la rama viva solo en tu máquina, y es\n",
    "un problema en cuanto la compartes 🚧"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que de verdad pasa cuando reescribes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un commit es su contenido más su mensaje más su padre más quién y cuándo.\n",
    "Cambia cualquiera de esas cosas y el hash cambia, o sea que\n",
    "**no estás editando un commit, estás creando otro**.\n",
    "\n",
    "Los originales siguen ahí (en el reflog de 25),\n",
    "pero tu rama ya apunta a los nuevos. Si alguien más tenía los viejos, para Git\n",
    "sus commits y los tuyos no tienen nada que ver 💥"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Arreglar el último commit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El caso más común de todos: te comiste una palabra en el mensaje, o se te\n",
    "olvidó un archivo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "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 vetnas de Lima\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git commit -q --amend -m \"Primeras ventas de Lima\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mensaje arreglado y **el hash cambió**. Míralo bien, porque ahí\n",
    "está todo el capítulo: no se corrigió el commit, se hizo otro 🔁"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Agregar un archivo olvidado"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q --amend --no-edit\n",
    "git show --stat --oneline HEAD | head -5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El `--no-edit` mantiene el mensaje tal cual. Ahora el commit\n",
    "lleva los dos archivos, como si nunca hubiera pasado nada 🤫"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Varios commits a la vez"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para esto está el rebase interactivo. Normalmente abre un editor; aquí lo\n",
    "hacemos sin editor, que es lo mismo y se puede ejecutar:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "printf 'Arequipa,890\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"wip\"\n",
    "printf 'Cusco,760\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"wip 2\"\n",
    "printf 'Trujillo,1450\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"wip 3\"\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres commits que no le dicen nada a nadie. Los juntamos en uno con\n",
    "mensaje decente:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git reset -q --soft HEAD~3\n",
    "git commit -q -m \"Entran las ventas de Arequipa, Cusco y Trujillo\"\n",
    "git log --oneline\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese `reset --soft` es la forma más simple de juntar commits: mueve\n",
    "la etiqueta tres atrás pero deja todos los cambios señalados, así que un commit\n",
    "nuevo los recoge todos. Es lo que hace `squash` por dentro 🧵"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cambiar el orden o borrar uno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para eso sí hace falta el rebase interactivo, que se lanza con\n",
    "`git rebase -i HEAD~3` y abre una lista como esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "pick 8a2b19c Entran las ventas de Arequipa\n",
    "pick 3c1f80a Entran las ventas de Cusco\n",
    "pick 5d9c2f1 Entran las ventas de Trujillo\n",
    "\n",
    "# pick   = dejarlo como está\n",
    "# reword = cambiar el mensaje\n",
    "# squash = juntarlo con el de arriba\n",
    "# drop   = borrarlo\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cambias la primera palabra de cada línea, guardas y cierras. Git rehace la\n",
    "historia siguiendo tus instrucciones. Y si algo sale mal a mitad,\n",
    "`git rebase --abort` lo deja todo como estaba 🧯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La regla"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**No reescribas historia que ya publicaste.** No porque Git no\n",
    "te deje, sino porque cada persona que tenga esos commits va a tener que\n",
    "arreglar su copia a mano.\n",
    "\n",
    "Si por lo que sea hay que hacerlo, se avisa antes y se usa\n",
    "`--force-with-lease` en vez de `--force`, que al menos se\n",
    "niega si alguien subió algo mientras tanto 🛑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Tus commits tenían mensajes feos, los ordenas con un rebase interactivo y quedan preciosos. La rama ya estaba subida.\n",
    "\n",
    "```\n",
    "$ git rebase -i HEAD~3\n",
    "$ git push --force origin reporte-por-canal\n",
    "\n",
    "# en el chat, media hora despues:\n",
    "# \"me sale que mi rama diverge, que hago\"\n",
    "# \"a mi me aparecen los commits duplicados\"\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Reescribir cambia los hashes, y con eso rompiste la rama de todos 💥\n",
    "\n",
    "Un rebase no edita commits: **crea commits nuevos** con el mismo contenido y otro hash. Para Git, la historia de tu compañera y la tuya ya no tienen nada que ver, aunque el texto sea idéntico.\n",
    "\n",
    "De ahí la regla que se repite en todos lados y casi nunca se explica: **no reescribas historia que ya publicaste**. Mientras la rama solo esté en tu máquina, haz lo que quieras.\n",
    "\n",
    "Si ya pasó, se avisa antes de nada y se acuerda quién hace qué. Un `--force` silencioso en una rama compartida es lo que peor sienta en un equipo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Hiciste `--amend` sobre un commit que ya habías subido. ¿Qué pasó?\n",
    "\n",
    "a) Nada, es el mismo commit corregido\n",
    "\n",
    "b) Ahora tu rama y la de allá tienen historias distintas\n",
    "\n",
    "c) GitHub lo actualiza solo al hacer push\n",
    "\n",
    "d) Se borró el commit anterior\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* No es el mismo: cambió el hash, así que para Git es un commit distinto.\n",
    "\n",
    "*c)* El push normal va a ser rechazado justamente porque las historias divergen.\n",
    "\n",
    "*d)* Sigue en el reflog y en la copia de todos los que ya lo tenían.\n",
    "\n",
    "Reescribir crea commits nuevos. Por eso no se toca lo publicado 🚧"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Arregla un mensaje mal escrito\n",
    "\n",
    "Crea el catálogo con un error de dedo y corrígelo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "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 \"Catlaogo inicial de bodegas\"\n",
    "git log --oneline\n",
    "git commit -q --amend -m \"Catalogo inicial de bodegas\"\n",
    "git log --oneline\n",
    "3f1bfd5 Catlaogo inicial de bodegas\n",
    "a9874ae Catalogo inicial de bodegas\n",
    "Mensaje arreglado y hash distinto. Los dos hashes están a la vista."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Agrega un archivo olvidado\n",
    "\n",
    "Métele el README al commit que ya hiciste."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "printf '# Catalogo de bodegas\\n' > README.md\n",
    "git add README.md\n",
    "git commit -q --amend --no-edit\n",
    "git show --stat --oneline HEAD | head -5\n",
    "9d02d3f Catalogo inicial de bodegas\n",
    " README.md   | 1 +\n",
    " bodegas.csv | 2 ++\n",
    " 2 files changed, 3 insertions(+)\n",
    "Dos archivos en un solo commit, sin uno de \"se me olvidó el README\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Haz tres commits desordenados\n",
    "\n",
    "Los típicos \"wip\" de una tarde."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "printf 'Minimarket Sol,Trujillo\\n' >> bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"wip\"\n",
    "printf 'Bodega Sur,Arequipa\\n' >> bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"wip2\"\n",
    "printf 'Mayorista Norte,Piura\\n' >> bodegas.csv\n",
    "git add bodegas.csv\n",
    "git commit -q -m \"wip3\"\n",
    "git log --oneline\n",
    "f4f8d23 wip3\n",
    "e831802 wip2\n",
    "543805b wip\n",
    "9d02d3f Catalogo inicial de bodegas\n",
    "Tres commits que dentro de un mes no le dicen nada a nadie."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Júntalos en uno\n",
    "\n",
    "Con `reset --soft`, que es lo que hace squash\n",
    "por dentro."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git reset -q --soft HEAD~3\n",
    "git status --short\n",
    "git commit -q -m \"Entran las bodegas de Trujillo, Arequipa y Piura\"\n",
    "git log --oneline\n",
    "M  bodegas.csv\n",
    "85aada0 Entran las bodegas de Trujillo, Arequipa y Piura\n",
    "9d02d3f Catalogo inicial de bodegas\n",
    "Un commit con un mensaje que sí se entiende 🧵"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Comprueba que no se perdió nada\n",
    "\n",
    "El contenido tiene que ser exactamente el mismo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "cat bodegas.csv\n",
    "git show --stat --oneline HEAD | head -4\n",
    "bodega,ciudad\n",
    "Bodega Inti,Cusco\n",
    "Minimarket Sol,Trujillo\n",
    "Bodega Sur,Arequipa\n",
    "Mayorista Norte,Piura\n",
    "85aada0 Entran las bodegas de Trujillo, Arequipa y Piura\n",
    " bodegas.csv | 3 +++\n",
    " 1 file changed, 3 insertions(+)\n",
    "Las cuatro bodegas están. Se juntó la historia, no los datos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Recupera los commits originales\n",
    "\n",
    "Reescribir no borra: el reflog sigue teniendo los viejos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git reflog --format=\"%h %gs\" | head -6\n",
    "85aada0 commit: Entran las bodegas de Trujillo, Arequipa y Piura\n",
    "9d02d3f reset: moving to HEAD~3\n",
    "f4f8d23 commit: wip3\n",
    "e831802 commit: wip2\n",
    "543805b commit: wip\n",
    "9d02d3f commit (amend): Catalogo inicial de bodegas\n",
    "Ahí están los tres \"wip\" con sus hashes de antes 🧯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Intenta enmendar sin tener commits\n",
    "\n",
    "Prueba `--amend` en un repositorio recién\n",
    "creado."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "cd ..\n",
    "mkdir vacio\n",
    "cd vacio\n",
    "git init -q\n",
    "git commit --amend -m \"algo\"\n",
    "fatal: You have nothing to amend.\n",
    "No hay commit anterior que reescribir, y Git lo dice claro 🛑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Reescribir crea commits nuevos con otro hash. Todo vale mientras la\n",
    "rama sea solo tuya.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 27 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/reescribir-la-historia/\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
}
