Développement Web

Résoudre les conflits de fusion dans Git

12 دقائق للقراءة
📍 À lire d’abord : Maîtriser Git et GitHub — le guide complet. Ce tutoriel vous apprend à résoudre sereinement les conflits de fusion.

Le mot « conflit » fait peur à tous les débutants de Git. Pourtant, un conflit n’est ni un bug ni une catastrophe : c’est Git qui, honnêtement, vous dit « deux personnes ont modifié les mêmes lignes, je ne sais pas laquelle garder — décide à ma place ». Une fois compris ce message, la résolution devient une procédure calme et mécanique. À la fin de ce tutoriel, vous saurez provoquer un conflit volontairement, lire ses marqueurs, choisir la bonne version et finaliser la fusion ou le rebasage — sans jamais paniquer ni perdre de travail.

🎯 Ce que vous allez apprendre

  • Comprendre précisément pourquoi et quand un conflit survient.
  • Lire et interpréter les marqueurs de conflit dans un fichier.
  • Résoudre un conflit manuellement, puis finaliser avec git add et --continue.
  • Choisir une version d’un bloc avec --ours / --theirs, en évitant le piège de l’inversion pendant un rebasage.
  • Améliorer l’affichage des conflits avec le style zdiff3.
  • Adopter des habitudes qui réduisent la fréquence des conflits.

🛠️ Ce que vous allez construire

Sur le dépôt boutique-inventaire, vous créerez délibérément deux branches qui modifient la même ligne de app.js, déclencherez le conflit lors de la fusion, puis le résoudrez proprement. Vous répéterez ensuite l’exercice dans le contexte d’un rebasage, pour bien percevoir la différence — subtile mais essentielle.

Prérequis

  • Maîtriser les branches et la fusion : voir Rebase vs merge et le tutoriel sur les branches.
  • Un éditeur de texte pour modifier les fichiers en conflit.
  • Niveau intermédiaire. ⏱️ Temps estimé : environ 45 minutes.

Étape 1 — Pourquoi un conflit survient

Git fusionne automatiquement la plupart des changements : si une branche modifie le haut d’un fichier et l’autre le bas, il combine les deux sans broncher. Le conflit n’apparaît que lorsque deux branches modifient les mêmes lignes de façon divergente, ou quand l’une supprime un fichier que l’autre modifie. Dans ces cas, Git refuse de deviner : il s’arrête et vous confie l’arbitrage.

Comprendre cela dédramatise tout. Un conflit n’est pas le signe que vous avez « mal fait » ; c’est la conséquence normale d’un travail parallèle sur une zone commune. Votre rôle est simplement de dire à Git quelle version doit l’emporter — ou comment combiner les deux.

Étape 2 — Provoquer un conflit pour l’apprivoiser

Rien ne vaut la pratique. Créons deux branches qui modifient la même ligne, afin de fabriquer un conflit contrôlé. Partons d’une ligne connue dans app.js.

git switch main
echo "const TVA = 0.18;" > app.js
git add app.js
git commit -m "Définit le taux de TVA"
git switch -c feature/taux-promo
echo "const TVA = 0.20;" > app.js
git commit -am "Passe la TVA à 20%"
git switch main
echo "const TVA = 0.10;" > app.js
git commit -am "Baisse la TVA à 10%"

Les deux branches ont maintenant une valeur différente sur la même ligne. Tentons la fusion : le conflit est garanti.

git merge feature/taux-promo

Git répond CONFLICT (content): Merge conflict in app.js et Automatic merge failed; fix conflicts and then commit the result. La fusion est en pause. C’est exactement ce que nous voulions : un terrain d’entraînement.

Étape 3 — Lire les marqueurs de conflit

Ouvrez app.js dans votre éditeur. Git y a inséré des marqueurs qui délimitent les versions en désaccord :

<<<<<<< HEAD
const TVA = 0.10;
=======
const TVA = 0.20;
>>>>>>> feature/taux-promo

Décryptons. Entre <<<<<<< HEAD et la ligne ======= se trouve votre version, celle de la branche courante (« la nôtre », ours). Entre ======= et >>>>>>> se trouve la version entrante, celle de la branche fusionnée (« la leur », theirs). Le nom après >>>>>>> rappelle de quelle branche elle provient.

Résoudre consiste à supprimer les marqueurs et à ne laisser que le contenu final voulu. Vous pouvez garder l’une des deux versions, ou écrire une troisième valeur qui réconcilie les deux. Supposons que le taux correct soit 18 % :

const TVA = 0.18;

Point d’étape — Le fichier ne doit plus contenir aucun des trois marqueurs (<<<<<<<, =======, >>>>>>>). Un git status liste le fichier sous Unmerged paths tant qu’il n’est pas marqué résolu.

Étape 4 — Finaliser la résolution

Une fois le fichier nettoyé et la bonne version en place, on signale à Git que le conflit est réglé en ajoutant le fichier à l’index — c’est git add qui sert de « validation » de la résolution. On finalise ensuite la fusion.

git add app.js
git merge --continue

Git ouvre (ou réutilise) le message du commit de fusion ; enregistrez-le. La fusion est terminée, le conflit résolu, l’historique cohérent. Sur d’anciennes versions ou par habitude, git commit seul finalise aussi une fusion en cours. Si à tout moment vous voulez tout annuler et revenir à l’état d’avant la fusion, une seule commande suffit :

git merge --abort

Cette commande est votre filet de sécurité : elle restaure proprement le dépôt comme si vous n’aviez jamais lancé la fusion. Utilisez-la sans hésiter quand un conflit vous dépasse — vous repartirez à tête reposée.

Étape 5 — Choisir un côté, et le piège du rebasage

Parfois, vous savez d’emblée qu’un fichier entier doit prendre une version ou l’autre. Plutôt que d’éditer à la main, demandez à Git de garder un côté. Pendant une fusion, --ours désigne la branche courante et --theirs la branche entrante.

git checkout --ours app.js     # garder la version de la branche courante
git checkout --theirs app.js   # garder la version entrante
git add app.js

Voici le piège qui surprend tout le monde : pendant un rebasage, les rôles de --ours et --theirs sont inversés. La raison est logique une fois comprise — lors d’un rebasage, Git rejoue vos commits au-dessus de la branche cible, qu’il considère donc comme « la base » (ours), tandis que vos propres commits rejoués deviennent « les leurs » (theirs). Concrètement, pendant un rebasage, --ours fait référence à la branche sur laquelle vous rebasez, et --theirs à votre travail. Gardez cette inversion en tête : c’est la source d’erreur n°1 lors des conflits de rebasage.

Les commandes de poursuite changent aussi de nom selon le contexte. Pendant un rebasage, après résolution et git add, on poursuit avec git rebase --continue ; on peut sauter un commit problématique avec git rebase --skip, ou tout annuler avec git rebase --abort.

Étape 6 — Mieux voir le conflit avec zdiff3

Le style d’affichage par défaut ne montre que les deux versions en désaccord. Or, voir l’ancêtre commun — la version d’origine avant les deux modifications — aide énormément à comprendre l’intention de chacun. Le style zdiff3, disponible depuis Git 2.35 (janvier 2022), ajoute ce contexte tout en réduisant le bruit des marqueurs.

git config --global merge.conflictStyle zdiff3

Avec ce réglage, un conflit affiche une section supplémentaire délimitée par ||||||| contenant l’ancêtre commun, entre votre version et la version entrante. Vous voyez ainsi d’où chacun est parti, ce qui transforme bien des résolutions obscures en évidences. C’est l’un des réglages les plus rentables pour quiconque rencontre régulièrement des conflits.

Étape 7 — Outils et automatisation

Pour les conflits complexes touchant plusieurs fichiers, un outil graphique de fusion peut aider à visualiser les trois versions côte à côte. Git en lance un avec une seule commande, à condition d’avoir configuré l’outil de votre choix.

git mergetool

Autre fonctionnalité précieuse pour les projets où les mêmes conflits reviennent (typiquement lors de rebasages répétés d’une longue branche) : rerere, pour reuse recorded resolution. Une fois activé, Git mémorise comment vous avez résolu un conflit donné et rejoue automatiquement la même résolution s’il le revoit.

git config --global rerere.enabled true

Vous gagnez un temps considérable sur les conflits récurrents, sans jamais perdre le contrôle : Git applique la résolution mémorisée mais vous laisse toujours vérifier le résultat avant de valider.

Au-delà du contenu : conflits de suppression et de renommage

Tous les conflits ne portent pas sur des lignes. Deux autres familles surprennent souvent. Le conflit modification/suppression survient quand une branche modifie un fichier que l’autre a supprimé : Git ne sait pas s’il doit garder la version modifiée ou entériner la suppression. Le message l’indique, et git status affiche le fichier comme « deleted by us » ou « deleted by them ». Vous tranchez en gardant le fichier (git add le fichier) ou en confirmant la suppression (git rm le fichier).

Le conflit de renommage apparaît quand les deux branches déplacent ou renomment le même fichier différemment. Git tente de suivre les renommages, mais au-delà d’un certain seuil de divergence il abandonne et signale le conflit. La résolution consiste à choisir le nom final voulu, à supprimer les variantes en trop, puis à marquer la résolution avec git add. Connaître ces cas évite la confusion du « mais il n’y a aucun marqueur dans mon fichier » : ici, le conflit porte sur l’existence ou le nom du fichier, pas sur son contenu.

🐞 Pièges fréquents

Symptôme / erreur Cause probable Correctif
Le commit échoue : Unmerged paths Un fichier en conflit n’a pas été marqué résolu Éditer, supprimer les marqueurs, git add le fichier
Des marqueurs <<<<<<< restent dans le code livré Résolution oubliée ou partielle Rechercher les marqueurs dans tout le projet avant de commiter
--ours garde la mauvaise version pendant un rebasage Inversion ours/theirs en rebasage Se rappeler : en rebasage, --ours = la branche cible
Conflit qui revient à chaque rebasage Branche longue, mêmes zones retouchées Activer rerere ; rebaser plus souvent
Fusion bloquée, on veut abandonner Conflit trop complexe sur le moment git merge --abort ou git rebase --abort

Travailler avec une connexion limitée

La résolution de conflits est, comme la fusion et le rebasage, une opération locale : elle ne requiert aucune connexion. Mais la meilleure stratégie quand le réseau est intermittent est surtout de réduire la probabilité des conflits, car les résoudre à plusieurs nécessite parfois d’échanger. Trois habitudes y aident : faire des commits petits et fréquents, synchroniser souvent sa branche avec la principale (par exemple via git pull --rebase dès qu’une connexion est disponible), et se répartir le travail de façon à ce que deux personnes touchent rarement le même fichier au même moment. Moins de conflits, c’est moins de coordination en ligne nécessaire.

✅ Récapitulatif

Un conflit n’est qu’une question de Git à laquelle vous répondez. Vous savez maintenant le provoquer pour vous entraîner, lire les marqueurs (ours entre <<<<<<< et =======, theirs ensuite), résoudre en éditant puis en faisant git add, finaliser avec --continue ou tout annuler avec --abort. Vous connaissez les raccourcis --ours / --theirs et leur inversion pendant un rebasage, le confort du style zdiff3, et les outils mergetool et rerere. Fort de ces compétences locales, vous êtes prêt à collaborer à grande échelle : partager votre travail, proposer des contributions et les faire relire.

🧾 Aide-mémoire

Commande Rôle
git status Lister les fichiers en conflit (Unmerged paths)
git add <fichier> Marquer un conflit comme résolu
git merge --continue / git commit Finaliser une fusion résolue
git merge --abort Annuler une fusion en conflit
git rebase --continue / --skip / --abort Poursuivre / sauter / annuler un rebasage
git checkout --ours / --theirs <fichier> Garder un côté (attention à l’inversion en rebasage)
git config merge.conflictStyle zdiff3 Afficher l’ancêtre commun dans les conflits
git config rerere.enabled true Mémoriser et rejouer les résolutions

💪 À vous de jouer

Créez un conflit sur la première ligne de README.md entre main et une branche feature/titre. Résolvez-le en gardant une troisième formulation qui combine les deux intentions, puis finalisez la fusion.

Voir une solution
git switch main
echo "# Boutique" > README.md && git commit -am "Titre court"
git switch -c feature/titre
echo "# Boutique Inventaire - Gestion de stock" > README.md && git commit -am "Titre détaillé"
git switch main
echo "# Boutique Inventaire" > README.md && git commit -am "Titre moyen"
git merge feature/titre
# Éditer README.md : garder "# Boutique Inventaire - Gestion de stock", retirer les marqueurs
git add README.md
git merge --continue

En choisissant une formulation qui reprend le meilleur des deux, vous illustrez qu’une résolution n’est pas toujours « l’un ou l’autre » : elle peut être une synthèse.

FAQ

Q : Un conflit signifie-t-il que j’ai fait une erreur ?
R : Non. C’est la conséquence normale de modifications parallèles sur les mêmes lignes. Git vous demande simplement d’arbitrer.

Q : Comment savoir quelle version est « la mienne » ?
R : Lors d’une fusion, votre version (branche courante) est entre <<<<<<< HEAD et ======= ; la version entrante suit. En rebasage, la perspective s’inverse.

Q : J’ai commité avec des marqueurs de conflit dedans, que faire ?
R : Rééditez le fichier pour retirer les marqueurs et la mauvaise version, puis créez un commit correctif. Pensez à chercher les marqueurs avant chaque commit.

Q : Le conflit est trop compliqué, puis-je tout reprendre ?
R : Oui : git merge --abort (ou git rebase --abort) restaure l’état d’avant. Vous pourrez recommencer calmement.

Q : Comment éviter les conflits ?
R : Commits petits et fréquents, synchronisation régulière avec la branche principale, et répartition du travail pour limiter les modifications simultanées des mêmes fichiers.

Ressources

Dans la même série

🔝 Retour au guide complet : Maîtriser Git et GitHub.

مشاركة