Récupérer des commits perdus avec le reflog de Git
Récupérez des commits Git perdus avec reflog : utilisez git reflog pour retrouver des commits orphelins, annuler resets et rebases, et restaurer des branches.
Pour récupérer un commit perdu, exécutez git reflog, copiez le hash du commit souhaité, puis restaurez-le avec git checkout -b recovered <hash>.
Vous pensiez à HEAD~1, vous avez tapé HEAD~2, et vous avez vu trois heures de travail disparaître de git log. Ces quelques secondes entre l’appui sur Entrée et le moment où l’on se souvient que le reflog existe sont les pires de Git. Lorsque vous exécutez git reset --hard, que vous ratez un rebase ou que vous supprimez une branche, vos commits ne sont presque jamais détruits : ils sont simplement orphelins, et leurs hash restent consignés dans votre reflog. Ce guide vous donne la recette de récupération rapide pour les trois scénarios qui déclenchent la panique, puis les limites concrètes qu’il vaut mieux connaître pour ne pas perdre votre travail une seconde fois. Toutes les commandes ci-dessous sont prêtes à être copiées-collées.
Points clés à retenir
git reflogenregistre chaque déplacement deHEADdans votre dépôt local (commit, checkout, reset, rebase, merge) : un commit « perdu » n’est donc généralement qu’à ungit reset --hard <hash>de son retour.- La récupération la plus sûre est
git checkout -b recovered <hash>: reconstruisez le commit sur une nouvelle branche et inspectez-le avant de toucher à votre véritable branche. - Le reflog ne peut pas récupérer les modifications non commitées du répertoire de travail, car Git ne les a jamais enregistrées dans une référence.
- Par défaut, Git conserve les entrées de reflog atteignables pendant 90 jours et les entrées inatteignables pendant 30 jours : récupérez donc rapidement et évitez d’exécuter
git gcavant d’avoir retrouvé vos commits. - Le reflog est strictement local et n’est jamais poussé : il peut donc sauver votre propre travail perdu, mais pas les commits non poussés d’un collègue.
Comment fonctionne le reflog de Git ?
Le reflog de Git est un journal local de toutes les positions occupées par les pointes de vos branches et par les autres références. Chaque commit, checkout, reset, rebase et merge déplace HEAD et est journalisé. Ainsi, lorsqu’un commit devient « perdu » (c’est-à-dire qu’aucune branche ni aucun tag ne le pointe plus), il est seulement orphelin, pas supprimé, et son hash figure toujours dans le reflog, prêt à être rattaché.
Exécutez la commande sans argument pour voir l’historique de HEAD :
git reflog
b58145c HEAD@{0}: reset: moving to HEAD~2
dd7c37e HEAD@{1}: commit: one more commit
b5b3286 HEAD@{2}: checkout: moving from main to feature
b58145c HEAD@{3}: commit: very important commit
Lisez chaque ligne comme suit : le hash court, l’index HEAD@{n} (le nombre de déplacements en arrière auquel se situe cette entrée), et une description de l’opération. Dans la sortie ci-dessus, HEAD@{0} est le reset destructeur qui vient de se produire, et HEAD@{1} (dd7c37e) est le commit dont il s’est éloigné, celui que vous voulez récupérer. En interne, git reflog show exécute la même chose que git log -g --abbrev-commit --pretty=oneline, et accepte n’importe quelle référence : git reflog show main vous donne donc l’historique d’une seule branche.
Discover how at OpenReplay.com.
Récupérer après un hard reset accidentel
Un git reset --hard déplace le pointeur de votre branche mais laisse l’ancien commit intact dans la base d’objets. Retrouvez-le dans le reflog, puis repointez dessus. Le commit qui précède immédiatement le reset est celui que vous avez perdu, généralement HEAD@{1}.
git reflog
# HEAD@{0}: reset: moving to HEAD~2
# HEAD@{1}: commit: one more commit <-- this hash
git checkout -b recovered dd7c37e
La récupération la plus sûre consiste à inspecter avant d’écraser : exécutez git checkout -b recovered <hash> pour reconstruire le commit sur une nouvelle branche, vérifiez-le avec git log, et décidez seulement ensuite si vous déplacez votre véritable branche. Une fois certain, vous pouvez faire sauter la branche directement :
git reset --hard dd7c37e # or: git reset --hard HEAD@{1}
Avertissement — double peine : git reset --hard supprime toutes les modifications non commitées en cours dans votre arbre de travail. Si votre arbre est sale, faites d’abord un git stash ou passez par la nouvelle branche. Sinon, la commande de récupération provoque une seconde perte. Immédiatement après un reset, vous pouvez aussi utiliser git reset --hard ORIG_HEAD, car git reset enregistre la position précédente de la pointe de la branche dans ORIG_HEAD avant de se déplacer.
Annuler un rebase raté
Un rebase réécrit l’historique, et un conflit mal résolu peut faire disparaître des commits en silence. Le reflog conserve la pointe d’avant le rebase. Après un rebase raté, git reset --hard ORIG_HEAD ramène votre branche à son point de départ. Mais ORIG_HEAD est écrasé par le reset, rebase ou merge suivant : si vous avez exécuté une autre commande de ce type depuis, retrouvez plutôt la pointe d’avant le rebase dans git reflog.
git reflog
# HEAD@{0}: rebase (finish): returning to refs/heads/feature
# HEAD@{1}: rebase (pick): one more commit
# HEAD@{2}: rebase (start): checkout main
# HEAD@{3}: checkout: moving from main to feature <-- pre-rebase tip
git reset --hard HEAD@{3}
Cherchez l’entrée rebase (start) ; la ligne juste avant correspond à l’état de votre branche avant le rebase. Si vous ne voulez récupérer que des commits spécifiques supprimés par le rebase, plutôt que d’annuler l’opération entière, récupérez leurs hash dans le reflog et rejouez-les :
git cherry-pick <hash>
Récupérer une branche supprimée
Supprimer une branche supprime la référence, pas les commits. Son dernier commit apparaît toujours dans le reflog de HEAD (depuis la dernière fois où vous l’avez checkoutée) : il suffit donc de retrouver ce hash et de recréer la branche à cet endroit.
git reflog
# HEAD@{0}: checkout: moving from example-branch to main
# HEAD@{1}: commit: very important commit <-- last commit on deleted branch
git checkout -b example-branch b5b3286 # or HEAD@{1}
git checkout -b <branch> <hash> recrée la branche pointant sur le commit récupéré, avec son historique intact. Si vous vous souvenez d’une partie d’un message de commit mais pas du hash, git log --oneline -g --grep='<fragment>' le recherche dans le reflog. (git switch -c est l’équivalent moderne de checkout -b ; les deux fonctionnent, et checkout n’est pas déprécié.)
Que ne peut pas récupérer le reflog de Git ?
Le reflog peut récupérer tout ce qui a déplacé HEAD ou la pointe d’une branche (commits, resets, rebases, branches supprimées), mais il ne peut pas récupérer les modifications non commitées du répertoire de travail, car Git ne les a jamais enregistrées dans une référence. Si une modification n’a jamais été commitée, il n’existe aucune mise à jour de référence vers laquelle le reflog pourrait renvoyer : chercher dans le reflog ne donnera donc aucun résultat.
| Le reflog PEUT récupérer | Le reflog NE PEUT PAS récupérer |
|---|---|
Les commits orphelins après un reset --hard | Les modifications non commitées de l’arbre de travail |
| Les commits supprimés par un rebase raté | Les modifications indexées mais non commitées |
| Les branches locales supprimées (récemment) | Les commits non poussés d’un collègue |
| Votre vue locale d’un dépôt distant force-pushé | Les entrées déjà expirées et nettoyées par gc |
Trois contraintes supplémentaires encadrent la récupération :
- C’est local et temporaire. Le reflog n’existe que dans votre répertoire
.gitet n’est jamais poussé : il sauve donc votre propre travail, mais pas les commits non poussés d’un collègue. - Les entrées expirent. Par défaut, Git conserve les entrées de reflog atteignables pendant 90 jours et les entrées inatteignables pendant 30 jours, via
gc.reflogExpireetgc.reflogExpireUnreachable. Les commits orphelins survivent jusqu’à leur expiration suivie d’un passage du ramasse-miettes : récupérez donc rapidement et évitez d’exécutergit gcavant d’avoir terminé. - Récupérez d’abord sur une nouvelle branche. Faites de
git checkout -b recovered <hash>votre réflexe par défaut, afin de pouvoir vérifier avant de modifier une branche partagée. Et rédigez des messages de commit descriptifs : le reflog les affiche, et « Fix login bug » est bien plus facile à repérer qu’un message vague.
Bonus (après un force-push) : le serveur distant n’expose aucun reflog que vous puissiez consulter, mais votre référence de suivi locale, si. Pour récupérer des commits effacés d’un dépôt distant par un force-push, consultez votre enregistrement local avec git reflog show origin/<branch>. L’entrée qui précède immédiatement le force-push contient l’ancienne pointe.
Conclusion
Le reflog est le filet de sécurité local de Git, et il transforme presque chaque moment de type « j’ai perdu mon travail » en une correction à deux commandes : lire le journal, repointer sur le hash. La seule chose qu’il ne peut pas sauver, c’est le travail que vous n’avez jamais commité ; la leçon durable est donc de commiter tôt et souvent, ce qui garantit qu’il y aura toujours une entrée de reflog à retrouver. La prochaine fois que le terminal vous fait manquer un battement de cœur, exécutez git reflog avant toute autre chose.
FAQ
Quelle est la différence entre git reflog et git log ?
git log affiche l'historique des commits atteignables depuis la pointe d'une branche, en suivant les pointeurs vers les parents : les commits orphelins n'y apparaissent donc jamais. git reflog affiche toutes les positions occupées par HEAD ou par une référence dans votre dépôt local — commits, checkouts, resets, rebases et merges — y compris les commits qu'aucune branche ne pointe plus. C'est pourquoi le reflog peut retrouver un commit après un hard reset là où git log en est incapable : le commit est orphelin, mais sa mise à jour de référence est toujours journalisée.
Le reflog de Git fonctionne-t-il après le clonage d'un dépôt ?
Non. Le reflog n'est stocké que dans votre répertoire .git local et n'est jamais poussé ni récupéré : un clone frais démarre donc avec un reflog vide, ne couvrant que les actions effectuées depuis le clonage. Il ne peut pas montrer les déplacements de HEAD antérieurs au clone ni révéler l'historique local d'un collègue. Le reflog sauve votre propre travail perdu dans votre propre dépôt ; ce n'est pas un outil de récupération partagé ou adossé au dépôt distant.
Puis-je récupérer un commit après l'expiration des entrées du reflog ?
Pas via le reflog lui-même. Par défaut, les entrées atteignables expirent après 90 jours et les inatteignables après 30 jours ; une fois qu'un passage du ramasse-miettes a élagué les objets orphelins, ils sont réellement perdus. Avant cela, vous pouvez parfois encore retrouver le hash avec git fsck --lost-found, qui liste les commits pendants (dangling) dans la base d'objets. La démarche fiable consiste à récupérer rapidement et à éviter d'exécuter git gc avant d'avoir retrouvé vos commits.
Comment retrouver un commit perdu si je ne connais pas son hash ?
Cherchez directement dans le reflog. Exécutez git log --oneline -g --grep='fragment' pour filtrer les entrées du reflog par une partie d'un message de commit, ou git reflog --date=relative pour parcourir les entrées par date et repérer l'opération à annuler. Pour les commits orphelins sans entrée dans le reflog, git fsck --lost-found liste les commits pendants que vous pouvez inspecter avec git show avant de les récupérer sur une nouvelle branche avec git checkout -b recovered <hash>.