Git Reset vs Revert vs Rebase
Dans cet exposé, nous explorerons les différentes approches pour manipuler les commits avec Git.
En tant que développeur, il est courant de se retrouver dans des situations où l'on souhaite revenir à un commit précédent, sans savoir comment s'y prendre. Même si vous êtes familier avec les commandes Git telles que reset, revert, et rebase, les subtiles différences entre elles peuvent ne pas être évidentes. Alors, plongeons-nous dans le sujet et démystifions ensemble les commandes git reset, revert et rebase.
Réinitialisation Git
La commande git reset est une fonction complexe qui permet d'annuler des modifications.
On peut considérer git reset comme un outil de rembobinage. Il vous permet de naviguer entre différents commits. Il existe trois modes d'exécution pour une commande git reset : --soft, --mixed et --hard. Par défaut, la commande git reset utilise le mode mixed. Le processus git reset implique trois mécanismes de gestion interne à Git : HEAD, la zone de préparation (index) et le répertoire de travail.
Le répertoire de travail est l'endroit où vous travaillez actuellement ; c'est là que résident vos fichiers. La commande git status vous permet de voir tous les fichiers et dossiers présents dans ce répertoire.
La zone de préparation, ou index, est l'endroit où Git suit et enregistre toutes les modifications apportées aux fichiers. Ces modifications enregistrées sont reflétées dans le répertoire .git. Vous utilisez la commande git add "nom_fichier" pour ajouter un fichier à la zone de préparation. La commande git status vous montrera ensuite quels fichiers s'y trouvent.
La branche actuelle dans Git est désignée par HEAD. Il pointe vers le dernier commit effectué sur la branche active. HEAD sert de pointeur pour toute référence. Lorsque vous changez de branche, le HEAD se déplace vers la nouvelle branche.
Examinons comment git reset fonctionne en modes hard, soft et mixed. Le mode hard est utilisé pour revenir au commit spécifié. Le répertoire de travail est rempli avec les fichiers de ce commit, et la zone de préparation est réinitialisée. Lors d'une réinitialisation soft, seul le pointeur est déplacé vers le commit spécifié. Les fichiers de tous les commits restent dans le répertoire de travail et la zone de préparation tels qu'ils étaient avant la réinitialisation. En mode mixed (par défaut), le pointeur et la zone de transit sont tous deux réinitialisés.
Git reset --hard
L'objectif de git reset --hard est de déplacer le HEAD vers le commit spécifié. Cela éliminera tous les commits postérieurs au commit indiqué. Cette commande va donc modifier l'historique des commits et pointera vers le commit choisi.
Dans cet exemple, je vais ajouter trois nouveaux fichiers, les commiter, puis effectuer une réinitialisation hard.
Comme vous pouvez le voir dans la commande ci-dessous, il n'y a actuellement rien à valider.
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean
Je vais maintenant créer 3 fichiers et y ajouter du contenu.
$ vi file1.txt $ vi file2.txt $ vi file3.txt
J'ajoute ensuite ces fichiers au dépôt.
$ git add file*
Lorsque vous exécutez à nouveau la commande status, elle reflètera les nouveaux fichiers que je viens de créer.
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: file1.txt new file: file2.txt new file: file3.txt
Avant de commiter, je vais vous montrer que j'ai actuellement un historique de 3 commits dans Git.
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Je vais maintenant commiter dans le dépôt.
$ git commit -m 'added 3 files' [master d69950b] added 3 files 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
Si je fais ls-files, vous verrez que les nouveaux fichiers ont été ajoutés.
$ git ls-files demo dummyfile newfile file1.txt file2.txt file3.txt
Lorsque j'exécute la commande log, j'ai maintenant 4 commits et le HEAD pointe vers le dernier commit.
$ git log --oneline d69950b (HEAD -> master) added 3 files 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Si je supprime manuellement le file1.txt et que je fais un git status, cela affichera un message indiquant que les modifications ne sont pas en attente de commit.
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: file1.txt no changes added to commit (use "git add" and/or "git commit -a")
Je vais maintenant exécuter la commande reset hard.
$ git reset --hard HEAD is now at d69950b added 3 files
Si je vérifie à nouveau l'état, je constaterai qu'il n'y a rien à commiter et que le fichier que j'avais supprimé est revenu dans le dépôt. La restauration s'est produite car, après avoir supprimé le fichier, je n'ai pas commité. Après une réinitialisation hard, le référentiel est revenu à son état précédent.
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean
Voici à quoi ressemble le journal de git après la réinitialisation.
$ git log commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 19:53:31 2020 +0530 added 3 files commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
Le but de la réinitialisation hard est de pointer vers le commit spécifié et de mettre à jour le répertoire de travail et la zone de préparation. Prenons un autre exemple. Voici comment se présente actuellement l'historique de mes commits :
Je vais exécuter la commande avec HEAD^, ce qui signifie que je veux réinitialiser au commit précédent.
$ git reset --hard HEAD^ HEAD is now at 0db602e one more commit
Vous pouvez voir que le pointeur HEAD est maintenant passé de d69950b à 0db602e.
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test

Si vous consultez le journal, le commit d69950b a disparu et la tête pointe maintenant vers le SHA 0db602e.
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 Test
Si vous exécutez la commande ls-files, vous verrez que file1.txt, file2.txt et file3.txt ne sont plus dans le dépôt car ce commit et ses fichiers ont été supprimés après la réinitialisation hard.
$ git ls-files demo dummyfile newfile
Git reset --soft
De la même manière, je vais maintenant vous montrer un exemple de réinitialisation soft. Supposons que j'ai à nouveau ajouté les 3 fichiers comme mentionné ci-dessus et que je les ai commités. Le journal git ressemblera à ce qui est indiqué ci-dessous. Vous pouvez voir que 'soft reset' est mon dernier commit, et HEAD pointe également vers celui-ci.
$ git log --oneline aa40085 (HEAD -> master) soft reset 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Vous pouvez consulter les détails du commit dans le journal à l'aide de la commande ci-dessous.
$ git log commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 21:01:36 2020 +0530 soft reset commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
Maintenant, en utilisant la réinitialisation soft, je souhaite revenir à l'un des anciens commits avec SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14.
Pour ce faire, je vais exécuter la commande ci-dessous. Il suffit de passer plus de 6 caractères de début du SHA, le SHA complet n'est pas nécessaire.
$ git reset --soft 0db602e085a4
Après avoir exécuté git log, je peux voir que le HEAD a été réinitialisé au commit que j'ai spécifié.
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
Mais la différence ici est que les fichiers du commit (aa400858aab3927e79116941c715749780a59fc9) où j'avais ajouté 3 fichiers sont toujours dans mon répertoire de travail. Ils n'ont pas été supprimés. C'est pourquoi il est préférable d'utiliser la réinitialisation soft plutôt que la réinitialisation hard. En mode soft, il n'y a aucun risque de perdre vos fichiers.
$ git ls-files demo dummyfile file1.txt file2.txt file3.txt newfile
Git revert
Dans Git, la commande revert sert à effectuer une opération d'annulation, c'est-à-dire d'annuler des modifications. Elle est similaire à la commande reset, mais la différence est qu'elle crée un nouveau commit pour revenir à un commit particulier. En bref, on peut dire que la commande git revert est un commit.
La commande git revert ne supprime aucune donnée lors de l'opération d'annulation.
Supposons que j'ajoute 3 fichiers et que j'effectue une opération git commit pour l'exemple de revert.
$ git commit -m 'add 3 files again' [master 812335d] add 3 files again 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
Le journal affichera le nouveau commit.
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Maintenant, je voudrais revenir à l'un de mes commits précédents, disons "59c86c9 new commit". J'exécuterai la commande ci-dessous.
$ git revert 59c86c9
Cela ouvrira un fichier dans lequel vous trouverez les détails du commit auquel vous essayez de revenir. Vous pouvez donner un nom à votre nouveau commit ici, puis enregistrer et fermer le fichier.
Revert "new commit" This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Your branch is ahead of 'origin/master' by 4 commits. # (use "git push" to publish your local commits) # # Changes to be committed: # modified: dummyfile
Après avoir enregistré et fermé le fichier, voici le résultat que vous obtiendrez.
$ git revert 59c86c9 [master af72b7a] Revert "new commit" 1 file changed, 1 insertion(+), 1 deletion(-)
Contrairement à reset, revert a effectué un nouveau commit supplémentaire. Si vous vérifiez à nouveau le journal, vous trouverez un nouveau commit en raison de l'opération de revert.
$ git log --oneline af72b7a (HEAD -> master) Revert "new commit" 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test

Le journal Git conservera tout l'historique des commits. Si vous souhaitez supprimer les commits de l'historique, la commande revert n'est pas le bon choix. Par contre, si vous souhaitez conserver les modifications de commit dans l'historique, revert est la commande appropriée à utiliser plutôt que reset.
Git rebase
Dans Git, rebase est un moyen de déplacer ou de combiner les commits d'une branche vers une autre. En tant que développeur, dans un scénario réel, je ne créerais pas mes fonctionnalités directement sur la branche principale. Je travaillerais plutôt sur ma propre branche (une "branche de fonctionnalité"), et une fois que j'ai quelques commits dans ma branche de fonctionnalité, je les déplacerais vers la branche principale.
La commande rebase peut parfois être un peu déroutante car elle ressemble beaucoup à la fusion. L'objectif de fusionner et de rebaser est d'intégrer les commits de ma branche de fonctionnalité à la branche principale ou à toute autre branche. Considérez le graphique suivant :

Supposons que vous travailliez en équipe avec d'autres développeurs. Dans ce cas, vous pouvez imaginer que cela peut devenir très complexe lorsque plusieurs développeurs travaillent sur différentes branches de fonctionnalités et qu'ils ont fusionné plusieurs modifications. Cela devient difficile à suivre.
C'est là que la commande rebase peut être utile. Cette fois, au lieu d'effectuer une fusion git, je vais effectuer une rebase. Je souhaite prendre mes deux commits de branche de fonctionnalité et les déplacer sur la branche master. Un rebase prendra tous mes commits de la branche de fonctionnalité et les déplacera au-dessus des commits de la branche principale. En fait, Git va dupliquer les commits de la branche de fonctionnalité sur la branche principale.

Cette approche vous donnera un historique linéaire clair avec tous les commits à la suite.

Cela permet de suivre facilement quels commits sont allés où. Vous pouvez imaginer que si vous faites partie d'une équipe avec de nombreux développeurs, tous les commits seront toujours alignés. Il est donc très facile à suivre même si plusieurs personnes travaillent sur le même projet en même temps.
Laissez-moi vous montrer cela en pratique.
Voici à quoi ressemble ma branche master actuellement. Elle a 4 commits.
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Je vais exécuter la commande ci-dessous pour créer et passer à une nouvelle branche appelée fonctionnalité, qui sera créée à partir du 2e commit, c'est-à-dire 59c86c9.
(master) $ git checkout -b feature 59c86c9 Switched to a new branch 'feature'
Si vous consultez le journal dans la branche de fonctionnalité, il n'y a que 2 commits provenant de master (la ligne principale).
(feature) $ git log --oneline 59c86c9 (HEAD -> feature) new commit e2f44fc (origin/master, origin/HEAD) test
Je vais créer la fonctionnalité 1 et la commiter dans la branche de fonctionnalité.
(feature) $ vi feature1.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 1' [feature c639e1b] feature 1 1 file changed, 1 insertion(+) create mode 100644 feature1.txt
Je vais créer une autre fonctionnalité, c'est-à-dire la fonctionnalité 2, dans la branche de fonctionnalité et la commiter.
(feature) $ vi feature2.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 2' [feature 0f4db49] feature 2 1 file changed, 1 insertion(+) create mode 100644 feature2.txt
Maintenant, si vous consultez le journal de la branche de fonctionnalité, il contient deux nouveaux commits que j'ai exécutés ci-dessus.
(feature) $ git log --oneline 0f4db49 (HEAD -> feature) feature 2 c639e1b feature 1 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Maintenant, je veux ajouter ces deux nouvelles fonctionnalités à la branche master. Pour cela, je vais utiliser la commande rebase. À partir de la branche de fonctionnalité, je vais rebaser sur la branche master. Cela va réancrer ma branche de fonctionnalité par rapport aux dernières modifications de la branche master.
(feature) $ git rebase master Successfully rebased and updated refs/heads/feature.
Je vais maintenant vérifier la branche master.
(feature) $ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits)
Et enfin, je vais rebaser la branche master sur ma branche feature. Cela prendra ces deux nouveaux commits sur ma branche de fonctionnalité et les rejouera au-dessus de ma branche principale.
(master) $ git rebase feature Successfully rebased and updated refs/heads/master.
Maintenant, si je vérifie le journal sur la branche master, je peux voir que les deux commits de ma branche de fonctionnalité ont été ajoutés avec succès à ma branche master.
(master) $ git log --oneline 766c996 (HEAD -> master, feature) feature 2 c036a11 feature 1 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Voici les commandes reset, revert et rebase dans Git.
Conclusion
Nous avons exploré les commandes reset, revert et rebase dans Git. J'espère que ce guide étape par étape vous a été utile. Vous savez maintenant comment manipuler vos commits en fonction de vos besoins en utilisant les commandes présentées dans cet article.