Git — Les commandes que personne n'utilise

Sortir des 5% de Git que tout le monde connaît — investigation, récupération, productivité

Pourquoi cette formation existe

Git a vingt ans en 2025. La plupart des développeurs l'utilisent quotidiennement depuis des années. Et pourtant, dans la grande majorité des cas, l'usage tient sur les doigts d'une main : add, commit, push, pull, merge. Cinq commandes — sur les plus de 150 que git embarque nativement.

Cette formation ne traite pas du Git que tout le monde connaît. Elle traite des outils que git met silencieusement à disposition et que personne n'utilise — pas par mauvaise volonté, mais parce qu'ils n'apparaissent jamais dans les tutoriels d'introduction, jamais dans les onboardings, jamais dans les pull requests. On apprend git par mimétisme, et le mimétisme transmet le minimum vital, pas l'expertise.

💡 Le constat de départ : les développeurs expérimentés ne sont pas plus rapides parce qu'ils tapent plus vite. Ils sont plus rapides parce qu'ils utilisent les bonnes commandes au bon moment. La différence se chiffre en heures gagnées chaque semaine.

L'angle du cours

Cette formation prend volontairement le contre-pied des cours git classiques. Pas de rappel sur git init, pas de schéma "trois zones" expliqué pour la millième fois. On suppose que vous savez déjà committer et pusher. À la place, on vous montre :

  • Les commandes que vous auriez dû connaître depuis trois ans.
  • Les options méconnues qui transforment des commandes banales en outils chirurgicaux.
  • Les blocs de configuration qui vous font passer dans le top 5 % des utilisateurs git sans rien apprendre de nouveau, juste en activant ce qui existe déjà.
  • Et surtout : la matrice de récupération — quoi faire quand vous avez l'impression d'avoir tout cassé.
Promesse : à la fin de ce cours, vous saurez retrouver un commit "perdu", refactorer proprement un historique public, résoudre des conflits sans transpirer, et déboguer une régression vieille de six mois en quelques minutes.

La règle des 5% / 95%

Si vous regardez le shell d'un développeur senior pendant une journée et que vous notez chaque commande git qu'il tape, vous obtiendrez probablement ce graphique : une dizaine de commandes qui reviennent en permanence, et une longue traîne de commandes rares mais décisives, utilisées une à deux fois par semaine — dans des situations où elles font littéralement gagner des heures.

~150 Commandes git natives
5 Commandes utilisées par 95% des devs
30+ Commandes/options qui changent la vie
10 Lignes de config qui font le top 5%

Pourquoi cet écart est si grand

Il y a trois raisons structurelles pour lesquelles git est sous-utilisé, et aucune n'est liée à la difficulté technique :

1. La transmission orale

Git s'enseigne en équipe, par dessus l'épaule, par copier-coller de commandes que les anciens utilisaient. Personne ne s'arrête pour ouvrir git help log et lire les 2 000 lignes du manuel. Résultat : les commandes que personne dans votre équipe n'a jamais utilisées resteront toujours inconnues, peu importe leur valeur.

2. La peur de tout casser

Git a la réputation — méritée pour les commandes destructives, imméritée pour les autres — d'être un outil dangereux. Beaucoup de développeurs s'auto-limitent à la zone qu'ils connaissent par peur de "perdre du code". Or, comme on va le voir dans le module Récupération, git est paradoxalement l'un des outils les plus difficiles à casser irrémédiablement. Il garde tout, pendant 90 jours par défaut.

3. L'ergonomie historique

Git a été conçu en 2005 pour le noyau Linux, par Linus Torvalds, en deux semaines. Son interface est restée marquée par cette origine : noms de commandes inconsistants (checkout qui faisait cinq choses différentes), options cryptiques (-- vs ...), messages d'erreur peu pédagogiques. Depuis 2019, l'équipe git publie progressivement des commandes "modernes" plus claires (switch, restore) que peu de gens utilisent encore.

Mythe : "Pour bien utiliser git, il faut comprendre en profondeur la structure interne (blobs, trees, packfiles, refs)."

🟢 Réalité : non. Il faut connaître une quinzaine de commandes ciblées et trois ou quatre patterns mentaux. La théorie aide, mais elle n'est pas un prérequis. Ce cours est volontairement pragmatique : on vous donne les outils, la théorie sous-jacente n'est expliquée que là où elle est strictement nécessaire à la pratique.

Le coût caché des mauvaises habitudes

Avant de regarder ce que git permet, regardons ce que la méconnaissance de git coûte concrètement aux équipes. Ces situations sont universelles — que vous travailliez dans une startup ou dans une banque, vous les avez toutes vécues au moins une fois.

❌ Sans les bons outils

"J'ai supprimé sans faire exprès une fonction critique il y a trois semaines, je n'arrive pas à retrouver dans quel commit ça s'est passé. Je vais devoir relire à la main 200 commits."

✅ Avec git log -S

git log -S "nomFonction" trouve en moins d'une seconde le commit exact qui a ajouté ou retiré cette chaîne. C'est ce qu'on appelle le "pickaxe" de git.

❌ Sans les bons outils

"Je rebase ma branche depuis main, et je dois résoudre dix fois les mêmes conflits, parce que chaque fois que je git rebase --continue, git redemande tout."

✅ Avec rerere.enabled

Une ligne dans le .gitconfig et git mémorise chaque résolution de conflit. La fois suivante, il la rejoue automatiquement. Game-changer absolu sur les rebases longs.

❌ Sans les bons outils

"On bosse sur une feature, un bug critique tombe en prod. Il faut stash, switcher de branche, builder, débugger, fixer, push, revenir, unstash, prier que rien ne soit en conflit."

✅ Avec git worktree

git worktree add ../hotfix main crée un dossier indépendant lié au même .git. Vous avez deux checkouts simultanés. Plus jamais besoin de stash pour interrompre votre travail.

❌ Sans les bons outils

"J'ai fait git reset --hard sur la mauvaise branche. J'ai perdu trois jours de travail."

✅ Avec git reflog

Tout commit que vous avez créé — même "perdu" par un reset, un rebase raté ou un checkout malheureux — reste accessible dans le reflog pendant 90 jours. git reset --hard HEAD@{2} et tout revient.

⚠️ Le coût agrégé. Comptez deux à trois situations comme celles-ci par développeur et par mois. Sur une équipe de dix personnes, ça représente facilement 40 à 60 heures perdues par mois — soit un quart de poste équivalent. Ce coût est invisible dans les rapports, mais il est très réel.

Ce que vous allez apprendre

La formation est découpée en sept modules thématiques, conçus pour être lus dans l'ordre la première fois, puis consultés comme une référence ensuite.

💎 Top 10 underrated

Les dix commandes que vous auriez dû connaître depuis longtemps : add -p, worktree, bisect run, reflog, commit --fixup, force-with-lease, rerere, log -S, stash branch, name-rev.

⚙️ .gitconfig magique

Les dix lignes de configuration qui transforment votre expérience git, sans rien apprendre de nouveau : juste en activant ce qui existe déjà mais reste désactivé par défaut.

🔎 Forensique

Enquêter dans l'historique : pickaxe (log -S), blame intelligent (-w -C -C -C), bisect automatisé, recherches dans les diffs entiers, suivi de fichiers à travers les renames.

🤝 Rebase moderne

rerere, --fixup + --autosquash, rebase --onto, zdiff3. La méthode propre pour réécrire l'historique sans douleur.

⏪ Récupération ⭐

Le module phare : reflog, fsck --lost-found, stash branch, et une matrice interactive "j'ai fait quoi comme bêtise ?" → la commande de sauvetage correspondante.

🌳 Worktrees & monorepos

git worktree, clone --filter=blob:none, sparse-checkout, LFS. Les outils essentiels quand votre repo dépasse les 500 Mo ou que vous jonglez entre plusieurs branches.

🐙 Bonus — gh CLI

Le CLI officiel GitHub : pull requests, issues, releases, workflows Actions, secrets, codespaces, gh api brut + GraphQL + jq, et 6 workflows end-to-end. Le module qui supprime le plus d'allers-retours navigateur.

🦄 Cheatsheet

Une page imprimable récapitulant toutes les commandes du cours, organisées par usage. À garder à portée de clavier.

💡 Bonus surprise. Le module 🐙 gh CLI n'est pas du Git natif — c'est le CLI GitHub. Mais il complète tellement bien le reste qu'il aurait été dommage de l'ignorer. Lisez-le après les modules Git pour ne plus jamais quitter le terminal pour cliquer dans une PR.

Prérequis

Cette formation s'adresse à des développeurs qui utilisent git quotidiennement depuis au moins six mois. Concrètement, vous devriez être à l'aise avec :

  • Le cycle de base addcommitpush.
  • La notion de branche, de merge, de pull request.
  • L'utilisation d'une plateforme git distante (GitHub, GitLab, Bitbucket, Azure DevOps).
  • Un terminal — la plupart des commandes présentées ne sont disponibles qu'en ligne de commande, ou sont mal exposées par les interfaces graphiques.

Si vous débutez avec git, je vous recommande de commencer par un cours d'introduction (la documentation officielle de git est gratuite et très bien faite) et de revenir à ce cours dans quelques mois.

💡 Versions de git. Les commandes présentées fonctionnent sur git ≥ 2.30 (publiée fin 2020). La plupart fonctionnent depuis bien plus longtemps. Pour vérifier votre version : git --version.

Mode d'emploi du cours

Légende des badges

💎 Commande méconnue mais qui change la donne au quotidien.
💎💎 Game-changer absolu. À mettre en place dès aujourd'hui.

Les blocs visuels

Vous croiserez plusieurs types d'encarts au fil du cours :

💡 Note — astuce ou précision contextuelle.
⚠️ Avertissement — opération à manier avec précaution.
Bonne pratique — habitude à adopter.
Mythe — idée reçue à corriger.
🟢 Réalité — ce qui se passe vraiment.

Les terminaux

Toutes les commandes du cours sont présentées dans des blocs terminal stylés. Au survol, un bouton Copier apparaît en haut à droite : un clic, et la commande est dans votre presse-papiers, sans le préfixe $.

$ git log --oneline -5 a3f2c1d Refactor authentication flow 8b7e4f0 Add OAuth provider config 2d9a1e3 Fix race condition in token refresh fc4b8a7 Update dependencies (security) 1a0c5d2 Initial commit

Comment progresser

Trois recommandations concrètes pour tirer le maximum de cette formation :

  1. Lisez le module 2 (Top 10) intégralement, même les commandes que vous pensez connaître. Beaucoup ont des options peu utilisées qui changent tout.
  2. Appliquez immédiatement le module 3 (.gitconfig). C'est dix lignes à copier-coller, et l'effet est instantané sur votre quotidien.
  3. Mettez le module 6 (Récupération) en favori. Vous y reviendrez le jour où ça partira en vrille — et ce jour-là, vous serez heureux d'avoir la matrice sous la main.
Prêt ? Direction l'onglet 💎 Top 10 underrated. C'est là que ça commence vraiment.

← Retour au catalogue de formations

💎 Top 10 underrated

Ces dix commandes ne sont pas plus difficiles que celles que vous utilisez tous les jours. Elles existent parfois depuis dix ou quinze ans dans git. Et pourtant, dans 95 % des équipes, personne ne les emploie — pas par mauvaise volonté, mais parce qu'elles ne se transmettent pas oralement et n'apparaissent jamais dans les tutoriels d'introduction.

Pour chaque commande, le format est le même : la douleur qu'elle fait disparaître, un exemple concret de session terminal, et le bénéfice attendu. Lisez l'ensemble dans l'ordre la première fois — chaque section est courte, et l'enchaînement construit un vocabulaire commun qui rendra la suite du cours plus dense.

💡 Conventions du module. Le badge 💎 signale une commande qui change franchement la productivité. 💎💎 marque les game-changers à mettre en place dès aujourd'hui.

💎 1. git add -p — commits chirurgicaux par hunk

git add --patch (alias : -p)

Vous avez modifié trois choses dans le même fichier : un fix de bug, une refacto cosmétique, et une trace de debug oubliée. git add fichier.js stage tout — vous committez le bordel d'un coup.

add -p ouvre un mode interactif qui vous présente chaque hunk (bloc de changement) un par un. Vous répondez y pour stager, n pour ignorer, s pour découper en hunks plus petits, e pour éditer le hunk à la main.

$ git add -p auth.js diff --git a/auth.js b/auth.js @@ -12,6 +12,9 @@ function login(user) { if (!user) throw new Error('no user'); + // fix: race condition on token refresh + if (tokenIsRefreshing) await tokenRefresh; return signIn(user); Stage this hunk [y,n,q,a,d,s,e,?]? y @@ -45,2 +48,3 @@ function logout() { clearSession(); + console.log('DEBUG logout'); // ← trace oubliée Stage this hunk [y,n,q,a,d,s,e,?]? n

Le gain : un commit = une intention. Vos pull requests deviennent lisibles, vos git revert chirurgicaux, vos bisect efficaces. C'est l'unique changement d'habitude qui améliore le plus la qualité de vos commits, sans aucun outil externe.

Pour aller plus loin : les options reset -p, checkout -p, stash -p fonctionnent exactement pareil. Tout le workflow git supporte le mode hunk.

💎💎 2. git worktree — plusieurs checkouts sans stash

git worktree add <path> <branch>

Vous codez sur une feature, un hotfix critique tombe. Stash, checkout main, build, fix, push, retour, unstash, prier qu'aucun fichier n'ait été touché entre temps. Vingt minutes perdues à chaque interruption.

git worktree crée un second dossier indépendant, lié au même .git, sur la branche que vous voulez. Vous avez deux checkouts vivants en parallèle, chacun avec son propre node_modules, son propre serveur de dev, ses propres fichiers ouverts dans l'IDE.

$ git worktree add ../app-hotfix main Preparing worktree (checking out 'main') HEAD is now at a3f2c1d Last release $ cd ../app-hotfix $ git checkout -b hotfix/login-crash # Je fixe, je push, je merge — sans toucher à mon autre worktree $ cd - # Mon dev server tourne toujours, mon IDE est intact, ma feature aussi

Le gain : plus jamais d'interruption coûteuse. Le hotfix vit dans son dossier, votre feature dans le sien. Quand le hotfix est mergé, git worktree remove ../app-hotfix et c'est nettoyé.

⚠️ Une même branche ne peut être checkout que dans un seul worktree à la fois (sécurité anti-corruption). Pour switcher dans un worktree existant, allez-y avec cd et utilisez switch normalement.

💎 3. git bisect run — dichotomie automatisée

git bisect run ./test.sh

Une régression est apparue il y a "quelque part" entre la version 1.4 et la 1.5. Soit 80 commits. Vous allez checkouter chaque commit à la main, lancer le test, noter, recommencer. Une journée de travail.

git bisect tout court fait déjà la dichotomie (~7 étapes pour 80 commits). Mais l'option run automatise tout : git checkoute chaque commit intermédiaire, exécute votre script, lit le code de retour (0 = bon, 1+ = mauvais, 125 = skip), et continue tout seul.

$ git bisect start $ git bisect bad # HEAD est cassé $ git bisect good v1.4 # v1.4 marchait $ git bisect run npm test # go café ☕ Bisecting: 40 revisions left to test after this (roughly 5 steps) ... 8b7e4f0c is the first bad commit Author: Alice <alice@corp.com> Date: Mon Mar 11 14:22:18 2026 +0100 refactor: extract token refresh helper src/auth/refresh.js | 28 +++++++++++++++------------- $ git bisect reset

Le gain : 7 minutes au lieu d'une journée. Et surtout : un commit fautif identifié au commit près, pas une plage approximative. Vous savez exactement ce qui a cassé et quand.

💎💎 4. git reflog — le filet de sécurité ultime

git reflog + git reset --hard HEAD@{N}

Vous avez fait git reset --hard sur la mauvaise branche. Vous avez amendé un commit qui contenait trois jours de travail. Vous avez rebase et tout s'est mélangé. Adieu, code.

Faux. Tout commit que git a vu passer est gardé 90 jours dans le reflog, même s'il n'est plus accessible depuis aucune branche. Le reflog, c'est l'historique de votre HEAD : chaque mouvement (commit, checkout, reset, rebase) y laisse une trace.

$ git reflog a3f2c1d HEAD@{0}: reset: moving to HEAD~5 ← 😱 le reset fatal 8b7e4f0 HEAD@{1}: commit: WIP three days of work ← 💎 ce qu'on veut récupérer 2d9a1e3 HEAD@{2}: commit: refactor auth module fc4b8a7 HEAD@{3}: pull: Fast-forward 1a0c5d2 HEAD@{4}: checkout: moving from main to feature/oauth $ git reset --hard HEAD@{1} HEAD is now at 8b7e4f0 WIP three days of work # Tout est revenu, comme si rien ne s'était passé

Le gain : arrêter d'avoir peur de git. Tant que vous n'avez pas attendu 90 jours sans toucher à votre repo, vous pouvez tout récupérer. Le module ⏪ Récupération détaille toutes les variantes.

💎💎 5. commit --fixup + rebase --autosquash

git commit --fixup=<SHA> puis git rebase -i --autosquash <base>

Le reviewer demande une modif sur un commit que vous avez fait il y a trois commits. Vous lancez un rebase interactif, vous éditez à la main, vous mélangez les pick et les edit, vous vous trompez, vous abort, vous recommencez.

commit --fixup=<SHA> crée un commit dont le message commence par fixup! suivi du sujet du commit cible. Au prochain rebase -i --autosquash, git positionne et squash automatiquement le fixup au bon endroit. Zéro édition manuelle.

$ git log --oneline -5 a3f2c1d Add OAuth refresh 8b7e4f0 Fix race in token refresh ← le commit à corriger 2d9a1e3 Refactor authentication fc4b8a7 Update dependencies 1a0c5d2 Initial $ # Je modifie ce qu'il faut, puis : $ git add -p $ git commit --fixup=8b7e4f0 $ git rebase -i --autosquash 2d9a1e3 # L'éditeur s'ouvre déjà avec le fixup positionné au bon endroit # pas besoin de toucher quoi que ce soit, on sauvegarde et basta

Le gain : amender un commit ancien devient aussi simple qu'amender le dernier. Activez rebase.autosquash = true dans votre ~/.gitconfig et vous n'aurez même plus besoin du flag.

💎 6. git push --force-with-lease — force push sécurisé

git push --force-with-lease

Après un rebase, vous devez force-push. Vous tapez git push --force. Sauf que pendant ce temps, un collègue avait poussé un commit sur la même branche. Vous venez d'écraser son travail. Sans avertissement.

--force-with-lease est strictement identique à --force sauf qu'il vérifie d'abord que le remote est encore à l'état que vous croyiez. S'il a bougé, le push est refusé. Vous récupérez le travail du collègue, vous re-rebasez, et vous re-pushez.

$ git push --force-with-lease ! [rejected] feature/oauth -> feature/oauth (stale info) error: failed to push some refs to 'origin' hint: The remote contains work that you do not have locally. # Ouf : on a évité d'écraser le commit du collègue. # On fetch, on rebase au-dessus, on re-push. $ git fetch && git rebase origin/feature/oauth $ git push --force-with-lease Total 12 (delta 4), reused 0 (delta 0) To origin + 8b7e4f0...c4d2e1a feature/oauth -> feature/oauth (forced update)

Le gain : aucun écrasement accidentel possible. Faites-vous une règle absolue : plus jamais de --force nu. Toujours --force-with-lease.

Alias utile : git config --global alias.pushf "push --force-with-lease". Comme ça, git pushf devient le réflexe naturel.

💎💎 7. rerere — Reuse Recorded Resolution

git config --global rerere.enabled true

Vous rebasez une branche de 30 commits sur main. Le commit 4 a un conflit, vous résolvez. Le commit 9 a le même conflit (parce qu'il modifie la même zone), vous le résolvez à l'identique. Le commit 17 aussi. Le commit 24 aussi. Vous craquez et vous abort.

rerere = Reuse Recorded Resolution. Une fois activé (une seule fois, dans votre .gitconfig), git mémorise chaque résolution de conflit et la rejoue automatiquement chaque fois qu'il rencontre le même conflit. Sur un rebase long ou un cherry-pick répété, c'est immédiatement transparent.

$ git config --global rerere.enabled true # Une seule fois dans votre vie. C'est tout. $ git rebase main Auto-merging src/auth.js CONFLICT (content): Merge conflict in src/auth.js Recorded preimage for 'src/auth.js' # Je résous une fois $ git add src/auth.js && git rebase --continue # Quelques commits plus loin, même conflit : Auto-merging src/auth.js CONFLICT (content): Merge conflict in src/auth.js Resolved 'src/auth.js' using previous resolution. # ✨ Magie. Plus rien à faire, juste --continue

Le gain : un rebase de 30 commits avec 5 conflits identiques se résout en 1 fois au lieu de 5. Sur des branches longues ou des merges récurrents, c'est plusieurs heures gagnées par mois.

💎 8. git log -S — le pickaxe

git log -S "chaîne" -- chemin/optionnel

"On avait une fonction computeDiscount() il y a six mois. Elle a disparu. Qui l'a supprimée ? Quand ? Pourquoi ?" Lire 800 commits à la main, non merci.

log -S est appelé le pickaxe. Il liste uniquement les commits qui changent le nombre d'occurrences d'une chaîne donnée — autrement dit, qui l'ajoutent ou la suppriment. C'est l'outil le plus précis qui existe pour répondre à la question "qui a touché ça".

$ git log -S "computeDiscount" --oneline c4d2e1a Refactor pricing engine, drop unused helpers ← 💎 le coupable 3f0a8b2 Add discount edge case for VIP customers 1e9d4c7 Initial pricing module $ git show c4d2e1a --stat Refactor pricing engine, drop unused helpers Author: Bob <bob@corp.com> Date: Tue Jan 14 11:08:42 2026 +0100 src/pricing/discount.js | 47 ----------------------------- # Bob l'a supprimée le 14 janvier en disant que c'était unused. # On a maintenant la conversation à avoir.

Le gain : trois secondes pour répondre à une question d'archéologie qui en prendrait une heure autrement. Variante : log -G "regex" pour matcher un motif dans les diffs entiers (pas juste compter les occurrences).

💎 9. git stash branch — convertir un stash en branche

git stash branch <new-branch> [stash@{N}]

Vous avez stashé du WIP la semaine dernière. Vous tentez stash pop. Conflits partout, parce que la branche a beaucoup bougé entre temps. Vous abandonnez et perdez le stash.

git stash branch crée une nouvelle branche partant du commit où le stash a été créé, applique le stash dessus (sans conflit, puisque le contexte est exact), et supprime le stash. Vous pouvez ensuite committer tranquillement et merger comme une branche normale.

$ git stash list stash@{0}: WIP on feature/oauth: 8b7e4f0 token refresh stash@{1}: WIP on main: 2d9a1e3 refactor auth $ git stash branch wip/oauth-refresh stash@{0} Switched to a new branch 'wip/oauth-refresh' On branch wip/oauth-refresh Changes not staged for commit: modified: src/auth.js Dropped stash@{0} (a3f2c1d...) # Branche créée à l'état d'origine du stash, modifs appliquées proprement. # Plus aucun conflit puisqu'on est exactement dans le contexte d'origine.

Le gain : un stash devient un brouillon de branche. Vous récupérez 100 % de votre WIP, même vieux de plusieurs semaines, sans bagarre.

💎 10. git name-rev — quelle branche contient ce SHA ?

git name-rev <SHA> ou git name-rev --all

Un SHA tombe d'une CI, d'un ticket Jira, d'un message Slack. Vous le copiez : "à quelle branche il appartient ?" Vous lancez git log --all --oneline | grep… et vous croisez les doigts.

git name-rev répond à la question instantanément : il vous donne le nom symbolique du commit (la branche ou le tag le plus proche). Indispensable quand un commit "orphelin" sort d'un reflog ou d'un fsck.

$ git name-rev 8b7e4f0 8b7e4f0 remotes/origin/feature/oauth~3 # Ce SHA est sur origin/feature/oauth, 3 commits avant le tip. $ git name-rev --name-only HEAD main $ git branch --contains 8b7e4f0 feature/oauth release/2026.04 # Variante : toutes les branches qui contiennent ce commit.

Le gain : aucune enquête manuelle. Le SHA parle de lui-même. Combiné à git reflog, c'est la paire idéale pour récupérer un commit "perdu" et savoir d'où il venait.

Et après ?

Ces dix commandes couvrent l'essentiel des frictions du quotidien. Mais elles donnent leur plein potentiel quand elles sont combinées avec une bonne configuration globale (module suivant) et appliquées à des cas concrets (modules forensique, rebase, récupération).

Action immédiate : activez dès maintenant les deux configs critiques : git config --global rerere.enabled true et git config --global rebase.autosquash true. Vous en profiterez dès aujourd'hui.

← Retour au catalogue de formations

⚙️ .gitconfig magique

La plupart des frictions quotidiennes avec git ne viennent pas de commandes manquantes. Elles viennent de défauts datés que git n'a jamais activés rétroactivement, par souci de rétrocompatibilité. Activez-les manuellement et la friction disparaît du jour au lendemain.

Ce module présente dix lignes de configuration qui vous font passer dans le top 5 % des utilisateurs git — sans rien apprendre de nouveau, juste en allumant ce qui existe déjà. Ajoutez-les à votre ~/.gitconfig (ou installez-les via les commandes git config --global) et oubliez-les. Elles travaillent silencieusement.

💡 Pourquoi ces défauts ne sont pas activés. Git a une politique stricte de rétrocompatibilité : changer un défaut casserait des scripts et des intégrations vieux de quinze ans. La plupart des nouveaux comportements arrivent donc en opt-in. Charge à vous de les opter dedans.

Le bloc à copier-coller

Ouvrez ~/.gitconfig et collez ce bloc à la fin (sous votre section [user]). Les sections suivantes détaillent chaque ligne — pourquoi elle existe, ce qu'elle change, quand la désactiver.

[init] defaultBranch = main [pull] rebase = true # plus de merge commits parasites ff = only # refuse un pull non fast-forward [push] autoSetupRemote = true # plus besoin de -u origin <branch> default = current [rebase] autosquash = true # commit --fixup s'auto-applique autostash = true # stash auto pendant rebase [rerere] enabled = true # mémorise les résolutions de conflits [merge] conflictStyle = zdiff3 # marqueurs à 3 panneaux [diff] algorithm = histogram # diffs plus précis colorMoved = zebra # détecte les blocs déplacés [core] fsmonitor = true # status instantané sur gros repos [maintenance] auto = true # gc en background
Variante CLI : si vous préférez ne pas éditer le fichier à la main, chaque ligne a sa commande équivalente détaillée dans les sections suivantes.

1. [init] defaultBranch = main

Par défaut, git init crée une branche master. La quasi-totalité des plateformes (GitHub, GitLab, Bitbucket) ont basculé sur main depuis 2020. Ce paramètre aligne enfin votre git init local avec le reste du monde.

$ git config --global init.defaultBranch main

2. [pull] rebase = true + ff = only

Sans configuration, git pull crée un merge commit chaque fois que votre branche locale a divergé du remote. Résultat : votre historique se pollue de "Merge branch 'main' of …" qui ne servent absolument à rien.

pull.rebase = true remplace le merge par un rebase de vos commits par-dessus ceux du remote. Historique linéaire, propre, lisible.

pull.ff = only ajoute une garde : si le pull n'est pas fast-forward (parce que vous avez des commits locaux non rebasés), git refuse plutôt que de créer un merge dans le dos. Vous décidez explicitement comment réagir.

$ git config --global pull.rebase true $ git config --global pull.ff only
⚠️ Si vous travaillez sur des branches partagées et déjà poussées, un rebase réécrit l'historique. Combinez toujours avec --force-with-lease au push (jamais --force nu).

3. [push] autoSetupRemote = true

Combien de fois avez-vous tapé git push sur une nouvelle branche pour vous prendre : "fatal: The current branch has no upstream branch", puis copier-coller la commande suggérée git push -u origin <branch> ?

autoSetupRemote = true tracke automatiquement la branche distante du même nom au premier push. Plus jamais de -u.

$ git config --global push.autoSetupRemote true $ git config --global push.default current

4. [rebase] autosquash = true + autostash = true

autosquash rend l'option --autosquash implicite sur tous vos rebase -i. Combiné à git commit --fixup=<SHA>, c'est le workflow le plus efficace qui existe pour amender un commit ancien (voir le module 🤝 Rebase moderne).

autostash stash automatiquement vos modifications en cours avant un rebase, puis les unstash à la fin. Plus besoin de stasher à la main avant un rebase main.

$ git config --global rebase.autosquash true $ git config --global rebase.autostash true

5. [rerere] enabled = true

Reuse Recorded Resolution. Détaillé dans le module 💎 Top 10. Une seule ligne. Activée pour toujours. Effet immédiat sur tous les rebases, merges et cherry-picks suivants.

$ git config --global rerere.enabled true
Si vous ne devez en activer qu'une seule, c'est celle-là.

6. [merge] conflictStyle = zdiff3

Par défaut, les marqueurs de conflit git n'affichent que deux versions : la vôtre (<<< HEAD) et celle de l'autre branche (>>> branch). C'est très souvent insuffisant pour comprendre l'intention de chacun.

zdiff3 ajoute la version commune d'origine (la base de merge) entre les deux. Vous voyez d'un coup d'œil ce que chacun a modifié par rapport au point de départ partagé. Compréhension multipliée par 3 sur les conflits non triviaux.

$ git config --global merge.conflictStyle zdiff3 # Exemple de marqueur zdiff3 dans un fichier en conflit : <<<<<<< HEAD const TIMEOUT = 60_000; # vous : 60s ||||||| base const TIMEOUT = 30_000; # origine commune : 30s ======= const TIMEOUT = 120_000; # eux : 120s >>>>>>> feature/long-timeout

Sans zdiff3, vous voyiez 60 vs 120 et deviez deviner ce qui était la valeur d'origine. Avec, vous comprenez immédiatement que vous avez doublé alors qu'eux ont quadruplé — la conversation à avoir avec l'auteur n'est plus la même.

7. [diff] algorithm = histogram + colorMoved = zebra

L'algorithme de diff par défaut (myers) date de 1986 et produit parfois des diffs absurdes — par exemple en alignant des accolades fermantes au lieu du contenu logique. histogram (introduit en 2017) est nettement plus précis sur du code moderne, en particulier les refactos et les ajouts de fonctions.

colorMoved = zebra détecte les blocs déplacés dans un diff (déplacement, pas modification) et les colore différemment. Indispensable pour reviewer une refacto qui bouge du code d'un fichier à un autre.

$ git config --global diff.algorithm histogram $ git config --global diff.colorMoved zebra

8. [core] fsmonitor = true

Sur un repo de plus de 10 000 fichiers, chaque git status peut prendre plusieurs secondes : git scanne récursivement l'arborescence pour détecter les changements. fsmonitor délègue cette détection au système de fichiers (FSEvents sur macOS, équivalents sur Windows et Linux récents). Résultat : git status instantané, même sur un monorepo de 100 000 fichiers.

$ git config --global core.fsmonitor true $ git config --global core.untrackedCache true # bonus : cache les untracked
⚠️ Disponible sur git ≥ 2.37 (août 2022). Vérifiez votre version avec git --version avant d'activer.

9. [maintenance] auto = true

Git accumule de l'historique, des refs, des objets non packés. Sans intervention, un repo actif gonfle et ses opérations ralentissent. git maintenance exécute en background les tâches d'entretien (gc, repack, prefetch des remotes, écriture des bitmaps) sans bloquer votre terminal.

$ git maintenance start # dans le repo concerné, une fois

Une fois lancée, la maintenance tourne périodiquement via cron (Linux/macOS) ou scheduled tasks (Windows), sans rien demander.

Bonus : alias et confort

Au-delà des dix lignes critiques, voici quelques alias qui rendent l'usage quotidien beaucoup plus agréable. À ajouter dans la section [alias] de votre ~/.gitconfig :

[alias] # Log graphique compact lg = log --graph --pretty=format:'%C(yellow)%h%Creset -%C(red)%d%Creset %s %C(green)(%cr) %C(blue)<%an>%Creset' --abbrev-commit # Statut court (équivalent moderne) st = status -sb # Force-push sécurisé pushf = push --force-with-lease # Voir le dernier commit avec le diff last = log -1 HEAD --stat -p # Branches triées par dernière activité recent = for-each-ref --sort=-committerdate --count=15 \ --format='%(committerdate:short) %(refname:short)' refs/heads/ # Annule le dernier commit en gardant les modifs en working tree undo = reset --soft HEAD~1 [color "ui"] ui = auto [column] ui = auto # branch/tag en colonnes auto-ajustées [branch] sort = -committerdate # git branch trie par date

Où ranger sa config — global, local, conditionnel

Git lit la configuration à trois niveaux, du moins prioritaire au plus prioritaire :

Niveau Fichier Commande Quand l'utiliser
System /etc/gitconfig --system Politique d'entreprise. Rarement modifié.
Global ~/.gitconfig --global Vos préférences personnelles. C'est ici que va le bloc magique.
Local .git/config --local (défaut) Spécifique à un repo (ex. email pro vs perso).

Configurations conditionnelles

Vous travaillez sur des repos pro ET perso ? Utilisez includeIf dans votre .gitconfig global pour appliquer une config différente selon le chemin du repo :

# ~/.gitconfig [includeIf "gitdir:~/work/"] path = ~/.gitconfig-work [includeIf "gitdir:~/perso/"] path = ~/.gitconfig-perso # ~/.gitconfig-work [user] email = vous@entreprise.com signingkey = ABC123…

Tous les repos sous ~/work/ hériteront automatiquement de l'identité pro, ceux sous ~/perso/ de l'identité perso. Plus de risque de pousser un commit avec la mauvaise adresse.

Vérifier sa config : git config --list --show-origin affiche toutes les valeurs effectives et le fichier qui les définit. Indispensable pour déboguer un comportement inattendu.

← Retour au catalogue de formations

🔎 Forensique

La forensique git, c'est l'ensemble des techniques pour enquêter dans un historique : retrouver quand, pourquoi et par qui un changement est arrivé. C'est le module qui vous fait gagner le plus de temps quand vous reprenez du code que vous n'avez pas écrit, ou quand une régression apparaît sans coupable évident.

Le réflexe le plus répandu — git log | grep — donne des résultats médiocres. Ces commandes natives sont infiniment plus précises et plus rapides.

💎 Pickaxe : log -S et log -G

git log -S "chaîne" — chercher un ajout/retrait de chaîne

"Quelqu'un a supprimé l'appel à computeTax() quelque part dans les six derniers mois. Lequel des 400 commits ?"

log -S ne ramène que les commits qui changent le nombre d'occurrences de la chaîne. Pas ceux qui la mentionnent dans un message, pas ceux qui touchent une ligne adjacente. Précision chirurgicale.

$ git log -S "computeTax" --oneline -- src/ c4d2e1a Drop legacy tax helpers 3f0a8b2 Add VAT support 1e9d4c7 Initial pricing module $ git show c4d2e1a -- src/pricing.js # On voit l'exact diff qui a viré computeTax(), avec son auteur et sa date

git log -G "regex" — pickaxe par regex dans les diffs

log -G est la version regex : il matche n'importe quelle modification de ligne dont le contenu (ajouté ou retiré) correspond au motif. Plus large, plus puissant, parfois plus bruyant.

$ git log -G "TODO|FIXME|XXX" --since="3 months ago" --oneline # Tous les commits qui ont touché un commentaire TODO/FIXME/XXX # sur les 3 derniers mois. Parfait pour un audit de dette technique.
Astuce : ajoutez -p pour voir le diff de chaque commit trouvé. Ajoutez -- chemin/ pour limiter la recherche à un sous-arbre.

💎 Blame intelligent : -w -C -C -C

git blame -w -C -C -C path

Vous lancez git blame sur une fonction. Toutes les lignes pointent vers le même commit récent : "Run prettier", "Reformat imports", "Move file". Le vrai auteur du code est invisible.

-w ignore les changements de whitespace (espaces, tabs, retours à la ligne). -C -C -C demande à git de détecter agressivement les copier-coller : la même ligne extraite d'un autre fichier garde son auteur d'origine. Combinés, ces flags reconstituent le vrai historique.

$ git blame -w -C -C -C src/auth.js 8b7e4f0 src/legacy/auth.js (Alice 2024-03-12) function login(user) { 8b7e4f0 src/legacy/auth.js (Alice 2024-03-12) if (!user) throw new Error('no user'); 2d9a1e3 src/legacy/auth.js (Bob 2025-11-08) if (tokenIsRefreshing) await tokenRefresh; 8b7e4f0 src/legacy/auth.js (Alice 2024-03-12) return signIn(user); # La 3e colonne montre le fichier *d'origine* (legacy/auth.js) # avant le déplacement. Et l'auteur réel, pas le déplaceur.

Le gain : retrouver le vrai auteur d'une ligne, même après un reformatage massif, un déplacement de fichier ou un refactor. Game-changer pour comprendre une décision de design.

💡 Chaque -C additionnel élargit la portée : -C = même fichier, -C -C = fichiers modifiés dans le même commit, -C -C -C = tout fichier de l'historique. Plus c'est long, plus c'est lent — mais sur des cas durs, ça en vaut largement la peine.

💎 Diffs vraiment lisibles

git diff --color-words

Sur du texte (markdown, prose, configuration) ou du code avec des lignes très longues, le diff ligne-par-ligne par défaut est illisible. --color-words diffe mot par mot, en colorant uniquement les mots modifiés. Lisibilité multipliée par dix sur de la prose.

$ git diff --color-words README.md # Au lieu de voir deux lignes complètes en rouge/vert, # vous voyez juste les mots qui ont changé.

git diff master...feature — la triple barre

Vous reviewez une PR. Vous tapez git diff master feature. Le diff inclut tous les commits ajoutés à master depuis le fork — qui n'ont rien à voir avec la PR. Lecture impossible.

master..feature (deux points) montre tout ce qui a divergé. master...feature (trois points) montre uniquement ce que feature apporte par rapport au merge-base commun. C'est le diff que vous voyez sur GitHub.

$ git diff main...feature/oauth # UNIQUEMENT ce qu'apporte feature/oauth, comme dans la PR GitHub. $ git log --left-right main...feature/oauth --oneline > c4d2e1a Add OAuth refresh > 8b7e4f0 Token refresh race fix < a3f2c1d Bump dependency lodash < 2d9a1e3 Hotfix CVE-2026-1234 # < = uniquement à main, > = uniquement à feature/oauth. # Vue panoramique de la divergence.

💎 Lire l'historique sans le bruit des merges

git log --first-parent main

Sur une branche main où chaque PR est mergée par merge commit, l'historique brut de git log est illisible : il mélange les commits intermédiaires de chaque branche. --first-parent ne suit que la première ligne de descendance, c'est-à-dire la séquence des merges. Vue propre et chronologique des PR mergées.

$ git log --first-parent --oneline main a3f2c1d (HEAD -> main) Merge PR #421 — OAuth refresh fc4b8a7 Merge PR #420 — Update lodash 1a0c5d2 Merge PR #419 — Pricing engine refactor # Une ligne par PR mergée. Idéal pour générer un changelog ou # repérer la PR qui a introduit un changement.

💎 Suivre un fichier renommé : log --follow

git log --follow path

Vous voulez l'historique d'un fichier. Vous tapez git log src/new-name.js. Vous obtenez 3 commits. Sauf que le fichier s'appelait old-name.js avant un git mv il y a six mois — et il a 200 commits avant ça, invisibles.

--follow traverse les renames et reconstitue l'historique complet, même à travers plusieurs déplacements successifs.

$ git log --follow --oneline -- src/auth/refresh.js c4d2e1a Refactor refresh logic 8b7e4f0 Move refresh into auth/ subfolder 2d9a1e3 Rename token.js → refresh.js fc4b8a7 Initial token implementation # L'historique remonte avant le rename ET avant le déplacement.
⚠️ --follow ne fonctionne que sur un seul fichier à la fois. Pour plusieurs fichiers, combinez plusieurs commandes ou utilisez log -p + --diff-filter=R pour repérer les renames.

💎 git grep — bien plus que grep

git grep -p "pattern"

git grep est plusieurs fois plus rapide que grep -r (il utilise les index git) et respecte .gitignore. Mais sa vraie tuerie, c'est l'option -p : elle affiche la fonction englobante de chaque match, pas juste la ligne brute. Pour comprendre un match, c'est précieux.

$ git grep -p "throw new Error" -- src/ src/auth.js=function login(user) { src/auth.js: throw new Error('no user'); src/api.js=async function fetchUser(id) { src/api.js: throw new Error(`user ${id} not found`); # Vous voyez immédiatement DANS QUELLE FONCTION le throw arrive. $ git grep -e "setTimeout" --and -e "await" # Lignes contenant À LA FOIS setTimeout et await — anti-pattern # classique de gestion async incorrecte. $ git grep "oldApi" $(git rev-list --all) # Cherche dans TOUS les commits de tous les temps. Lent mais imbattable # pour retrouver une référence supprimée depuis longtemps.

💎 Bisect automatisé : bisect run

Le bisect manuel est déjà excellent : git checkoute le commit médian entre un "bon" et un "mauvais" connu, vous testez, vous marquez, il recommence. Pour 80 commits, ~7 étapes.

bisect run automatise tout. Vous fournissez un script qui retourne 0 si le commit est bon, ≠ 0 sinon. Git enchaîne les checkouts, lance le script, lit le code de retour, continue.

$ git bisect start $ git bisect bad HEAD $ git bisect good v2026.03.0 # Script qui détecte la régression (par exemple via un test ciblé) : $ cat > /tmp/check.sh <<'EOF' #!/usr/bin/env bash npm install --silent || exit 125 # 125 = "skip ce commit" npm test -- pricing.test.js EOF $ chmod +x /tmp/check.sh $ git bisect run /tmp/check.sh Bisecting: 40 revisions left to test after this (roughly 5 steps) ... (5 minutes plus tard) ... 8b7e4f0c is the first bad commit Author: Bob <bob@corp.com> refactor: extract discount engine src/pricing/engine.js | 84 +++++++++----------- $ git bisect reset
💡 Le code 125 est spécial : il dit à git "skip ce commit, je ne peux pas tester ici". Utile quand un commit intermédiaire ne build pas pour une raison sans rapport avec la régression.

💎 Identifier un SHA orphelin

Un SHA tombe d'une CI, d'un commentaire, d'un reflog. Trois questions immédiates : quelle branche le contient ? combien de commits derrière HEAD ? est-il toujours accessible ?

$ git name-rev 8b7e4f0 8b7e4f0 remotes/origin/feature/oauth~3 # Sur origin/feature/oauth, 3 commits avant le tip. $ git branch --contains 8b7e4f0 feature/oauth release/2026.04 # Toutes les branches qui contiennent ce commit. $ git describe --tags --abbrev=0 8b7e4f0 v2026.03.4 # Le tag le plus récent atteignable depuis ce commit. Très utilisé en CI # pour calculer un numéro de version sémantique.

Stats contributeurs : shortlog

shortlog -sn liste chaque contributeur avec le nombre de commits, trié. Combiné avec --since, c'est la base d'un point d'équipe ou d'un rapport mensuel.

$ git shortlog -sn --since="1 month ago" --no-merges 34 Alice 28 Bob 21 Charlie 8 CI Bot $ git shortlog -sn -- src/payment/ # Qui a contribué au module payment ? Réponse en 100ms.

Playbook : enquête type "régression mystérieuse"

Mise en situation : un test E2E qui passait il y a deux semaines échoue maintenant. Vous n'avez aucune idée du commit fautif. Voici la séquence d'investigation type :

  1. Identifier la fonction touchée par le test qui échoue (lire la stack trace).
  2. git log -S "nomFonction" --since="3 weeks ago" → liste des commits qui l'ont modifiée.
  3. Si la liste est courte : git show <sha> sur chacun, lecture du diff.
  4. Si la liste est longue ou rien d'évident : passer à git bisect run avec un script qui exécute le test E2E. Coût : 5–10 minutes pour une plage de 200 commits.
  5. Une fois le commit trouvé : git blame -w -C -C -C sur les lignes touchées pour comprendre l'historique de la zone modifiée.
  6. Documenter dans le ticket : SHA fautif, auteur, contexte du commit, hypothèse de fix.
Total : 15 minutes au lieu d'une journée. Et un rapport d'enquête précis qui sert de pièce jointe au ticket de fix.

← Retour au catalogue de formations

🤝 Rebase moderne

Le rebase a longtemps eu mauvaise réputation : opaque, dangereux, conflits à répétition. Cette réputation date d'avant l'arrivée de quatre outils qui changent tout : rerere, --autosquash, --autostash et conflictStyle = zdiff3. Activez ces quatre lignes une fois pour toutes (voir le module ⚙️ .gitconfig magique), et le rebase devient une opération anodine.

Ce module présente le workflow rebase moderne : comment amender un commit ancien sans douleur, comment transplanter une série de commits, comment réécrire un historique public en gardant les merges, et comment éviter les pièges classiques.

Modèle mental : copier, pas déplacer

Un rebase ne déplace pas vos commits — il les recrée ailleurs. Chaque commit obtient un nouveau SHA. L'ancien reste accessible dans le reflog pendant 90 jours, mais il n'est plus pointé par aucune branche. C'est pour ça qu'un rebase mal géré "perd" des commits aux collaborateurs : leurs anciens SHAs n'existent plus côté serveur.

❌ Mauvais réflexe

Rebase agressif sur une branche déjà partagée, suivi d'un git push --force. Vos collègues récupèrent une branche qu'ils ne reconnaissent plus.

✅ Bon réflexe

Rebase sur vos branches feature personnelles avant la première PR, et sur les branches partagées uniquement après annonce + --force-with-lease.

Mythe : "Le rebase, c'est dangereux."

🟢 Réalité : dangereux uniquement si vous rebasez une branche partagée et force-pushez sans concertation. Sur vos branches feature, c'est l'opération la plus saine pour garder un historique propre.

💎💎 rerere — Reuse Recorded Resolution

Le tueur silencieux des rebases longs. Détaillé dans le module 💎 Top 10. Une fois activé, git mémorise chaque résolution de conflit et la rejoue à l'identique partout où le même conflit réapparaît.

$ git config --global rerere.enabled true # Pour vérifier ce que rerere a mémorisé sur le repo courant : $ git rerere status src/auth.js $ git rerere diff # Affiche la résolution mémorisée pour chaque fichier en conflit. $ git rerere clear # Oublie les résolutions du conflit en cours (utile si on s'est trompé).

💎💎 commit --fixup + rebase --autosquash

Le pattern le plus important de tout le module rebase. Pour amender un commit qui n'est pas le dernier, oubliez rebase -i manuel — utilisez ce duo.

Workflow complet

  1. Vous identifiez le commit cible : git log --oneline.
  2. Vous faites vos modifications, vous les stagez (idéalement avec git add -p).
  3. git commit --fixup=<SHA> crée un commit spécial dont le message commence par fixup!.
  4. git rebase -i --autosquash <base> ouvre l'éditeur avec le fixup déjà positionné et marqué pour squash.
  5. Vous sauvegardez sans toucher à rien. Le commit cible est réécrit avec le contenu du fixup. Historique propre.
$ git log --oneline -6 a3f2c1d (HEAD) Add tests for OAuth refresh 8b7e4f0 Add OAuth refresh helper ← cible : il manque un edge case 2d9a1e3 Wire up OAuth provider config fc4b8a7 Refactor session manager 1a0c5d2 Update dependencies 0c5d21e (main) Initial commit $ # Je modifie le helper, j'ajoute le edge case manquant $ git add -p src/oauth/refresh.js $ git commit --fixup=8b7e4f0 [feature/oauth c1a2b3d] fixup! Add OAuth refresh helper $ git rebase -i --autosquash main # L'éditeur s'ouvre déjà ainsi (rien à modifier) : pick 1a0c5d2 Update dependencies pick fc4b8a7 Refactor session manager pick 2d9a1e3 Wire up OAuth provider config pick 8b7e4f0 Add OAuth refresh helper fixup c1a2b3d fixup! Add OAuth refresh helper ← positionné auto pick a3f2c1d Add tests for OAuth refresh # On sauvegarde. Le fixup est mergé dans 8b7e4f0. Historique propre.
✅ Avec rebase.autosquash = true dans votre ~/.gitconfig, vous pouvez omettre le flag --autosquash. Toute la magie tient en une commande : git rebase -i main.

Variante : --squash au lieu de --fixup

git commit --squash=<SHA> fait pareil mais ouvre l'éditeur de message à la fin du rebase pour vous laisser réviser le message du commit fusionné. Utile quand le fixup change vraiment l'intention du commit cible.

💎 --autostash — rebaser avec du WIP

Vous avez du WIP dans votre working tree, et vous voulez rebaser sur main. Sans --autostash, git refuse : "You have unstaged changes." Vous devez stasher à la main, rebaser, unstasher.

--autostash automatise tout : git stash → rebase → unstash. Combiné à rebase.autostash = true dans votre config, c'est transparent.

$ git status On branch feature/oauth Changes not staged for commit: modified: src/auth.js $ git rebase --autostash main Created autostash: 8a1d2e3 Successfully rebased and updated refs/heads/feature/oauth. Applied autostash. $ git status On branch feature/oauth Changes not staged for commit: modified: src/auth.js # Le WIP est revenu intact, par-dessus la branche rebasée.

💎 rebase --onto — transplanter une série

git rebase --onto <newbase> <upstream> [<branch>]

Vous avez branché feature/B à partir de feature/A. feature/A finit par être abandonnée. Vous voulez maintenant que feature/B parte directement de main, sans embarquer les commits de A.

--onto est l'outil chirurgical pour ça : il transplante une plage de commits sur une nouvelle base. La syntaxe est trompeuse mais se lit ainsi : "prends les commits qui sont sur <branch> mais pas sur <upstream>, et applique-les sur <newbase>".

# Situation : feature/B branche depuis feature/A # # main → ... → X # \ # A1 → A2 → A3 (feature/A à abandonner) # \ # B1 → B2 → B3 (feature/B à garder) $ git rebase --onto main feature/A feature/B # Lecture : "transplante feature/B sur main, en lui retirant ce qui # venait de feature/A" # Résultat : # # main → ... → X → B1' → B2' → B3' (feature/B propre) # \ # A1 → A2 → A3 (feature/A intacte, à supprimer plus tard)
--onto sauve aussi les rebases ratés : si vous avez interrompu un rebase et que tout est en désordre, vous pouvez transplanter manuellement la plage propre depuis le reflog.

--rebase-merges — préserver la topologie

Par défaut, git rebase aplatit l'historique : les merges intermédiaires sont absorbés. Si votre branche contient des merges intentionnels (par exemple un merge d'une sous-branche shared/lib), --rebase-merges les préserve dans l'historique réécrit.

$ git rebase --rebase-merges main # L'historique de la branche est rebasé, mais les merge commits # internes sont préservés (avec leur structure de parents multiples).
💡 Cas d'usage typique : vous suivez une stratégie release branch où chaque release porte des merges documentés. Vous voulez la rebaser sur main sans perdre la structure.

💎 Conflits à 3 panneaux : conflictStyle = zdiff3

Détaillé dans ⚙️ .gitconfig magique. Pendant un rebase, chaque conflit est marqué dans les fichiers touchés. Avec zdiff3, vous voyez la version d'origine en plus des deux versions divergentes.

<<<<<<< HEAD # votre version const RETRY_DELAY = 500; ||||||| merged common ancestors # point de départ commun const RETRY_DELAY = 100; ======= # version venant du rebase const RETRY_DELAY = 1000; >>>>>>> commit-en-cours-de-replay

Sans zdiff3, vous voyiez 500 vs 1000 et deviez deviner d'où chacun partait. Avec, vous comprenez immédiatement : tout le monde est parti de 100, vous avez multiplié par 5, eux par 10. La discussion à avoir n'est plus la même.

Stratégies fines de merge

--strategy-option=patience

Sur un gros refactor avec beaucoup de blocs déplacés, l'algorithme de merge par défaut produit parfois des conflits absurdes. patience est plus lent mais beaucoup plus précis pour aligner les blocs.

$ git rebase main --strategy-option=patience $ git merge feature --strategy-option=patience

checkout --ours / --theirs

Pendant un conflit, si vous savez que vous voulez forcer une version pour un fichier entier (sans inspecter le contenu) :

$ git checkout --ours package-lock.json $ git checkout --theirs docs/CHANGELOG.md $ git add package-lock.json docs/CHANGELOG.md $ git rebase --continue
⚠️ Pendant un rebase, --ours et --theirs sont inversés par rapport à un merge classique. --ours = la branche sur laquelle vous rebasez (ex. main), --theirs = vos commits en cours de replay. C'est contre-intuitif, faites attention.

git ls-files -u — lister les conflits non résolus

$ git ls-files -u 100644 a3f2c1d 1 src/auth.js 100644 8b7e4f0 2 src/auth.js 100644 2d9a1e3 3 src/auth.js # Stage 1 = base, 2 = ours, 3 = theirs. Trois lignes = conflit non résolu.

Revert groupé sans merge commit

Vous devez revert une plage de commits, mais vous voulez tout regrouper dans un seul commit final (au lieu d'un commit revert par commit reverté).

$ git revert -n A..B # -n = --no-commit : applique les reverts dans le working tree # sans committer. À vous de regrouper et committer ensuite. $ git commit -m "revert: rollback feature X (refs #1234)"

Pour revert un merge commit, le flag -m 1 est obligatoire (sinon git ne sait pas quelle ligne d'historique conserver) :

$ git revert -m 1 a3f2c1d # Revert le merge a3f2c1d en gardant la ligne du parent #1 (mainline).

git filter-repo — réécriture massive

Quand rebase -i ne suffit plus : retirer un secret poussé par erreur dans des centaines de commits, supprimer un gros fichier qui pollue l'historique, renommer un mainteneur dans tous les commits… git filter-repo est le successeur officiel de filter-branch. Beaucoup plus rapide, beaucoup plus sûr.

$ pip install git-filter-repo # Purger un secret de tout l'historique : $ git filter-repo --replace-text <(echo "sk_live_ABCD1234==>REDACTED") # Supprimer un gros fichier de tout l'historique : $ git filter-repo --invert-paths --path data/old-database.dump # Renommer un auteur : $ git filter-repo --mailmap mailmap.txt
⚠️ Opération destructive. Réécrit tous les SHAs. Tous vos collaborateurs devront re-cloner. À utiliser en dernier recours, après concertation avec l'équipe et après une sauvegarde du repo.

Règles d'or à graver dans le marbre

  1. Rebasez avant de pusher, pas après. Une branche feature jamais poussée peut être rebasée librement.
  2. Ne rebasez jamais une branche partagée (main, develop, release/*) sans concertation explicite.
  3. Toujours --force-with-lease, jamais --force nu.
  4. Activez les quatre lignes magiques : rerere.enabled, rebase.autosquash, rebase.autostash, merge.conflictStyle = zdiff3.
  5. En cas de panique pendant un rebase : git rebase --abort ramène tout au point de départ. Vraiment tout. Vous ne perdez rien.
Récap mental. Le rebase moderne, c'est quatre lignes de config + deux commandes (commit --fixup, rebase -i --autosquash) + une seule règle (--force-with-lease). Vous tenez le tout sur une carte de visite.

← Retour au catalogue de formations

⏪ Récupération ⭐ Module phare

C'est le module à mettre en favori. Vous y reviendrez le jour où ça partira en vrille — un reset --hard sur la mauvaise branche, un amend qui efface trois jours de travail, un pop de stash qui se transforme en conflit infernal, un force push qui écrase le travail d'un collègue.

La promesse est simple : presque tout est récupérable tant que vous n'avez pas attendu plus de 90 jours. Encore faut-il savoir où chercher. Ce module vous donne les outils (reflog, fsck --lost-found, stash branch, update-ref) puis une matrice interactive qui mappe chaque bêtise courante à sa procédure de sauvetage.

Avant toute panique : arrêtez-vous. Ne tapez plus rien. Surtout pas un autre reset --hard ou un checkout sur autre chose. Lisez la section ci-dessous, puis utilisez la matrice. Plus vous touchez, plus vous risquez de pousser le commit récupérable hors de la fenêtre du reflog.

Philosophie : git oublie peu

Git ne supprime presque rien immédiatement. Quand vous "perdez" un commit (par un reset, un amend, un rebase), git le rend juste inaccessible depuis les branches. L'objet lui-même reste dans .git/objects/ jusqu'à ce que le garbage collector passe — ce qui n'arrive pas avant 90 jours par défaut (paramètre gc.reflogExpire).

Type de "perte" Récupérable ? Outil principal
Commit perdu après reset/amend/rebase ✅ 90 jours git reflog
Branche supprimée localement ✅ 90 jours git reflog + branch
Stash droppé ✅ 30 jours par défaut git fsck --unreachable
Commit jamais référencé (orphelin pur) ⚠️ jusqu'au prochain gc git fsck --lost-found
Modifs jamais committées (working tree) perdues
Branche distante force-pushée écrasée ⚠️ si quelqu'un l'a encore localement récup. depuis un collègue
⚠️ Seule perte irrécupérable : les modifications jamais committées et jamais stashées. Tout ce qui a touché la base d'objets git est récupérable. Réflexe : committer souvent, même du WIP sale.

💎💎 Le reflog en profondeur

Le reflog est l'historique des mouvements de HEAD (et de chaque branche). Chaque commit, checkout, reset, rebase, merge y laisse une entrée datée. C'est votre filet de sécurité principal.

$ git reflog a3f2c1d HEAD@{0}: reset: moving to HEAD~5 8b7e4f0 HEAD@{1}: commit: WIP three days of work ← à récupérer 2d9a1e3 HEAD@{2}: rebase finished: returning to refs/heads/feature fc4b8a7 HEAD@{3}: rebase: Add OAuth refresh 1a0c5d2 HEAD@{4}: checkout: moving from main to feature # Reflog d'une branche spécifique : $ git reflog show feature/oauth # Reflog avec dates absolues (très utile pour retrouver "avant-hier") : $ git reflog --date=iso

Récupérer un commit perdu

# Option 1 : reset hard sur l'entrée du reflog $ git reset --hard HEAD@{1} # Option 2 : créer une branche à partir du commit $ git branch sauvetage HEAD@{1} $ git checkout sauvetage # Option 3 : cherry-pick d'un seul commit perdu $ git cherry-pick 8b7e4f0
Bonus : git diff @{1} compare votre HEAD actuel à l'état du reflog d'avant. Pratique pour voir ce qui a changé depuis la dernière opération.

💎 git fsck --lost-found

Quand le reflog ne suffit pas (par exemple, un commit créé puis immédiatement écarté sans jamais être pointé par une branche), fsck liste les objets orphelins de la base d'objets : commits sans référence, blobs (fichiers) sans tree, trees sans commit.

$ git fsck --lost-found Checking object directories: 100% (256/256), done. dangling commit 8b7e4f0c1a2b3d4e5f6789... dangling commit c4d2e1a9876543210fedcba... dangling blob ab12cd34ef56789012345678... $ git show 8b7e4f0c # Inspection : c'est bien le commit "WIP three days of work" perdu ? $ git branch sauvetage 8b7e4f0c # On l'attache à une branche neuve. Il est sauvé.

Variante : objets non référencés

$ git fsck --unreachable --no-reflogs # Liste tout objet qu'aucune ref + aucun reflog ne pointe. # C'est ce que le prochain gc va supprimer. Dernière chance.
⚠️ Si vous avez lancé git gc --prune=now manuellement, tous les objets non référencés sont définitivement supprimés. La récupération devient impossible. Ne lancez jamais gc --prune=now sans raison sérieuse.

💎 Sauvetage de stash

Les stashs sont stockés comme des commits spéciaux référencés par refs/stash. Quand vous faites git stash drop ou que stash pop déstashe et supprime, le stash devient orphelin — mais reste dans la base d'objets.

# Lister tous les commits orphelins de type "stash" : $ git fsck --unreachable | grep commit | cut -d ' ' -f3 | \ xargs git log --no-walk --pretty=format:"%h %ar %s" | grep -i stash a3f2c1d 2 days ago WIP on feature/oauth: token refresh 8b7e4f0 5 days ago WIP on main: refactor pricing # On le restaure : $ git stash apply a3f2c1d # Ou, mieux : on en fait une branche pour éviter les conflits $ git stash branch sauvetage-stash a3f2c1d

Stash partiel

$ git stash push -m "wip auth" -- src/auth.js # Stash uniquement un fichier précis. $ git stash --keep-index # Stash uniquement ce qui n'est PAS stagé. # Utile pour tester ce que vous allez committer.

🆘 Matrice interactive — "j'ai fait quoi ?"

Filtrez par type de bêtise ou tapez ce que vous venez de faire. Chaque carte donne la procédure de sauvetage exacte.

J'ai fait git reset --hard sur la mauvaise branche

RESET URGENT

Mes commits semblent avoir disparu. Le working tree est revenu à un état antérieur.

$ git reflog # Repérer la ligne qui suit le reset (juste avant lui dans le temps). # Ex : HEAD@{1}: commit: WIP avant le drame $ git reset --hard HEAD@{1}

✅ Tout revient. Tant que vous n'avez pas refait d'opération destructive depuis, le reflog tient toujours.

J'ai amendé un commit qui contenait du travail important

COMMIT URGENT

git commit --amend remplace le dernier commit. L'ancien semble disparu — il ne l'est pas.

$ git reflog # L'ancien commit est sur la ligne juste avant l'amend (HEAD@{1}). $ git reset --hard HEAD@{1} # Ou, plus prudent : créer une branche de sauvetage. $ git branch backup-before-amend HEAD@{1}

✅ Avant tout amend important, prenez l'habitude de git branch backup pour créer un point de retour explicite.

J'ai supprimé une branche locale par erreur

BRANCHE RÉCUPÉRABLE

git branch -D feature/oauth a effacé la branche. Pas grave : son tip est dans le reflog général.

$ git reflog # Cherchez la dernière entrée "checkout" ou "commit" sur cette branche. # Disons HEAD@{8} pointe sur le tip de feature/oauth. $ git branch feature/oauth HEAD@{8} # La branche est recréée à l'identique.

✅ Astuce : git config gc.reflogExpire 365.days pour porter le filet de sécurité à un an.

J'ai supprimé une branche distante par erreur

BRANCHE CRITIQUE

git push origin --delete feature/oauth. Le remote a oublié, mais des collègues l'ont peut-être encore.

# Si VOUS l'aviez localement avant la suppression : $ git push origin feature/oauth # Restauration immédiate côté serveur. # Si vous ne l'aviez pas, demandez à un collègue de pusher la sienne. # Sur GitHub : Settings → Branches → "Restore" possible quelques jours.

⚠️ Sur GitHub/GitLab, l'interface web propose souvent une restauration manuelle pendant quelques jours après la suppression. Vérifiez avant tout.

Mon stash a disparu / je l'ai droppé

STASH RÉCUPÉRABLE

git stash drop ou git stash clear ont effacé l'entrée. Le commit sous-jacent est encore là.

$ git fsck --unreachable | grep commit # Liste tous les commits orphelins. Inspectez ceux dont le message # commence par "WIP on" : $ git show <sha> # On le restaure proprement : $ git stash branch sauvetage <sha>

✅ Récupérable tant que git gc --prune=now n'a pas été lancé. Par défaut, ~30 jours.

Mon stash pop a conflicté et je suis perdu

STASH RÉCUPÉRABLE

Bonne nouvelle : un stash pop qui produit des conflits ne supprime pas le stash.

$ git stash list stash@{0}: WIP on feature/oauth: ... # Toujours là. On peut tout abandonner et réessayer autrement : $ git reset --hard # annule le pop conflictuel $ git stash branch wip-recovery # pop dans une branche neuve = zéro conflit

✅ La technique stash branch évite quasiment toujours les conflits, car elle restaure le stash sur le commit où il a été créé.

J'ai fait git push --force et écrasé le commit d'un collègue

PUSH CRITIQUE

Les commits du collègue n'apparaissent plus côté serveur. Ils existent peut-être encore localement chez lui.

# 1. Prévenez le collègue immédiatement, AVANT qu'il fasse fetch. # 2. S'il a encore ses commits localement : $ git log --oneline origin/feature/oauth..feature/oauth # (côté collègue) Ses commits non poussés sont encore là. $ git push origin feature/oauth # (côté collègue) Il les re-pousse. # 3. Sinon : GitHub/GitLab gardent l'événement de force-push dans # "Activity / Audit log" avec le SHA écrasé. Cherchez-y, puis : $ git fetch origin <sha-écrasé> $ git branch sauvetage <sha-écrasé>

⚠️ Leçon : configurez push.default = current et utilisez toujours --force-with-lease au lieu de --force nu.

J'ai raté un rebase interactif (squash/drop trop agressif)

REBASE URGENT

Pendant le rebase : git rebase --abort ramène tout au point de départ. Après le rebase : reflog.

# Pendant le rebase (avant la fin) : $ git rebase --abort # Après la fin du rebase, si on s'est rendu compte trop tard : $ git reflog a3f2c1d HEAD@{0}: rebase finished 8b7e4f0 HEAD@{1}: rebase: pick fc4b8a7 2d9a1e3 HEAD@{2}: rebase: pick 1a0c5d2 e1f2c3d HEAD@{3}: checkout: moving to feature/oauth ← état avant rebase $ git reset --hard HEAD@{3}

✅ Avant un rebase non trivial : git branch backup-before-rebase. Si ça part en vrille, git reset --hard backup-before-rebase.

J'ai fait git checkout -- file et perdu mes modifs non commitées

WORKING TREE DIFFICILE

Si rien n'a jamais été stagé ni stashé : perdu. Si un git add a été fait à un moment :

$ git fsck --lost-found dangling blob ab12cd34ef56789012345678... $ git show ab12cd34 > src/recovered.js # Tout fichier qui a été staged au moins une fois est encore # dans la base d'objets sous forme de "dangling blob".

⚠️ Seule perte vraiment irrécupérable : modifs jamais stagées ni stashées. Réflexe : git add . en début de pause, même sans committer.

J'ai lancé git clean -fdx et perdu des fichiers non trackés

WORKING TREE DIFFICILE

clean supprime via le filesystem, pas via git. Récupération éventuelle uniquement avec un outil de file recovery système.

# Sur macOS / Linux : photorec, extundelete, ou Time Machine. # Sur Windows : Recuva, ou versions précédentes du dossier. # Aucun outil git ne peut aider — les fichiers ne sont jamais entrés # dans la base d'objets.

✅ Réflexe absolu : toujours git clean -ndx (n = dry run) avant git clean -fdx. Le -n liste sans supprimer. Cinq secondes pour éviter une catastrophe.

Mon cherry-pick a conflicté et je veux annuler

COMMIT FACILE
$ git cherry-pick --abort # Annule le cherry-pick en cours, restaure le working tree. # Si déjà committé et qu'on s'est trompé : $ git reset --hard HEAD~1

✅ Tous les processus en plusieurs étapes (rebase, merge, cherry-pick, am) ont un --abort qui ramène propre.

J'ai poussé un secret (API key, token, mot de passe)

SECRET CRITIQUE

Le secret est compromis dès qu'il a touché un serveur git. La rotation est obligatoire. Le retirer de l'historique est secondaire.

# 1. ROTATION IMMÉDIATE du secret côté provider (AWS, Stripe, etc.). # Considérez-le compromis — il est probablement déjà indexé. # 2. Purge de l'historique avec git-filter-repo : $ pip install git-filter-repo $ echo 'sk_live_ABCD1234==>REDACTED' > /tmp/secrets.txt $ git filter-repo --replace-text /tmp/secrets.txt # 3. Force push (avec lease) sur toutes les branches affectées : $ git push --force-with-lease origin --all $ git push --force-with-lease origin --tags # 4. Tous les collaborateurs doivent re-cloner. # 5. Sur GitHub : Settings → Security → "Allow force pushes" peut être # requis temporairement. Et contactez le support pour purger les # refs cachées.

⚠️ Le secret est compromis dès la première seconde côté serveur. Rotation > purge. Toujours.

J'ai oublié de pusher un commit, est-il perdu ?

PUSH FACILE

Non. Tant que vous ne touchez pas votre repo local, le commit est là.

$ git log --oneline origin/feature/oauth..feature/oauth # Liste les commits locaux non encore poussés. $ git push

git status vous indique "Your branch is ahead of 'origin/...' by N commits."

Mon working tree est dans un état bizarre, je veux tout abandonner

WORKING TREE FACILE
# 1. Toujours commencer par voir ce qu'on s'apprête à supprimer : $ git status $ git clean -ndx # dry run : liste les non-trackés à supprimer # 2. Si tout est ok, on nettoie pour de bon : $ git reset --hard HEAD # annule toutes les modifs trackées $ git clean -fdx # supprime tout le non-tracké, dont .env

⚠️ -x supprime aussi les fichiers ignorés (.env, node_modules, etc.). Sans -x, ils sont préservés.

Je veux corriger le message du dernier commit

COMMIT FACILE
$ git commit --amend # L'éditeur s'ouvre avec le message actuel, modifiez et sauvegardez. # Variante non-interactive : $ git commit --amend -m "feat: nouveau message correct"

⚠️ Si le commit est déjà poussé, vous devrez force-push : git push --force-with-lease. Concertez-vous avec l'équipe.

😶 Aucun cas ne correspond à votre recherche. Reformulez ou revenez à Tout.

git update-ref et refs custom

update-ref est l'outil bas niveau pour manipuler les références (branches, tags, refs custom) sans passer par les commandes haut niveau. Indispensable pour des opérations de sauvetage avancées.

# Créer une ref custom pour préserver un commit en sauvetage : $ git update-ref refs/sauvetage/avant-rebase HEAD # Lister vos refs custom : $ git for-each-ref refs/sauvetage/ # Supprimer une ref custom : $ git update-ref -d refs/sauvetage/avant-rebase # Supprimer une branche sans confirmation safety (DANGEREUX) : $ git update-ref -d refs/heads/feature/zombie
Pattern utile : avant toute opération risquée sur une branche importante, créez une ref de sauvetage. Elle ne gêne aucune opération git, n'apparaît pas dans git branch, et empêche le gc de supprimer le commit tant qu'elle existe.

Cas spécial : un secret a été poussé

Voir la fiche dédiée dans la matrice ci-dessus. Trois rappels critiques à graver :

  1. La rotation du secret est non négociable et doit être faite en premier. Il est probablement déjà indexé par des bots qui scannent GitHub en continu.
  2. La purge de l'historique est secondaire mais nécessaire pour éviter la prolifération via les forks et les clones existants.
  3. Tous les collaborateurs doivent re-cloner après une réécriture d'historique. Sinon ils risquent de re-pusher les anciens objets.

Règles de survie

  1. Avant toute opération risquée : git branch backup-<date>. C'est gratuit, ça prend 1 seconde, ça sauve des heures.
  2. Activez le reflog longue durée : git config --global gc.reflogExpire 365.days (au lieu des 90 jours par défaut).
  3. Ne lancez jamais git gc --prune=now sans raison sérieuse. C'est la seule opération qui supprime vraiment des objets.
  4. Toujours --dry-run ou -n avant un clean, un filter-repo, ou un push --force.
  5. En cas de panique : arrêtez de taper. Lisez, réfléchissez, utilisez la matrice. Plus vous touchez, plus vous risquez de pousser le commit récupérable hors fenêtre.
Mantra à retenir : "Si l'objet a touché la base git, il est encore là." Le reflog et fsck --lost-found sont vos meilleurs amis.

← Retour au catalogue de formations

🌳 Worktrees & monorepos

Deux problèmes, une page. Worktree répond à "comment éviter de stash quand un hotfix tombe en pleine feature". Partial clone, sparse-checkout et LFS répondent à "comment travailler sereinement sur un repo de plusieurs Go avec des binaires".

Les deux s'imposent dès qu'on dépasse la zone de confort du petit projet : équipes qui jonglent entre PRs, monorepos d'entreprise, projets Oculix-like avec .dylib, modèles ML, captures vidéo. La maîtrise de ces outils fait la différence entre un git fluide et un git pénible.

💎💎 Pourquoi worktree change tout

❌ Workflow classique (stash)

git stashcheckout maincheckout -b hotfix → fix → push → PR → checkout featurestash pop → prier que rien ne soit en conflit. Le serveur de dev a redémarré, les fichiers ouverts dans l'IDE sont perdus, node_modules a été reconstruit deux fois.

✅ Workflow worktree

git worktree add ../app-hotfix main → un second dossier indépendant avec son propre IDE, son propre dev server, ses propres dépendances. Vous fixez en parallèle, sans toucher à votre feature. Quand le hotfix est mergé, git worktree remove ../app-hotfix et c'est fini.

💡 Le détail qui change tout : les deux worktrees partagent le même .git. Vos fetch, vos branches, vos commits sont vus par les deux dossiers. Vous pouvez littéralement committer dans un dossier puis rebase dans l'autre.

git worktree — les bases

# Créer un worktree sur une branche existante : $ git worktree add ../app-main main # Créer un worktree sur une nouvelle branche, partant de main : $ git worktree add -b hotfix/login ../app-hotfix main # Créer un worktree "détaché" (HEAD sur un commit, sans branche) : $ git worktree add --detach ../app-v1.4 v1.4.0 # Lister tous les worktrees : $ git worktree list /home/julien/app a3f2c1d [feature/oauth] /home/julien/app-hotfix fc4b8a7 [hotfix/login] /home/julien/app-v1.4 8b7e4f0 (detached HEAD) # Supprimer un worktree (le dossier ET la référence) : $ git worktree remove ../app-hotfix # Si le dossier a été supprimé manuellement, nettoyer la référence : $ git worktree prune

Workflows réels

Workflow 1 : hotfix sans interruption

Vous codez sur feature/oauth, votre IDE a 12 onglets ouverts, votre dev server tourne, vos tests sont lancés. Un bug critique en prod.

$ git worktree add -b hotfix/login-crash ../app-hotfix main $ cd ../app-hotfix $ npm install && npm test # Je fixe, je teste, je push, je merge la PR. # Pendant ce temps, mon IDE et mon dev server sont intacts dans /app/. $ cd - $ git worktree remove ../app-hotfix

Workflow 2 : review de PR sans casser sa feature

Un collègue demande une review approfondie. Plutôt que stasher et checkouter sa branche :

$ git fetch origin pull/421/head:pr-421 $ git worktree add ../app-pr-421 pr-421 $ cd ../app-pr-421 && npm install && npm test

Workflow 3 : tester deux versions en parallèle

Reproduire un bug client sur la v2.3 tout en continuant à développer sur main :

$ git worktree add --detach ../app-v2.3 v2.3.0 $ git worktree add --detach ../app-v2.4 v2.4.0 # Deux dossiers, deux versions, deux serveurs sur deux ports. # Bisect manuel humain en quelques minutes.

Workflow 4 : un dossier par release en CI

Sur un runner CI mutualisé, plutôt que cloner trois fois pour construire trois versions : un seul clone, plusieurs worktrees. Le clone est fait une seule fois, les worktrees ne dupliquent pas la base d'objets.

Pièges classiques

  1. Une même branche ne peut pas être checkout dans deux worktrees simultanément (sécurité anti-corruption). Pour switcher dans un worktree existant, allez-y avec cd et git switch normalement.
  2. node_modules et autres caches sont locaux à chaque worktree. Vous payez un npm install par worktree. Sur de gros monorepos, configurez un cache global (pnpm store, yarn berry, etc.).
  3. Les hooks git sont partagés via .git/hooks/. Un hook qui dépend du dossier courant peut casser entre worktrees. Préférez des hooks idempotents.
  4. Les fichiers ignorés (.env) ne sont pas recopiés entre worktrees. Pensez à un script de bootstrap ou à un direnv partagé.
Convention recommandée : placez tous vos worktrees à plat dans un dossier dédié, par exemple ~/repos/projet/{main,hotfix,pr-421}/. Beaucoup plus lisible que mélanger dans ~/.

💎 Monorepos : clone --filter=blob:none

Sur un repo de 5 Go avec 200 000 commits, un git clone standard prend 10 minutes et occupe 5 Go. La plupart de ces octets sont du contenu historique que vous ne consulterez jamais.

--filter=blob:none demande à git de cloner l'historique complet (tous les commits, toutes les structures) mais aucun blob (contenu de fichiers). Les blobs sont téléchargés à la demande quand vous checkoutez un commit donné. Diviseur ×10 du temps de clone, ×5 de l'espace disque.

$ git clone --filter=blob:none https://github.com/org/big-monorepo.git Cloning into 'big-monorepo'... remote: Enumerating objects: 1234567, done. remote: Counting objects: 100% (1234567/1234567), done. remote: Total 1234567 (delta 0), reused 0 Receiving objects: 100% (1234567/1234567), 240 MiB | 18 MB/s Resolving deltas: 100% (876543/876543), done. # Le checkout est instantané. Les blobs sont récupérés au besoin : $ cd big-monorepo && git log -p src/feature.js # Au premier accès, git fetch les blobs nécessaires depuis le remote.
💡 Variante : --filter=tree:0 (encore plus radical) ne récupère même pas les trees. À réserver à des cas spécifiques (CI qui ne fait que builder un commit donné).

Shallow clone et treeless

--depth 1 : juste le tip

Si vous voulez juste builder à partir du HEAD (CI typique), --depth 1 ne récupère qu'un commit, sans aucune histoire :

$ git clone --depth 1 https://github.com/org/repo.git
Stratégie Vitesse Histoire Quand
clone standard Lent Complète Dev local long terme
--filter=blob:none Rapide Complète (commits/trees) Dev local sur gros repo
--filter=tree:0 Très rapide Aucune (lazy) Lecture/build ponctuel
--depth 1 Le plus rapide Aucune (truncated) CI build-only, Docker images
⚠️ Les shallow clones empêchent git log, git blame historique, bisect. À utiliser uniquement quand vous savez ne pas en avoir besoin (CI court).

💎 Sparse-checkout : ne checkout qu'une partie

Sur un monorepo qui contient frontend, backend, mobile, infra, docs, vous ne touchez en réalité qu'un sous-arbre. Pourquoi checkouter les 50 000 fichiers des autres ?

sparse-checkout matérialise uniquement les chemins que vous demandez. Le reste reste en base d'objets, n'apparaît pas dans le filesystem. git status reste rapide, votre IDE n'indexe pas ce qui ne vous concerne pas.

$ git clone --filter=blob:none --no-checkout <url> $ cd repo # Activer sparse-checkout en mode "cone" (dossiers complets) : $ git sparse-checkout init --cone # Définir les dossiers visibles : $ git sparse-checkout set api/ libs/shared/ docs/ # Désormais le filesystem ne contient QUE api/ libs/shared/ docs/ $ git checkout main $ ls api/ libs/ docs/ README.md # Élargir / réduire à la volée : $ git sparse-checkout add mobile/ $ git sparse-checkout list # Tout récupérer (revenir au comportement normal) : $ git sparse-checkout disable
Combo magique sur monorepo : clone --filter=blob:none --no-checkout + sparse-checkout init --cone + sparse-checkout set <dossiers>. Vous démarrez en quelques secondes au lieu de 10 minutes, et votre IDE n'indexe que ce qui vous concerne.

Git LFS — gérer les binaires sans plomber le repo

Git stocke chaque version complète des fichiers binaires (pas de delta efficace possible). Ajouter un .dylib de 50 Mo à votre repo, le modifier 100 fois, et votre repo pèse 5 Go irrévocablement.

Git LFS (Large File Storage) remplace les binaires par des pointeurs stockés dans git, et héberge le contenu réel sur un serveur dédié. Le repo reste léger ; les binaires sont téléchargés à la demande au checkout.

$ git lfs install # une seule fois par machine $ git lfs track "*.dylib" "*.so" "*.mp4" "models/*" # Ajoute des règles dans .gitattributes (à committer). $ git add .gitattributes $ git commit -m "chore: track binaries with LFS" # Voir ce qui est suivi par LFS : $ git lfs ls-files # Cloner sans télécharger les blobs LFS (ex. CI qui ne build pas l'iOS) : $ GIT_LFS_SKIP_SMUDGE=1 git clone <url>
⚠️ LFS n'efface pas le passé. Les fichiers binaires déjà committés en clair restent dans l'historique. Pour les purger, il faut git filter-repo (voir module 🤝 Rebase moderne), puis tout le monde re-clone.
💡 Cas d'usage typiques pour LFS : bibliothèques précompilées (.dylib, .so, .dll), captures d'écran et vidéos de tests E2E, modèles ML (.h5, .onnx), fonts custom, datasets de tests.

Maintenance des gros repos

Sur un monorepo actif, l'accumulation d'objets ralentit toutes les opérations. Quelques outils à connaître :

# Lance la maintenance en background (gc, repack, prefetch) : $ git maintenance start # Voir l'espace disque occupé par git : $ git count-objects -vH count: 12345 size-pack: 1.2 GiB in-pack: 234567 prune-packable: 0 garbage: 0 # Activer fsmonitor pour status instantané (cf. .gitconfig magique) : $ git config core.fsmonitor true $ git config core.untrackedCache true # Compacter agressivement (à exécuter occasionnellement, prend du temps) : $ git gc --aggressive

Checklist "gros repo" (style projet Oculix)

Pour un repo applicatif qui héberge code + binaires (libs natives, captures, modèles) sur plusieurs Go, voici la stack recommandée :

  1. Clone partial : git clone --filter=blob:none par défaut pour tous les développeurs.
  2. Sparse-checkout par module si l'équipe est divisée par domaine (frontend / backend / mobile).
  3. Git LFS pour tout binaire > 1 Mo, défini dans .gitattributes committé.
  4. Worktrees documentés comme workflow standard pour hotfix, review de PR, tests multi-versions.
  5. Config globale : core.fsmonitor = true, maintenance.auto = true dans ~/.gitconfig.
  6. CI : --depth 1 pour les jobs short-lived, --filter=blob:none pour les jobs qui ont besoin de l'historique.
✅ Avec ces 6 réflexes, un monorepo de 10 Go reste aussi confortable à utiliser qu'un projet de 50 Mo.

← Retour au catalogue de formations

🐙 Bonus — gh CLI

Git seul ne sait rien de GitHub. Pour interagir avec ce qui vit autour du repo (pull requests, issues, releases, workflows Actions, secrets, codespaces), il faut soit cliquer dans le navigateur, soit utiliser gh — le CLI officiel GitHub. Une fois adopté, c'est l'outil qui supprime le plus d'allers-retours navigateur de votre journée.

Ce module est volontairement exhaustif : il couvre toutes les sous-commandes utiles, leurs options critiques, les patterns d'automatisation (gh api, gh + jq) et finit sur des workflows end-to-end réels. Lisez-le une fois, gardez-le en référence.

~30 sous-commandes principales
100% de l'API GitHub via gh api
10× moins de clics navigateur
0 token à recopier (auth web)
Mythe : "gh, c'est pour scripter, donc compliqué."

🟢 Réalité : 80 % de l'usage tient sur gh pr create, gh pr checkout, gh pr view --web, gh run watch. Le reste s'apprend au fur et à mesure.

Installation & authentification

Installer gh

$ # macOS $ brew install gh $ # Debian / Ubuntu $ sudo apt install gh $ # Windows $ winget install --id GitHub.cli $ gh --version gh version 2.51.0 (2026-04-15)

Login — auth web (recommandé)

$ gh auth login ? What account do you want to log into? GitHub.com ? What is your preferred protocol for Git operations? HTTPS ? Authenticate Git with your GitHub credentials? Yes ? How would you like to authenticate GitHub CLI? Login with a web browser ! First copy your one-time code: A1B2-C3D4 Press Enter to open github.com in your browser... # Vous validez dans le navigateur, et c'est fini : pas de token à # recopier dans un fichier, le credential helper Git est configuré # automatiquement. $ gh auth status github.com ✓ Logged in to github.com as julienmer (keyring) ✓ Git operations for github.com configured to use https protocol. ✓ Token: gho_***** ✓ Token scopes: gist, read:org, repo, workflow

Variantes utiles

$ gh auth login --with-token < mytoken.txt # auth par token (CI) $ gh auth login --hostname ghe.entreprise.com # GitHub Enterprise $ gh auth refresh -s write:packages # ajouter un scope $ gh auth token # récupérer le token courant $ gh auth switch # changer de compte (multi-comptes) $ gh auth logout
Multi-comptes (perso + pro) : gh auth login plusieurs fois, gh auth switch pour basculer. Pratique avec le includeIf de ⚙️ .gitconfig magique.

Config & alias

$ gh config set git_protocol ssh $ gh config set editor "code -w" # éditeur pour les messages $ gh config set pager less $ gh config set prompt enabled $ gh config list git_protocol: ssh editor: code -w pager: less prompt: enabled http_unix_socket: browser:

Alias gh

$ gh alias set co 'pr checkout' $ gh alias set prv 'pr view --web' $ gh alias set prl 'pr list --author @me' $ gh alias set ci 'run list --limit 5' $ gh alias set bugs 'issue list --label bug --state open' # Alias d'argument shell (préfixé par !) : $ gh alias set --shell 'open-pr' 'gh pr view --web $(git branch --show-current)' $ gh alias list

Repos

# Cloner avec la syntaxe owner/name (raccourci) : $ gh repo clone julienmerconsulting/clean-qa-academy $ gh repo clone julienmerconsulting/clean-qa-academy -- --depth 1 # Créer un repo (mode interactif puis non-interactif) : $ gh repo create $ gh repo create my-tool --public --source=. --push \ --description "CLI utility" \ --add-readme # Forker un repo, optionnellement le cloner : $ gh repo fork org/projet --clone --remote # Voir le README dans le terminal : $ gh repo view julienmerconsulting/clean-qa-academy $ gh repo view --web # dans le navigateur # Lister vos repos : $ gh repo list --limit 50 $ gh repo list myorg --visibility private # Modifier un repo (description, topics, branche par défaut, visibility) : $ gh repo edit --description "new tagline" \ --add-topic qa --add-topic testing \ --default-branch main \ --visibility public # Synchroniser un fork avec l'upstream : $ gh repo sync myuser/forked # Définir le repo par défaut (utile quand le current dir contient # plusieurs remotes) : $ gh repo set-default # Archiver / désarchiver / supprimer : $ gh repo archive owner/name $ gh repo unarchive owner/name $ gh repo delete owner/name --yes
⚠️ gh repo delete demande une confirmation interactive par défaut. --yes bypass — utilisez avec extrême prudence.

💎💎 Pull requests — le gros morceau

C'est l'usage principal de gh. Tout le cycle de vie d'une PR — création, review, merge — peut se faire sans quitter le terminal.

Créer une PR

# Mode interactif : title, body, base, reviewer, label, assignee... $ gh pr create # Tout en un, non-interactif : $ gh pr create \ --title "feat(auth): OAuth refresh" \ --body "Closes #421. Implements token rotation." \ --base main \ --reviewer alice,bob \ --assignee @me \ --label backend,security \ --milestone "v2026.05" # Body depuis un fichier (utile pour des descriptions longues) : $ gh pr create --title "…" --body-file ./PR_BODY.md # PR draft (work in progress) : $ gh pr create --draft --title "WIP: auth refresh" # Ouvre directement le formulaire web pré-rempli : $ gh pr create --web

Lister, filtrer, voir

$ gh pr list # PR ouvertes du repo $ gh pr list --author @me # vos PR $ gh pr list --assignee @me --state open $ gh pr list --label bug --label security $ gh pr list --base develop --limit 50 # Voir une PR (dans le terminal puis dans le navigateur) : $ gh pr view 421 $ gh pr view 421 --web $ gh pr view # PR de la branche courante # Voir le diff complet d'une PR : $ gh pr diff 421 $ gh pr diff 421 --name-only # juste les fichiers touchés # Statut global de toutes vos PR (assignées, créées, mentionnées) : $ gh pr status

Checkout de PR (le killer-feature)

$ gh pr checkout 421 # Crée une branche locale avec le code de la PR. # Marche même si la PR vient d'un fork (gh ajoute un remote temp). $ gh pr checkout 421 --detach # Variante détachée : pas de branche, juste HEAD. # Combo magique avec git worktree (cf. module 🌳 Worktrees) : $ git worktree add ../app-pr-421 $ cd ../app-pr-421 && gh pr checkout 421 # Review d'une PR sans interrompre votre feature en cours.

Modifier une PR ouverte

$ gh pr edit 421 --title "feat(auth): OAuth refresh + rotation" $ gh pr edit 421 --add-label backend --remove-label wip $ gh pr edit 421 --add-reviewer charlie $ gh pr edit 421 --milestone "v2026.05" # Marquer une PR draft comme "ready for review" : $ gh pr ready 421 # Convertir une PR en draft : $ gh pr ready 421 --undo # Ajouter un commentaire : $ gh pr comment 421 --body "Updated, please re-review" $ gh pr comment 421 --body-file ./reply.md # Statut des checks CI : $ gh pr checks 421 $ gh pr checks 421 --watch # bloque jusqu'à la fin # Fermer / rouvrir : $ gh pr close 421 $ gh pr reopen 421

💎 Reviews depuis le terminal

# Approuver : $ gh pr review 421 --approve $ gh pr review 421 --approve --body "LGTM, nice refactor 👍" # Demander des changements : $ gh pr review 421 --request-changes \ --body "Race condition possible sur tokenRefresh, voir ligne 84" # Commentaire général sans verdict : $ gh pr review 421 --comment --body "Question sur l'init des scopes" # Body multilignes depuis un fichier : $ gh pr review 421 --request-changes --body-file ./review.md
💡 Pour des line-comments précis (commenter une ligne spécifique d'un diff), il faut passer par gh api (voir la section gh api) ou par le navigateur. Le CLI couvre la review globale, pas le commentaire ligne par ligne.

Merge automatique

# Merger maintenant (avec squash, rebase ou merge commit) : $ gh pr merge 421 --squash $ gh pr merge 421 --rebase $ gh pr merge 421 --merge # Avec suppression de la branche après merge (très utile) : $ gh pr merge 421 --squash --delete-branch # Personnaliser le titre/body du commit de merge : $ gh pr merge 421 --squash \ --subject "feat(auth): OAuth refresh (#421)" \ --body "Closes #420. Adds token rotation." # Auto-merge : la PR sera mergée dès que tous les checks passent # ET que les reviews requises sont OK. Magique pour vendredi 17h : $ gh pr merge 421 --auto --squash --delete-branch # Annuler une auto-merge en attente : $ gh pr merge 421 --disable-auto
Pattern recommandé : gh pr merge $PR --auto --squash --delete-branch dès que la PR passe en review. La PR se merge toute seule quand le dernier reviewer approuve et que la CI verdit. Plus besoin de revenir surveiller.

Issues

Créer / éditer / fermer

$ gh issue create $ gh issue create \ --title "OAuth refresh fails on token expiry" \ --body-file ./bug-report.md \ --label bug,backend \ --assignee @me \ --milestone "v2026.05" $ gh issue edit 1234 --add-label priority:high $ gh issue close 1234 --reason completed $ gh issue close 1234 --reason not_planned $ gh issue reopen 1234

Lister, voir, commenter

$ gh issue list $ gh issue list --state all --label bug --limit 100 $ gh issue list --mention @me $ gh issue list --milestone "v2026.05" $ gh issue view 1234 $ gh issue view 1234 --comments # avec le fil entier $ gh issue view 1234 --web $ gh issue comment 1234 --body "Reproduit sur Safari iOS 18" $ gh issue comment 1234 --body-file ./investigation.md

Branche depuis une issue

$ gh issue develop 1234 --checkout # Crée une branche feature/1234-oauth-refresh-fails (depuis le titre) # côté serveur, et la checkoute localement. La branche est liée à # l'issue (auto-close au merge). $ gh issue develop 1234 --name fix/token-expiry --checkout

Transfert, pin, lock

$ gh issue transfer 1234 anotherorg/anotherrepo $ gh issue pin 1234 $ gh issue unpin 1234 $ gh issue lock 1234 --reason off-topic $ gh issue unlock 1234

Workflows & Actions

# Lister tous les workflows déclarés dans .github/workflows/ : $ gh workflow list NAME STATE ID CI active 18234567 Deploy staging active 18234568 Release active 18234569 Lint disabled_manually 18234570 # Voir le YAML d'un workflow : $ gh workflow view ci.yml $ gh workflow view ci.yml --yaml # Déclencher manuellement un workflow (workflow_dispatch) : $ gh workflow run deploy.yml \ --ref main \ -f environment=staging \ -f dry_run=true # Désactiver / réactiver un workflow : $ gh workflow disable ci.yml $ gh workflow enable ci.yml

💎 Runs : suivi, debug, rerun

# Lister les derniers runs : $ gh run list --limit 10 STATUS WORKFLOW EVENT BRANCH ELAPSED AGE ✓ CI push main 4m20s 2m ago X CI pull_request feature/oauth 3m12s 8m ago ✓ Deploy workflow_run main 1m45s 11m ago # Filtrer : $ gh run list --workflow ci.yml --branch feature/oauth $ gh run list --user @me --status failure # Voir les détails d'un run : $ gh run view 12345678 $ gh run view 12345678 --log # tous les logs $ gh run view 12345678 --log-failed # uniquement les jobs en échec $ gh run view 12345678 --web # Suivre un run en direct (live tail) : $ gh run watch $ gh run watch 12345678 --exit-status # exit ≠ 0 si le run échoue # Re-run : $ gh run rerun 12345678 # tout $ gh run rerun 12345678 --failed # uniquement les jobs en échec $ gh run rerun 12345678 --debug # avec ACTIONS_STEP_DEBUG=true # Annuler un run en cours : $ gh run cancel 12345678 # Télécharger des artefacts : $ gh run download 12345678 $ gh run download 12345678 -n coverage-report -D ./reports/
Combo killer pour CI : gh pr create --fill && gh pr checks --watch. Vous créez la PR (titre/body pré-remplis depuis les commits) et vous attendez la fin de la CI dans le même terminal.

Releases

# Créer une release (mode interactif) : $ gh release create v2026.05.0 # Avec notes générées automatiquement depuis les commits : $ gh release create v2026.05.0 --generate-notes # Tout en un, avec assets : $ gh release create v2026.05.0 \ --title "v2026.05.0 — OAuth refresh" \ --notes-file CHANGELOG.md \ --target main \ --prerelease \ ./dist/app-macos.tar.gz \ ./dist/app-linux.tar.gz \ ./dist/app-windows.zip # Lister, voir, télécharger, supprimer : $ gh release list --limit 20 $ gh release view v2026.05.0 $ gh release view --web v2026.05.0 $ gh release download v2026.05.0 $ gh release download v2026.05.0 --pattern '*macos*' $ gh release upload v2026.05.0 ./extra-asset.zip $ gh release delete v2026.05.0 --yes # Marquer une preview comme stable : $ gh release edit v2026.05.0 --prerelease=false --latest

Gists

$ gh gist create snippet.js # gist privé par défaut $ gh gist create snippet.js --public $ gh gist create --filename note.md - # depuis stdin $ echo "# Hello" | gh gist create --filename hello.md --public $ gh gist list # vos gists $ gh gist view <id> $ gh gist edit <id> --filename snippet.js $ gh gist clone <id> ./local-gist $ gh gist delete <id>

Codespaces

# Créer un codespace pour le repo courant : $ gh codespace create $ gh codespace create -R owner/repo -b feature/oauth \ -m standardLinux32gb $ gh codespace list $ gh codespace ssh # interactif, choisit le codespace $ gh codespace ssh --codespace <name> -- "npm test" $ gh codespace ports # forwarded ports $ gh codespace ports forward 3000:3000 $ gh codespace cp -r ./local-folder remote:/workspaces/repo/ $ gh codespace logs $ gh codespace stop $ gh codespace delete # définitif

Extensions

Les extensions gh sont des sous-commandes installables qui étendent le CLI. Beaucoup sont des pépites.

$ gh extension list $ gh extension browse # UI interactive de découverte $ gh extension search "label" $ gh extension install dlvhdr/gh-dash # dashboard PR/issues TUI $ gh extension install vilmibm/gh-screensaver $ gh extension upgrade --all $ gh extension remove gh-dash

Quelques extensions très utiles

Extension Apport
gh-dash Dashboard TUI pour suivre vos PR et issues en temps réel.
gh-poi Nettoie en lot les branches locales déjà mergées.
gh-copilot Suggestions de commandes par IA (gh copilot suggest).
gh-graph Heatmap de contributions du terminal.
gh-actions-importer Migration Jenkins/GitLab/Travis → GitHub Actions.

Secrets & variables (Actions)

# Lister les secrets du repo : $ gh secret list $ gh secret list --app dependabot $ gh secret list --env production # Définir un secret (depuis stdin, depuis un fichier, depuis l'env) : $ echo -n "sk_live_..." | gh secret set STRIPE_KEY $ gh secret set DEPLOY_KEY --body-file ./deploy_key.pem $ gh secret set NPM_TOKEN --env production $ gh secret set --org myorg INTERNAL_API_KEY --visibility selected \ --repos repo-a,repo-b $ gh secret delete STRIPE_KEY $ gh secret delete --env production NPM_TOKEN # Variables (équivalent secrets, mais lisibles côté run) : $ gh variable list $ gh variable set NODE_VERSION --body 20.11.1 $ gh variable delete NODE_VERSION
⚠️ Les secrets sont write-only via le CLI : impossible de les relire. Si vous perdez la valeur, il faut la régénérer côté provider.

Projects v2 (boards)

$ gh project list --owner julienmerconsulting $ gh project view 4 --owner julienmerconsulting $ gh project view 4 --owner julienmerconsulting --web # Ajouter une issue ou une PR à un project : $ gh project item-add 4 --owner julienmerconsulting \ --url https://github.com/julienmerconsulting/clean-qa-academy/issues/123 # Lister les items, modifier un champ : $ gh project item-list 4 --owner julienmerconsulting --format json $ gh project item-edit --id <item-id> --field-id <field-id> --text "Done"
💡 Pour des manipulations avancées de Projects v2, l'API GraphQL via gh api graphql est souvent plus efficace que les sous-commandes.

💎💎 gh api — la backdoor universelle

Quand le CLI ne couvre pas un endpoint, gh api appelle n'importe quel endpoint REST ou GraphQL de GitHub, en gérant l'auth, la pagination, les en-têtes, le parsing JSON. C'est par lui que tous les scripts maison passent.

REST de base

$ gh api /user $ gh api /repos/julienmerconsulting/clean-qa-academy $ gh api /repos/{owner}/{repo}/issues --paginate # {owner}/{repo} sont auto-résolus depuis le repo courant. # Méthodes : $ gh api -X POST /repos/{owner}/{repo}/issues \ -f title="Bug" \ -f body="Steps to reproduce…" \ -F labels[]=bug -F labels[]=urgent # -f = champ chaîne, -F = champ typé (number/bool/array/null). # --paginate suit toutes les pages automatiquement. # --jq pour extraire avec une expression jq native : $ gh api -X GET /search/repositories \ -f q="language:python stars:>10000" \ --jq '.items[] | "\(.stargazers_count)\t\(.full_name)"'

GraphQL

$ gh api graphql -f query=' query($login:String!) { user(login: $login) { pullRequests(first: 5, states: OPEN) { nodes { number title repository { nameWithOwner } } } } }' -f login=julienmer # Pagination GraphQL via --paginate : $ gh api graphql --paginate -f query=' query($endCursor: String) { viewer { repositories(first: 100, after: $endCursor) { nodes { nameWithOwner } pageInfo { hasNextPage endCursor } } } }'

En-têtes & aperçus d'API

$ gh api -H "Accept: application/vnd.github.v3.raw" \ /repos/{owner}/{repo}/contents/README.md $ gh api -i /user # inclut les en-têtes HTTP $ gh api --include rate_limit # debug rate limit
Pourquoi gh api bat curl : auth automatique, gestion native du {owner}/{repo}, pagination intégrée (--paginate), parsing (--jq), et respect des limites de rate automatiquement.

Browse, status & cache

# Ouvrir le repo courant dans le navigateur : $ gh browse # Ouvrir un fichier précis, optionnellement à une ligne : $ gh browse src/auth.js $ gh browse src/auth.js:42 $ gh browse src/auth.js --branch feature/oauth # Vue rapide des notifications GitHub : $ gh status # Cache : gh cache des données de PR, runs, etc. Le voir, le purger : $ gh cache list $ gh cache delete <cache-id> $ gh cache delete --all

💎 Combo gh + jq — scripts puissants

Toutes les commandes gh acceptent --json <fields> et --jq <expr>. Combinés, ils transforment gh en source de données exploitable par n'importe quel script.

# Lister juste les numéros et titres des PR ouvertes : $ gh pr list --json number,title --jq '.[] | "\(.number)\t\(.title)"' # Top 10 contributeurs sur les 30 derniers jours : $ gh api --paginate /repos/{owner}/{repo}/commits \ -f since=$(date -u -d '30 days ago' +%FT%TZ) \ --jq '.[].author.login' | sort | uniq -c | sort -rn | head -10 # Toutes les PR avec un check failed sur la branche courante : $ gh pr list --json number,statusCheckRollup \ --jq '.[] | select(.statusCheckRollup[]?.conclusion == "FAILURE") | .number' # Re-run automatique de tous les runs failed du dernier jour : $ gh run list --limit 100 --json databaseId,status,createdAt \ --jq '.[] | select(.status == "failure") | .databaseId' \ | xargs -I{} gh run rerun {} --failed # Champs disponibles pour --json (autocomplet help) : $ gh pr list --json # sans valeur, liste tous les champs valides

Workflows end-to-end

1. Créer une feature, ouvrir la PR, attendre la CI

$ git switch -c feature/oauth # … code, commit, commit … $ git push # avec push.autoSetupRemote $ gh pr create --fill --reviewer alice,bob # titre/body depuis les commits $ gh pr checks --watch $ gh pr merge --auto --squash --delete-branch

2. Reviewer une PR sans toucher à votre feature en cours

$ git worktree add ../app-pr-421 $ cd ../app-pr-421 && gh pr checkout 421 $ npm install && npm test $ gh pr review 421 --approve --body "LGTM, tested locally" $ cd - && git worktree remove ../app-pr-421

3. Bisect une régression sur une PR

$ gh pr checkout 421 $ git bisect start $ git bisect bad $ git bisect good main $ git bisect run npm test # Identification du commit fautif dans la PR, puis : $ gh pr comment 421 \ --body "Bisect : la régression vient de \`8b7e4f0\` (extract token helper)"

4. Release manuelle avec changelog auto

$ git tag -a v2026.05.0 -m "Release 2026.05" $ git push origin v2026.05.0 $ gh release create v2026.05.0 \ --generate-notes \ --target main \ ./dist/*.tar.gz $ gh release view v2026.05.0 --web

5. Triage hebdo des issues "bug" non assignées

$ gh issue list --label bug --state open --assignee "" \ --json number,title,createdAt \ --jq '.[] | "#\(.number) — \(.title) [\(.createdAt[:10])]"'

6. Re-run automatique des CI flaky

$ gh run list --workflow ci.yml --branch main \ --limit 10 --json databaseId,status \ --jq '.[] | select(.status == "failure") | .databaseId' \ | head -3 | xargs -I{} gh run rerun {} --failed

Pièges classiques

  1. Repo par défaut ambigu : dans un repo avec plusieurs remotes (fork + upstream), gh peut cibler le mauvais. Solution : gh repo set-default.
  2. Token GH_TOKEN écrasé : si la variable d'env GH_TOKEN est définie, elle a priorité sur gh auth login. Vérifiez avec gh auth status en cas de comportement bizarre.
  3. Scopes manquants : certaines opérations (workflows, packages) demandent des scopes additionnels. gh auth refresh -s workflow,write:packages.
  4. Rate limit silencieux : gh api sans --paginate peut renvoyer une page tronquée sans warning. Pour les listes longues, toujours --paginate.
  5. Auto-merge bloqué : nécessite que la branch protection rule autorise auto-merge dans les settings du repo. Sinon gh pr merge --auto échoue silencieusement.
  6. Codespaces : suppression définitive. gh codespace delete supprime définitivement (pas de poubelle). Pour juste suspendre, utilisez gh codespace stop.

← Retour au catalogue de formations

🦄 Cheatsheet

Récapitulatif visuel de toutes les commandes du cours, organisées par usage. Le bouton Imprimer applique une feuille de style optimisée (sans tabs, sans sommaire, deux colonnes) pour tenir sur une à deux pages A4 — à garder à portée de clavier.

🔎 Investigation / forensique

  • git log -S "txt"commits qui ajoutent/retirent une chaîne (pickaxe)
  • git log -G "regex"pareil avec regex dans les diffs
  • git log --first-parent mainhistorique linéaire (1 ligne/PR)
  • git log --left-right A...Bdiff symétrique (< à A, > à B)
  • git log --follow -- pathsuit un fichier à travers les renames
  • git log --mergecommits pertinents au conflit en cours
  • git shortlog -sn --since="1 month ago"stats contributeurs par période
  • git blame -w -C -C -C pathblame qui ignore whitespace + détecte copies
  • git diff --color-wordsdiff word-by-word (parfait pour markdown)
  • git diff main...featurediff vs merge-base (3 points = comme GitHub)
  • git name-rev <sha>"quelle branche contient ce SHA ?"
  • git describe --tags --abbrev=0dernier tag depuis HEAD (versioning CI)

⏪ Récupération / filet de sécurité

  • git refloghistorique de HEAD (90 jours par défaut)
  • git reset --hard HEAD@{N}retour à un état du reflog
  • git fsck --lost-foundcommits orphelins (après amend/rebase raté)
  • git fsck --unreachable --no-reflogsobjets que le prochain gc va supprimer
  • git stash branch <name>convertit un stash en branche (zéro conflit)
  • git stash push -m "wip" -- pathstash partiel ciblé
  • git stash --keep-indexstash uniquement le non-stagé
  • git update-ref refs/sauvetage/X HEADref custom avant opération risquée
  • git rebase --abortannule un rebase en cours, retour au départ
  • git cherry-pick --abortidem pour cherry-pick

🔧 Réécriture d'historique

  • git commit --fixup=<sha>crée un fixup ciblé
  • git rebase -i --autosquash <base>applique les fixups automatiquement
  • git rebase --autostash mainrebase qui stash auto vos modifs
  • git rebase --onto NEW BASE BRANCHtransplante une série de commits
  • git rebase --rebase-mergesrebase qui préserve la topologie des merges
  • git revert -n A..Brevert sans commit (regrouper)
  • git revert -m 1 <merge-sha>revert d'un merge commit
  • git filter-repo --replace-text filepurger un secret (réécriture massive)
  • git filter-repo --invert-paths --path Xsupprimer un fichier de tout l'historique

🤝 Conflits / merges

  • git config rerere.enabled truemémorise les résolutions (game changer)
  • git config merge.conflictStyle zdiff3marqueurs à 3 panneaux (base + ours + theirs)
  • git checkout --ours pathforce votre version sur un fichier
  • git checkout --theirs pathforce la version entrante
  • git merge --strategy-option=patiencealgo de diff plus malin (gros refactors)
  • git ls-files -uliste les fichiers en conflit non résolus
  • git rerere status / diff / clearinspecter / oublier les résolutions mémorisées

🔎 Recherche / archéologie

  • git bisect start + bad / gooddichotomie manuelle
  • git bisect run ./test.shdichotomie automatisée par script (0=ok, 125=skip)
  • git bisect resettermine la session bisect
  • git grep -p "pattern"grep avec fonction englobante
  • git grep -e A --and -e Blignes contenant A ET B
  • git grep "x" $(git rev-list --all)grep dans toute l'histoire
  • git branch --contains <sha>liste les branches qui contiennent ce commit

🌳 Worktrees

  • git worktree add ../X main2e checkout, même .git, plus jamais de stash
  • git worktree add -b new ../X maincrée la branche en même temps
  • git worktree add --detach ../X <tag>checkout détaché (multi-versions)
  • git worktree listliste tous vos worktrees
  • git worktree remove pathsupprime dossier + référence
  • git worktree prunenettoie les références orphelines

🏃 Productivité au quotidien

  • git switch <branch>remplaçant moderne de checkout
  • git restore pathremplaçant moderne de checkout -- path
  • git add -pstage par hunk (commits chirurgicaux)
  • git reset -p / checkout -p / stash -ptout le workflow supporte le mode hunk
  • git push --force-with-leaseforce push sécurisé (jamais --force nu)
  • git pull --rebasepull sans merge commit parasite
  • git cherry-pick -x A..Bcherry-pick d'une plage avec mention d'origine
  • git commit --allow-empty -m "ci: trigger"commit vide pour relancer la CI
  • git fetch origin pull/123/head:pr-123checkout local d'une PR GitHub par numéro

📦 Repos massifs

  • git clone --filter=blob:none <url>partial clone (blobs lazy) ×10 plus rapide
  • git clone --filter=tree:0 <url>encore plus radical (CI build-only)
  • git clone --depth 1 <url>shallow clone (juste le tip)
  • git sparse-checkout init --coneactive sparse en mode dossiers complets
  • git sparse-checkout set api/ docs/ne checkout que ces chemins
  • git sparse-checkout disabletout récupérer
  • git lfs track "*.dylib" "models/*"déléguer les binaires à LFS
  • GIT_LFS_SKIP_SMUDGE=1 git clonecloner sans télécharger les blobs LFS

🧹 Hygiène

  • git fetch --prunesupprime les remote-tracking branches mortes
  • git remote prune originversion explicite
  • git clean -ndxDRY RUN (toujours avant clean -fdx !)
  • git clean -fdxnettoie tout le non-tracké, dont .env
  • git check-ignore -v path"pourquoi ce fichier est ignoré ?"
  • git maintenance startgc / repack en background
  • git count-objects -vHespace disque occupé par git

📝 Notes (vraiment méconnu)

  • git notes add -m "reviewed by JM"annote un commit sans changer son SHA
  • git notes show <sha>lit la note
  • git push origin refs/notes/*partage les notes avec l'équipe
  • git fetch origin refs/notes/*:refs/notes/*récupère les notes de l'équipe

🎁 Trucs que personne connaît

  • git instaweblance gitweb dans un navigateur sur le repo local
  • git replace <old> <new>remplace virtuellement un commit (sans réécrire)
  • git for-each-ref --sort=-committerdate refs/heads/branches triées par dernière activité
  • git rev-parse @{push}"quel est mon upstream de push ?"
  • git diff @{1}diff vs l'état d'il y a un step de reflog
  • git update-ref -d refs/heads/Xsupprime une branche sans safety check
  • git mailmapunifie les identités (.mailmap dans le repo)

🛠️ Aliases recommandés

  • alias.lg = log --graph --pretty=format:'%C(yellow)%h ...'log graphique compact
  • alias.st = status -sbstatus court
  • alias.pushf = push --force-with-leaseforce push sécurisé
  • alias.last = log -1 HEAD --stat -pdernier commit avec diff
  • alias.recent = for-each-ref --sort=-committerdate --count=15 ...15 dernières branches actives
  • alias.undo = reset --soft HEAD~1annule le dernier commit, garde les modifs

🐙 gh CLI — essentials

  • gh auth loginlogin web (pas de token à recopier)
  • gh repo clone owner/nameclone par raccourci
  • gh repo create my-tool --public --source=. --pushcrée + push le repo courant
  • gh repo fork org/projet --clone --remotefork + clone + remote upstream
  • gh repo view --webouvre le repo dans le navigateur
  • gh browse src/file.js:42ouvre un fichier précis à une ligne
  • gh statusnotifications GitHub
  • gh search code "setTimeout" --filename "*.test.js"search code

🐙 gh CLI — PR & reviews

  • gh pr create --fill --reviewer alice,bobcrée la PR (titre/body depuis les commits)
  • gh pr create --draft --title "WIP: ..."PR en brouillon
  • gh pr list --author @mevos PR
  • gh pr statusvue panoramique de toutes vos PR
  • gh pr checkout 421checkout local d'une PR (même fork)
  • gh pr diff 421diff de la PR dans le terminal
  • gh pr review 421 --approve --body "LGTM"approve depuis le terminal
  • gh pr review 421 --request-changes --body-file ./review.mdreview longue
  • gh pr ready 421passe une PR draft en ready
  • gh pr checks 421 --watchattendre la fin des checks CI
  • gh pr merge 421 --auto --squash --delete-branchauto-merge dès que checks + reviews OK

🐙 gh CLI — Actions, releases, secrets

  • gh workflow list / gh workflow view ci.ymldéclarés dans .github/workflows/
  • gh workflow run deploy.yml --ref main -f env=stagingdéclenchement manuel (workflow_dispatch)
  • gh run list --status failureruns en échec
  • gh run view 12345 --log-failedlogs des jobs failed seulement
  • gh run watchlive tail du run en cours
  • gh run rerun 12345 --failedre-run des jobs failed
  • gh run download 12345 -n coveragetélécharger un artefact
  • gh release create v2026.05 --generate-notes ./dist/*.tar.gzrelease + notes auto + assets
  • gh release edit vX.Y --prerelease=false --latestpasser une preview en stable
  • echo -n "$KEY" | gh secret set STRIPE_KEYajoute un secret Actions
  • gh secret set NPM_TOKEN --env productionsecret par environnement
  • gh issue develop 1234 --checkoutcrée & checkoute une branche depuis une issue

🐙 gh CLI — gh api & jq

  • gh api /userinfos du compte courant
  • gh api /repos/{owner}/{repo}/issues --paginate{owner}/{repo} auto-résolus, toutes les pages
  • gh api -X POST /repos/{owner}/{repo}/issues -f title="Bug" -F labels[]=bugPOST avec champs typés
  • gh api graphql -f query='...' -f login=julienmerGraphQL avec variables
  • gh api graphql --paginate -f query='...'pagination GraphQL native
  • gh pr list --json number,title --jq '.[] | "\(.number)\t\(.title)"'extraction inline avec --jq
  • gh run list --json databaseId,status --jq '...failure...' | xargs -I{} gh run rerun {} --failedre-run massif des CI failed

🐙 gh CLI — codespaces, gists, ext.

  • gh codespace create -R owner/repo -b feature/xcodespace ciblé sur une branche
  • gh codespace ssh -- "npm test"exécute une commande à distance
  • gh codespace ports forward 3000:3000forward de port
  • gh codespace cp -r ./src remote:/workspaces/repo/copie locale ↔ codespace
  • gh codespace stop / gh codespace deletestop = pause, delete = définitif
  • gh gist create snippet.js --publicgist public depuis fichier
  • echo "..." | gh gist create --filename note.md -gist depuis stdin
  • gh extension install dlvhdr/gh-dashdashboard PR/issues TUI
  • gh extension install vilmibm/gh-poinettoie les branches mergées
  • gh extension upgrade --allmet à jour toutes les extensions

Le bloc ~/.gitconfig à coller en une fois

Détails ligne par ligne dans le module ⚙️ .gitconfig magique. À copier dans votre ~/.gitconfig, sous votre section [user].

[init] defaultBranch = main [pull] rebase = true ff = only [push] autoSetupRemote = true default = current [rebase] autosquash = true autostash = true [rerere] enabled = true [merge] conflictStyle = zdiff3 [diff] algorithm = histogram colorMoved = zebra [core] fsmonitor = true untrackedCache = true [maintenance] auto = true [branch] sort = -committerdate [column] ui = auto
Une seule ligne à retenir si vous n'en activez qu'une : rerere.enabled = true. C'est celle qui change le plus la productivité quotidienne.

← Retour au catalogue de formations