15%

Économisez 15% sur tous les services d'hébergement

Testez vos compétences et obtenez Réduction sur tout plan d'hébergement

Utilisez le code :

Skills
Commencer
22.10.2024

Git Reset vs. Git Checkout vs. Git Revert : Un Guide Technique Complet

Comprendre la différence entre git reset, git checkout et git revert est essentiel pour tout développeur travaillant avec le contrôle de version. En résumé : git reset réécrit l’historique en déplaçant le pointeur HEAD ; git checkout navigue entre les branches, les commits ou les fichiers sans modifier l’historique ; et git revert annule un commit en créant un nouveau commit inverse, laissant l’historique intact. Choisir la mauvaise commande — surtout sur une branche partagée — peut corrompre l’historique des commits de votre équipe ou provoquer une perte de données irréversible.

Ce guide va au-delà de la syntaxe de surface pour expliquer les mécanismes internes de Git, l’état exact de votre arbre de travail et de l’index après chaque opération, ainsi que les scénarios réels où chaque commande est le bon choix (ou un choix catastrophiquement mauvais).

Comment Git gère l’état : les trois arbres

Avant de comparer les commandes, vous avez besoin d’un modèle mental solide de la façon dont Git suit l’état. Git fonctionne sur trois couches distinctes :

  • Répertoire de travail — les fichiers que vous voyez et modifiez sur le disque.
  • Zone de staging (index) — un instantané de ce qui sera inclus dans le prochain commit.
  • Historique des commits (HEAD) — la chaîne d’objets de commit immuables stockés dans .git/.

Chaque commande d’annulation dans Git cible une ou plusieurs de ces couches. La confusion entre reset, checkout et revert provient presque toujours du fait de ne pas savoir quelles couches une commande donnée affecte.

git reset : réécriture de l’historique local

git reset déplace le pointeur HEAD de la branche courante vers un commit spécifié. Selon le drapeau de mode que vous passez, il peut également mettre à jour l’index et le répertoire de travail. C’est une opération de réécriture de l’historique et elle doit être traitée comme destructive lorsqu’elle est utilisée avec --hard.

Modes de reset expliqués

git reset --soft <commit>   # Move HEAD only; index and working tree unchanged
git reset --mixed <commit>  # Move HEAD + reset index; working tree unchanged (default)
git reset --hard <commit>   # Move HEAD + reset index + reset working tree
ModeHEAD se déplaceIndex réinitialiséArbre de travail réinitialiséModifications préservées
--softOuiNonNonStagées et non stagées
--mixedOuiOuiNonNon stagées uniquement
--hardOuiOuiOuiAucune — définitivement perdues

Cas d’utilisation pratiques

Fusionner des commits avant un push. Si vous avez trois commits de travail en cours désordonnés qui n’ont pas encore été poussés, git reset --soft HEAD~3 les regroupe dans l’index afin que vous puissiez les recommitter en un seul commit propre.

Désindexer un fichier ajouté accidentellement. Exécuter git reset HEAD <file> (ou git reset sans référence de commit) supprime un fichier de l’index sans toucher au répertoire de travail — identique en effet à git restore --staged <file> dans Git 2.23+.

Récupérer d’une fusion locale ratée. git reset --hard ORIG_HEAD restaure l’état qui existait immédiatement avant une fusion, car Git écrit le HEAD pré-fusion dans ORIG_HEAD automatiquement.

Piège critique : pousser après un reset

Si vous réinitialisez une branche qui a déjà été poussée vers un dépôt distant puis effectuez un force-push, chaque collaborateur dont la branche locale suit ce dépôt distant aura un historique divergent. C’est l’une des causes les plus courantes de perte de commits dans les environnements d’équipe. N’exécutez jamais git reset suivi de git push --force sur une branche partagée sans coordination explicite de l’équipe.

# Dangerous on shared branches — use only on private/local branches
git reset --hard HEAD~2
git push --force origin feature/my-branch

git checkout : navigation sans modification de l’historique

git checkout est une commande polyvalente qui bascule l’arbre de travail pour correspondre à une branche, un commit spécifique ou un seul fichier. Elle ne modifie pas l’historique des commits. Dans Git 2.23 et versions ultérieures, ses responsabilités ont été divisées en git switch (pour les branches) et git restore (pour les fichiers), mais git checkout reste entièrement fonctionnel et est encore dominant dans les environnements de production.

Référence de syntaxe

git checkout <branch_name>          # Switch to an existing branch
git checkout -b <new_branch>        # Create and switch to a new branch
git checkout <commit_hash>          # Enter detached HEAD state at a specific commit
git checkout -- <file_name>         # Discard working directory changes to a file
git checkout <commit_hash> -- <file> # Restore a single file from a specific commit

État HEAD détaché : ce que cela signifie réellement

Lorsque vous exécutez git checkout <commit_hash>, Git déplace HEAD pour pointer directement vers un objet commit plutôt que vers une référence de branche. C’est ce qu’on appelle l’état HEAD détaché. Tout commit que vous effectuez dans cet état n’est accessible depuis aucune branche — il est orphelin et sera éventuellement récupéré par le ramasse-miettes de Git, sauf si vous créez une branche pour le capturer.

git checkout 4f7a2c1          # HEAD now points directly to commit 4f7a2c1
git checkout -b hotfix/patch  # Rescue those commits by creating a branch

Un scénario réel courant : un développeur extrait un ancien commit pour tester une régression, effectue accidentellement un correctif, le committe, puis revient à main — perdant entièrement le correctif car il n’était jamais attaché à une branche.

Restaurer un seul fichier depuis l’historique

L’une des formes les plus sous-utilisées de git checkout est la restauration ciblée de fichiers :

git checkout HEAD~3 -- src/config/database.php

Cela extrait database.php tel qu’il existait trois commits auparavant directement dans votre index et répertoire de travail, sans toucher à aucun autre fichier ni déplacer HEAD. C’est l’équivalent chirurgical d’un changement de branche complet.

git revert : annulation sécurisée pour l’historique partagé

git revert crée un nouveau commit qui applique le diff inverse d’un commit spécifié. Le commit original reste dans l’historique intact. C’est le seul mécanisme d’annulation sécurisé pour les commits qui ont déjà été poussés vers une branche distante partagée, car il ne réécrit pas l’historique — il l’étend.

Référence de syntaxe

git revert HEAD                  # Revert the most recent commit
git revert <commit_hash>         # Revert a specific commit by hash
git revert HEAD~3..HEAD          # Revert a range of commits (creates multiple revert commits)
git revert -n <commit_hash>      # Stage the revert without committing (--no-commit)

Le drapeau –no-commit : regrouper les annulations

Lors de l’annulation de plusieurs commits, créer un commit de revert par commit original peut polluer le journal. Le drapeau -n (ou --no-commit) indexe toutes les annulations sans les committer, vous permettant de les regrouper en un seul commit de revert :

git revert -n HEAD~4..HEAD
git commit -m "Revert: roll back broken authentication refactor"

Les annulations de commits de fusion nécessitent une attention particulière

Annuler un commit de fusion nécessite de spécifier le parent principal avec le drapeau -m, car Git doit savoir quel côté de la fusion traiter comme l’historique "correct" :

git revert -m 1 <merge_commit_hash>

Ici, -m 1 désigne le premier parent (généralement la branche dans laquelle vous avez fusionné) comme ligne principale. Omettre ce drapeau sur un commit de fusion provoquera une erreur de Git. C’est un obstacle courant lors de l’annulation d’une mauvaise fusion de release dans les pipelines CI/CD.

Comparaison côte à côte : Reset vs. Checkout vs. Revert

Critère`git reset``git checkout``git revert`
Modifie l’historique des commitsOuiNonNon (s’y ajoute)
Affecte le répertoire de travailAvec --hard ou --mixedOuiOui (via un nouveau commit)
Affecte la zone de staging (index)Oui (sauf --soft)Uniquement avec la forme fichierNon
Sécurisé sur les branches partagées/distantesNonOui (lecture seule)Oui
Crée un nouveau commitNonNonOui
RéversiblePartiellement (via ORIG_HEAD)OuiOui
Gère les commits de fusionNonOui (navigation)Oui (avec -m)
Équivalent moderne Git 2.23+Identiquegit switch / git restoreIdentique

Quand utiliser chaque commande : matrice de décision

Utilisez git reset quand :

  • Vous travaillez sur une branche locale non poussée et souhaitez nettoyer, fusionner ou supprimer des commits.
  • Vous devez désindexer des fichiers avant un commit.
  • Vous souhaitez annuler une fusion locale qui n’a pas été partagée.
  • Vous êtes le seul développeur sur une branche de fonctionnalité et devez réécrire son historique avant d’ouvrir une pull request.

Utilisez git checkout quand :

  • Vous devez basculer entre des branches lors d’un développement actif.
  • Vous souhaitez inspecter l’état du dépôt à un commit historique sans rien modifier.
  • Vous devez restaurer un seul fichier à son état à un commit spécifique.
  • Vous créez une nouvelle branche à partir d’un point spécifique de l’historique.

Utilisez git revert quand :

  • Un commit a déjà été poussé vers une branche distante ou partagée.
  • Vous travaillez en équipe et devez maintenir un historique transparent et auditable.
  • Vous devez annuler un commit spécifique qui n’est pas le plus récent (revert non linéaire).
  • Votre projet exige une conformité ou des pistes d’audit où la suppression de l’historique est interdite.

Scénario avancé : récupération après git reset –hard

Si vous avez accidentellement exécuté git reset --hard et perdu des commits, ils ne sont pas immédiatement perdus. Le reflog de Git enregistre chaque position vers laquelle HEAD a pointé, même après un hard reset :

git reflog
# Output example:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# 7e8f9a0 HEAD@{1}: commit: Add payment gateway integration
# ...

git reset --hard HEAD@{1}   # Restore to the commit before the accidental reset

Les entrées du reflog expirent après 90 jours par défaut (gc.reflogExpire), donc cette fenêtre de récupération n’est pas infinie. Sur un serveur de production ou un environnement VPS Hosting exécutant un service Git auto-hébergé comme Gitea ou GitLab, vous devez vous assurer que votre répertoire .git est inclus dans les routines de sauvegarde régulières précisément en raison de cette expiration.

Héberger votre infrastructure Git

Exploiter un serveur Git auto-hébergé — que ce soit GitLab CE, Gitea ou Gogs — exige des E/S de stockage fiables et une disponibilité constante. Un seul fichier pack corrompu ou un push interrompu pendant un cycle git gc peut endommager l’intégrité du dépôt. Pour les équipes gérant plusieurs dépôts, un Serveur Dédié fournit des ressources isolées, un accès root complet pour affiner les paramètres core.packedGitWindowSize et pack.threads de Git, ainsi que le débit NVMe nécessaire pour les grands monodépôts.

Pour les petites équipes ou les développeurs individuels qui ont besoin d’un environnement Linux propre pour exécuter des hooks Git, des scripts CI et des pipelines de déploiement, un VPS avec cPanel offre un plan de contrôle géré avec un accès SSH complet — supprimant la charge de l’administration manuelle du serveur tout en conservant la flexibilité de configurer les hooks côté serveur Git et les contrôles d’accès.

Si votre flux de travail implique des déploiements automatisés déclenchés par git push, sécuriser votre serveur avec un Certificat SSL valide est indispensable — tant pour chiffrer les charges utiles des webhooks que pour authentifier les dépôts distants Git basés sur HTTPS sans désactiver la vérification des certificats.

Points techniques clés à retenir

  • git reset --hard est la seule commande parmi les trois qui peut provoquer une perte de données permanente et irrécupérable si le reflog a expiré.
  • git revert est la seule commande qui peut être utilisée en toute sécurité après un git push sans nécessiter un force-push.
  • L’état HEAD détaché résultant de git checkout <hash> ne supprime pas les commits — mais tout nouveau commit effectué dans cet état sera orphelin, sauf si vous exécutez immédiatement git checkout -b <new_branch>.
  • Le drapeau -n sur git revert est essentiel pour maintenir un journal propre lors de l’annulation de plusieurs commits à la fois.
  • Git 2.23+ divise git checkout en git switch et git restore pour plus de clarté — comprendre la commande originale rend les deux alternatives modernes immédiatement intuitives.
  • Vérifiez toujours votre branche courante avec git status et git log --oneline -5 avant d’exécuter toute opération d’annulation.
  • Sur une infrastructure partagée, appliquez des règles de protection de branche (GitHub, GitLab, Gitea les prennent tous en charge) pour bloquer git push --force sur les branches main et release au niveau du serveur.

Foire aux questions

Quelle est la différence entre git reset --soft et git reset --mixed ?

Les deux déplacent HEAD vers le commit spécifié, mais --soft laisse vos modifications indexées dans l’index, tandis que --mixed (la valeur par défaut) efface également l’index, laissant les modifications uniquement dans le répertoire de travail. Aucun des deux ne touche les fichiers sur le disque.

Puis-je récupérer des commits après git reset --hard ?

Oui, dans la fenêtre d’expiration du reflog (90 jours par défaut). Exécutez git reflog pour trouver le hash du commit de l’état perdu, puis exécutez git reset --hard <hash> ou git checkout -b recovery <hash> pour le restaurer.

Pourquoi git revert sur un commit de fusion nécessite-t-il le drapeau -m ?

Un commit de fusion a deux parents. Git ne peut pas déterminer quel diff de parent inverser sans que vous spécifiiez la ligne principale. -m 1 indique à Git de traiter le premier parent comme le tronc, annulant les modifications introduites par la branche fusionnée.

git checkout -- <file> est-il identique à git restore <file> ?

Fonctionnellement oui — les deux suppriment les modifications non indexées du répertoire de travail d’un fichier en le restaurant depuis l’index. git restore a été introduit dans Git 2.23 comme remplacement moins ambigu, mais git checkout -- <file> produit des résultats identiques sur toutes les versions de Git.

Quand ne dois-je absolument jamais utiliser git reset sur une branche ?

N’utilisez jamais git reset (surtout avec --hard ou --mixed) sur une branche que d’autres développeurs ont déjà clonée ou tirée. Ce faisant, leur historique local diverge du dépôt distant, obligeant chaque collaborateur à effectuer un reset forcé ou un re-clonage — et risquant de supprimer silencieusement des commits qui n’existent que sur leurs machines.

15%

Économisez 15% sur tous les services d'hébergement

Testez vos compétences et obtenez Réduction sur tout plan d'hébergement

Utilisez le code :

Skills
Commencer