Skip to main content
Niveau : Avancé

Chapitre 3 : Les notions avancées

Concepts abordés

  • Reflog
  • Références relatives (~ et ^)
  • Bisect

Reflog

Le reflog est un journal de toutes les modifications des références (HEAD, branches, etc.) dans votre dépôt local. Il enregistre chaque déplacement de HEAD, chaque commit, chaque reset, etc.

La commande git reflog affiche l'historique des références. Chaque entrée montre l'ancien hash, le nouveau hash, l'action effectuée et un message descriptif.

La lecture du reflog permet de retrouver des commits "perdus" après un reset ou une suppression de branche. Les entrées sont référencées par leur position (ex: HEAD@{1} pour l'état précédent) ou par un hash partiel.

Le reflog est local uniquement et expire après 90 jours par défaut. Il est essentiel pour récupérer des commits supprimés accidentellement : git checkout <hash-du-reflog> puis création d'une nouvelle branche pour sauvegarder le commit.

Références relatives

Git permet de référencer des commits de manière relative à partir d'un commit donné, sans connaître son hash exact. Deux opérateurs principaux sont disponibles : ~ (tilde) et ^ (caret).

L'opérateur ~ remonte dans l'historique linéaire : HEAD~1 est le parent de HEAD, HEAD~2 est le grand-parent, HEAD~n est le n-ième ancêtre. Par défaut, ~ équivaut à ~1.

L'opérateur ^ permet de naviguer parmi les parents d'un commit de merge. Un commit de merge a plusieurs parents : HEAD^1 est le premier parent (la branche sur laquelle le merge a été fait), HEAD^2 est le second parent (la branche fusionnée). Pour un commit normal (un seul parent), ^ équivaut à ^1.

Ces opérateurs peuvent être combinés : HEAD~3^2 signifie "le second parent de l'arrière grand-parent de HEAD", utile pour naviguer dans un historique avec des merges. HEAD^^ est équivalent à HEAD~2 pour un historique linéaire, mais HEAD^2 sélectionne le second parent d'un merge.

Ces références sont pratiques pour des commandes comme git log, git diff, git show, git rebase ou git reset sans avoir à chercher le hash exact d'un commit.

Bisect

git bisect est un outil de recherche binaire pour identifier le commit qui a introduit un bug. Le processus consiste à marquer un commit "bon" (sans le bug) et un commit "mauvais" (avec le bug), puis Git teste automatiquement les commits intermédiaires pour isoler le commit problématique.

L'utilisation typique : git bisect start pour démarrer, git bisect bad sur le commit actuel, git bisect good <hash> sur un commit ancien connu comme fonctionnel. Git vous place ensuite sur un commit intermédiaire, vous testez, et vous indiquez git bisect good ou git bisect bad. Le processus se répète jusqu'à identifier le commit exact.

Pour automatiser le processus, utilisez git bisect run <script> qui exécute un script de test à chaque étape, rendant la recherche entièrement automatique.

Questions clés (validation des acquis du chapitre)

  • Reflog : dans quels cas le reflog "sauve" un travail perdu ? Pourquoi ne remplace-t-il pas un backup/remote ?
  • Récupération : après un reset --hard, comment retrouver le commit perdu et le "sauvegarder" durablement ?
  • Références relatives : quelle différence entre HEAD~3 et HEAD^^^ ? Donnez un cas où ^2 a du sens.
  • Parents d'un merge : à quoi correspondent HEAD^1 et HEAD^2 sur un commit de merge ?
  • Analyse : comment utiliser git diff avec ^/~ pour comparer l'état avant/après un merge ?
  • Bisect : pourquoi git bisect est-il efficace (principe) ? Quel prérequis rend son usage fiable ?
  • Bisect : quelle différence entre bisect manuel (good/bad) et git bisect run ?

Exercices :

Exercice 8 — Reflog : récupérer un commit "perdu"

Objectif : restaurer un état après une manipulation destructive.
Étapes :

  • Sur une branche créer 2 commits, puis faire un git reset --hard HEAD~1 (vous venez de "perdre" le dernier commit).
  • Retrouver le hash via git reflog.
  • Revenir sur le commit perdu (checkout) puis le sécuriser en créant une branche (ex : recover/<date>).

Exercice 9 — Références relatives ~ et ^ sur un merge

Objectif : utiliser ~ pour remonter et ^n pour naviguer dans les parents d'un merge.
Étapes :

  • Forcer la création d'un commit de merge (ex : merge feature/... dans main avec --no-ff).
  • Afficher les 2 parents : git show HEAD^1 et git show HEAD^2 (depuis le commit de merge).
  • Comparer les contenus avec git diff HEAD^1..HEAD puis git diff HEAD^2..HEAD.
  • Utiliser HEAD~n pour retrouver un ancêtre et expliquer ce que vous pointez.

Exercice 10 — Bisect : isoler le commit qui introduit un bug

Objectif : identifier rapidement une régression dans une série de commits.
Étapes :

  • Créer une petite suite de commits où un comportement change (ex : un script/fonction qui doit retourner une valeur attendue).
  • Marquer un commit "good" et un commit "bad", lancer git bisect.
  • À chaque étape, tester et marquer good / bad jusqu'à identification du commit fautif.
  • Récupérer le hash du commit identifié + message expliquant la cause de la régression.