Aller au contenu
English

Les conflits, étape par étape

  • Leçon 10
  • 40 min
  • Module 4 : Résoudre et récupérer

Ce que vous saurez faire

Terminez une fusion quand vous et un collègue avez modifié la même ligne. Après cette leçon, vous pourrez lire les deux modifications et conserver l’intention de chaque personne. Vous pourrez tester le résultat et terminer la fusion, ou l’arrêter et recommencer.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 40 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Les dépôts d’exercice dans ~/projects/nextia-learning-labs. Vous les avez clonés dans Les étapes d’une demande de tirage : préparer le laboratoire d’exercice. Si vous ne les avez pas, récupérez-les une fois :

    Fenêtre de terminal
    cd ~/projects
    git clone https://github.com/Nextia-Intelligence/nextia-learning-labs.git
  • Préparez l’état de départ de ce module. --reset supprime l’ancien dossier c14-lab et le recrée. Il ne change rien d’autre. Exécutez la même commande à tout moment pour recommencer ce module.

    Terminal
    cd ~/projects
    sh nextia-learning-labs/C14/setup.sh m04 --reset
    Sortie
    Ready: /Users/amira/projects/c14-lab
    ticket-notes your clone
    sam-notes Sam's clone
    team-remote.git the shared remote
    Next: cd c14-lab/ticket-notes
    To start this scenario again: sh nextia-learning-labs/C14/setup.sh m04 --reset
  • Entrez dans votre clone avec cd c14-lab/ticket-notes. git status affiche Your branch is ahead of 'origin/main' by 1 commit. (votre branche a un commit d’avance sur origin/main).

  • Le script configure pull.ff only et merge.conflictStyle zdiff3 dans les deux clones. Vos propres commits utilisent le nom défini dans Configurer Git : Étape 2 : indiquer votre identité à Git.

  • Le script a créé ses commits avec des empreintes fixes, identiques à celles de cette leçon. Les commits que vous créez ont des empreintes différentes. Utilisez celles de votre propre sortie.

Dans le module 2, Amira a fusionné le travail de Sam, et Git l’a fait seul. Git compare les deux versions ligne par ligne. Si chaque personne a modifié des lignes différentes, Git conserve les deux modifications.

Git utilise aussi une troisième version : la base, le dernier commit commun aux deux versions. Le module 3 l’appelait la base de fusion. La base montre ce que chaque personne a modifié. Si les deux personnes ont modifié la même ligne depuis la base, Git ne peut pas déterminer la bonne version. C’est un conflit. Git arrête la fusion, écrit les deux versions dans le fichier et attend votre décision.

Un conflit n’est pas une erreur, et rien n’est perdu. C’est une question à laquelle seule une personne peut répondre : que signifiait chaque modification, et que doit dire la ligne maintenant?

Dans une fusion, les deux versions portent des noms :

  • Ours (la nôtre) est votre branche actuelle, celle vers laquelle pointe HEAD. Dans cette leçon, c’est la branche main d’Amira.
  • Theirs (la leur) est le commit que vous intégrez par fusion. Dans cette leçon, c’est le commit de Sam provenant de origin/main.

Le script de préparation a configuré merge.conflictStyle zdiff3 dans les deux clones. Avec ce réglage, Git affiche aussi les lignes de base entre les deux versions. Vous pouvez alors voir ce que chaque personne a modifié, pas seulement le résultat. Activez ce réglage pour tous vos dépôts :

Terminal
git config --global merge.conflictStyle zdiff3

Exemple détaillé : deux modifications au billet 102

Section intitulée « Exemple détaillé : deux modifications au billet 102 »

Amira a ajouté une action au billet 102 : Sent a new reset link. (un nouveau lien de réinitialisation a été envoyé). En même temps, Sam a ajouté la cause du problème : The email was in the spam folder. (le courriel était dans le dossier des pourriels). Sam a poussé ses changements en premier. Amira essaie de pousser les siens :

Terminal (Amira)
git push
Sortie
To /Users/amira/projects/c14-lab/team-remote.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to '/Users/amira/projects/c14-lab/team-remote.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Elle récupère les changements. À cause de pull.ff only, Git les récupère seulement, puis s’arrête :

Terminal (Amira)
git pull
Output (first and last lines)
From /Users/amira/projects/c14-lab/team-remote
05e00df..7e16da6 main -> origin/main
hint: Diverging branches can't be fast-forwarded, you need to either:
...
fatal: Not possible to fast-forward, aborting.

Les historiques ont divergé. Elle décide de fusionner, comme dans le module 2 :

Terminal (Amira)
git pull --no-rebase
Sortie
Auto-merging tickets.txt
CONFLICT (content): Merge conflict in tickets.txt
Automatic merge failed; fix conflicts and then commit the result.

La fusion a commencé, mais elle n’est pas terminée. Git n’a pas créé de commit de fusion. git status montre ce qui reste à régler :

Terminal (Amira)
git status
Sortie
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
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: tickets.txt
no changes added to commit (use "git add" and/or "git commit -a")

both modified (modifié des deux côtés) signifie que les deux versions ont modifié tickets.txt au même endroit. Un chemin non fusionné est un fichier dont vous n’avez pas encore résolu le conflit. La sortie indique aussi les deux options : résoudre le conflit et créer un commit, ou abandonner la fusion.

Amira ouvre tickets.txt dans son éditeur :

tickets.txt
Ticket 101: printer offline. Restarted the spooler.
<<<<<<< HEAD
Ticket 102: password reset email not received. Sent a new reset link.
||||||| 05e00df
Ticket 102: password reset email not received.
=======
Ticket 102: password reset email not received. The email was in the spam folder.
>>>>>>> 7e16da60a8af11e14488d6be51aeff28dadcfc1d
Ticket 103: shared drive is slow. Moved the user to the new server.
Ticket 104: laptop will not charge. Replaced the power adapter.

Git a écrit quatre lignes de marqueurs autour du conflit. Les lignes à l’extérieur des marqueurs ont fusionné sans problème.

  • <<<<<<< HEAD commence notre version : la version de la ligne d’Amira.
  • ||||||| 05e00df commence la base : la ligne dans 05e00df, avant les deux modifications.
  • ======= commence leur version : la version de la ligne de Sam.
  • >>>>>>> 7e16da6… termine le conflit. L’empreinte correspond au commit de Sam.

Comparez chaque version à la base. Amira a ajouté Sent a new reset link. Sam a ajouté The email was in the spam folder. Sans la base, il est plus difficile de voir que chaque personne a seulement ajouté du texte.

Le texte d’une ligne montre ce qui a changé. Le message de commit explique pourquoi. git log --merge liste les commits de chaque côté qui ont modifié les fichiers en conflit :

Terminal (Amira)
git log --merge --oneline
Sortie
9e1b9df (HEAD -> main) Add the action for ticket 102
7e16da6 (origin/main, origin/HEAD) Add the cause of ticket 102

Pour voir la modification complète de Sam, elle utilise git show avec son empreinte :

Terminal (Amira)
git show 7e16da6
Sortie
commit 7e16da60a8af11e14488d6be51aeff28dadcfc1d
Author: Sam Okafor <sam@example.com>
Date: Tue Oct 6 09:10:00 2026 -0400
Add the cause of ticket 102
diff --git a/tickets.txt b/tickets.txt
index 76ca965..ae59119 100644
--- a/tickets.txt
+++ b/tickets.txt
@@ -1,4 +1,4 @@
Ticket 101: printer offline. Restarted the spooler.
Ticket 102: password reset email not received.
Ticket 102: password reset email not received. The email was in the spam folder.
Ticket 103: shared drive is slow. Moved the user to the new server.
Ticket 104: laptop will not charge. Replaced the power adapter.

Sam a noté la cause. Amira a noté l’action. Les deux faits sont vrais et utiles. La bonne ligne conserve donc les deux. Si elle ne comprend pas le sens d’une modification, elle demande à son auteur avant de décider.

Pour résoudre un conflit, modifiez le fichier jusqu’à ce qu’il contienne le bon texte et aucun marqueur. Amira remplace les sept lignes de <<<<<<< à >>>>>>> par une seule ligne. La cause vient en premier, puis l’action :

tickets.txt
Ticket 101: printer offline. Restarted the spooler.
Ticket 102: password reset email not received. The email was in the spam folder. Sent a new reset link.
Ticket 103: shared drive is slow. Moved the user to the new server.
Ticket 104: laptop will not charge. Replaced the power adapter.

Elle enregistre le fichier. Elle vérifie ensuite si elle a oublié des marqueurs. git diff --check signale les marqueurs de conflit restants. S’il n’en trouve aucun, il n’affiche rien :

Terminal (Amira)
git diff --check

Ensuite, elle exécute le test de l’équipe. check.sh est un petit script dans le dépôt. Il vérifie que chaque ligne de tickets.txt commence par Ticket <number>: (Billet suivi d’un numéro et d’un deux-points) :

Terminal (Amira)
sh check.sh
Sortie
PASS: 4 ticket lines checked

Un fichier peut fusionner sans marqueurs et quand même être incorrect. Le test vous permet de le découvrir avant votre équipe.

Elle indique maintenant à Git que le conflit est résolu. git add marque le fichier comme résolu :

Terminal (Amira)
git add tickets.txt
git status
Sortie
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: tickets.txt

Elle termine la fusion avec un commit. --no-edit conserve le message préparé par Git :

Terminal (Amira)
git commit --no-edit
git log --oneline --graph -4
Sortie
[main 5c84cbf] Merge branch 'main' of /Users/amira/projects/c14-lab/team-remote
* 5c84cbf (HEAD -> main) Merge branch 'main' of /Users/amira/projects/c14-lab/team-remote
|\
| * 7e16da6 (origin/main, origin/HEAD) Add the cause of ticket 102
* | 9e1b9df Add the action for ticket 102
|/
* 05e00df Add ticket 104

Le commit de fusion a deux parents : son commit et celui de Sam. Votre commit de fusion a une empreinte différente. Maintenant, main contient le travail de Sam. Elle peut donc pousser ses changements sans risque :

Terminal (Amira)
git push
Sortie
To /Users/amira/projects/c14-lab/team-remote.git
7e16da6..5c84cbf main -> main

Parfois, vous ne pouvez pas résoudre un conflit tout de suite. Vous devez peut-être consulter l’autre auteur, ou vous avez mal modifié le fichier. git merge --abort arrête la fusion. Il remet votre branche, la zone de préparation et les fichiers dans leur état d’avant la fusion.

Amira a essayé cette commande avant de résoudre le conflit. Elle l’a exécutée pendant que git status affichait encore You have unmerged paths (vous avez des chemins non fusionnés) :

Terminal (Amira)
git merge --abort
git status
git log --oneline -1
Sortie
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
nothing to commit, working tree clean
9e1b9df (HEAD -> main) Add the action for ticket 102

main pointe de nouveau vers son propre commit, et le fichier ne contient aucun marqueur. Le commit de Sam reste dans origin/main, car la récupération n’est pas annulée. Quand elle est prête, elle exécute encore git pull --no-rebase, et le même conflit apparaît.

D’abord, reproduisez l’exemple détaillé dans votre propre ticket-notes, de git push jusqu’au dernier git push. Ensuite, créez vous-même un deuxième conflit. Vous jouez Sam dans sam-notes et Amira dans ticket-notes, et vous modifiez tous les deux la ligne du billet 103.

  1. Allez dans le clone de Sam et récupérez la fusion que vous avez poussée :

    Fenêtre de terminal
    cd ~/projects/c14-lab/sam-notes
    git pull

    La sortie contient Fast-forward (avance rapide).

  2. Dans votre éditeur, ouvrez tickets.txt dans sam-notes. Remplacez la ligne du billet 103 par ce texte, puis enregistrez :

    Ticket 103: shared drive is slow. Moved the user to the new server. The user confirmed the fix.
  3. Créez un commit et poussez les changements en tant que Sam :

    Fenêtre de terminal
    git commit -am "Close ticket 103"
    git push
  4. Allez dans votre clone. Remplacez la même ligne dans ticket-notes/tickets.txt par ce texte, puis enregistrez :

    Ticket 103: shared drive is slow. Moved the user to the new server. Ticket raised by Finance.
  5. Créez un commit, puis essayez de pousser les changements :

    Fenêtre de terminal
    cd ~/projects/c14-lab/ticket-notes
    git commit -am "Add who raised ticket 103"
    git push

    La commande affiche ! [rejected] main -> main (fetch first) (envoi rejeté : récupérez d’abord les changements).

  6. Fusionnez le travail de Sam :

    Fenêtre de terminal
    git pull --no-rebase

    La sortie affiche CONFLICT (content): Merge conflict in tickets.txt (conflit de contenu : conflit de fusion dans tickets.txt).

  7. Exécutez git status. tickets.txt apparaît comme both modified.

  8. Exécutez git log --merge --oneline. La sortie affiche votre commit et celui de Sam, Close ticket 103 (fermer le billet 103).

  9. Ouvrez tickets.txt. Trouvez la ligne de base entre ||||||| et =======. Comparez chaque version à cette ligne.

  10. Remplacez tout le bloc, de <<<<<<< à >>>>>>>, par une ligne qui conserve les deux faits :

    Ticket 103: shared drive is slow. Moved the user to the new server. Ticket raised by Finance. The user confirmed the fix.
  11. Vérifiez et testez :

    Fenêtre de terminal
    git diff --check
    sh check.sh

    git diff --check n’affiche rien. check.sh affiche PASS: 4 ticket lines checked (réussite : 4 lignes de billets vérifiées).

  12. Marquez le fichier comme résolu, terminez la fusion et poussez les changements :

    Fenêtre de terminal
    git add tickets.txt
    git commit --no-edit
    git push
  13. Exécutez git log --oneline --graph -4. Le commit du haut est une fusion avec deux parents : Add who raised ticket 103 (ajouter qui a ouvert le billet 103) et Close ticket 103.

Un conflit ne nécessite pas toujours de garder les deux côtés. Créez ce conflit, puis décidez.

  1. Dans sam-notes, exécutez git pull. Remplacez la dernière ligne de README.md par Notes about support tickets for the help desk. Use one line for each ticket. (Notes sur les billets de soutien du service d’assistance. Utilisez une ligne par billet.) Créez un commit avec le message Explain the ticket line format in the README (Expliquer le format des lignes de billets dans le README), puis poussez.
  2. Dans ticket-notes, remplacez la même ligne par Notes about support tickets for the help desk. Write each ticket on one line. (Notes sur les billets de soutien du service d’assistance. Écrivez chaque billet sur une ligne.) Créez un commit avec le message Describe the ticket format (Décrire le format des billets).
  3. Exécutez git pull --no-rebase, puis résolvez le conflit dans README.md. Terminez la fusion et poussez.

Avant de modifier le fichier, répondez : que voulait chaque personne? Le résultat doit-il garder les deux côtés?

Afficher une réponse modèle

Voici à quoi ressemble le conflit. Vos deux empreintes sont différentes :

README.md
# Ticket notes
<<<<<<< HEAD
Notes about support tickets for the help desk. Write each ticket on one line.
||||||| 928f2ed
Notes about support tickets for the help desk.
=======
Notes about support tickets for the help desk. Use one line for each ticket.
>>>>>>> 555dcc3fcf504b021d15a76f90f351179bc04655

git log --merge --oneline affiche les deux messages : Describe the ticket format et Explain the ticket line format in the README. Les deux personnes voulaient la même chose, mais l’ont exprimée avec des mots différents. Si vous gardez les deux côtés, le README répète la même règle. Gardez donc un seul côté. Ici, gardez la ligne de Sam, car sa version est déjà dans le dépôt distant :

README.md
# Ticket notes
Notes about support tickets for the help desk. Use one line for each ticket.

Terminez ensuite comme avant :

Fenêtre de terminal
git diff --check
sh check.sh
git add README.md
git commit --no-edit
git push

git diff HEAD^1 HEAD montre comment la fusion a modifié votre côté : votre phrase a été remplacée par celle de Sam. Votre phrase reste dans votre commit Describe the ticket format. L’historique conserve donc les deux.

Erreur courante

Marqueurs de conflit dans un commit

Symptôme : après une fusion, sh check.sh échoue et affiche les lignes contenant des marqueurs :

Sortie
FAIL: these lines in tickets.txt do not start with 'Ticket <number>: '
2:<<<<<<< HEAD
4:||||||| 05e00df
6:=======
8:>>>>>>> 7e16da60a8af11e14488d6be51aeff28dadcfc1d

git diff --check HEAD^ HEAD affiche tickets.txt:2: leftover conflict marker (marqueur de conflit restant), et la même chose pour les lignes 4, 6 et 8.

Cause : vous avez exécuté git add et git commit avant de supprimer les marqueurs. git add ne vérifie pas le contenu d’un fichier. Cette commande indique seulement à Git que vous avez terminé.

Solution : ne modifiez pas le commit de fusion. Corrigez le fichier dans un nouveau commit.

  1. Ouvrez le fichier et modifiez le bloc pour qu’il contienne le bon texte, sans marqueurs.

  2. Exécutez git diff --check et sh check.sh. Le test doit afficher PASS (réussite).

  3. Créez un commit avec la correction :

    Fenêtre de terminal
    git commit -am "Remove leftover conflict markers"
    Sortie
    [main 9a2eb8f] Remove leftover conflict markers
    1 file changed, 1 insertion(+), 7 deletions(-)
  4. Poussez. La prochaine fois, exécutez git diff --check et le test avant git add.

Erreur courante

Impossible de créer un commit, car certains fichiers ne sont pas fusionnés

Symptôme : vous avez modifié le fichier et supprimé les marqueurs. git commit --no-edit affiche :

Sortie
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
U tickets.txt

Cause : Git ne lit pas le fichier pour déterminer si vous avez terminé. Il attend git add. U signifie que le fichier n’est pas encore fusionné.

Solution : exécutez git add tickets.txt, puis de nouveau git commit --no-edit. git status affiche All conflicts fixed but you are still merging (Tous les conflits sont résolus, mais la fusion continue) quand le fichier est marqué comme résolu.

Vérifiez votre compréhension

Prédisez : le commit d'Amira modifie la ligne 2 de tickets.txt. Le commit de Sam ajoute une ligne à la fin du fichier. Est-ce que git pull --no-rebase s'arrête avec un conflit?

Afficher la réponse

Non. Les deux modifications sont à des endroits différents, et la ligne 2 n’est pas voisine de la dernière ligne. Git prend les deux modifications et crée le commit de fusion. Un conflit survient seulement si les deux côtés ont modifié les mêmes lignes, ou des lignes voisines, depuis la base. Malgré cela, exécutez le test après chaque fusion. Une fusion sans conflit peut quand même produire un résultat incorrect.

Vérifiez votre compréhension

Vérification pratique : après l'arrêt de git pull --no-rebase sur un conflit, listez les commandes qui terminent la fusion sans risque, dans l'ordre. Nommez ensuite la commande qui annule plutôt la fusion.

Afficher la réponse
  1. git status, pour trouver les fichiers qui affichent both modified (modifié des deux côtés).
  2. git log --merge --oneline et git show <hash>, pour voir ce que chaque personne a modifié et pourquoi.
  3. Modifiez chaque fichier, gardez le bon texte et supprimez tous les marqueurs.
  4. git diff --check (n’affiche rien) et le test, sh check.sh (affiche PASS).
  5. git add <file> pour chaque fichier résolu.
  6. git commit --no-edit, puis git push.

git merge --abort annule la fusion et rétablit l’état d’avant son début.

Récapitulatif

  • Un conflit survient quand les deux côtés ont modifié les mêmes lignes depuis la base. Git s’arrête et vous laisse décider.
  • Dans une fusion, notre côté est votre branche actuelle (HEAD), et leur côté est le commit que vous intégrez. Avec zdiff3, la base apparaît entre les deux.
  • Lisez les messages de commit avec git log --merge et git show pour comprendre l’intention de chaque personne. Gardez ensuite les deux modifications, ou seulement celle qui est correcte.
  • Avant de créer un commit, exécutez git diff --check et les tests. Ensuite, git add marque le fichier comme résolu, et git commit termine la fusion.
  • git merge --abort arrête une fusion et rétablit l’état d’avant son début.

Leçon suivante : Annuler en toute sécurité