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