Git avancé : reflog et récupération
Git conserve beaucoup plus qu’il n’y paraît : un commit « perdu » reste en général récupérable pendant plusieurs semaines. Cette page détaille les outils (rappel court dans Annuler et corriger).
Le reflog
Section intitulée « Le reflog »Le reflog est le journal local des déplacements de HEAD et des branches : chaque commit, changement de branche, rebase, reset ou fusion y laisse une entrée.
git reflog # déplacements de HEADgit reflog show main # déplacements de la branche maingit reflog --date=iso # avec les datesgit log -g --abbrev-commit --pretty=oneline # présentation proche de git loga1b2c3d (HEAD -> main) HEAD@{0}: commit: corriger le calcul9f8e7d6 HEAD@{1}: rebase (finish): returning to refs/heads/main5c4b3a2 HEAD@{2}: rebase (pick): ajouter le formulaire7e6d5c4 HEAD@{3}: checkout: moving from fonctionnalite to main2b1a0f9 HEAD@{4}: commit: ajouter le formulaire| Notation | Sens |
|---|---|
HEAD@{0} |
Position actuelle |
HEAD@{3} |
Il y a trois déplacements |
HEAD@{2.days.ago} |
Où était HEAD il y a deux jours |
main@{yesterday} |
Où était la branche main hier |
Durée de conservation
Section intitulée « Durée de conservation »| Réglage | Défaut | Rôle |
|---|---|---|
gc.reflogExpire |
90 jours | Entrées du reflog atteignables |
gc.reflogExpireUnreachable |
30 jours | Entrées pointant vers des commits inaccessibles |
git config --get gc.reflogExpiregit config --global gc.reflogExpire "180 days" # conserver plus longtempsgit reflog expire --expire=now --all # vider (dangereux : voir plus bas)Passé ce délai, git gc peut supprimer définitivement les commits qui ne sont plus référencés.
Scénario 1 : un reset --hard de trop
Section intitulée « Scénario 1 : un reset --hard de trop »git reset --hard HEAD~3 # trois commits « disparus »git refloggit reset --hard HEAD@{1} # revenir à l'état d'avant le resetOu, plus prudent, créer une branche au point retrouvé :
git branch recuperation a1b2c3dgit switch recuperationScénario 2 : un rebase qui a mal tourné
Section intitulée « Scénario 2 : un rebase qui a mal tourné »git reflog# repérer l'entrée juste avant « rebase (start) »git reset --hard HEAD@{8}Une entrée plus sûre : ORIG_HEAD conserve la position avant les opérations risquées (rebase, reset, merge) :
git reset --hard ORIG_HEADORIG_HEAD est écrasé par l’opération suivante : l’utiliser tout de suite après l’erreur.
Scénario 3 : une branche supprimée par erreur
Section intitulée « Scénario 3 : une branche supprimée par erreur »git branch -D fonctionnalite # supprimée…git reflog # chercher « checkout: moving from fonctionnalite to … »git branch fonctionnalite 2b1a0f9 # recréée au dernier commit connuChercher dans le reflog le dernier commit de la branche (ou l’entrée de départ du checkout). Git affiche aussi l’identifiant à la suppression : Deleted branch fonctionnalite (was 2b1a0f9).
Scénario 4 : un git stash drop ou un stash perdu
Section intitulée « Scénario 4 : un git stash drop ou un stash perdu »git fsck --unreachable | grep commit # liste les commits orphelinsgit fsck --lost-found # écrit les objets orphelins dans .git/lost-foundgit stash apply a1b2c3d # un commit de stash peut être réappliqué par son identifiantLes stashes sont des commits : après un drop, Git affiche l’identifiant (Dropped refs/stash@{0} (a1b2c3d…)) qui reste utilisable.
Scénario 5 : des modifications jamais validées
Section intitulée « Scénario 5 : des modifications jamais validées »Des modifications non commitées supprimées par git checkout -- . ou git reset --hard sont perdues (elles n’ont jamais été dans un commit). Si elles avaient été indexées (git add), les fichiers existent comme objets orphelins :
git fsck --lost-foundls .git/lost-found/other/ # blobs récupérés (fichiers entiers)git show a1b2c3d4 > fichier-recupere.txtRègle : committer souvent (même des commits provisoires que l’on nettoiera avec un rebase interactif).
Scénario 6 : récupérer après un push --force
Section intitulée « Scénario 6 : récupérer après un push --force »Sur le dépôt distant, le reflog n’est pas disponible. Solutions :
- Un collègue (ou vous) a l’ancien état dans son reflog local :
git reflog show origin/main. - Les références distantes locales :
git reflog show origin/mainconserve l’ancien commit deorigin/mainavant la mise à jour pargit fetch. - Le serveur Gitea peut conserver l’historique de la branche ou un journal d’activité.
git reflog show origin/maingit push origin a1b2c3d:main --force-with-lease # rétablir l'ancien état (avec précaution)git reset : les trois modes
Section intitulée « git reset : les trois modes »| Mode | HEAD | Index (git add) |
Dossier de travail |
|---|---|---|---|
--soft |
Déplacé | Conservé | Conservé |
--mixed (défaut) |
Déplacé | Réinitialisé | Conservé |
--hard |
Déplacé | Réinitialisé | Réinitialisé (modifications perdues) |
git reset --soft HEAD~2 # annuler 2 commits, garder tout prêt à être recommittégit reset HEAD~1 # annuler 1 commit, modifications à réindexergit reset --hard origin/main # aligner sur le distant (perd les modifications locales)git reset --keep HEAD~1 # comme --hard mais refuse si cela écraserait des modifications locales--keep est une alternative plus sûre à --hard.
git restore et git switch
Section intitulée « git restore et git switch »git restore fichier # annule les modifications du dossier de travailgit restore --staged fichier # retire de l'indexgit restore --source=HEAD~2 fichier # restaure le fichier tel qu'il était il y a 2 commitsgit restore --source=a1b2c3d -- dossier/git switch -c nouvelle-branche a1b2c3d # branche à partir d'un commitgit switch --detach a1b2c3d # HEAD détaché pour explorerObjets, références et ramasse-miettes
Section intitulée « Objets, références et ramasse-miettes »Git stocke ses données dans des objets (blob, tree, commit, tag) identifiés par leur empreinte. Un objet n’est supprimé que s’il est inaccessible depuis toute référence et plus ancien que la durée de grâce.
git count-objects -vH # taille du dépôt, objets libresgit gc # regroupe et nettoiegit gc --prune=now # supprime immédiatement les objets inaccessiblesgit fsck --full # vérifie l'intégritégit prune --dry-run # ce qui serait supprimégit cat-file -t a1b2c3d # type d'un objetgit cat-file -p a1b2c3d # contenuVérifier l’intégrité d’un dépôt
Section intitulée « Vérifier l’intégrité d’un dépôt »git fsck --fullgit statusgit log --oneline -3fsck signale les objets corrompus ou manquants (disque défaillant, copie interrompue). Un dépôt corrompu se restaure souvent en recloner depuis le serveur et en recopiant ses commits locaux depuis le dépôt abîmé (git format-patch / git am, ou git remote add + git fetch depuis l’ancien dossier).
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
git reflog ne montre pas le commit |
Trop ancien (expiration), autre dépôt, ou commit jamais créé |
fatal: bad object |
Objet manquant ou corrompu : git fsck, reclonage |
Le commit retrouvé n’apparaît pas dans git log |
Aucune branche ne le référence : git branch recuperation <id> |
HEAD@{n} change à chaque commande |
Normal : le reflog avance à chaque opération ; noter l’identifiant du commit |
| Modifications non commitées perdues | Irrécupérables (sauf fichiers indexés : git fsck --lost-found) |
git gc très long |
Dépôt volumineux : git maintenance, voir Gros dépôts et sous-projets |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune propre à Ubuntu : le reflog et fsck fonctionnent de la même façon dans toutes les versions récentes de Git.