Conflictos en Git: diagnóstico, resolución y prevención

Capítulo 5

Tiempo estimado de lectura: 8 minutos

+ Ejercicio

¿Qué es un conflicto en Git y por qué ocurre?

Un conflicto aparece cuando Git intenta integrar cambios (por ejemplo, al hacer merge, rebase o aplicar un cherry-pick) y no puede decidir automáticamente qué versión debe quedar en una o más partes de uno o varios archivos. Git funciona muy bien cuando los cambios están en líneas distintas o en archivos distintos; el conflicto surge cuando dos cambios compiten por el mismo “lugar” del archivo o cuando una operación deja al repositorio en un estado ambiguo.

Causas típicas:

  • Ediciones en las mismas líneas por dos ramas diferentes (caso más común).
  • Un archivo renombrado o movido en una rama y modificado en otra.
  • Un archivo borrado en una rama y modificado en otra.
  • Cambios masivos de formato (reordenar imports, autoformateo, cambios de indentación) que “ensucian” el diff y aumentan colisiones.
  • Archivos no pensados para merge (p. ej., JSON grande reordenado, archivos generados, lockfiles) donde pequeñas diferencias producen conflictos frecuentes.

Proceso reproducible para diagnosticar y resolver conflictos

La idea es seguir siempre el mismo flujo: identificarinspeccionardecidirprobarfinalizar la integración.

1) Identificar qué archivos están en conflicto

Cuando Git detecta conflictos, lo indica en la salida del comando (por ejemplo, durante un merge o un rebase). Para ver el estado exacto:

git status

Busca secciones como Unmerged paths o mensajes del tipo “both modified”. Para listar solo archivos en conflicto:

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

git diff --name-only --diff-filter=U

Si quieres ver el contenido conflictivo con contexto:

git diff

2) Leer los marcadores de conflicto

Git marca el área conflictiva dentro del archivo con delimitadores. Ejemplo:

<<<<<<< HEAD
const timeout = 5000;
=======
const timeout = 8000;
>>>>>>> feature/increase-timeout
  • <<<<<<< HEAD: lo que tienes actualmente en tu rama (en un merge típico, tu rama destino).
  • =======: separador.
  • >>>>>>> ...: lo que viene de la otra rama/commit que estás integrando.

Tu trabajo es convertir ese bloque en una versión final coherente (sin marcadores) que compile/funcione y respete la intención de ambos cambios cuando sea posible.

3) Elegir una estrategia de resolución

Antes de editar, decide qué significa “resolver” en tu caso. Estrategias comunes:

  • Elegir un lado: quedarte con “lo mío” o “lo de ellos”. Útil cuando uno de los cambios es claramente incorrecto o redundante.
  • Combinar: integrar ambas ideas (lo más habitual en código).
  • Reescribir: ninguna versión es correcta por sí sola; creas una tercera solución.
  • Posponer: si falta información, consulta al autor del cambio o revisa el contexto (issues, PR, tests). Mejor preguntar que adivinar.

Comandos útiles para elegir un lado rápidamente (según el tipo de integración):

  • Durante un merge, para un archivo concreto: git checkout --ours -- ruta/al/archivo o git checkout --theirs -- ruta/al/archivo (en Git moderno también existe git restore con opciones equivalentes).
  • Durante un rebase, “ours/theirs” puede invertirse según el paso; por eso conviene confirmar con git status y revisar el diff antes de aceptar a ciegas.

4) Editar, limpiar marcadores y validar el resultado

Abre cada archivo en conflicto, elimina los marcadores y deja el contenido final. Luego verifica:

git diff

Esto te ayuda a confirmar que no quedaron marcadores y que el cambio final tiene sentido.

5) Marcar como resuelto y completar la integración

Una vez resuelto un archivo, debes “prepararlo” (stage) para indicar a Git que el conflicto está resuelto:

git add ruta/al/archivo

Cuando todos los conflictos estén resueltos:

  • Si estabas haciendo un merge:
git commit
  • Si estabas haciendo un rebase:
git rebase --continue

Si en algún momento necesitas abortar (porque la resolución se complicó o era el enfoque equivocado):

  • Abortar merge:
git merge --abort
  • Abortar rebase:
git rebase --abort

Casos reales (y cómo resolverlos con criterio)

Caso 1: Conflicto en código (función modificada en dos ramas)

Escenario: una rama cambia el nombre de una variable y otra rama agrega validación. Ambas tocaron las mismas líneas.

Conflicto:

<<<<<<< HEAD
function createUser(name) {
  return api.post('/users', { name });
}
=======
function createUser(username) {
  if (!username) throw new Error('username required');
  return api.post('/users', { username });
}
>>>>>>> feature/validation

Resolución combinada (mantener el nombre coherente y la validación):

function createUser(username) {
  if (!username) throw new Error('username required');
  return api.post('/users', { username });
}

Validación recomendada:

  • Ejecuta tests o al menos un build local.
  • Busca usos de createUser para confirmar que el cambio de parámetro no rompe llamadas.

Caso 2: Conflicto en archivo de configuración (JSON/YAML)

Escenario: dos ramas modifican package.json o un YAML de CI. Estos archivos suelen generar conflictos por reordenamiento o cambios cercanos.

Ejemplo en JSON (dependencias):

<<<<<<< HEAD
{
  "dependencies": {
    "express": "^4.18.0",
    "zod": "^3.22.0"
  }
}
=======
{
  "dependencies": {
    "express": "^4.18.0",
    "dotenv": "^16.4.0"
  }
}
>>>>>>> feature/add-dotenv

Resolución típica: conservar ambas dependencias si ambas son necesarias y mantener un formato consistente:

{
  "dependencies": {
    "dotenv": "^16.4.0",
    "express": "^4.18.0",
    "zod": "^3.22.0"
  }
}

Consejos específicos para configuración:

  • No adivines: si una dependencia se agregó por una razón, revisa el cambio asociado (qué archivo la usa).
  • Valida con herramientas: ejecuta npm test/npm run build o el comando equivalente del proyecto; valida YAML/JSON si el proyecto lo hace.
  • Cuidado con lockfiles: si hay package-lock.json/pnpm-lock.yaml, a menudo también requerirá resolución o regeneración controlada.

Caso 3: Conflicto en documentos (Markdown)

Escenario: dos personas editan el mismo párrafo de un README o una guía. Aquí el objetivo es claridad y coherencia editorial.

Conflicto:

<<<<<<< HEAD
## Instalación
Ejecuta el instalador y reinicia el sistema.
=======
## Instalación
Descarga el paquete, ejecútalo y verifica la versión con `tool --version`.
>>>>>>> docs/update-install

Resolución recomendada (combinar instrucciones sin duplicar):

## Instalación
Descarga el paquete, ejecuta el instalador y reinicia si es necesario. Luego verifica la instalación con `tool --version`.

Consejo: en documentos, lee el archivo completo para mantener tono, terminología y estructura (encabezados, listas, etc.).

Herramientas para entender y resolver mejor

git diff: ver exactamente qué cambió

Usos prácticos:

  • Ver cambios no preparados: git diff
  • Ver cambios preparados (staged): git diff --staged
  • Ver solo conflictos: git diff --name-only --diff-filter=U

Durante un conflicto, git diff te ayuda a confirmar que tu resolución final realmente integra lo necesario.

git mergetool: resolver con una herramienta visual

git mergetool abre una herramienta de merge (según tu configuración) para comparar “base”, “ours” y “theirs” y producir el resultado final. Es especialmente útil cuando el conflicto es grande o hay muchos bloques.

Configuración genérica (idea):

  • Git permite definir una herramienta por defecto con git config (por ejemplo, una herramienta visual instalada en tu sistema).
  • También puedes desactivar prompts repetitivos y elegir cómo se guardan archivos temporales.

Ejemplo de uso:

git mergetool

Recomendación práctica: aunque uses mergetool, revisa el resultado final con git diff y ejecuta pruebas. Una herramienta visual reduce errores de edición, pero no decide la lógica por ti.

Inspección rápida del historial alrededor del conflicto

Cuando no entiendes por qué existe un cambio, suele ayudar ver el contexto:

  • Ver quién tocó líneas específicas: git blame ruta/al/archivo
  • Buscar el commit que introdujo una línea o patrón: git log -S "texto" -- ruta/al/archivo

Esto no resuelve el conflicto automáticamente, pero mejora tu decisión (especialmente en configuración y código sensible).

Buenas prácticas para minimizar conflictos (sin frenar al equipo)

  • Cambios pequeños y frecuentes: PRs/ramas con cambios acotados generan menos colisiones y son más fáciles de revisar.
  • Comunicación en equipo: si dos personas van a tocar el mismo módulo/archivo, coordinen (aunque sea por un mensaje corto) para dividir responsabilidades.
  • Evitar “reformateos” masivos mezclados con cambios lógicos: separa commits de formato (lint/formatter) de commits funcionales.
  • Rebase frecuente en ramas cortas: si tu equipo lo usa, actualizar tu rama con frecuencia reduce el “gran choque” al final. Especialmente útil si tu rama vive pocos días.
  • Propiedad de archivos sensibles: define responsables o reglas para archivos de alto conflicto (CI, dependencias, configs).
  • Generados fuera del control de versiones cuando aplique: si un archivo se regenera siempre, considera no versionarlo (según el proyecto) para evitar conflictos inútiles.

Checklist de verificación (para evitar regresiones tras resolver)

  • Todos los archivos conflictivos están resueltos y preparados: git diff --name-only --diff-filter=U devuelve vacío.
  • No quedan marcadores de conflicto en el repo (búsqueda rápida): busca <<<<<<<, =======, >>>>>>>.
  • El proyecto compila/ejecuta: build local o comando principal del proyecto.
  • Tests pasan (al menos los más relevantes o un subconjunto rápido).
  • Revisaste archivos de configuración relacionados (dependencias, CI, variables) y son consistentes.
  • Revisaste el diff final antes de terminar: git diff y/o git diff --staged.
  • El comportamiento esperado se mantiene: prueba manual mínima del flujo afectado.
  • Si hubo cambios de API/contrato (funciones, endpoints, config), verificaste usos en el código y documentación.

Ahora responde el ejercicio sobre el contenido:

Durante la resolución de un conflicto, ¿qué acción indica a Git que un archivo ya fue resuelto y permite finalizar la integración?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

Tras editar el archivo y quitar los marcadores de conflicto, debes usar git add para marcarlo como resuelto. Luego podrás completar el proceso con git commit (merge) o git rebase --continue (rebase).

Siguiente capítulo

Recuperación y corrección en Git: revert, reset y amend sin romper el historial

Arrow Right Icon
Portada de libro electrónico gratuitaGit y GitHub para programadores principiantes: control de versiones para proyectos
42%

Git y GitHub para programadores principiantes: control de versiones para proyectos

Nuevo curso

12 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.