Aller au contenu
Développement medium

Git reset démystifié : les 3 arbres

11 min de lecture

git reset déplace HEAD et peut mettre à jour l'index et le working directory selon le mode choisi : --soft (HEAD seul), --mixed (HEAD + index, par défaut) ou --hard (les trois). Ce guide vous apprend à choisir le bon mode selon votre situation, à comprendre les 3 arbres internes de Git, et à éviter toute perte de données lors d'un reset.

Prérequis : Enregistrer des modifications et Sélection de révisions.

  • Comprendre les 3 arbres de Git : HEAD, index et répertoire de travail
  • Maîtriser --soft, --mixed et --hard et leurs effets respectifs
  • Distinguer reset, checkout, restore et switch : quand utiliser quoi
  • Annuler des modifications à différents stades du workflow

Git manipule en permanence trois « arbres » (structures de données) qui contiennent chacun une version complète de votre projet. HEAD pointe sur le dernier commit de la branche courante, l'index stocke le contenu qui partira au prochain commit, et le working directory contient les fichiers que votre éditeur ouvre. Comprendre lequel des trois une commande touche suffit à prévoir si vous risquez de perdre du travail.

ArbreRôleCommande pour le voir
HEADDernier commit (snapshot du repo)git log --oneline -1
Index (staging area)Prochain commit en préparationgit diff --cached
Working DirectoryFichiers sur le disquegit diff

Le workflow normal fait circuler les modifications de droite à gauche :

Working Dir → Index → HEAD
git add git commit

git reset fait l'inverse : il déplace HEAD et peut propager le changement vers l'index et/ou le working directory.

Les trois modes se distinguent par le nombre d'arbres qu'ils mettent à jour, dans un ordre cumulatif : --soft s'arrête à HEAD, --mixed va jusqu'à l'index, --hard descend jusqu'aux fichiers du disque. Sans option, Git applique --mixed. Un seul de ces modes, --hard, peut détruire du travail que vous n'avez jamais commité.

HEAD recule mais rien d'autre ne bouge : le contenu du commit annulé se retrouve intact dans l'index, prêt à être recommité.

Fenêtre de terminal
git reset --soft HEAD~1
  • HEAD recule d'un commit
  • Index inchangé → les modifications restent stagées
  • Working directory inchangé

Usage : « annuler le dernier commit mais garder les modifications prêtes à être recommitées ».

Fenêtre de terminal
# Regrouper les 3 derniers commits en un seul
git reset --soft HEAD~3
git commit -m "feat: fonctionnalité complète"

C'est le mode appliqué quand vous n'écrivez aucune option : les modifications retombent en non stagées, il faudra les repasser par git add.

Fenêtre de terminal
git reset HEAD~1 # --mixed est le défaut
git reset --mixed HEAD~1 # Identique
  • HEAD recule d'un commit
  • Index mis à jour → les modifications passent en non stagées
  • Working directory inchangé

Usage : « annuler le commit et le staging, mais garder les fichiers modifiés ».

Fenêtre de terminal
# Désélectionner un fichier (sans toucher au working dir)
git reset HEAD fichier.py
# Équivalent moderne :
git restore --staged fichier.py

Ce mode réécrit les fichiers du disque pour les aligner sur le commit visé, sans demander de confirmation ni proposer de sauvegarde.

Fenêtre de terminal
git reset --hard HEAD~1
  • HEAD recule d'un commit
  • Index vidé
  • Working directory écrasé → modifications perdues

Usage : « tout annuler, revenir à l'état exact d'un commit ».

Fenêtre de terminal
# Revenir exactement à l'état de origin/main
git reset --hard origin/main

Ce tableau se lit de gauche à droite : chaque colonne correspond à un arbre, et la dernière annonce le risque réel. Repérez la ligne du mode que vous vous apprêtez à taper, puis la colonne Working Dir. Tant qu'elle affiche « Inchangé », vos fichiers sur le disque sont préservés, quelle que soit la révision ciblée.

ModeHEADIndexWorking DirDonnées perdues ?
--softDéplacéInchangéInchangéNon
--mixedDéplacéMis à jourInchangéNon
--hardDéplacéMis à jourMis à jourOui (non commitées)

Quand vous spécifiez un chemin, reset ne déplace pas HEAD. Il met à jour uniquement l'index pour ce fichier :

Fenêtre de terminal
# Désélectionner un fichier (retirer du staging)
git reset HEAD fichier.py
# Identique à :
git restore --staged fichier.py

Avec une révision :

Fenêtre de terminal
# Remettre fichier.py dans l'index à la version de HEAD~3
git reset HEAD~3 -- fichier.py

Le working directory n'est pas modifié, seul l'index est mis à jour.

Depuis Git 2.23, switch et restore couvrent les usages qui étaient auparavant tous concentrés sur checkout. Elles sont plus explicites parce qu'une commande ne fait plus qu'une chose. Le tableau donne la correspondance entre l'ancienne écriture et la nouvelle.

ActionAncienne syntaxeNouvelle syntaxe
Changer de branchegit checkout branchegit switch branche
Désélectionner un fichiergit reset HEAD fichiergit restore --staged fichier
Annuler les modifications d'un fichiergit checkout -- fichiergit restore fichier
Revenir à un commit (détached HEAD)git checkout a1b2c3dgit switch --detach a1b2c3d

git reset reste nécessaire pour déplacer HEAD d'une branche (soft/mixed/hard). Les autres cas ont des commandes dédiées plus claires.

Les deux commandes annulent un changement, mais pas au même prix. reset réécrit l'historique : les commits abandonnés changent de place et disparaissent de la branche. revert ajoute au contraire un nouveau commit qui applique l'inverse du commit visé, donc rien n'est réécrit. Ce critère décide à lui seul dans le cas d'une branche déjà poussée.

Critèregit resetgit revert
MécanismeSupprime les commitsCrée un commit d'annulation
HistoriqueRéécrit (SHA changent)Préserve (ajout d'un commit)
Commits partagésNon, casse l'historique des autresOui, sûr pour les branches partagées
GranularitéRevient à un pointAnnule un commit spécifique

Règle simple : reset pour les commits locaux non poussés, revert pour les commits partagés.

Voici les cinq situations où git reset sert réellement au quotidien, de la plus anodine à la plus délicate. Chacune indique le mode à employer et l'état dans lequel vous retrouvez vos fichiers ensuite. Toutes supposent une branche locale non poussée, sauf mention contraire.

Annuler le dernier commit (garder les modifications)

Section intitulée « Annuler le dernier commit (garder les modifications) »

Le commit disparaît de l'historique mais son contenu reste stagé, ce qui permet d'en refaire un tout de suite avec un meilleur message.

Fenêtre de terminal
git reset --soft HEAD~1
# Les modifications sont dans le staging, prêtes à être modifiées

Sans révision ni chemin, git reset vise HEAD et se contente de vider l'index : aucun commit n'est touché, aucun fichier n'est modifié.

Fenêtre de terminal
git reset
# Toutes les modifications passent de stagé à non stagé

Revenir à origin/main après une série d'erreurs

Section intitulée « Revenir à origin/main après une série d'erreurs »

Lancez d'abord git fetch : origin/main est une référence locale qui peut dater, et le reset s'alignera sur cette copie, pas sur l'état réel du serveur. L'opération est destructive pour tout travail non commité.

Fenêtre de terminal
git reset --hard origin/main
# Working directory identique à origin/main

--mixed remet les fichiers en non stagés, ce qui vous laisse les rajouter un par un avec git add pour reconstituer plusieurs commits.

Fenêtre de terminal
git reset --mixed HEAD~1
# Les modifications sont dans le working dir
git add partie-1.py
git commit -m "feat: partie 1"
git add partie-2.py
git commit -m "feat: partie 2"

Le reflog enregistre chaque position prise par HEAD, y compris celles qu'aucune branche ne référence plus ; les commits perdus y restent atteignables tant que le garbage collector ne les a pas supprimés. En revanche il ne consigne que des commits : le travail qui n'avait jamais été commité reste irrécupérable par cette voie.

Fenêtre de terminal
# Le reflog conserve la position précédente
git reflog
# a1b2c3d HEAD@{1}: reset: moving to origin/main
# e4f5a6b HEAD@{2}: commit: ma feature importante
git reset --hard HEAD@{2}
# Restauré !

Quatre situations reviennent systématiquement après un reset mal ciblé. Partez du symptôme que vous observez dans git status, la colonne du milieu donne la manipulation qui l'a provoqué. La dernière ligne du tableau décrit un comportement normal, pas un incident.

SymptômeCause probableSolution
Fichiers perdus après --hardReset destructifgit reflog + git reset --hard HEAD@{n}
reset n'a pas désélectionnéOmission du chemingit reset HEAD fichier.py (avec le chemin)
Collaborateurs cassés après resetCommits partagés réécritsUtilisez revert pour les commits partagés
Index ne correspond pas au working dir--mixed appliquéNormal : --mixed vide l'index, pas le working dir
  • Git a 3 arbres : HEAD, Index (staging), Working Directory
  • --soft : déplace HEAD → modifications restent stagées
  • --mixed (défaut) : déplace HEAD + vide l'index → modifications non stagées
  • --hard : tout réinitialise → destructif pour les fichiers non commitées
  • reset sur un fichier = mettre à jour l'index (pas HEAD)
  • reset pour commits locaux, revert pour commits partagés
  • Le reflog sauve toujours, même après un --hard accidentel

Les questions ci-dessous portent sur les confusions les plus fréquentes entre les trois modes, la récupération après un --hard et le cas des branches déjà partagées.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn