Aller au contenu
English

Annuler en toute sécurité

  • Leçon 11
  • 45 min
  • Module 4 : Résoudre et récupérer

Ce que vous saurez faire

Annulez une erreur sans nuire au travail de votre équipe. Après cette leçon, vous pourrez choisir la bonne commande pour chaque cas : une modification, une modification préparée, un commit local ou un commit déjà partagé.

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

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le laboratoire ~/projects/c14-lab dans l’état m04, avec votre fusion envoyée. Vous l’avez envoyée à la fin de Les conflits, étape par étape : résoudre, tester et créer un commit.
  • Dans ~/projects/c14-lab/ticket-notes, exécutez git pull. La sortie doit indiquer Already up to date. (déjà à jour) ou Fast-forward (avance rapide).
  • Si git pull indique Not possible to fast-forward (avance rapide impossible), votre fusion de la dernière leçon n’est pas terminée. Faites d’abord l’exemple détaillé de Les conflits, étape par étape, puis envoyez la fusion.
  • Pour repartir d’un état connu, exécutez sh nextia-learning-labs/C14/setup.sh m04 --reset dans ~/projects. Refaites ensuite l’exemple détaillé de Les conflits, étape par étape, jusqu’au dernier git push.
  • Vos propres commits, comme les commits de fusion, ont des empreintes différentes de celles de cette leçon. Les empreintes des commits de Sam provenant du script de préparation sont identiques.

Git propose trois commandes qui « annulent ». Elles agissent à différents endroits. Une seule convient au travail que votre équipe possède déjà.

  • Restore modifie les fichiers dans votre arbre de travail ou votre zone de préparation. Cette commande ne touche aucun commit. Utilisez-la pour les modifications que vous n’avez pas encore enregistrées dans un commit.
  • Revert crée un nouveau commit qui fait l’inverse d’un ancien commit. Si l’ancien commit ajoutait une ligne, le commit d’annulation la supprime. L’ancien commit reste dans l’historique. Utilisez cette commande pour les commits que d’autres personnes possèdent déjà.
  • Reset déplace votre branche actuelle vers un autre commit. Les commits après ce point ne sont plus sur la branche. Utilisez cette commande seulement pour les commits que personne d’autre ne possède.
Commande Ce qu’elle modifie Sans risque pour les commits envoyés?
git restore --staged <file> La zone de préparation. Votre modification reste dans le fichier. Ne sert pas aux commits
git restore <file> Le fichier dans l’arbre de travail. Votre modification est perdue. Ne sert pas aux commits
git revert <hash> Ajoute un nouveau commit à la branche. Oui
git reset <hash> Recule l’étiquette de la branche. Non

Pourquoi reset présente-t-il un risque pour les commits envoyés? Vos collègues possèdent déjà ces commits. Si vous reculez main, puis forcez l’envoi, les commits disparaissent du dépôt distant. La prochaine récupération de vos collègues échoue ou ramène les commits. Tout ce qui repose sur eux cesse de fonctionner. C’est une réécriture de l’historique partagé. Faites-le seulement si toute l’équipe donne d’abord son accord. Ne le faites jamais sur une branche partagée comme main pour corriger rapidement un problème. Une annulation avec revert ajoute à l’historique sans rien supprimer. Tout le monde peut donc la récupérer normalement.

Un arbre de décision. Première question : la modification est-elle enregistrée dans un commit? Sinon, demandez : est-elle préparée? Si oui, utilisez git restore --staged pour la retirer de la zone de préparation et garder la modification. Sinon, utilisez git restore pour supprimer la modification, sans possibilité d'annulation. Si la modification est enregistrée dans un commit, demandez : le commit est-il envoyé ou partagé? Si oui, utilisez git revert pour créer un nouveau commit qui l'annule. Sinon, demandez : voulez-vous garder les modifications dans vos fichiers? Si oui, utilisez git reset --soft HEAD~1. Sinon, utilisez git reset --hard HEAD~1, qui supprime le commit et les modifications.
Choisissez la commande d'annulation selon deux faits : la modification est-elle enregistrée dans un commit, et quelqu'un d'autre la possède-t-il?
Description textuelle du diagramme
  1. La modification est-elle enregistrée dans un commit? Sinon, passez à la question 2. Si oui, passez à la question 3.
  2. La modification est-elle préparée? Si oui, exécutez git restore --staged <file> : elle quitte la zone de préparation et reste dans votre fichier. Sinon, exécutez git restore <file> : la modification est supprimée, sans possibilité d’annulation.
  3. Le commit est-il envoyé, ou quelqu’un d’autre le possède-t-il? Si oui, exécutez git revert <hash> : un nouveau commit l’annule. Sinon, passez à la question 4.
  4. Voulez-vous garder les modifications dans vos fichiers? Si oui, exécutez git reset --soft HEAD~1 : le commit disparaît, mais ses modifications restent préparées. Sinon, exécutez git reset --hard HEAD~1 : le commit et ses modifications disparaissent.

Annuler une modification non enregistrée dans un commit

Section intitulée « Annuler une modification non enregistrée dans un commit »

Amira ajoute une ligne de test à tickets.txt et la prépare par erreur. git status indique la commande qui la retire de la zone de préparation :

git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: tickets.txt
Terminal (Amira)
git restore --staged tickets.txt
git status --short
Sortie
M tickets.txt

Le M est passé à la deuxième colonne. La modification n’est plus préparée, mais elle reste dans le fichier. Amira ne veut pas garder cette ligne.

Terminal (Amira)
git restore tickets.txt
git status --short

La deuxième commande n’affiche rien : l’arbre de travail est propre et la ligne de test a disparu.

Exemple détaillé : annuler le commit défectueux de Sam

Section intitulée « Exemple détaillé : annuler le commit défectueux de Sam »

Amira consulte contacts.txt. L’équipe réseau est absente :

contacts.txt
Accounts team: ext. 4300
Help desk: ext. 4100

Elle liste les commits qui ont modifié ce fichier. Un -- avant un nom de fichier signifie « seulement les commits qui ont modifié ce fichier » :

Terminal (Amira)
git log --oneline -- contacts.txt
Sortie
f3bc1c5 Sort contacts by name
55b7943 Add contacts

Le message dit « sort » (trier), mais un message n’est pas la modification. Elle vérifie ce que le commit a réellement fait :

Terminal (Amira)
git show f3bc1c5
Sortie
commit f3bc1c576e109cc78d291f676c393381d5177526
Author: Sam Okafor <sam@example.com>
Date: Tue Oct 6 09:08:00 2026 -0400
Sort contacts by name
diff --git a/contacts.txt b/contacts.txt
index 0ce0116..209c541 100644
--- a/contacts.txt
+++ b/contacts.txt
@@ -1,3 +1,2 @@
Help desk: ext. 4100
Network team: ext. 4410
Accounts team: ext. 4300
Help desk: ext. 4100

Sam a trié la liste. Il a aussi supprimé Network team: ext. 4410 (équipe réseau : poste 4410) par erreur. Sam a envoyé ce commit, puis le commit Add ticket 104 (ajouter le billet 104). Les deux commits sont dans le dépôt distant. La fusion d’Amira, faite à la dernière leçon, repose sur eux. Elle ne doit donc pas les supprimer de l’historique. Elle annule le commit défectueux avec revert.

git revert applique la modification inverse dans un nouveau commit. --no-edit conserve le message écrit par Git :

Terminal (Amira)
git revert --no-edit f3bc1c5
Sortie
[main 6643efd] Revert "Sort contacts by name"
Date: Mon Oct 5 22:05:28 2026 -0400
1 file changed, 2 insertions(+), 1 deletion(-)

Elle examine le nouveau commit :

Terminal (Amira)
git log -1
Sortie
commit 6643efd8cee1fce74247da2fd0441d3117d7b7bc
Author: Amira Khan <amira@example.com>
Date: Mon Oct 5 22:05:28 2026 -0400
Revert "Sort contacts by name"
This reverts commit f3bc1c576e109cc78d291f676c393381d5177526.

Le message indique l’empreinte complète du commit qu’il annule. Toute personne qui lit l’historique plus tard peut retrouver les deux commits.

Une annulation avec revert annule un seul commit. Amira vérifie les deux fichiers :

Terminal (Amira)
cat contacts.txt
grep "Ticket 104" tickets.txt
sh check.sh
Sortie
Help desk: ext. 4100
Network team: ext. 4410
Accounts team: ext. 4300
Ticket 104: laptop will not charge. Replaced the power adapter.
PASS: 4 ticket lines checked

grep affiche les lignes d’un fichier qui contiennent le texte. L’équipe réseau est de retour. Le commit ultérieur de Sam, Add ticket 104, et la fusion d’Amira sont toujours présents. L’annulation est un nouveau commit ordinaire. Un envoi normal fonctionne donc :

Terminal (Amira)
git push
Sortie
To /Users/amira/projects/c14-lab/team-remote.git
b850b9e..6643efd main -> main

Le tri était utile. Amira informe donc Sam de l’annulation. Sam peut trier de nouveau la liste dans un nouveau commit, cette fois avec les trois équipes.

Reset : seulement pour les commits que personne d’autre ne possède

Section intitulée « Reset : seulement pour les commits que personne d’autre ne possède »

Reset déplace l’étiquette de votre branche vers un autre commit. Cette commande a trois modes. Chaque mode déplace la branche. Ils traitent différemment les modifications des commits qui quittent la branche :

Mode La branche La zone de préparation Vos fichiers
git reset --soft HEAD~1 Recule d’un commit Garde les modifications préparées Gardent les modifications
git reset HEAD~1 (--mixed, le mode par défaut) Recule d’un commit Les modifications ne sont pas préparées Gardent les modifications
git reset --hard HEAD~1 Recule d’un commit Correspond au commit Les modifications sont supprimées

HEAD~1 signifie « un commit avant HEAD ». HEAD~2 signifie deux commits avant.

Pour essayer reset sans risque, utilisez un clone jetable : une copie distincte du dépôt que vous supprimez après l’exercice. Rien de ce que vous y faites ne modifie votre ticket-notes. N’envoyez rien depuis ce clone.

Amira crée le clone à côté des autres et y ajoute un commit :

Terminal (Amira)
cd ~/projects/c14-lab
git clone team-remote.git scratch
cd scratch
Sortie
Cloning into 'scratch'...
done.

Elle ajoute la ligne Ticket 105: monitor flickers. Replaced the cable. (billet 105 : l’écran clignote. Câble remplacé.) à tickets.txt. Elle l’enregistre dans un commit avec git commit -am "Add ticket 105". Le commit existe seulement dans scratch :

git log --oneline -2
5dd5cac (HEAD -> main) Add ticket 105
6643efd (origin/main, origin/HEAD) Revert "Sort contacts by name"

Soft : le commit disparaît, mais sa modification reste préparée. Utilisez ce mode pour modifier le dernier commit avant de l’envoyer.

Terminal (Amira, in scratch)
git reset --soft HEAD~1
git status --short
Sortie
M tickets.txt

Elle crée de nouveau un commit avec le même message, puis essaie le mode par défaut.

Mixed : le commit disparaît, mais sa modification reste dans le fichier, sans être préparée.

Terminal (Amira, in scratch)
git commit -m "Add ticket 105"
git reset HEAD~1
Sortie (dernières lignes)
Unstaged changes after reset:
M tickets.txt

Hard : elle crée encore un commit, puis supprime le commit et sa modification.

Terminal (Amira, in scratch)
git commit -am "Add ticket 105"
git reset --hard HEAD~1
Output (last line)
HEAD is now at 6643efd Revert "Sort contacts by name"

git status --short n’affiche rien. La ligne du billet 105 a disparu du fichier. Dans Récupérer son travail, vous apprendrez à récupérer un commit comme celui-ci.

Exercice guidé : annuler un commit, puis réinitialiser dans un clone

Section intitulée « Exercice guidé : annuler un commit, puis réinitialiser dans un clone »
  1. Allez dans votre clone et vérifiez qu’il est à jour :

    Fenêtre de terminal
    cd ~/projects/c14-lab/ticket-notes
    git pull
  2. Ajoutez la ligne Test line. Delete me. (ligne de test à supprimer) à la fin de tickets.txt, puis enregistrez. Ajoutez ensuite le fichier à la zone de préparation :

    Fenêtre de terminal
    git add tickets.txt
  3. Retirez-le de la zone de préparation, puis vérifiez :

    Fenêtre de terminal
    git restore --staged tickets.txt
    git status --short

    La sortie est M tickets.txt : la modification est dans le fichier, mais pas dans la zone de préparation.

  4. Exécutez git diff tickets.txt. Vérifiez que la ligne de test est la seule modification. Supprimez-la ensuite :

    Fenêtre de terminal
    git restore tickets.txt
    git status --short

    git status --short n’affiche rien.

  5. Trouvez le commit (enregistrement des modifications) qui a supprimé l’équipe Network :

    Fenêtre de terminal
    git log --oneline -- contacts.txt
    git show f3bc1c5

    La différence montre -Network team: ext. 4410 (suppression de l’équipe Network, poste 4410).

  6. Annulez ce commit :

    Fenêtre de terminal
    git revert --no-edit f3bc1c5

    La sortie commence par [main et montre Revert "Sort contacts by name" (annuler le tri des contacts par nom).

  7. Vérifiez le résultat :

    Fenêtre de terminal
    cat contacts.txt
    grep "Ticket 104" tickets.txt
    sh check.sh

    contacts.txt contient trois équipes. La ligne du billet 104 est toujours là. Le test affiche PASS (réussite).

  8. Exécutez git log -1. Le message contient This reverts commit f3bc1c576e109cc78d291f676c393381d5177526. (ceci annule le commit indiqué).

  9. Envoyez les commits au dépôt distant :

    Fenêtre de terminal
    git push

    La sortie montre main -> main.

  10. Créez un clone temporaire :

    Fenêtre de terminal
    cd ~/projects/c14-lab
    git clone team-remote.git scratch
    cd scratch
  11. Dans scratch, ajoutez une ligne pour le billet 105 dans tickets.txt. Enregistrez, puis créez un commit :

    Fenêtre de terminal
    git commit -am "Add ticket 105"
  12. Exécutez git reset --soft HEAD~1, puis git status --short. La sortie est M tickets.txt : la modification est dans la zone de préparation.

  13. Créez à nouveau un commit avec git commit -m "Add ticket 105". Exécutez ensuite git reset HEAD~1, puis git status --short. La sortie est M tickets.txt : la modification n’est pas dans la zone de préparation.

  14. Vérifiez avec pwd que vous êtes dans scratch. Créez ensuite un nouveau commit et réinitialisez avec --hard :

    Fenêtre de terminal
    git commit -am "Add ticket 105"
    git reset --hard HEAD~1
    git status --short

    git status --short n’affiche rien. La ligne du billet 105 a disparu.

  15. Supprimez le clone temporaire. La prochaine leçon en crée un nouveau. rm -rf supprime un dossier sans possibilité d’annulation. Vérifiez donc son nom avant d’appuyer sur Enter (Entrée) :

    Fenêtre de terminal
    cd ~/projects/c14-lab
    rm -rf scratch

À vous de jouer : annuler un commit et garder le suivant

Section intitulée « À vous de jouer : annuler un commit et garder le suivant »

Sam fait une autre erreur, puis fait du bon travail après. Jouez les deux rôles.

  1. Dans sam-notes, exécutez git pull. Dans contacts.txt, remplacez Accounts team: ext. 4300 (équipe Accounts, poste 4300) par Accounts team: ext. 4030 (équipe Accounts, poste 4030). Créez un commit avec le message Update the accounts team extension (mettre à jour le poste de l’équipe Accounts).
  2. Toujours dans le rôle de Sam, ajoutez la ligne Ticket 105: new starter has no email account. Created the account. (billet 105 : une nouvelle personne n’a pas de compte courriel; compte créé) à tickets.txt. Créez un commit avec le message Add ticket 105 (ajouter le billet 105). Envoyez les deux commits au dépôt distant.
  3. Dans ticket-notes, récupérez les commits du dépôt distant. Le bon poste est 4300. Annulez la modification incorrecte de Sam et gardez son billet 105. Envoyez le résultat au dépôt distant.

Notez les commandes que vous avez utilisées. Expliquez pourquoi vous n’avez pas utilisé git reset.

Afficher une réponse modèle
Fenêtre de terminal
cd ~/projects/c14-lab/ticket-notes
git pull
git log --oneline -3
Sortie (dernières lignes)
f30d4c7 Add ticket 105
815c329 Update the accounts team extension
6643efd Revert "Sort contacts by name"

Vos empreintes sont différentes. Vérifiez la modification avant de l’annuler. git show 815c329 montre -Accounts team: ext. 4300 et +Accounts team: ext. 4030 (remplacement du poste 4300 par 4030). Ensuite :

Fenêtre de terminal
git revert --no-edit 815c329
cat contacts.txt
tail -1 tickets.txt
sh check.sh
git push
Sortie (extrait)
[main 2bf80ca] Revert "Update the accounts team extension"
Help desk: ext. 4100
Network team: ext. 4410
Accounts team: ext. 4300
Ticket 105: new starter has no email account. Created the account.
PASS: 5 ticket lines checked

tail -1 affiche la dernière ligne d’un fichier. Le poste est de nouveau 4300, et le billet 105 de Sam est toujours là.

Pourquoi ne pas réinitialiser? Les deux commits de Sam sont sur le dépôt distant. Pour supprimer 815c329 par réinitialisation, vous devez ramener main avant ce commit. Cela supprime aussi Add ticket 105. Le dépôt distant rejette ensuite votre envoi, car votre branche main est en retard. L’annulation supprime seulement les effets du commit incorrect, et tout le monde peut la récupérer.

Erreur courante

Réinitialiser un commit déjà envoyé, puis voir l'envoi rejeté

Symptôme : vous avez utilisé une réinitialisation pour supprimer un commit déjà envoyé au dépôt distant. Ici, Amira essaie de supprimer f3bc1c5 dans un clone temporaire avec git reset --hard f3bc1c5~1 :

Sortie
HEAD is now at e058b3f Add ticket 103

grep "Ticket 104" tickets.txt n’affiche rien. Le commit suivant de Sam et la fusion ont aussi disparu de main. Ensuite, git status et git push affichent :

Sortie
Your branch is behind 'origin/main' by 5 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
Sortie
! [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. ...

Le nombre de commits peut être différent dans votre dépôt.

Cause : la réinitialisation a ramené votre branche main en arrière. Elle a supprimé le commit incorrect et tous les commits suivants. Le dépôt distant contient toujours ces commits, donc votre branche main est en retard. Git rejette l’envoi, car il supprimerait les commits de votre équipe du dépôt distant.

Solution : ne forcez pas l’envoi. Récupérez les commits partagés, puis annulez le commit incorrect.

  1. Exécutez git pull. C’est une avance rapide : main avance jusqu’au même commit que origin/main.

    Sortie (premières lignes)
    Updating e058b3f..dbad696
    Fast-forward
  2. Exécutez git log --oneline -5 et cherchez un commit Revert (annulation). Dans cet exercice, main contient déjà votre annulation de l’exemple détaillé. Arrêtez-vous donc ici et supprimez le clone temporaire.

  3. Dans un vrai dépôt, exécutez git revert --no-edit <hash> dans votre propre clone, pas dans un clone temporaire. Exécutez ensuite sh check.sh et git push.

Un envoi forcé, git push --force, remplacerait la branche distante main par la vôtre, et les commits de Sam disparaîtraient. Ne forcez jamais un envoi vers une branche partagée pour corriger une erreur. Si votre équipe doit vraiment réécrire l’historique partagé, entendez-vous d’abord ensemble.

Vérifiez votre compréhension

Prédisez : Amira annule un commit qui a ajouté la ligne « Billet 110: ... ». À quoi ressemble l'historique après l'annulation, et contient-il encore l'ancien commit?

Afficher la réponse

L’historique contient un commit de plus au sommet : Revert "...", avec This reverts commit <hash>. (ceci annule le commit indiqué) dans son message. Ce nouveau commit supprime la ligne. L’ancien commit reste dans l’historique avec sa ligne, donc git show <old hash> l’affiche toujours. L’annulation ajoute des éléments à l’historique. Elle n’en supprime jamais.

Vérifiez votre compréhension

Vérification pratique : pour chaque situation, nommez la commande. (a) Vous avez ajouté un fichier à la zone de préparation par erreur. (b) Vous avez modifié un fichier et voulez retrouver la dernière version enregistrée dans un commit. (c) Un commit incorrect est sur le dépôt distant. (d) Votre dernier commit n'a pas été envoyé, et vous voulez le modifier.

Afficher la réponse
  • (a) git restore --staged <file>. La modification reste dans le fichier.
  • (b) git restore <file>. Lisez d’abord git diff <file> : la modification ne pourra pas être récupérée.
  • (c) git revert <hash>, puis testez et envoyez au dépôt distant. Les commits suivants des autres personnes restent.
  • (d) git reset --soft HEAD~1. Les modifications restent dans la zone de préparation. Corrigez-les, puis créez un nouveau commit.

Récapitulatif

  • Restore (restaurer) agit sur les fichiers dont les modifications ne sont pas enregistrées dans un commit. --staged les retire de la zone de préparation; sans cette option, la modification est supprimée.
  • Revert crée un nouveau commit qui annule un ancien commit. C’est la façon sûre d’annuler un commit que l’équipe possède déjà. Les commits suivants restent.
  • Reset (réinitialiser) ramène votre branche en arrière. Utilisez-le seulement pour les commits que personne d’autre ne possède. --soft et le mode par défaut gardent vos modifications. --hard les supprime.
  • Ne réécrivez pas l’historique partagé pour corriger une erreur. Si une réinitialisation fait échouer un envoi, récupérez les commits, puis annulez le commit incorrect.
  • Essayez d’abord les commandes destructives dans un clone temporaire.

Leçon suivante : Récupérer son travail