Introducción
Cuando Git no puede combinar automáticamente dos cambios en la misma región de un archivo, deja marcadores de conflicto en la copia de trabajo y te permite resolver manualmente. Los marcadores no son magia; registran exactamente lo que cada lado hizo respecto a una base común.
Anatomía
<<<<<<< HEAD
line as it is on our branch
=======
line as it is on their branch
>>>>>>> feature/login
La sección entre <<< y === es "ours" (el branch actual). Entre === y >>> es "theirs". Resolver es mantener el texto correcto, eliminar los marcadores y stagear.
Marcadores three-way (estilo diff3)
git config --global merge.conflictStyle diff3
Ahora los marcadores también incluyen la versión base:
<<<<<<< HEAD
our version
||||||| merged common ancestor
base version
=======
their version
>>>>>>> feature/login
Ver la base a menudo aclara qué pretendía cada lado. Aún mejor, desde Git 2.35:
git config --global merge.conflictStyle zdiff3
que recorta el contexto común.
Cómo los produce Git
Para cada par de hunks conflictivos, Git ejecuta un diff three-way entre base, ours y theirs. Si ambos lados cambiaron la misma región de manera diferente, Git escribe ambas versiones rodeadas de marcadores. De lo contrario, el lado que cambió gana automáticamente.
Resolviendo
git status # listar paths conflictivos
edit each file
git add <file> # marcar como resuelto
git commit # usa el mensaje preparado
O usa una herramienta de merge:
git mergetool
git config --global merge.tool meld
Inspeccionando cada lado
git show :1:path # base
git show :2:path # ours
git show :3:path # theirs
git checkout --ours path
git checkout --theirs path
La sintaxis con dos puntos lee directamente desde los stages conflictivos del index.
Abortando
git merge --abort
git rebase --abort
git cherry-pick --abort
rerere
"Reuse recorded resolution" recuerda tus resoluciones de conflicto y las reaplica la próxima vez que aparezca el mismo conflicto:
git config --global rerere.enabled true
Indispensable para cadenas largas de rebase.
Renames y conflictos binarios
No todos los conflictos producen marcadores de texto. Conflictos rename/rename, rename/delete y modify/delete dejan el index en un estado con múltiples stages pero sin marcadores <<< en el archivo. Los archivos binarios no se pueden mergear textualmente para nada; ambas versiones se escriben a disco y el index registra el conflicto:
git status
# both modified: image.png
# deleted by us: old.txt
# added by them: new.txt
git checkout --ours image.png # mantener nuestra versión
git checkout --theirs new.txt
git add image.png new.txt
Siempre lee git status al momento del conflicto; etiqueta explícitamente estos tipos de conflicto menos comunes.
Errores comunes
Resolver eliminando los marcadores sin leer los contenidos, descartando accidentalmente un lado. Siempre lee ambas mitades y la base. Olvidar hacer git add tras editar; git commit rechazará con "unmerged paths". Tratar --ours y --theirs como idénticos a -X ours y -X theirs; el primero reemplaza un archivo conflictivo entero, el segundo es una pista a la estrategia de merge. Finalmente, copiar un archivo parcialmente resuelto desde otro lado y saltarse Git; el index aún tiene entradas en stage 1/2/3 hasta que hagas git add.