Aller au contenu

Git avancé : recherche et diagnostic

Git est aussi un outil d’enquête : retrouver quand, pourquoi et par qui une ligne a changé, et quel commit a introduit un bogue.

Fenêtre de terminal
git log --oneline --graph --decorate --all -30
git log --since="2 weeks ago" --until="yesterday"
git log --author="Alice"
git log --grep="correction" -i # dans le message
git log --no-merges # sans les commits de fusion
git log --first-parent main # suivre seulement la branche principale
git log -- src/app.js # commits qui touchent un fichier
git log --follow -- src/app.js # suit le fichier malgré les renommages
git log main..fonctionnalite # commits de la branche absents de main
git log --diff-filter=D --name-only # fichiers supprimés
git log --stat # fichiers modifiés par commit
git log -p -3 # patch des 3 derniers commits
Fenêtre de terminal
git log --pretty=format:'%h %ad %an : %s' --date=short
git log --pretty=format:'%C(yellow)%h%Creset %C(green)%ad%Creset %s %C(blue)<%an>%Creset' --date=short
git config --global alias.lg "log --graph --pretty=format:'%C(yellow)%h%Creset -%C(auto)%d%Creset %s %C(green)(%cr) %C(blue)<%an>%Creset' --abbrev-commit"
Code Contenu
%h / %H Empreinte courte / complète
%an, %ae Auteur, e-mail
%ad, %cr Date, date relative
%s, %b Sujet, corps
%d Références (branches, étiquettes)
Fenêtre de terminal
git blame src/app.js
git blame -L 40,60 src/app.js # lignes 40 à 60
git blame -w src/app.js # ignore les changements d'espaces
git blame -M src/app.js # détecte le code déplacé dans le fichier
git blame -C -C src/app.js # détecte le code copié depuis d'autres fichiers
git blame --ignore-rev a1b2c3d src/app.js # ignore un commit (reformatage global)
git blame --ignore-revs-file .git-blame-ignore-revs src/app.js
git config --global blame.ignoreRevsFile .git-blame-ignore-revs

.git-blame-ignore-revs liste les commits de reformatage massif (Prettier : intégration continue et hooks) pour qu’ils n’apparaissent plus comme auteurs de chaque ligne.

blame indique le dernier commit qui a touché la ligne, pas forcément son origine : suivre avec git show <commit> puis git blame <commit>^ -- fichier pour remonter plus loin.

Pickaxe : quand cette chaîne est-elle apparue ou disparue ?

Section intitulée « Pickaxe : quand cette chaîne est-elle apparue ou disparue ? »
Fenêtre de terminal
git log -S"calculerTotal" --oneline # commits qui changent le NOMBRE d'occurrences
git log -S"calculerTotal" -p # avec le patch
git log -G"calculer(Total|Remise)" --oneline # expression régulière dans les lignes modifiées
git log -S"timeout" --pickaxe-regex -- src/ # -S avec expression régulière
Option Détecte
-S<texte> Un commit qui ajoute ou retire une occurrence du texte
-G<regex> Un commit dont le patch contient une ligne correspondant à la regex

Parfait pour retrouver quand une fonction a été supprimée ou quand une constante a été introduite.

Fenêtre de terminal
git log -L :calculerTotal:src/panier.js # évolution de la fonction
git log -L 20,45:src/panier.js # évolution des lignes 20 à 45

Affiche chaque commit qui a modifié ce passage, avec le patch limité à ce passage : bien plus précis qu’un git log -p fichier.

Fenêtre de terminal
git grep "TODO"
git grep -n -i "motdepasse" -- '*.js'
git grep -c "console.log" # nombre d'occurrences par fichier
git grep "calculerTotal" a1b2c3d # dans une version passée
git grep -e "foo" --and -e "bar" # les deux sur la même ligne
git grep --heading --break -n "export default"

Plus rapide que grep -r (il ne lit que les fichiers suivis) et il sait chercher dans n’importe quelle révision.

Rappel des bases : Annuler et corriger. Version automatique :

Fenêtre de terminal
git bisect start
git bisect bad # le commit actuel est fautif
git bisect good v1.2.0 # une version connue pour fonctionner
git bisect run npm test # Git teste chaque étape tout seul
git bisect log # historique de la recherche
git bisect reset # revenir à la branche d'origine

git bisect run exécute la commande à chaque étape : code de sortie 0 = bon, 1 à 127 (sauf 125) = mauvais, 125 = commit impossible à tester (à sauter). Pour un script de test dédié :

cat > /tmp/test-bogue.sh <<'SCRIPT'
#!/bin/sh
npm ci --silent && node scripts/reproduire-le-bogue.js
SCRIPT
chmod +x /tmp/test-bogue.sh
git bisect start HEAD v1.2.0
git bisect run /tmp/test-bogue.sh

Un historique de commits atomiques qui compilent tous rend bisect très efficace (c’est un argument supplémentaire en faveur des commits propres : Rebase et historique).

Fenêtre de terminal
git shortlog -sn --no-merges # commits par auteur
git shortlog -sn --since="1 year ago"
git log --format='%an' | sort | uniq -c | sort -rn | head
git diff --stat main...fonctionnalite
git diff --numstat main...fonctionnalite
git log --since="1 month ago" --name-only --pretty=format: | sort | uniq -c | sort -rn | head # fichiers les plus modifiés
git describe --tags # version la plus proche (v1.2.0-14-ga1b2c3d)
git tag --contains a1b2c3d # dans quelles versions se trouve ce commit
git branch --contains a1b2c3d -a # dans quelles branches
git rev-list --count main..fonctionnalite # nombre de commits d'avance

git describe donne un nom lisible pour un commit ; git tag --contains répond à « dans quelle version cette correction est-elle arrivée ? ».

Fenêtre de terminal
git diff # dossier de travail contre l'index
git diff --staged # index contre le dernier commit
git diff a1b2c3d f4e5d6c -- src/ # deux révisions
git diff main...fonctionnalite # depuis l'ancêtre commun
git diff --word-diff # différences mot par mot
git diff --color-moved # met en évidence le code déplacé
git diff -w --ignore-blank-lines
git diff --name-status main # fichiers ajoutés, modifiés, supprimés
git diff --check # espaces en fin de ligne, marqueurs de conflit oubliés
git config --global diff.algorithm histogram # algorithme de comparaison souvent plus lisible
git range-diff main@{1} main fonctionnalite # comparer deux versions d'une branche réécrite

git range-diff compare deux séries de commits (avant et après un rebase) : idéal pour relire les changements d’une demande de fusion mise à jour.

Retrouver l’origine d’une régression : méthode

Section intitulée « Retrouver l’origine d’une régression : méthode »
  1. Reproduire le bogue avec une commande (un test qui échoue).
  2. Identifier une version bonne (étiquette, date) et une mauvaise.
  3. git bisect run avec cette commande.
  4. Lire le commit trouvé (git show), vérifier pourquoi il casse.
  5. Ajouter un test qui échoue sans la correction, puis corriger.
Fenêtre de terminal
git log --all --grep="issue #42" -i
git log --all --oneline | grep -i "migration"
git log -i --grep="^fix" --oneline # conventions de messages (voir commitlint)
git log --format='%s' | grep -E '^(feat|fix)' | wc -l

Des messages au format Conventional Commits (Husky : commitlint) rendent ces recherches fiables.

Symptôme Piste
git log -- fichier ne montre rien Fichier renommé : --follow, ou chemin incorrect
blame pointe un commit de reformatage --ignore-revs-file
bisect ne converge pas Test non déterministe, ou commit impossible à compiler : git bisect skip (ou code 125)
git log -S ne trouve pas -S compte les occurrences : un déplacement sans changement de nombre n’est pas vu ; essayer -G
git grep ne trouve pas un fichier récent Fichier non suivi : git add -N ou git grep --untracked
Sortie très longue git --no-pager, ou git config --global core.pager "less -FRX"
fatal: ambiguous argument Référence et chemin confondus : git log branche -- chemin

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 ; range-diff et --color-moved demandent des versions récentes de Git, présentes sur les deux versions.