Aller au contenu

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 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.

Fenêtre de terminal
git reflog # déplacements de HEAD
git reflog show main # déplacements de la branche main
git reflog --date=iso # avec les dates
git log -g --abbrev-commit --pretty=oneline # présentation proche de git log
a1b2c3d (HEAD -> main) HEAD@{0}: commit: corriger le calcul
9f8e7d6 HEAD@{1}: rebase (finish): returning to refs/heads/main
5c4b3a2 HEAD@{2}: rebase (pick): ajouter le formulaire
7e6d5c4 HEAD@{3}: checkout: moving from fonctionnalite to main
2b1a0f9 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
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
Fenêtre de terminal
git config --get gc.reflogExpire
git config --global gc.reflogExpire "180 days" # conserver plus longtemps
git 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.

Fenêtre de terminal
git reset --hard HEAD~3 # trois commits « disparus »
git reflog
git reset --hard HEAD@{1} # revenir à l'état d'avant le reset

Ou, plus prudent, créer une branche au point retrouvé :

Fenêtre de terminal
git branch recuperation a1b2c3d
git switch recuperation
Fenêtre de terminal
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) :

Fenêtre de terminal
git reset --hard ORIG_HEAD

ORIG_HEAD est écrasé par l’opération suivante : l’utiliser tout de suite après l’erreur.

Fenêtre de terminal
git branch -D fonctionnalite # supprimée…
git reflog # chercher « checkout: moving from fonctionnalite to … »
git branch fonctionnalite 2b1a0f9 # recréée au dernier commit connu

Chercher 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).

Fenêtre de terminal
git fsck --unreachable | grep commit # liste les commits orphelins
git fsck --lost-found # écrit les objets orphelins dans .git/lost-found
git stash apply a1b2c3d # un commit de stash peut être réappliqué par son identifiant

Les stashes sont des commits : après un drop, Git affiche l’identifiant (Dropped refs/stash@{0} (a1b2c3d…)) qui reste utilisable.

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 :

Fenêtre de terminal
git fsck --lost-found
ls .git/lost-found/other/ # blobs récupérés (fichiers entiers)
git show a1b2c3d4 > fichier-recupere.txt

Règle : committer souvent (même des commits provisoires que l’on nettoiera avec un rebase interactif).

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/main conserve l’ancien commit de origin/main avant la mise à jour par git fetch.
  • Le serveur Gitea peut conserver l’historique de la branche ou un journal d’activité.
Fenêtre de terminal
git reflog show origin/main
git push origin a1b2c3d:main --force-with-lease # rétablir l'ancien état (avec précaution)
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)
Fenêtre de terminal
git reset --soft HEAD~2 # annuler 2 commits, garder tout prêt à être recommitté
git reset HEAD~1 # annuler 1 commit, modifications à réindexer
git 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.

Fenêtre de terminal
git restore fichier # annule les modifications du dossier de travail
git restore --staged fichier # retire de l'index
git restore --source=HEAD~2 fichier # restaure le fichier tel qu'il était il y a 2 commits
git restore --source=a1b2c3d -- dossier/
git switch -c nouvelle-branche a1b2c3d # branche à partir d'un commit
git switch --detach a1b2c3d # HEAD détaché pour explorer

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.

Fenêtre de terminal
git count-objects -vH # taille du dépôt, objets libres
git gc # regroupe et nettoie
git gc --prune=now # supprime immédiatement les objets inaccessibles
git fsck --full # vérifie l'intégrité
git prune --dry-run # ce qui serait supprimé
git cat-file -t a1b2c3d # type d'un objet
git cat-file -p a1b2c3d # contenu
Fenêtre de terminal
git fsck --full
git status
git log --oneline -3

fsck 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).

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.