W3docs

git reset

Découvrez la commande git reset, les trois arbres de Git et leur relation avec git reset, avec des exemples pratiques.

git reset

git reset est la commande principale pour annuler des modifications dans votre dépôt local. Selon le mode choisi, elle peut désindexer un fichier, supprimer les modifications indexées ou faire reculer une branche vers un commit antérieur. Parce qu'elle peut réécrire l'emplacement pointé par votre branche et supprimer du travail, elle est puissante mais facile à mal utiliser.

Cette page explique les trois « arbres » sur lesquels opère git reset, présente ses trois modes (--soft, --mixed, --hard) avec des exemples, et indique quand utiliser git revert à la place.

Quand utiliser git reset

Utilisez git reset lorsque vous souhaitez réécrire l'historique local ou la zone d'index qui n'a pas encore été partagé :

  • Désindexer un fichier ajouté par erreur : git reset <file>.
  • Vider toute la zone d'index pour reconstruire votre prochain commit de zéro : git reset.
  • Supprimer des commits et des modifications locaux entièrement : git reset --hard <commit>.
  • Fusionner ou réécrire les derniers commits avant de pousser.

Si les commits que vous souhaitez annuler ont déjà été poussés vers une branche partagée, utilisez git revert à la place — cette commande enregistre un nouveau commit qui inverse les modifications sans réécrire l'historique dont d'autres personnes dépendent.

Git reset et les trois arbres

git reset possède trois formes d'invocation qui correspondent aux trois systèmes internes de gestion d'état de Git, souvent appelés les trois arbres de Git :

  • HEAD — l'historique des commits (l'instantané vers lequel pointe actuellement votre branche).
  • Index de staging — les modifications indexées pour le prochain commit.
  • Répertoire de travail — les fichiers sur le disque dans votre éditeur.

Nous allons examiner chacun de ces systèmes à tour de rôle.

Le répertoire de travail

Le premier arbre est le répertoire de travail. Il représente les fichiers sur le système de fichiers de votre ordinateur que votre éditeur peut modifier. Le répertoire de travail est synchronisé avec un commit spécifique du projet extrait : lorsqu'un projet est extrait, Git décompresse les versions des fichiers du dépôt sur le disque.

Si nous modifions un fichier suivi, git status le signale comme une modification non indexée dans le répertoire de travail :

echo 'hello git reset' > edited_file
git status 
#On branch master 
#Changes not staged for commit: 
#(use "git add ..." to update what will be committed) 
#(use "git checkout -- ..." to discard changes in working directory) 
#modified: edited_file

Index de staging

Le deuxième arbre est l'index de staging, qui suit les modifications indexées pour le prochain commit. Git vous cache normalement les détails internes de la zone d'index. Vous le verrez également désigné par plusieurs autres noms : cache, cache de répertoire, fichiers indexés ou zone de staging.

Pour inspecter l'index de staging directement, nous pouvons utiliser git ls-files -s, un outil de débogage qui affiche les entrées indexées avec leurs hachages d'objets :

git ls-files -s
#543644 a32de29bb3c1d643328b29ae775ad8c2e48c3256 0 edited_file

Historique des commits

Le troisième arbre est l'historique des commits. La commande git commit prend tout ce qui se trouve dans l'index de staging et l'enregistre comme un instantané permanent dans l'historique :

git commit -am "edit content of test_file"
#[master ab23324] edit the content of edited_file
#1 file changed, 1 insertion(+)
git status
#On branch master
#nothing to commit, working tree clean

Dans l'exemple ci-dessus, vous pouvez voir le nouveau commit avec le message « edit content of test_file ». Les modifications sont attachées à l'historique des commits. À ce stade, l'exécution de git status ne montre aucune modification à venir dans l'un des arbres. En invoquant git log, vous verrez l'historique des commits. Une fois les modifications effectuées à travers les trois arbres, git reset peut être utilisé.

Comment ça fonctionne

À première vue, la commande git reset présente certaines similitudes avec git checkout, car elles opèrent toutes les deux sur HEAD. La commande git checkout opère exclusivement sur le pointeur de référence HEAD, tandis que la commande git reset déplace le pointeur de référence HEAD et le pointeur de référence de la branche courante. L'illustration ci-dessous vous aidera à mieux comprendre son comportement :

git reset1

Cette illustration présente la séquence de commits sur la branche master. Comme vous pouvez le voir, la référence HEAD et la référence de la branche master pointent actuellement vers le commit d. Nous verrons comment l'image change en cas de git checkout b et de git reset b.

git checkout b

Lors de l'exécution de la commande git checkout, la référence master pointe toujours vers le commit d. En ce qui concerne la référence HEAD, elle a été déplacée et son pointeur a changé pour pointer vers le commit b. Par conséquent, le dépôt est maintenant dans un état « HEAD détachée ».

git reset2

git reset b

La commande git reset déplace à la fois le HEAD et la référence de la branche courante vers le commit spécifié. Elle peut également modifier l'état de l'index de staging et du répertoire de travail. Trois options de ligne de commande — --soft, --mixed et --hard — contrôlent jusqu'où porte ce reset :

git reset3

Options principales

Par défaut, git reset s'exécute avec les arguments --mixed et HEAD. Donc invoquer git reset est identique à git reset --mixed HEAD. Le HEAD ici est le commit cible — vous pouvez le remplacer par n'importe quelle référence de commit, comme un hachage SHA-1 (git reset 0a1b2c3) ou un pointeur relatif (git reset HEAD~2).

Le tableau ci-dessous résume quels arbres chaque mode affecte :

ModeHistorique des commits (HEAD)Index de stagingRépertoire de travail
--softdéplacéinchangéinchangé
--mixed (par défaut)déplacéréinitialisé à la cibleinchangé
--harddéplacéréinitialisé à la cibleréinitialisé à la cible (modifications perdues)

Une façon utile de s'en souvenir : --soft conserve vos modifications indexées, --mixed les conserve comme modifications non indexées, et --hard les supprime complètement.

git reset4

--hard

--hard est l'option la plus puissante et la plus dangereuse. Elle déplace la référence de l'historique des commits vers le commit cible, puis force l'index de staging et le répertoire de travail à correspondre à ce commit. Toute modification en attente dans l'index de staging et le répertoire de travail est supprimée — et cette perte ne peut pas être annulée avec git reset.

Avertissement : --hard supprime définitivement le travail non commité. Exécutez d'abord git status pour confirmer ce que vous êtes sur le point de perdre.

Pour illustrer, créez d'abord quelques modifications en attente :

echo 'test content' > test_file
git add test_file
echo 'modified content' >> edited_file

Un nouveau fichier nommé test_file a été créé et indexé, et le contenu de edited_file a été modifié dans le répertoire de travail. Vérifions l'état du dépôt avec la commande git status :

git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#new file: test_file
#Changes not staged for commit:
#(use "git add ..." to update what will be committed)
#(use "git checkout -- ..." to discard changes in working directory)
#modified: edited_file

Il y a maintenant des modifications en attente dans deux arbres : l'index de staging contient le nouveau test_file, et le répertoire de travail contient les modifications apportées à edited_file. Regardons l'état de l'index de staging :

git ls-files -s
#123126 7a32454a5477b1bf4765946147c49509a431f963 0 test_file
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_file

Le test_file a été ajouté à l'index. Le edited_file a été mis à jour, mais le SHA de l'index de staging (d7d77c1b04b5edd5acfc85de0b592449e5303770) reste le même. Ces modifications se trouvent dans le répertoire de travail. Elles ne sont pas promues vers l'index de staging car nous n'avons pas exécuté la commande git add. Exécutons maintenant git reset --hard et inspectons le nouvel état du dépôt :

git reset --hard
#HEAD is now at ab23324 update content of edited_file
git status
#On branch master
#nothing to commit, working tree clean
git ls-files -s
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_file

L'option --hard a effectué un « hard reset ». Git indique que HEAD pointe vers le commit récent ab23324. Ensuite, l'état du dépôt est vérifié avec git status. Git indique qu'il n'y a pas de modifications en attente. En ce qui concerne l'état de l'index de staging, il a été réinitialisé à un point antérieur à l'ajout de test_file. Les modifications de edited_file et l'ajout de test_file ont été effacés. Cette perte ne peut pas être annulée.

--mixed

--mixed est le mode par défaut. Il déplace les pointeurs de référence et réinitialise l'index de staging vers le commit cible, mais laisse le répertoire de travail intact. Les modifications qui ont été désindexées réapparaissent comme des modifications dans le répertoire de travail, donc rien n'est perdu — vous avez simplement la possibilité de les réindexer.

echo 'new file content' > test_file
git add test_file
echo 'append content' >> edited_file
git add edited_file
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#new file: test_file
#modified: edited_file
git ls-files -s
#123126 6a32154a5477b1bf4765946147c49509a4323d32 0 test_file
#123126 3c3262db063f9e9426901092c00a3394b4bd3445 0 edited_file

Dans l'exemple ci-dessus, test_file a été ajouté et le contenu de edited_file a été modifié, et les deux modifications ont été appliquées à l'index de staging avec git add. Avec cet état du dépôt, il est temps d'invoquer git reset :

git reset --mixed
git status
#On branch master
#Changes not staged for commit:
#(use "git add ..." to update what will be committed)
#(use "git checkout -- ..." to discard changes in working directory)
#modified: edited_file
#Untracked files:
#(use "git add ..." to include in what will be committed)
#test_file
#no changes added to commit (use "git add" and/or "git commit -a")
git ls-files -s
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_file

--mixed est le mode par défaut. Il a le même effet que git reset. Le git status indique qu'il y a des modifications dans edited_file et que test_file est un fichier non suivi. C'est le comportement exact de --mixed. L'index de staging a été réinitialisé et les modifications en attente ont été déplacées vers le répertoire de travail.

--soft

L'argument --soft déplace les pointeurs de référence et s'arrête là. Il ne touche pas l'index de staging ni le répertoire de travail, donc tout ce qui avait été commité reste indexé et prêt à être re-commité. C'est le mode à utiliser lorsque vous souhaitez fusionner plusieurs commits en un seul.

git reset --soft
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#modified: edited_file
git ls-files -s
#123126 32a252710639e5da6b515416fd779d0741e4561a 0 edited_file

Un soft reset ne déplace que l'historique des commits. Par défaut, il cible HEAD. Créons un nouveau commit puis essayons un reset --soft avec un commit cible autre que HEAD :

git commit -m "add changes to edited_file"

Notre dépôt a maintenant trois commits. Pour trouver le premier, nous vérifions son ID dans la sortie de git log :

git log
#commit 62e793f6941c7e0d4ad9a1345a175fe8f45cb9df
#Author: w3docs
#Date: Fri Nov 1 14:02:07 2019 -0800
#add changes to edited_file
#commit ab23324a6da9f0dec51ed16d3d8823f28e1a72a
#Author: w3docs
#Date: Fri Nov 1 11:31:58 2019 -0800
#change content of edited_file
#commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
#Author: w3docs
#Date: Thu Sep 31 18:40:29 2019 -0800
#initial commit

L'entrée du bas est le commit initial ; nous utiliserons son ID comme cible pour le soft reset. Vérifions d'abord l'état actuel du dépôt :

git status && git ls-files -s
#On branch master
#nothing to commit, working tree clean
#123126 32a252710639e5da6b515416fd779d0741e4561a  0 edited_file

Nous pouvons maintenant effectuer un soft reset vers le premier commit :

git reset --soft 780411da3b47117270c0e3a8d5dcfd11d28d04a4
git status && git ls-files -s
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#modified: edited_file
#123126 32a252710639e5da6b515416fd779d0741e4561a  0 edited_file

Dans l'exemple ci-dessus, nous avons effectué un soft reset et invoqué la combinaison de commandes git status et git ls-files, qui affiche l'état du dépôt. La commande git status indique qu'il y a des modifications dans edited_file, les mettant en évidence comme des modifications indexées pour le prochain commit. La sortie de git ls-files montre que l'index de staging est resté inchangé et conserve le SHA 32a252710639e5da6b515416fd779d0741e4561a. Examinons l'historique des commits après le soft reset avec git log :

git log
#commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
#Author: w3docs
#Date: Thu Sep 31 18:40:29 2019 -0800
#initial commit

La sortie ne montre désormais qu'un seul commit dans l'historique. Comme pour toutes les invocations de git reset, --soft réinitialise d'abord l'arbre des commits. Contrairement aux exemples précédents avec --hard et --mixed qui ciblaient HEAD, ce soft reset a reculé l'arbre des commits dans le temps vers un commit plus ancien — tandis que le travail lui-même est resté en sécurité dans l'index de staging.

La différence entre les commandes reset et revert

git revert est généralement un moyen plus sûr d'annuler des modifications que git reset, car git reset peut faire perdre du travail. Un git reset ne supprime pas immédiatement un commit, mais il peut laisser le commit orphelin — aucune branche ni aucun tag n'y pointe plus, donc il n'y a pas de moyen direct de l'atteindre. Git supprime éventuellement les objets orphelins lors de l'exécution de son ramasse-miettes (git gc), qui par défaut élague les objets inaccessibles datant de plus de 90 jours environ (les entrées du reflog expirent après 90 jours, ou 30 jours pour les objets inaccessibles). Jusqu'alors, vous pouvez généralement récupérer un commit orphelin avec la commande git reflog.

L'autre différence clé : git revert est conçu pour annuler des commits publics, déjà partagés en ajoutant un nouveau commit qui inverse les modifications, tandis que git reset est conçu pour annuler des modifications locales dans le répertoire de travail et l'index de staging.

Ne jamais réinitialiser l'historique publié

N'exécutez pas git reset <commit> lorsqu'il existe des instantanés après <commit> qui ont déjà été poussés vers un dépôt partagé. Une fois que vous publiez un commit, d'autres développeurs en dépendent. Réécrire ou supprimer des commits que des coéquipiers ont déjà récupérés provoquera des historiques divergents et de douloureux conflits de fusion. Utilisez git reset uniquement sur des commits qui existent exclusivement dans votre dépôt local. Pour annuler des modifications publiques, utilisez la commande git revert à la place.

Exemples

Retirer un fichier spécifique de la zone d'index sans modifier le répertoire de travail — cela désindexe le fichier tout en conservant vos modifications :

git reset <file>

Réinitialiser toute la zone d'index pour qu'elle corresponde au dernier commit, en laissant le répertoire de travail inchangé. Cela désindexe chaque fichier sans écraser les modifications, vous permettant de reconstruire l'instantané indexé de zéro :

git reset

Réinitialiser à la fois la zone d'index et le répertoire de travail pour qu'ils correspondent au dernier commit. Cela supprime toutes les modifications non commitées dans le répertoire de travail également :

git reset --hard

Reculer le sommet de la branche vers un commit donné et réinitialiser la zone d'index pour qu'elle corresponde, mais laisser le répertoire de travail intact :

git reset <commit>

Reculer le sommet de la branche courante vers un commit donné et réinitialiser à la fois la zone d'index et le répertoire de travail pour qu'ils lui correspondent :

git reset --hard <commit>

Suppression des commits locaux

Comme montré ci-dessus, vous pouvez utiliser git reset pour supprimer des commits dans votre dépôt local. Dans l'exemple ci-dessous, git reset --hard HEAD~2 recule la branche courante de deux commits, supprimant les deux instantanés les plus récents de l'historique du projet :

# Create a new file called `yourname.txt` and add some code to it
# Commit it to the project history
git add yourname.txt
git commit -m "Start to develop a project"
# Edit `yourname.txt` again and change some other tracked files, too
# Commit another snapshot
git commit -a -m "Continue developing"
# Scrap the project and remove the related commits
git reset --hard HEAD~2

Désindexer des fichiers

Une utilisation très courante de git reset est d'affiner ce qui ira dans le prochain commit. Dans l'exemple ci-dessous, nous avons deux fichiers, task.txt et index.txt, qui ont tous deux été indexés. git reset nous permet de désindexer les modifications qui n'appartiennent pas au prochain commit, afin que chaque fichier puisse être commité séparément :

# Edit task.txt and index.txt
# Stage everything in the current directory
git add .
# Realize that the changes in task.txt and index.txt
# should be committed in different snapshots
# Unstage index.txt
git reset index.txt
# Commit only task.txt
git commit -m "Edit task.txt"
# Commit index.txt in a separate snapshot
git add index.txt
git commit -m "Edit index.txt"

Pratique

Pratique
Quelles sont les fonctionnalités et options de la commande 'git reset' dans Git ?
Quelles sont les fonctionnalités et options de la commande 'git reset' dans Git ?
Was this page helpful?