Aller au contenu

git reset

git reset

Git reset et les trois arbres

La commande git reset est un outil utilisé pour annuler des changements. Elle existe sous trois formes d’appel correspondant aux trois systèmes internes de gestion d’état de Git, appelés les trois arbres de Git. Ces systèmes incluent HEAD (l’historique des commits), l’index de staging et le répertoire de travail. Nous allons examiner chacun de ces systèmes.

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, disponibles pour l’éditeur de code afin d’y appliquer des modifications. Le répertoire de travail est considéré comme un commit spécifique du projet extrait. Lorsque le projet est extrait, cela signifie que ses fichiers ont été décompressés et récupérés depuis le dépôt Git.

git reset command

bash
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

L’arbre suivant est l’index de staging, qui suit les changements mis en staging dans le répertoire de travail. En général, les détails de performance de la zone de staging sont cachés aux utilisateurs par Git. Parfois, lorsqu’on parle de la zone de staging, différentes expressions sont utilisées, comme cache, cache de répertoire, fichiers en staging, zone de staging, etc.

Ici, nous avons besoin de git ls-files. command, qui est considérée comme un outil de débogage, et qui vérifie l’état de l’index de staging.

git ls-files -s

bash
git ls-files -s
#543644 a32de29bb3c1d643328b29ae775ad8c2e48c3256 0 edited_file

Historique des commits

Le dernier arbre est l’historique des commits. La commande git commit enregistre les changements dans un instantané permanent placé dans l’index de staging.

git reset, commit history

bash
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 changements sont attachés à l’historique des commits. À ce stade, l’exécution de git status n’affiche aucun changement à venir pour l’un quelconque des arbres. En lançant git log, vous verrez l’historique des commits. Une fois les changements effectués à travers les trois arbres, git reset peut être utilisé.

Comment cela fonctionne

À première vue, la commande git reset présente quelques similitudes avec git checkout, car elles agissent toutes deux sur HEAD. La commande git checkout agit exclusivement sur le pointeur de référence HEAD, tandis que la commande git reset agit sur le pointeur de référence HEAD et sur le pointeur de référence de la branche courante. Vous comprendrez mieux son comportement avec l’illustration ci-dessous :

git reset1

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

git checkout b

Lors de l’exécution de la commande git checkout, la ref master pointe toujours vers le commit d. Quant à la ref HEAD, elle a été déplacée et son pointeur a été changé vers le commit b. En conséquence, le dépôt est maintenant dans un état de « detached HEAD ».

git reset2

git reset b

La commande git reset déplace à la fois les refs HEAD et de branche vers le commit défini. De plus, elle modifie l’état des trois arbres. Il existe trois arguments en ligne de commande --soft, --mixed et --hard qui définissent la modification de l’index de staging et des arbres du répertoire de travail.

git reset3

Options principales

Par défaut, la commande git reset a pour arguments constants --mixed et HEAD. Ainsi, invoquer git reset revient à invoquer git reset --mixed HEAD. Ici, HEAD est le commit indiqué. Vous pouvez utiliser n’importe quel hash de commit Git SHA-1 à la place.

git reset4

--hard

L’option la plus couramment utilisée est --hard. Son utilisation comporte toutefois certains risques. Avec --hard, les pointeurs de référence de l’historique des commits commencent à pointer vers le commit indiqué. Ensuite, l’index de staging et le répertoire de travail sont réinitialisés pour correspondre au commit indiqué. Les changements qui étaient auparavant en attente dans l’index de staging et le répertoire de travail sont réinitialisés pour correspondre à l’état de l’arbre du commit. Tout commit en attente dans l’index de staging et le répertoire de travail sera perdu. L’exemple ci-dessous va démontrer ce qui précède. Tout d’abord, exécutez les commandes suivantes :

git reset usage

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

Un nouveau fichier nommé test_file a été créé et ajouté au dépôt. En outre, le contenu de edited_file sera modifié. Vérifions maintenant l’état du dépôt avec ces changements à l’aide de la commande git status.

git status

bash
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

Comme vous pouvez le voir, il y a maintenant des changements en attente. Le changement en attente pour l’arbre de l’index de staging est l’ajout de test_file, et celui pour le répertoire de travail est la modification de edited_file. Voyons maintenant l’état de l’index de staging :

git ls-files -s

bash
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 changements se trouvent dans le répertoire de travail. Ils ne sont pas promus vers l’index de staging puisque nous n’avons pas utilisé la commande git add. À ce stade, nous pouvons exécuter git reset --hard et voir le nouvel état du dépôt :

git reset --hard

bash
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 exécuté 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 aucun changement en attente. Quant à l’état de l’index de staging, il a été réinitialisé à un point antérieur à l’ajout de test_file. Les changements de edited_file et l’ajout de test_file ont été effacés. Cette perte ne peut pas être annulée.

--mixed

Le mode de fonctionnement est par défaut --mixed. Il met à jour les pointeurs de référence. L’index de staging est réinitialisé au commit indiqué. Les changements annulés de l’index de staging sont placés dans le répertoire de travail.

the git reset command

bash
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, un test_file a été ajouté et le contenu de edited_file a été modifié. Ensuite, ces changements sont appliqués à l’index de staging à l’aide de git add. Avec cet état du dépôt, il est maintenant temps d’invoquer git reset.

git reset --mixed

bash
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. git status montre qu’il y a des changements dans edited_file et que test_file est un fichier non suivi. C’est exactement le comportement de --mixed. L’index de staging a été réinitialisé et les changements en attente sont déplacés vers le répertoire de travail.

--soft

L’argument --soft met à jour les pointeurs de référence et arrête la réinitialisation. Cependant, il n’affecte pas l’index de staging ni le répertoire de travail.

git reset --soft

bash
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 réinitialise uniquement l’historique des commits. Par défaut, il est invoqué avec HEAD comme commit cible. Créons maintenant un nouveau commit pour essayer un --soft avec un commit cible qui n’est pas HEAD :

git reset and commit

bash
git commit -m "add changes to edited_file"

Maintenant, notre dépôt contient trois commits. Afin de trouver le premier, nous devons vérifier son ID, ce qui peut être fait en consultant la sortie de git log.

git reset and git log

bash
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

C’est l’ID du commit initial. Il sera maintenant utilisé comme cible pour le soft reset. Avant cela, nous devons vérifier l’état actuel du dépôt :

git status && git ls-files -s

bash
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

bash
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 exécuté la commande combinée git status et git ls-files, qui affiche l’état du dépôt. La commande git status montre qu’il y a des changements dans edited_file, en les mettant en évidence comme des changements mis en staging 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 en plus l’état du dépôt après le soft reset à l’aide de git log :

git reset

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

Comme on peut le voir, la sortie ci-dessus indique qu’il n’y a qu’un seul commit dans l’historique des commits. Comme pour toutes les invocations de git reset, --soft réinitialise d’abord l’arbre des commits.

Contrairement à --hard et --mixed, qui agissent tous deux sur HEAD, un soft reset a ramené l’arbre des commits en arrière dans le temps.

La différence entre les commandes reset et revert

Git revert est considéré comme une manière plus sûre d’annuler des changements que git reset. Il y a une forte probabilité que le travail puisse être perdu avec git reset. git reset ne supprime pas un commit, mais il peut rendre le commit « orphelin ». Cela signifie qu’il n’existe aucun moyen direct d’y accéder. En conséquence, Git supprimera tous les commits orphelins lorsqu’il exécutera le ramasse-miettes interne. Par défaut, Git exécute le ramasse-miettes interne tous les 30 jours. Les commits orphelins sont généralement retrouvés à l’aide de la commande git reflog.

Une autre différence entre ces deux commandes est que git revert est configuré pour annuler des commits publics, et git reset est configuré pour annuler des changements locaux dans le répertoire de travail et l’index de staging.

L’inadmissibilité de la réinitialisation de l’historique public

N’utilisez pas git reset <commit> lorsqu’il existe des instantanés après <commit> qui ont été poussés vers un dépôt public. Lorsque vous publiez un commit, tenez compte du fait que d’autres développeurs en dépendent aussi. Supprimer des commits sur lesquels travaillent aussi d’autres membres de l’équipe causera beaucoup de problèmes. Utilisez git reset <commit> uniquement sur des changements locaux. Pour corriger des changements publics, utilisez la commande git revert.

Exemples

Utilisez ce qui suit pour supprimer le fichier spécifié de la zone de staging, sans modifier le répertoire de travail. Cela retirera un fichier du staging sans écraser les changements :

git reset file

bash
git reset file

Utilisez ce qui suit pour réinitialiser la zone de staging afin qu’elle corresponde au dernier commit, tout en laissant le répertoire de travail inchangé. Cela retirera tous les fichiers du staging sans écraser les changements, vous donnant la possibilité de reconstruire l’instantané mis en staging à partir de zéro :

git reset staging area

bash
git reset

Utilisez ce qui suit pour réinitialiser la zone de staging et le répertoire de travail afin qu’ils correspondent au dernier commit. Cela retirera les changements du staging en écrasant aussi tous les changements dans le répertoire de travail :

git reset --hard command

bash
git reset --hard

Utilisez ce qui suit pour ramener la pointe de la branche en arrière dans le temps jusqu’au commit, en réinitialisant la zone de staging pour qu’elle corresponde, sans toucher au répertoire de travail :

git reset commit

bash
git reset commit

Utilisez ce qui suit pour déplacer la pointe de la branche courante en arrière jusqu’au commit et réinitialiser la zone de staging et le répertoire de travail pour qu’ils correspondent :

git reset --hard commit

bash
git reset --hard commit

Suppression des commits locaux

Comme mentionné ci-dessus, vous pouvez utiliser la commande git reset pour supprimer des commits dans le dépôt local. L’exemple ci-dessous montre une telle utilisation de git reset. La commande git reset --hard HEAD~2 fait reculer la branche courante de deux commits et supprime les deux instantanés récemment créés de l’historique du projet.

git add command

bash
# 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

La commande git reset est généralement utilisée pour créer des instantanés mis en staging. Dans l’exemple ci-dessous, nous avons 2 fichiers appelés task.txt et index.txt, qui ont été ajoutés au dépôt. git reset nous permet de retirer du staging les changements qui ne sont pas liés au prochain commit.

git reset unstaging files

bash
# 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"

Practice

What are the features and options of the 'git reset' command in Git?

Trouvez-vous cela utile?

Aperçu dual-run — comparez avec les routes Symfony en production.