GitVoici le dix-neuvième volet de la formation Git. Dans mon précédent article, nous avons vu en détail le rôle de la référence HEAD. Aujourd’hui nous allons voir ensemble ce que vous pouvez faire si vous souhaitez supprimer ou autrement défaire un commit avec Git. Ce sera l’occasion de mettre en pratique ce que nous avons appris sur HEAD.

Plutôt soft ou hard ?

Faisons une petite démonstration pratique avec le dépôt Git formation-git/atelier-18. Effectuez une nouvelle copie de travail de ce dépôt :

$ cd ~/formation-git/
$ cp -R atelier-18/ atelier-26
$ cd atelier-26/

Affichons sommairement son historique :

$ git log --oneline
cbeca14 (HEAD -> master) Ajout du fichier Cartes.
7def037 Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

D’après les logs, le dernier commit a ajouté un fichier au dépôt. Voyons ce que cela a donné en détail :

$ git diff HEAD~1 HEAD
diff --git a/Cartes.md b/Cartes.md
new file mode 100644
index 0000000..a6e35b6
--- /dev/null
+++ b/Cartes.md
@@ -0,0 +1,9 @@
+# Cartes routières
+
+- [ ] France
+
+- [ ] Italie
+
+- [ ] Suisse
+
+- [ ] Autriche

À ce stade, vous vous dites que réflexion faite, ce n’est pas la peine d’établir une liste de cartes routières, puisque vous disposez d’un GPS avec des cartes pour toute l’Europe. Le fichier Cartes.md est donc inutile, tout comme le dernier commit qui l’a ajouté au dépôt. Est-ce qu’il n’y aurait pas moyen de supprimer ce dernier commit et le fichier qui va avec ?

Oui, avec la commande git reset, qui permet de déplacer la référence HEAD.

Un reset peut fonctionner de trois manières :

  • soft
  • mixed
  • hard

Voyons par la pratique ce que cela donne et commençons par la méthode douce :

$ git reset --soft HEAD~1

Examinons le résultat de cette opération :

$ git log --oneline
7def037 (HEAD -> master) Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.
$ ls
Cartes.md  Entretien.md  Roadtrip.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   Cartes.md

Voici en gros ce qui s’est passé :

  • Le dernier commit a été supprimé.
  • Plus exactement, HEAD a été déplacé vers HEAD~1.
  • Le fichier Cartes.md a été placé dans l’index.
  • On en trouve une copie dans le répertoire de travail.

Si jamais je change d’avis, je peux donc rétablir l’état de mon dépôt comme ceci :

$ git commit -m "Ajout du fichier Cartes."
[master 591f650] Ajout du fichier Cartes.
 1 file changed, 9 insertions(+)
 create mode 100644 Cartes.md
$ git log --oneline
591f650 (HEAD -> master) Ajout du fichier Cartes.
7def037 Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

Comment se passent les choses si j’effectue un reset en mode mixed ? Essayons :

$ git reset --mixed HEAD~1
$ git log --oneline
7def037 (HEAD -> master) Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.
$ ls
Cartes.md  Entretien.md  Roadtrip.md
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        Cartes.md

nothing added to commit but untracked files present (use "git add" to track)
  • Le dernier commit a disparu.
  • Cette fois-ci, le fichier Cartes.md n’a pas été copié dans l’index.
  • En revanche, il est toujours présent dans le répertoire de travail.

Si je veux annuler l’opération de suppression du commit, je peux toujours faire marche arrière comme ceci :

$ git add Cartes.md 
$ git commit -m "Ajout du fichier Cartes."
[master 51ac9f6] Ajout du fichier Cartes.
 1 file changed, 9 insertions(+)
 create mode 100644 Cartes.md
$ git log --oneline
51ac9f6 (HEAD -> master) Ajout du fichier Cartes.
7def037 Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

Il ne nous reste plus qu’à inspecter le fonctionnement du mode hard :

$ git reset --hard HEAD~1
HEAD is now at 7def037 Ajout d'une destination.
$ git log --oneline
7def037 (HEAD -> master) Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.
$ ls
Entretien.md  Roadtrip.md
$ git status
On branch master
nothing to commit, working tree clean
  • Le dernier commit a disparu des logs.
  • Le fichier Cartes.md a également été envoyé au paradis des octets. On ne le trouve plus ni dans l’index, ni dans le répertoire de travail.

Deux remarques sur ces différentes façons d’effectuer un reset :

  • Le mode mixed est le mode utilisé par défaut : git reset HEAD~1 équivaut à git reset --mixed HEAD~1.
  • Contrairement aux modes soft et mixed, le mode hard est un mode destructif.

Défaire un commit

Il existe une autre façon de revenir sur ses pas et détricoter ce qu’on vient de faire, c’est la commande git revert. Là encore, un exemple pratique vous permettra de mieux comprendre. Effectuez une nouvelle copie de travail du dépôt Git formation-git/atelier-18 :

$ cd ~/formation-git/
$ cp -R atelier-18/ atelier-27
$ cd atelier-27/

C’est toujours le même dépôt que dans l’exemple ci-dessus :

$ git log --oneline
cbeca14 (HEAD -> master) Ajout du fichier Cartes.
7def037 Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

Tout à l’heure nous avons invoqué git diff pour savoir en quoi consistait exactement ce dernier commit :

$ git diff HEAD~1 HEAD
diff --git a/Cartes.md b/Cartes.md
new file mode 100644
index 0000000..a6e35b6
--- /dev/null
+++ b/Cartes.md
@@ -0,0 +1,9 @@
+# Cartes routières
+
+- [ ] France
+
+- [ ] Italie
+
+- [ ] Suisse
+
+- [ ] Autriche

Si je souhaite faire marche arrière, j’ai le choix :

  • Je peux supprimer le commit grâce à git reset, comme nous venons de le voir.
  • Alternativement, je peux faire une sorte de « commit inversé » grâce à la commande git revert.

Voyons ce que cela donne dans la pratique :

$ git revert HEAD

Cette opération ouvre l’éditeur par défaut avec un message de commit :

Revert "Ajout du fichier Cartes."
This reverts commit cbeca14d6f520ccf1ad9c15098d8a6dcbb66c112.

Je garde le message de commit tel quel et je l’enregistre :

Removing Cartes.md
[master 301909d] Revert "Ajout du fichier Cartes."
 1 file changed, 9 deletions(-)
 delete mode 100644 Cartes.md

L’opération s’est soldée par un nouveau commit :

$ git log --oneline
301909d (HEAD -> master) Revert "Ajout du fichier Cartes."
cbeca14 Ajout du fichier Cartes.
7def037 Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

Maintenant, si j’effectue un git diff pour voir la différence entre les deux précédents commits, je m’aperçois qu’ils sont exactement opposés :

$ git diff HEAD~2 HEAD~1
diff --git a/Cartes.md b/Cartes.md
new file mode 100644
index 0000000..a6e35b6
--- /dev/null
+++ b/Cartes.md
@@ -0,0 +1,9 @@
+# Cartes routières
+
+- [ ] France
+
+- [ ] Italie
+
+- [ ] Suisse
+
+- [ ] Autriche
$ git diff HEAD~1 HEAD
diff --git a/Cartes.md b/Cartes.md
deleted file mode 100644
index a6e35b6..0000000
--- a/Cartes.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Cartes routières
-
-- [ ] France
-
-- [ ] Italie
-
-- [ ] Suisse
-
-- [ ] Autriche

ImportantNotez la différence cruciale dans la syntaxe :

  • Lorsque vous écrivez git revert HEAD, vous écrivez en argument l’ID du commit que vous souhaitez défaire.
  • Avec git reset HEAD~1, vous fournissez l’ID du commit vers lequel vous souhaitez revenir.

Vous voilà donc confronté à un choix : utiliser reset ou revert ? À vous de voir si vous préférez réécrire l’histoire ou garder une trace de vos errances.

Éditer un message de commit

Il nous reste à voir un dernier cas de figure. Il peut arriver que l’on effectue un commit avec un message pas assez explicite ou autrement inapproprié. Et après coup on se dit qu’on aurait pu faire mieux. Dans ce cas, Git nous permet de rectifier le tir.

Revenez dans le dépôt Git ~/formation-git/atelier-26 et affichez les logs :

$ cd ../atelier-26
$ git log --oneline
7def037 (HEAD -> master) Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

Le message de mon dernier commit dit « Ajout d’une destination ». Admettons que j’aurais préféré écrire « Ajout d’une étape ». Dans ce cas, je peux faire ceci :

$ git commit --amend -m "Ajout d'une étape."
[master 2393db1] Ajout d'une étape.
 Date: Tue Jan 31 07:56:14 2023 +0100
 1 file changed, 2 insertions(+)

Ici j’ai rectifié le tir avec un nouveau commit au contenu identique mais avec un message différent :

$ git log --oneline
2393db1 (HEAD -> master) Ajout d'une étape.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

ImportantGardez à l’esprit que cette fonctionnalité est limitée au dernier commit en cours. Elle ne vous permettra pas d’éditer des messages de commits antérieurs.

Exercice

  • Effectuez trois copies du dépôt Git atelier-12 et nommez-les respectivement atelier-28, atelier-29 et atelier-30.
  • Inspectez sommairement le contenu et l’historique du dépôt atelier-28.
  • Essayez de savoir en quoi consistait le dernier commit sur la branche hello-cow.
  • Supprimez ce commit en mode soft.
  • Changez d’avis et revenez en arrière.
  • Supprimez le dernier commit en mode mixed.
  • Changez d’avis et revenez en arrière.
  • Supprimez le dernier commit en mode hard.
  • Changez d’avis et… rendez-vous compte en grinçant des dents qu’il est trop tard pour changer d’avis.
  • Placez-vous à la racine du dépôt Git dans le répertoire atelier-29.
  • Défaites le dernier commit de la branche hello-cow en gardant une trace de ce détricotage.
  • Affichez le détail des deux derniers commits et comparez-les.
  • Placez-vous à la racine du dépôt Git dans le répertoire atelier-30.
  • Affichez les logs du dépôt.
  • Remplacez le message du dernier commit sur la branche hello-cow par « Afficher une vache fatiguée ».

Lire la suite : Cloner un dépôt


La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.

 

Catégories : Formation

0 commentaire

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *