0%
Tutorial 7 de 10
70% completado

Resolución de conflictos

Aprende a identificar y resolver los conflictos de fusión que surgen al colaborar.

Resolución de conflictos

Los conflictos de Git son inevitables en el desarrollo colaborativo. Este tutorial te enseñará a comprender, identificar y resolver de forma eficaz los conflictos de fusión para mantener un workflow fluido en equipo.

¿Qué es un conflicto de Git?

Un conflicto surge cuando Git no puede fusionar automáticamente las modificaciones de dos ramas porque modifican las mismas líneas de código de manera incompatible.

mainfeature-afeature-bInitialModify line 5Modify line 5 differentlyCONFLICT!

Tipos de conflictos

1. Conflictos de contenido

Modificaciones simultáneas de las mismas líneas:

# Rama A modifica:
function greet() {
    return "Hello World!";
}

# Rama B modifica:
function greet() {
    return "Bonjour le monde!";
}

2. Conflictos de renombrado

# Rama A: renombra file.txt a document.txt
# Rama B: modifica el contenido de file.txt
# Resultado: Git no sabe qué hacer

3. Conflictos de eliminación

# Rama A: elimina file.txt
# Rama B: modifica file.txt
# Resultado: conflicto de eliminación/modificación

Anatomía de un conflicto

Marcadores de conflicto

<<<<<<< HEAD
function calculateTotal(items) {
    return items.reduce((sum, item) => sum + item.price, 0);
}
=======
function calculateTotal(items) {
    let total = 0;
    for (let item of items) {
        total += item.price;
    }
    return total;
}
>>>>>>> feature/new-calculation

Explicación de los marcadores:

  • <<<<<<< HEAD: inicio de tu versión (rama actual)
  • =======: separador entre las versiones
  • >>>>>>> feature/new-calculation: fin de la versión de la otra rama

Identificar los conflictos

Comandos de diagnóstico

# Ver el estado de los conflictos
git status

# Listar los archivos en conflicto
git diff --name-only --diff-filter=U

# Ver los detalles de los conflictos
git diff

# Ver los conflictos con más contexto
git diff --conflict=diff3

Salida típica de git status

On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   src/calculator.js
        both modified:   README.md

no changes added to commit (use "git add" or "git commit -a")

Estrategias de resolución

1. Resolución manual

# 1. Identificar los archivos en conflicto
git status

# 2. Abrir el archivo y editarlo manualmente
# Eliminar los marcadores <<<<<<, =======, >>>>>>>
# Conservar el código deseado

# 3. Marcar como resuelto
git add fichier-resolu.js

# 4. Finalizar la fusión
git commit

2. Elegir una versión completa

# Conservar tu versión (HEAD)
git checkout --ours fichier.js
git add fichier.js

# Conservar la versión de la otra rama
git checkout --theirs fichier.js
git add fichier.js

# Finalizar
git commit

3. Herramientas de merge

# Configurar una herramienta de merge
git config --global merge.tool vimdiff

# Lanzar la herramienta
git mergetool

# Otras herramientas populares
git config --global merge.tool vscode
git config --global merge.tool kdiff3
git config --global merge.tool meld

Resolución paso a paso

Ejemplo práctico completo

# Situación: conflicto durante un merge
git merge feature/user-profile
# Auto-merging src/user.js
# CONFLICT (content): Merge conflict in src/user.js
# Automatic merge failed; fix conflicts and then commit the result.

# 1. Examinar el conflicto
git status
cat src/user.js

Contenido del archivo con conflicto:

class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
<<<<<<< HEAD
        this.id = Math.random().toString(36);
        this.createdAt = new Date();
=======
        this.id = generateUniqueId();
        this.timestamp = Date.now();
>>>>>>> feature/user-profile
    }
}
# 2. Resolver el conflicto (edición manual)
# Decidir combinar los dos enfoques

Resolución:

class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
        this.id = generateUniqueId(); // Usar la función más robusta
        this.createdAt = new Date();  // Conservar el formato Date
        this.timestamp = Date.now();  // Añadir también el timestamp
    }
}
# 3. Marcar como resuelto
git add src/user.js

# 4. Comprobar que todos los conflictos están resueltos
git status

# 5. Finalizar el merge
git commit -m "Merge feature/user-profile

Resolved conflicts in user.js by combining both ID generation
approaches and keeping both timestamp formats for compatibility."

Herramientas de resolución

VS Code

VS Code ofrece una interfaz intuitiva para resolver los conflictos:

// settings.json
{
    "git.mergeEditor": true,
    "merge-conflict.autoNavigateNextConflict.enabled": true
}

Funcionalidades:

  • Accept Current Change: conservar tu versión
  • Accept Incoming Change: conservar la versión de la otra rama
  • Accept Both Changes: combinar ambas
  • Compare Changes: ver las diferencias lado a lado

GitKraken

Interfaz gráfica avanzada:

  • Visualización de las ramas y los conflictos
  • Editor de merge integrado
  • Historial de las resoluciones

Línea de comandos con vimdiff

# Configuración
git config --global merge.tool vimdiff

# Uso
git mergetool

# Navegación en vimdiff
# :diffget LOCAL    - Tomar la versión local
# :diffget REMOTE   - Tomar la versión remota
# :diffget BASE     - Tomar la versión base

Prevención de conflictos

Buenas prácticas

  1. Sincronización frecuente

    # Recuperar regularmente los cambios
    git fetch origin
    git merge origin/main
  2. Ramas cortas

    # Evitar las ramas de larga duración
    # Hacer merge con frecuencia de las funcionalidades pequeñas
  3. Comunicación en equipo

    # Coordinar las modificaciones sobre los mismos archivos
    # Usar las issues de GitHub para planificar
  4. Rebase antes del merge

    git fetch origin
    git rebase origin/main
    # Resolver los conflictos uno a uno
    git push --force-with-lease origin feature-branch

Organización del código

// Evitar los conflictos estructurando el código
// Separar las funcionalidades en módulos distintos

// ❌ Todo en un archivo
// app.js (500 líneas)

// ✅ Módulos separados
// auth.js
// user.js
// api.js
// utils.js

Conflictos avanzados

Conflictos durante el rebase

# Iniciar un rebase
git rebase main

# En caso de conflicto
# 1. Resolver el conflicto
# 2. Añadir los archivos resueltos
git add .

# 3. Continuar el rebase
git rebase --continue

# O abortar
git rebase --abort

Conflictos con cherry-pick

# Cherry-pick con conflicto
git cherry-pick abc1234

# Resolver y continuar
git add .
git cherry-pick --continue

# O abortar
git cherry-pick --abort

Conflictos de merge vs rebase

Conflictos de merge:

  • Se resuelven de una sola vez
  • Historial preservado
  • Se crea un commit de merge

Conflictos de rebase:

  • Se resuelven commit a commit
  • Historial lineal
  • Sin commit de merge

Ejercicios prácticos

Ejercicio 1: conflicto simple

# 1. Crear un repositorio de prueba
mkdir conflict-demo
cd conflict-demo
git init

# 2. Crear un archivo inicial
echo "function greet() {
    return 'Hello';
}" > greet.js
git add greet.js
git commit -m "Initial greeting function"

# 3. Crear dos ramas con modificaciones conflictivas
git checkout -b feature-french
echo "function greet() {
    return 'Bonjour';
}" > greet.js
git add greet.js
git commit -m "Add French greeting"

git checkout main
git checkout -b feature-spanish
echo "function greet() {
    return 'Hola';
}" > greet.js
git add greet.js
git commit -m "Add Spanish greeting"

# 4. Hacer merge y resolver el conflicto
git checkout main
git merge feature-french
git merge feature-spanish
# ¡Conflicto esperado!

# 5. Resolver combinando los idiomas
echo "function greet(language = 'en') {
    const greetings = {
        'en': 'Hello',
        'fr': 'Bonjour',
        'es': 'Hola'
    };
    return greetings[language] || greetings['en'];
}" > greet.js

git add greet.js
git commit -m "Resolve conflict by supporting multiple languages"

Ejercicio 2: conflicto complejo con varios archivos

# 1. Crear un proyecto con varios archivos
echo "# My App" > README.md
echo "const version = '1.0.0';" > version.js
echo "function add(a, b) { return a + b; }" > math.js
git add .
git commit -m "Initial project setup"

# 2. Rama A: modificaciones múltiples
git checkout -b feature-updates
echo "# My Awesome App
Version 2.0 with new features!" > README.md
echo "const version = '2.0.0';" > version.js
echo "function add(a, b) { 
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw new Error('Invalid input');
    }
    return a + b; 
}" > math.js
git add .
git commit -m "Update to version 2.0 with validation"

# 3. Rama B: modificaciones diferentes
git checkout main
git checkout -b feature-improvements
echo "# My App
Now with better performance!" > README.md
echo "const version = '1.1.0';" > version.js
echo "function add(a, b) { 
    // Optimized addition
    return Number(a) + Number(b); 
}" > math.js
git add .
git commit -m "Performance improvements v1.1"

# 4. Hacer merge y resolver todos los conflictos
git checkout main
git merge feature-updates
git merge feature-improvements
# Resolver cada archivo individualmente

Estrategias de equipo

Workflow con conflictos mínimos

# 1. Feature branches cortas
git checkout -b feature/small-change
# ... desarrollo rápido ...
git push origin feature/small-change
# ... PR y merge rápido ...

# 2. Rebase antes de la PR
git fetch origin
git rebase origin/main
git push --force-with-lease origin feature/small-change

# 3. Merge frecuente de main
git checkout feature/long-running
git merge origin/main
# Resolver los conflictos sobre la marcha

Comunicación de los conflictos

# Plantilla de mensaje para conflicto resuelto
## Conflicto resuelto en PR #123

**Archivos afectados:**
- src/user.js
- src/auth.js

**Naturaleza del conflicto:**
Modificaciones simultáneas de los métodos de autenticación

**Resolución elegida:**
Combinación de los dos enfoques:
- Conserva la validación de feature-a
- Integra la optimización de feature-b

**Pruebas realizadas:**
- [ ] Las pruebas unitarias pasan
- [ ] Pruebas de integración OK
- [ ] Validación manual

cc @team-lead @reviewer

Solución de problemas avanzada

Deshacer una resolución de conflicto

# Si has cometido un error en la resolución
git reset --hard HEAD~1  # Deshace el commit de merge
git merge feature-branch  # Vuelve a empezar el merge

Ver el historial de los conflictos

# Ver los merges anteriores
git log --merges --oneline

# Ver los detalles de un merge
git show commit-hash

# Ver los conflictos de un merge específico
git log --cc commit-hash

Resolución automática

# Configurar estrategias de merge
git config merge.ours.driver true  # Conservar siempre nuestra versión
git config merge.theirs.driver "cp %B %A"  # Conservar siempre su versión

# En .gitattributes
*.generated merge=ours
*.config merge=theirs

Recursos adicionales

Herramientas recomendadas

  • Beyond Compare: herramienta profesional de diff/merge
  • P4Merge: gratuita y eficaz
  • Meld: código abierto, multiplataforma
  • KDiff3: merge a 3 vías

Documentación

Conclusión

La resolución de conflictos es una competencia esencial para cualquier desarrollador que trabaje en equipo. Con práctica y las herramientas adecuadas, los conflictos pasan a ser una parte normal y manejable del workflow de desarrollo.

En el próximo tutorial exploraremos los workflows de Git avanzados que se utilizan en los equipos profesionales.


Tiempo de lectura estimado: 30 minutos
Nivel: Intermedio
Etapa anterior: Colaboración con GitHub
Siguiente etapa: Workflows de Git avanzados