{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Juntar dos ramas y entender el conflicto\n",
    "\n",
    "El merge, y qué hacer cuando Git no sabe con cuál quedarse\n",
    "\n",
    "Cuaderno de soluciones del capítulo 11 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/fusionar-y-conflictos/\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": [
    "## El caso fácil, que es casi siempre"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "mkdir tienda\n",
    "cd tienda\n",
    "git init -q\n",
    "printf 'ciudad,monto\\nLima,1200\\n' > ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Primeras ventas de Lima\"\n",
    "git switch -q -c catalogo-clientes\n",
    "printf 'bodega,ciudad\\nBodega Sol,Lima\\n' > clientes.csv\n",
    "git add clientes.csv\n",
    "git commit -q -m \"Se agrega el catalogo de bodegas\"\n",
    "git switch -q main\n",
    "git merge catalogo-clientes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Fast-forward*: como `main` no se había movido, Git\n",
    "simplemente adelantó el puntero. Ni siquiera hizo un commit de fusión, porque no\n",
    "hacía falta decidir nada."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "ls\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cuando las dos ramas avanzaron"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora que las dos tengan commits propios, tocando archivos distintos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c precios\n",
    "printf 'precio\\n34.90\\n' > precios.csv\n",
    "git add precios.csv\n",
    "git commit -q -m \"Se agrega la lista de precios\"\n",
    "git switch -q main\n",
    "printf 'Arequipa,890\\n' >> ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se agregan las ventas de Arequipa\"\n",
    "git merge --no-edit precios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí sí hubo un commit de fusión, el que junta las dos líneas de tiempo. Se\n",
    "ve bonito en el dibujo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git log --oneline --graph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora el conflicto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos ramas que tocan **la misma línea** del mismo archivo:"
   ]
  },
  {
   "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",
    "git switch -q -c corrige-lima\n",
    "sed -i 's/Lima,1200/Lima,1350/' ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se corrige el monto de Lima segun el sistema\"\n",
    "git switch -q main\n",
    "sed -i 's/Lima,1200/Lima,1290/' ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se corrige el monto de Lima segun la bodega\"\n",
    "git merge --no-edit corrige-lima"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "Auto-merging ventas.csv\n",
    "CONFLICT (content): Merge conflict in ventas.csv\n",
    "Automatic merge failed; fix conflicts and then commit the result.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso no es que se haya roto nada. Es Git diciendo: *una rama dice 1350 y la\n",
    "otra 1290, y yo no sé cuál es la buena*. Y tiene razón, esa decisión es\n",
    "tuya."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cómo se ve un conflicto por dentro"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Git marcó la zona en disputa con tres señales:\n",
    "\n",
    "- `<<<<<<< HEAD`, aquí empieza lo tuyo, lo de\n",
    "la rama donde estás.\n",
    "\n",
    "- `=======`, la frontera.\n",
    "\n",
    "- `>>>>>>> corrige-lima`, aquí termina lo de\n",
    "la rama que traes.\n",
    "\n",
    "Resolverlo es borrar esas líneas raras y dejar el archivo como debe quedar.\n",
    "Se puede a mano en cualquier editor. Aquí lo hago con un comando para que quede\n",
    "ejecutado:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'ciudad,monto\\nLima,1290\\nArequipa,890\\n' > ventas.csv\n",
    "cat ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se resuelve el conflicto de Lima con el monto de la bodega\"\n",
    "git log --oneline -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Listo. El `git add` es lo que le dice a Git \"ya lo decidí\", y el\n",
    "commit cierra la fusión 🎉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La salida de emergencia"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si el conflicto es más grande de lo que esperabas, no hay que resolverlo a\n",
    "la fuerza. Se puede volver atrás como si nada:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c otro-precio\n",
    "printf 'ciudad,monto\\nLima,9999\\nArequipa,890\\n' > ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se prueba otro monto para Lima\"\n",
    "git switch -q main\n",
    "printf 'ciudad,monto\\nLima,1000\\nArequipa,890\\n' > ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Se vuelve a corregir el monto de Lima\"\n",
    "git merge --no-edit otro-precio || true\n",
    "git merge --abort\n",
    "git status --short\n",
    "cat ventas.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`merge --abort` deja todo exactamente como estaba antes de\n",
    "intentar la fusión. Saber que existe quita casi todo el miedo 🧯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Resuelves un conflicto en el CSV de ventas, borras las marcas raras, guardas y sigues con tu día.\n",
    "\n",
    "```\n",
    "$ git add ventas.csv\n",
    "$ git commit -m \"Se resuelve el conflicto\"\n",
    "$ cat ventas.csv\n",
    "\n",
    "ciudad,monto\n",
    "Lima,1290\n",
    "=======\n",
    "Lima,1350\n",
    "Arequipa,890\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Se te quedó una marca dentro del archivo 🙈\n",
    "\n",
    "Al borrar a mano es facilísimo dejarse un `=======` o un `>>>>>>>`, y Git no revisa: para él el conflicto está resuelto en cuanto haces el `add`.\n",
    "\n",
    "En un CSV eso te mete una fila basura en el análisis. En un archivo de código, el programa no arranca y el error no dice nada de un conflicto, así que puedes pasarte una hora buscando en el sitio equivocado.\n",
    "\n",
    "La costumbre: antes de cerrar la fusión, `git diff --check`, que busca justo esas marcas. Y si el archivo es de datos, ábrelo y míralo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Te sale CONFLICT al hacer un merge. ¿Qué significa?\n",
    "\n",
    "a) Que rompiste el repositorio\n",
    "\n",
    "b) Que dos ramas cambiaron la misma línea y Git necesita que tú decidas\n",
    "\n",
    "c) Que hay que borrar una de las dos ramas\n",
    "\n",
    "d) Que la fusión falló y hay que empezar de nuevo\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* No se rompió nada. El repositorio está intacto y hasta puedes volver atrás con un comando.\n",
    "\n",
    "*c)* Las dos ramas siguen valiendo. Lo que hay que decidir es qué queda en las líneas donde no coinciden.\n",
    "\n",
    "*d)* La fusión está a medias, esperándote. Se termina resolviendo el archivo, o se cancela con merge --abort.\n",
    "\n",
    "Un conflicto no es un error, es una pregunta 🙋"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Una fusión sin drama\n",
    "\n",
    "Crea una rama que toque un archivo nuevo y júntala."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c canales\n",
    "printf 'canal\\nbodega\\nminimarket\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Se agrega el catalogo de canales\"\n",
    "git switch -q main\n",
    "git merge --no-edit canales"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Updating e0a1cf9..a4578ab\n",
    "Fast-forward\n",
    " canales.csv | 3 +++\n",
    " 1 file changed, 3 insertions(+)\n",
    " create mode 100644 canales.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Archivos distintos, cero conflictos. La inmensa mayoría de las fusiones son\n",
    "así de aburridas, que es como deben ser 😌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Comprueba que llegó todo\n",
    "\n",
    "Mira los archivos y la historia después de la fusión."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "ls\n",
    "git log --oneline -3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "canales.csv\n",
    "clientes.csv\n",
    "precios.csv\n",
    "ventas.csv\n",
    "a4578ab Se agrega el catalogo de canales\n",
    "e0a1cf9 Se vuelve a corregir el monto de Lima\n",
    "64abe86 Se resuelve el conflicto de Lima con el monto de la bodega\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El trabajo de las dos ramas junto, y en la historia se ve de dónde salió cada\n",
    "cosa."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Provoca un conflicto a propósito\n",
    "\n",
    "Cambia la misma línea en dos ramas. Tiene que fallar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c canal-horeca\n",
    "sed -i 's/minimarket/horeca/' canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"El segundo canal es horeca\"\n",
    "git switch -q main\n",
    "sed -i 's/minimarket/mayorista/' canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"El segundo canal es mayorista\"\n",
    "git merge --no-edit canal-horeca"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Auto-merging canales.csv\n",
    "CONFLICT (content): Merge conflict in canales.csv\n",
    "Automatic merge failed; fix conflicts and then commit the result.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Misma línea, dos verdades distintas. Git levanta la mano y te pregunta."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Mira el archivo en conflicto\n",
    "\n",
    "Abre el CSV y busca las tres marcas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "cat canales.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "canal\n",
    "bodega\n",
    "<<<<<<< HEAD\n",
    "mayorista\n",
    "=======\n",
    "horeca\n",
    ">>>>>>> canal-horeca\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Arriba lo tuyo, abajo lo que traes, y la frontera en medio. Una vez que lo\n",
    "ves una vez, ya no asusta más 👀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Resuélvelo quedándote con los dos\n",
    "\n",
    "A veces la respuesta no es elegir, es juntar. Deja los dos\n",
    "canales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "printf 'canal\\nbodega\\nmayorista\\nhoreca\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Se resuelve el conflicto dejando los dos canales\"\n",
    "cat canales.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "canal\n",
    "bodega\n",
    "mayorista\n",
    "horeca\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Resolver un conflicto es decidir qué debe quedar, y eso incluye la opción de\n",
    "que quede todo 🤗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Busca marcas olvidadas\n",
    "\n",
    "Comprueba que no quedó ninguna marca suelta en el\n",
    "repositorio."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "grep -rn \"<<<<<<<\\|=======\\|>>>>>>>\" . --exclude-dir=.git || echo \"limpio\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "limpio\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este grep es la versión manual de la costumbre de la trampa. Vale la pena\n",
    "correrlo después de cada conflicto que resuelvas a mano."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Cancela una fusión a mitad\n",
    "\n",
    "Empieza un conflicto y vuelve atrás sin resolverlo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%consola\n",
    "git switch -q -c canal-detalle\n",
    "sed -i 's/mayorista/detalle/' canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"El canal se llama detalle\"\n",
    "git switch -q main\n",
    "sed -i 's/mayorista/tradicional/' canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"El canal se llama tradicional\"\n",
    "git merge --no-edit canal-detalle || true\n",
    "git merge --abort\n",
    "git status --short\n",
    "cat canales.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Auto-merging canales.csv\n",
    "CONFLICT (content): Merge conflict in canales.csv\n",
    "Automatic merge failed; fix conflicts and then commit the result.\n",
    "canal\n",
    "bodega\n",
    "tradicional\n",
    "horeca\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Como si nunca hubiera pasado. Cuando un conflicto te supera, esta es la\n",
    "salida y no hay que sentirse mal por usarla 🚪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Un conflicto no es un error, es Git preguntándote algo que solo tú\n",
    "puedes contestar.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 11 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/fusionar-y-conflictos/\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
}
