Git - Récupérer des commits perdus

sam. 12 oct. 2019 by Marmotte

Git est un système de gestion de versions décentralisé. Le côté décentralisé implique que chaque client télécharge une copie complète du dépôt, la plupart des opérations étant donc réalisées directement sur la machine de l'utilisateur.

Cet article fait partie d'une série expliquant quelques fonctionnalités avancées que j'utilise régulièrement.

  1. Généralités et notations
  2. Quelques configurations
  3. Notes diverses
  4. Dépôts et répertoires de travail
  5. L'historique local
  6. Retravailler l'historique des commits
  7. Trouver le commit introduisant une régression
  8. Récupérer des commits perdus

En cas d'erreur de manipulation avec certaines commandes, comme git rebase ou git reset, qui peuvent altérer l'historique d'une branche et supprimer des commits, tout n'est pas perdu. En effet, peu de commandes suppriment réellement des données dans le dépôt. La plupart ne font qu'ajouter des données, et modifier les références (branches, tags, etc.).

Lister les commits non référencés

Pour retrouver des commits perdus, il est tout d'abord nécessaire de lister les commits non référencés. La commande git fsck --unreachable affiche une liste des objets non référencés, en indiquant leur type (blob, commit, tree).

Il est alors possible d'utiliser simplement le retour de cette commande pour afficher des informations sur les commits listés :

$ git show --no-patch $(git fsck --unreachable | grep commit | cut -d' ' -f3)

Note : Selon l'utilisation qui est faite de git, la liste peut être très longue. Par exemple, l'utilisation intensive de git commit --amend ou git commit --fixup et git rebase génère beaucoup de commits non référencés.

Note : Par défaut, les commits présents dans le reflog ne sont pas considérés comme non référencés. Pour les lister aussi, il est possible d'ajouter le paramètre --no-reflog à la commande git fsck.

Récupération des commits

Une fois le commit recherché trouvé, il est possible de l'utiliser comme n'importe quel autre commit. On peut donc créer une nouvelle branche pointant sur ce commit (git branch <new-branch> <lost-commit-hash>), l'appliquer sur la branche courante (git cherry-pick <lost-commit-hash>), etc.

Si ce commit correspondait à des modifications mises de coté (stash), par exemple en cas de nettoyage par erreur du reflog, il faudra utiliser la commande git stash pour le récupérer de manière correcte :

$ # Appliquer le stash dans la branche courante
$ git stash apply <lost-stash-hash>
$ # Créer une nouvelle branche à partir de ce stash
$ git stash branch <lost-stash-hash>
$ # Recréer un nouveau stash pour le conserver
$ git stash store -m "On master: lost stash message" <lost-stash-hash>