Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 614 minDébutant

Résoudre un conflit Git

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • expliquer pourquoi un conflit arrive ;
  • lire les marqueurs de conflit et git status pendant une fusion ;
  • résoudre un conflit pas à pas, dans le terminal ou dans VS Code ;
  • abandonner une fusion et limiter les conflits dans une équipe.

4 exercices corrigés

Un conflit Git survient quand deux branches modifient les mêmes lignes d'un même fichier, ou quand l'une modifie un fichier que l'autre supprime : Git ne peut pas choisir à ta place, il s'arrête et te demande de trancher. Résoudre un conflit, c'est éditer les fichiers marqués, garder le bon code, retirer les marqueurs, puis git add et terminer la fusion. Ce chapitre décode les marqueurs <<<<<<<, ======= et >>>>>>>, montre la résolution dans le terminal et dans VS Code, et explique comment éviter la plupart des conflits.

Pourquoi un conflit arrive

Git fusionne tout seul la grande majorité des modifications. Si Lina modifie le haut de config.js et Hugo le bas, Git combine les deux sans rien demander. Il ne s'arrête que dans quelques cas :

Type de conflitCe que dit GitSituation
ContenuCONFLICT (content)Les deux branches ont modifié les mêmes lignes, ou des lignes voisines
Modification et suppressionCONFLICT (modify/delete)Une branche a modifié un fichier que l'autre a supprimé
Double ajoutCONFLICT (add/add)Les deux branches ont créé un fichier de même nom avec des contenus différents
RenommageCONFLICT (rename/rename)Les deux branches ont renommé le même fichier différemment

Un conflit n'a rien de grave : rien n'est perdu, rien n'est cassé. Les deux versions sont sous tes yeux, et Git attend ta décision.

Le scénario

Sur main, le plafond de remboursement d'un repas est fixé à 20 € dans config.js. Lina crée feature/plafond et le passe à 25 €, comme le demande la nouvelle politique de l'entreprise. Pendant ce temps, Hugo corrige une faute sur main et, sans le savoir, change la même ligne pour mettre 22 €, un ancien chiffre trouvé dans un courriel. Lina fusionne :

bash
git switch main
git merge feature/plafond
text
Auto-merging config.js
CONFLICT (content): Merge conflict in config.js
Automatic merge failed; fix conflicts and then commit the result.

Git a fusionné tout ce qu'il pouvait et s'est arrêté sur config.js. La fusion est en cours : tant qu'elle n'est pas terminée ou abandonnée, ne fais rien d'autre.

git status pendant un conflit

bash
git status
text
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:   config.js

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

git status liste les fichiers en conflit (both modified) et rappelle les deux issues possibles : résoudre puis commiter, ou abandonner avec git merge --abort. Les fichiers fusionnés sans problème sont déjà préparés.

Les marqueurs de conflit

Ouvre config.js :

javascript
module.exports = {
  devise: 'EUR',
<<<<<<< HEAD
  plafondRepas: 22,
=======
  plafondRepas: 25,
>>>>>>> feature/plafond
  plafondNuitee: 90,
};
MarqueurSignification
<<<<<<< HEADDébut de la version de la branche courante, celle sur laquelle tu fusionnes (ici main)
=======Séparation entre les deux versions
>>>>>>> feature/plafondFin de la version de la branche que tu fais entrer

Les lignes en dehors des marqueurs ont été fusionnées sans problème. Seule la zone entre <<<<<<< et >>>>>>> demande une décision.

Résoudre le conflit pas à pas

  1. Comprendre les deux intentions. Pourquoi main dit 22 et la branche 25 ? Lis les messages des commits : git log --merge -p config.js affiche les commits des deux côtés qui ont touché le fichier. Si tu ne sais pas qui a raison, demande : ici, Lina vérifie la note de service, qui fixe 25 €.
  2. Éditer le fichier. Garde la bonne version, ou combine les deux si les deux modifications sont utiles, et supprime les trois marqueurs :
javascript
module.exports = {
  devise: 'EUR',
  plafondRepas: 25,
  plafondNuitee: 90,
};
  1. Vérifier. Cherche les marqueurs oubliés : git diff --check signale ceux qui restent. Lance l'application ou les tests : un fichier sans marqueur peut encore être faux.
  2. Marquer le fichier comme résolu en le préparant :
bash
git add config.js
  1. Terminer la fusion :
bash
git commit

Git propose le message Merge branch 'feature/plafond'. Complète-le si la décision mérite une explication : « Plafond repas à 25 € conformément à la note de service de janvier ». Tu peux aussi utiliser git merge --continue, qui fait la même chose.

Garder une version entière

Si tu sais d'avance qu'une des deux versions est la bonne pour tout un fichier, pas besoin d'éditer à la main :

bash
git checkout --ours config.js     # garder la version de la branche courante (HEAD)
git checkout --theirs config.js   # garder la version de la branche fusionnée
git add config.js

Attention au sens : pendant un merge, ours est la branche sur laquelle tu es. Pendant un rebase, c'est l'inverse, car Git rejoue tes commits sur l'autre branche : ours désigne la branche de base, theirs tes propres commits. C'est une source d'erreur fréquente : dans le doute, ouvre le fichier.

Pour un conflit modify/delete, décide si le fichier doit exister : git add fichier pour le garder, git rm fichier pour confirmer la suppression.

Voir aussi la version d'origine

Par défaut, le conflit montre tes deux versions mais pas celle d'avant, celle de l'ancêtre commun. Or c'est souvent elle qui permet de comprendre ce que chacun a voulu faire. Active le style zdiff3 :

bash
git config --global merge.conflictStyle zdiff3

Le conflit affiche alors une troisième section, après |||||||, avec la version de l'ancêtre commun :

text
<<<<<<< HEAD
  plafondRepas: 22,
||||||| 1b6c5d8
  plafondRepas: 20,
=======
  plafondRepas: 25,
>>>>>>> feature/plafond

On lit tout de suite que les deux côtés sont partis de 20 et ont chacun choisi une nouvelle valeur.

Résoudre dans VS Code

VS Code détecte les marqueurs et affiche au-dessus de chaque conflit quatre actions :

ActionEffet
Accept Current ChangeGarde la version de la branche courante (HEAD)
Accept Incoming ChangeGarde la version de la branche fusionnée
Accept Both ChangesGarde les deux, l'une après l'autre
Compare ChangesAffiche les deux versions côte à côte

Le bouton Resolve in Merge Editor ouvre un éditeur en trois volets : les deux versions en haut, le résultat en bas. Ces boutons retirent les marqueurs pour toi, mais ils ne comprennent pas ton code : Accept Both Changes sur deux définitions de la même variable donne un fichier qui ne compile pas. Relis toujours le résultat, puis prépare le fichier et termine la fusion dans le terminal ou avec le bouton Commit de l'onglet Contrôle de code source.

Abandonner une fusion

Le conflit est plus gros que prévu, ou tu as fusionné la mauvaise branche ? Tant que tu n'as pas commité la fusion :

bash
git merge --abort

Le dépôt revient exactement à l'état d'avant le git merge. Tu peux relancer plus tard, après en avoir parlé à ton coéquipier. Les commandes équivalentes existent pour les autres opérations qui peuvent s'arrêter sur un conflit : git rebase --abort, git cherry-pick --abort, git revert --abort.

Les conflits ailleurs qu'au merge

CommandeConflit possiblePour continuer après résolutionPour abandonner
git mergeOuigit add puis git commit (ou git merge --continue)git merge --abort
git pullOui, c'est une fusion ou un rebaseSelon le mode : comme merge ou comme rebasegit merge --abort ou git rebase --abort
git rebaseOui, commit par commitgit add puis git rebase --continuegit rebase --abort
git cherry-pickOuigit add puis git cherry-pick --continuegit cherry-pick --abort
git stash popOuigit add, puis git stash drop (l'entrée reste dans la pile)git reset --merge
git revertOuigit add puis git revert --continuegit revert --abort

Le principe est toujours le même : éditer, git add, continuer. git status te rappelle à chaque fois l'opération en cours et les commandes possibles.

Moins de conflits

On n'élimine pas les conflits, mais on les rend petits et rares :

  • Des branches courtes. Une branche qui vit deux jours accumule peu de différences ; une branche de trois semaines devient un cauchemar à fusionner.
  • Mettre sa branche à jour souvent avec main (par git merge main ou un rebase) : tu règles les petits conflits au fil de l'eau, pas tous à la fin.
  • Se répartir le travail : deux personnes qui réécrivent le même fichier le même jour vont au conflit. Les tickets et le tableau de projet servent aussi à ça.
  • Des commits ciblés : reformater tout un fichier (indentation, guillemets) dans le même commit qu'une correction crée des conflits sur chaque ligne. Fais le reformatage dans un commit à part, et mets-toi d'accord sur un formateur automatique (Prettier, PHP CS Fixer) avec un fichier de configuration versionné.
  • Les fins de ligne : Windows termine les lignes par CRLF, Linux et macOS par LF. Un fichier .gitattributes avec la ligne * text=auto laisse Git normaliser les fins de ligne et évite les conflits sur des fichiers « entièrement modifiés » sans raison visible.

Exercices

Exercice 1 sur 4 Lire un conflit Réussi À revoir

Pendant la fusion de feature/libelles dans main, Git affiche ce conflit dans messages.js :

text
<<<<<<< HEAD
const MSG_REFUS = "Note refusée : justificatif manquant";
=======
const MSG_REFUS = "Note de frais refusée";
const MSG_VALIDE = "Note de frais validée";
>>>>>>> feature/libelles
  1. Quelle version vient de main ? Laquelle de feature/libelles ?
  2. L'équipe veut le libellé de refus détaillé de main et le nouveau libellé de validation de la branche. Écris le contenu final.
  3. Quelles commandes terminent la fusion ?
Voir la réponseMasquer la réponse de l'exercice 1
  1. Entre <<<<<<< HEAD et =======, la version de main, la branche courante. Entre ======= et >>>>>>> feature/libelles, celle de la branche fusionnée.

  2. Sans aucun marqueur :

    javascript
    const MSG_REFUS = "Note refusée : justificatif manquant";
    const MSG_VALIDE = "Note de frais validée";
    
  3. git add messages.js puis git commit (ou git merge --continue).

Tu avais trouvé ?

Exercice 2 sur 4 Remettre dans l'ordre Réussi À revoir

Remets ces actions dans l'ordre pour résoudre un conflit sur routes/notes.js pendant une fusion :

  • a. git commit
  • b. ouvrir routes/notes.js et choisir le bon code
  • c. git status
  • d. git add routes/notes.js
  • e. supprimer les marqueurs restants et lancer les tests
  • f. git merge feature/pagination
Voir la réponseMasquer la réponse de l'exercice 2

f, c, b, e, d, a. On lance la fusion (f), on repère les fichiers en conflit (c), on édite (b), on nettoie et on vérifie (e), on marque le fichier comme résolu (d), on termine la fusion (a).

Tu avais trouvé ?

Exercice 3 sur 4 Ours ou theirs ? Réussi À revoir

  1. Tu es sur main et tu fusionnes feature/theme. Le fichier couleurs.css est en conflit et tu veux garder entièrement la version de feature/theme. Quelle commande lances-tu avant git add ?
  2. Tu es sur feature/theme et tu lances git rebase main. Le même fichier est en conflit, et tu veux encore garder ta version, celle de feature/theme. Quelle commande ?
Voir la réponseMasquer la réponse de l'exercice 3
  1. git checkout --theirs couleurs.css : pendant un merge, theirs est la branche fusionnée, feature/theme.
  2. git checkout --theirs couleurs.css aussi : pendant un rebase, Git rejoue tes commits de feature/theme sur main, donc ours désigne main (la base) et theirs les commits rejoués, les tiens. Les deux réponses sont identiques alors que tu es sur une branche différente : c'est tout le piège.

Tu avais trouvé ?

Exercice 4 sur 4 Diagnostic Réussi À revoir

Après un git pull, Hugo voit dans son navigateur une page blanche et, dans la console, SyntaxError: Unexpected token '<<' dans App.jsx. git status affiche nothing to commit, working tree clean. Que s'est-il passé, et comment réparer ?

Voir la réponseMasquer la réponse de l'exercice 4

Un coéquipier (ou Hugo lui-même) a commité un fichier qui contenait encore des marqueurs de conflit : le commit est propre pour Git, mais le code est invalide. Pour réparer : ouvrir App.jsx, chercher <<<<<<<, ======= et >>>>>>>, garder le bon code, vérifier que l'application démarre, puis commiter la correction (« Retire des marqueurs de conflit oubliés dans App.jsx ») et pousser. Pour retrouver le commit fautif : git log -S "<<<<<<<" --oneline.

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Merge ou rebase ? git rebase, squash et cherry-pickRevoir le sommaire du cours