05 - Résoudre les conflits sans paniquer
Ce que tu vas apprendre
- Pourquoi les conflits arrivent (et pourquoi c'est normal)
- Lire et résoudre les marqueurs de conflit
- Prevenir les conflits avec de bonnes habitudes
git rererepour ne jamais résoudre deux fois le meme conflit
Prerequisites
Les conflits ne sont pas une erreur
La première chose a comprendre : un conflit, c'est pas git qui plante. C'est git qui te dit "deux personnes ont modifie la meme zone, je ne peux pas deviner laquelle garder, décidé toi-meme". C'est un comportement normal et sain.
Le problème, c'est la reaction. J'ai vu un junior taper git merge --abort a chaque conflit pendant des semaines. Un autre faisait git checkout --theirs . sur tout le projet pour "faire passer le merge" sans lire un seul conflit. Il a ecrase le travail d'un collegue deux fois avant qu'on s'en rende compte.
Alors on va demystifier le truc.
Comment un conflit nait
Deux devs modifient la meme ligne du meme fichier sur deux branches différentes. Quand git essaie de fusionner (merge ou rebase), il ne sait pas quelle version garder.
Exemple. Sur main, le fichier src/config.ts contient :
bashconst API_URL = "https://api.paltemps.fr/v1"
Sur feat/add-search, quelqu'un a change en :
bashconst API_URL = "https://api.paltemps.fr/v2"
Sur fix/timeout, quelqu'un d'autre a change en :
bashconst API_URL = "https://api-eu.paltemps.fr/v1"
Si les deux branches sont mergees dans main, conflit. Git ne va pas inventer https://api-eu.paltemps.fr/v2 tout seul (meme si c'est peut-etre ce que tu veux).
Lire les marqueurs de conflit
Quand un conflit arrive, git modifie le fichier avec des marqueurs :
<<<<<<< HEAD
const API_URL = "https://api.paltemps.fr/v2"
=======
const API_URL = "https://api-eu.paltemps.fr/v1"
>>>>>>> fix/timeout
Trois zones :
- Entre
<<<<<<<et=======: la version de ta branche actuelle (HEAD) - Entre
=======et>>>>>>>: la version de la branche entrante - Les lignes
<<<<<<<,=======,>>>>>>>sont les marqueurs eux-memes
Pour résoudre : tu supprimes les marqueurs et tu gardes le code correct. Peut-etre une des deux versions, peut-etre un mix des deux, peut-etre un truc complètement différent.
bashconst API_URL = "https://api-eu.paltemps.fr/v2"
Puis :
bashgit add src/config.ts
git commit -m "fix: resolve API URL conflict, use EU endpoint with v2"
Si tu es en plein rebase (pas un merge), c'est git rebase --continue au lieu du commit.
Résolution avec VS Code
VS Code détecté les marqueurs de conflit et affiche des boutons au-dessus : "Accept Current Change", "Accept Incoming Change", "Accept Both Changes", "Compare Changes". C'est plus visuel qu'un terminal.
Pour les conflits simples (un fichier, une zone), c'est pratique. Pour les conflits complexes (plusieurs fichiers, logique entrelacee), je préféré lire le diff moi-meme. Les boutons "Accept Current" ou "Accept Incoming" sans reflechir, c'est comme --ours/--theirs : dangereux si tu ne comprends pas ce que tu fais.
Conflits pendant un merge vs pendant un rebase
La différence est subtile mais compte.
Pendant un merge, tous les conflits arrivent d'un coup. Tu resous tout, tu fais un commit de merge, c'est fini.
Pendant un rebase, les commits sont rejoues un par un. Tu peux avoir des conflits a chaque commit. Si ta branche a 10 commits et que 3 provoquent des conflits, tu vas résoudre trois fois. C'est plus de travail sur le moment, mais chaque résolution est plus petite et plus comprehensible.
bashgit rebase main
# CONFLICT in src/config.ts
# Resolve...
git add src/config.ts
git rebase --continue
# CONFLICT in src/api.ts
# Resolve...
git add src/api.ts
git rebase --continue
# Successfully rebased
Si un rebase interactif te semble trop penible a cause des conflits, c'est parfois un signe que ta branche diverge trop de main. Rebase plus souvent pour éviter ca.
Prevenir les conflits
Tu ne peux pas eliminer les conflits complètement. Mais tu peux les réduire.
Fais des branches courtes. Une branche qui vit deux jours généré moins de conflits qu'une branche qui vit deux semaines. Merge vite, merge souvent.
Rebase ta branche sur main régulièrement. Tous les matins, en arrivant :
bashgit fetch origin
git rebase origin/main
Ca intégré les changements des autres au fur et a mesure. Les conflits sont petits et faciles a résoudre. Si tu attends deux semaines, le conflit sera un monstre.
Communique. Si deux devs travaillent sur le meme fichier, il faut le savoir en amont. Dans mon équipe de trois, on se dit chaque matin sur quel fichier/module on bosse. Ca parait basique mais ca évité la moitie des conflits.
Decoupe tes fichiers. Un fichier de 500 lignes que tout le monde touche, c'est une machine a conflits. Splitte-le.
git rerere : la mémoire des conflits
rerere veut dire "REuse REcorded REsolution". Active-le :
bashgit config --global rerere.enabled true
A partir de la, chaque fois que tu resous un conflit, git enregistre la résolution. Si le meme conflit reapparait (par exemple, tu rebase a nouveau apres un --abort), git applique automatiquement ta résolution précédente.
C'est invisible. Ca marche en silence. Tu ne te rends meme pas compte que ca t'a economise du temps. Mais crois-moi, sur un projet ou tu rebase souvent et ou les memes zones de code sont contestees, c'est un gain réel.
Le bouton panique
Si tu es perdu, si le conflit est incomprehensible, si tu as peur de casser quelque chose :
bash# Annuler un merge en cours
git merge --abort
# Annuler un rebase en cours
git rebase --abort
Ca annule tout. Tu reviens a l'état d'avant. Zero consequence. Tu peux respirer, relire le code, demander de l'aide, et recommencer.
J'ai colle un post-it sur l'ecran d'un de mes juniors : "En cas de doute : --abort". Ca a marche.
Et si tu as deja commite un merge foireux, le reflog est la pour récupérer l'état précédent.
Article précédent : 04 - Stash, cherry-pick et bisect Article suivant : 06 - Git hooks