Introduction
Quand Git ne peut pas combiner automatiquement deux changements à la même région d'un fichier, il laisse des marqueurs de conflit dans la copie de travail et vous laisse résoudre manuellement. Les marqueurs ne sont pas magiques ; ils enregistrent exactement ce que chaque côté a fait par rapport à une base commune.
Anatomie
<<<<<<< HEAD
ligne telle qu'elle est sur notre branche
=======
ligne telle qu'elle est sur leur branche
>>>>>>> feature/login
La section entre <<< et === est « ours » (la branche courante). Entre === et >>> est « theirs ». La résolution consiste à garder le bon texte, supprimer les marqueurs et stager.
Marqueurs trois-voies (style diff3)
git config --global merge.conflictStyle diff3
Maintenant, les marqueurs incluent aussi la version de base :
<<<<<<< HEAD
notre version
||||||| merged common ancestor
version de base
=======
leur version
>>>>>>> feature/login
Voir la base clarifie souvent ce que chaque côté voulait faire. Encore mieux, depuis Git 2.35 :
git config --global merge.conflictStyle zdiff3
qui taille le contexte commun.
Comment Git les produit
Pour chaque paire de hunks en conflit, Git exécute un diff trois-voies entre base, ours et theirs. Si les deux côtés ont changé la même région différemment, Git écrit les deux versions entourées de marqueurs. Sinon, le côté qui a changé gagne automatiquement.
Résoudre
git status # liste les chemins en conflit
éditer chaque fichier
git add <file> # marquer comme résolu
git commit # utilise le message préparé
Ou utilisez un outil de merge :
git mergetool
git config --global merge.tool meld
Inspecter chaque côté
git show :1:path # base
git show :2:path # ours
git show :3:path # theirs
git checkout --ours path
git checkout --theirs path
La syntaxe deux-points lit directement les stages d'index en conflit.
Annuler
git merge --abort
git rebase --abort
git cherry-pick --abort
rerere
« Reuse recorded resolution » se souvient de vos résolutions de conflits et les réapplique la prochaine fois que le même conflit apparaît :
git config --global rerere.enabled true
Indispensable pour les chaînes de rebase de longue durée.
Renommages et conflits binaires
Tous les conflits ne produisent pas de marqueurs textuels. Les conflits rename/rename, rename/delete et modify/delete laissent l'index dans un état avec plusieurs stages mais aucun marqueur <<< dans le fichier. Les fichiers binaires ne peuvent pas être mergés textuellement du tout ; les deux versions sont écrites sur disque et l'index enregistre le conflit :
git status
# both modified: image.png
# deleted by us: old.txt
# added by them: new.txt
git checkout --ours image.png # garder notre version
git checkout --theirs new.txt
git add image.png new.txt
Lisez toujours git status au moment du conflit ; il étiquette explicitement ces types de conflits moins courants.
Erreurs fréquentes
Résoudre en supprimant les marqueurs sans lire les contenus, abandonnant accidentellement un côté. Lisez toujours les deux moitiés et la base. Oublier git add après l'édition ; git commit refusera avec « unmerged paths ». Traiter --ours et --theirs comme identiques à -X ours et -X theirs ; les premiers remplacent un fichier en conflit entier, les seconds sont un indice à la stratégie de merge. Enfin, copier un fichier partiellement résolu d'ailleurs et contourner Git ; l'index a encore des entrées stage 1/2/3 jusqu'à ce que vous fassiez git add.