</>

Git

Toutes les commandes Git essentielles, du premier commit au rebase interactif, avec des cas concrets (résoudre un conflit, nettoyer un historique, gérer une PR).

Les bases

Débutant

Installer Git, créer un dépôt, faire son premier commit.

Qu'est-ce que Git ?#

Git est un système de contrôle de version : il enregistre l'historique des modifications d'un projet, permet de revenir en arrière, de travailler à plusieurs sans écraser le travail des autres, et de gérer des branches parallèles.

Trois zones à connaître :

  • Working directory : vos fichiers tels que vous les voyez sur le disque.
  • Staging area (index) : les modifications que vous avez préparées avec git add, prêtes à être commitées.
  • Repository (.git) : l'historique des commits enregistrés.
Working directory  --git add-->  Staging area  --git commit-->  Repository

Installer et configurer Git#

# Vérifier que Git est installé
git --version

# Configurer votre identité (obligatoire avant le premier commit)
git config --global user.name "Votre Nom"
git config --global user.email "vous@example.com"

# Choisir votre éditeur par défaut (pour les messages de commit, rebase -i...)
git config --global core.editor "code --wait"

# Voir toute la configuration active
git config --list

Astuce : --global s'applique à tous vos dépôts. Sans --global, la config ne s'applique qu'au dépôt courant (utile pour un email pro différent sur un projet précis).

Initialiser un dépôt#

# Démarrer un nouveau dépôt Git dans le dossier courant
git init

# Démarrer un dépôt dans un nouveau dossier
git init mon-projet

Cloner un dépôt existant#

# Cloner via HTTPS
git clone https://github.com/utilisateur/projet.git

# Cloner via SSH (recommandé si vous avez une clé SSH configurée)
git clone git@github.com:utilisateur/projet.git

# Cloner dans un dossier précis
git clone git@github.com:utilisateur/projet.git mon-dossier

# Cloner seulement une branche précise
git clone --branch develop --single-branch git@github.com:utilisateur/projet.git

Voir l'état du dépôt#

# Affiche les fichiers modifiés, ajoutés, non suivis...
git status

# Version courte, plus lisible pour un usage quotidien
git status -s

git status est la commande la plus utilisée en Git : lancez-la souvent, elle vous dit toujours où vous en êtes.

Ajouter des fichiers au prochain commit#

# Ajouter un fichier précis
git add fichier.txt

# Ajouter plusieurs fichiers
git add fichier1.txt fichier2.txt

# Ajouter tout un dossier
git add src/

# Ajouter TOUT (nouveaux fichiers, modifications, suppressions)
git add .

# Ajouter interactivement, morceau par morceau (pour ne stager qu'une partie d'un fichier)
git add -p fichier.txt

Créer un commit#

# Commit avec message inline
git commit -m "Ajoute la page de connexion"

# Commit avec message + description détaillée
git commit -m "Ajoute la page de connexion" -m "Gère aussi la validation des champs email et mot de passe."

# Ajouter automatiquement les fichiers déjà suivis (skip git add) et commiter
git commit -am "Corrige le bug d'affichage sur mobile"

Bonne pratique : un commit = un changement logique. Préférez plusieurs petits commits clairs à un seul gros commit "wip".

Voir l'historique des commits#

# Historique complet
git log

# Une ligne par commit, condensé
git log --oneline

# Avec le graphe des branches
git log --oneline --graph --all

# Les N derniers commits
git log -5

# Les commits touchant un fichier précis
git log -- src/app.js

# Les commits d'un auteur précis
git log --author="Alice"

Voir les différences#

# Différences non indexées (working dir vs staging)
git diff

# Différences déjà indexées (staging vs dernier commit)
git diff --staged

# Différences entre deux commits
git diff abc1234 def5678

# Différences entre deux branches
git diff main..ma-branche

Ignorer des fichiers (.gitignore)#

Créez un fichier .gitignore à la racine du projet :

# Dépendances
node_modules/

# Fichiers d'environnement
.env
.env.local

# Build
dist/
.next/

# Fichiers système
.DS_Store
# Si un fichier est déjà suivi par Git, l'ajouter à .gitignore ne suffit pas :
# il faut d'abord le retirer du suivi (sans le supprimer du disque)
git rm --cached fichier-deja-suivi.txt

Cas pratique : vous avez commité .env par erreur puis ajouté .env au .gitignore, mais Git continue de le suivre ? C'est normal : .gitignore n'agit que sur les fichiers non suivis. Utilisez git rm --cached .env puis commitez ce changement.

Branches & fusion

Débutant

Créer, changer, fusionner et nettoyer des branches.

Créer une branche#

# Créer une nouvelle branche (sans y basculer)
git branch ma-fonctionnalite

# Lister les branches locales
git branch

# Lister aussi les branches distantes
git branch -a

Changer de branche#

# Basculer sur une branche existante (commande moderne)
git switch ma-fonctionnalite

# Équivalent historique, toujours très utilisé
git checkout ma-fonctionnalite

# Revenir à la branche précédente
git switch -

Créer et basculer en une seule commande#

# Avec switch (recommandé)
git switch -c ma-fonctionnalite

# Avec checkout (ancienne syntaxe, toujours valide)
git checkout -b ma-fonctionnalite

# Créer une branche à partir d'une autre branche (pas la courante)
git switch -c ma-fonctionnalite main

Cas pratique : vous voulez démarrer une nouvelle fonctionnalité en partant toujours de la dernière version de main, même si vous êtes actuellement ailleurs :

git fetch origin
git switch -c ma-fonctionnalite origin/main

Fusionner une branche (git merge)#

# Se placer sur la branche qui va RECEVOIR les changements
git switch main

# Fusionner ma-fonctionnalite dans main
git merge ma-fonctionnalite

Fast-forward vs merge commit#

  • Fast-forward : si main n'a reçu aucun nouveau commit depuis la création de ma-fonctionnalite, Git déplace simplement le pointeur de main. Pas de nouveau commit de fusion.
  • Merge commit : si main a évolué en parallèle, Git crée un commit de fusion qui a deux parents.
# Forcer un merge commit même si un fast-forward est possible
# (utile pour garder une trace visible de "quand" la feature a été intégrée)
git merge --no-ff ma-fonctionnalite

# Forcer un fast-forward, échoue si ce n'est pas possible
git merge --ff-only ma-fonctionnalite

Résoudre un conflit de fusion#

Un conflit apparaît quand la même partie d'un fichier a été modifiée différemment sur les deux branches.

git merge ma-fonctionnalite
# CONFLICT (content): Merge conflict in src/app.js

Git marque les zones en conflit dans le fichier :

<<<<<<< HEAD
const version = "2.0";
=======
const version = "2.1";
>>>>>>> ma-fonctionnalite

Étapes pour résoudre :

# 1. Ouvrez le fichier, choisissez le bon contenu, supprimez les marqueurs <<<, ===, >>>
# 2. Indiquez à Git que le conflit est résolu
git add src/app.js

# 3. Terminez la fusion
git commit

# Pour annuler complètement la fusion et revenir à l'état d'avant
git merge --abort

Voir les branches déjà fusionnées#

# Branches déjà fusionnées dans la branche courante (peuvent être supprimées sans risque)
git branch --merged

# Branches non fusionnées (attention avant de les supprimer)
git branch --no-merged

Supprimer une branche#

# Supprimer une branche locale déjà fusionnée
git branch -d ma-fonctionnalite

# Forcer la suppression même si elle n'est pas fusionnée (perte de commits possible)
git branch -D ma-fonctionnalite

# Supprimer une branche sur le dépôt distant
git push origin --delete ma-fonctionnalite

Renommer une branche#

# Renommer la branche courante
git branch -m nouveau-nom

# Renommer une branche précise depuis une autre branche
git branch -m ancien-nom nouveau-nom

# Mettre à jour le nom côté distant
git push origin -u nouveau-nom
git push origin --delete ancien-nom

Dépôts distants

Intermédiaire

remote, fetch, pull, push, upstream, fork.

Ajouter un dépôt distant#

# Ajouter un remote nommé "origin" (convention pour le remote principal)
git remote add origin git@github.com:utilisateur/projet.git

# Lister les remotes configurés
git remote -v

# Changer l'URL d'un remote existant
git remote set-url origin git@github.com:utilisateur/nouveau-nom.git

# Supprimer un remote
git remote remove origin

Récupérer sans fusionner (git fetch)#

# Télécharge les nouveaux commits/branches du remote, sans toucher à vos fichiers
git fetch origin

# Fetch de tous les remotes configurés
git fetch --all

# Fetch en supprimant les références locales vers des branches distantes supprimées
git fetch --prune

git fetch est sûr : il met à jour votre connaissance du dépôt distant (via origin/main par exemple) sans jamais modifier votre travail en cours.

Récupérer et fusionner (git pull)#

# Équivalent à : git fetch + git merge
git pull origin main

# Équivalent à : git fetch + git rebase (historique plus propre, recommandé en solo sur une branche perso)
git pull --rebase origin main

# Définir --rebase comme comportement par défaut de git pull
git config --global pull.rebase true

Cas pratique : votre git pull échoue avec "divergent branches" ? Depuis Git 2.27, il faut choisir explicitement une stratégie :

git config pull.rebase false   # merge (défaut historique)
git config pull.rebase true    # rebase
git config pull.ff only        # fast-forward uniquement, échoue sinon

Envoyer ses commits (git push)#

# Pousser la branche courante vers origin
git push origin ma-branche

# Premier push d'une nouvelle branche : la lier à sa branche distante (upstream)
git push -u origin ma-branche

# Une fois l'upstream configuré, un simple push suffit
git push

# Pousser toutes les branches
git push --all

Suivre une branche distante (upstream)#

# Voir quelle branche distante est suivie par la branche courante
git status -sb
# ou
git branch -vv

# Lier une branche locale existante à une branche distante existante
git branch --set-upstream-to=origin/ma-branche ma-branche

Cas pratique : cloner un fork et suivre l'original (upstream)#

Workflow classique en open source : vous forkez un projet, mais vous voulez quand même récupérer les mises à jour du dépôt original.

# 1. Cloner votre fork
git clone git@github.com:votre-compte/projet.git
cd projet

# 2. Ajouter le dépôt original comme second remote, nommé "upstream"
git remote add upstream git@github.com:auteur-original/projet.git

# 3. Vérifier
git remote -v
# origin    git@github.com:votre-compte/projet.git (fetch/push)
# upstream  git@github.com:auteur-original/projet.git (fetch/push)

# 4. Récupérer les mises à jour de l'original et les intégrer dans votre main
git fetch upstream
git switch main
git merge upstream/main
git push origin main

Supprimer une branche distante#

git push origin --delete ma-branche

# Nettoyer ensuite les références locales obsolètes
git fetch --prune

Annuler & corriger

Intermédiaire

restore, amend, reset, revert, clean.

Vue d'ensemble : quelle commande pour quel besoin ?#

Je veux...Commande
Annuler des changements non indexés dans un fichiergit restore fichier
Retirer un fichier du staging (sans perdre le changement)git restore --staged fichier
Modifier le message ou le contenu du dernier commit (non poussé)git commit --amend
Annuler le dernier commit mais garder les changementsgit reset --soft HEAD~1
Annuler complètement le dernier commit et les changementsgit reset --hard HEAD~1
Annuler un commit déjà poussé / partagégit revert <commit>
Supprimer les fichiers non suivis (non trackés par Git)git clean -fd

Annuler des modifications non indexées#

# Restaurer un fichier à son état du dernier commit (perd les modifications locales)
git restore fichier.txt

# Restaurer tous les fichiers modifiés
git restore .

Désindexer un fichier (retirer du staging)#

# Vous avez fait "git add" par erreur ? Retirez le fichier du staging
git restore --staged fichier.txt

# Ancienne syntaxe équivalente (toujours fonctionnelle)
git reset HEAD fichier.txt

Modifier le dernier commit (git commit --amend)#

# Corriger uniquement le message du dernier commit
git commit --amend -m "Nouveau message plus clair"

# Ajouter un fichier oublié au dernier commit, en gardant le même message
git add fichier-oublie.txt
git commit --amend --no-edit

⚠️ Attention : --amend réécrit le dernier commit (nouveau hash). Ne l'utilisez jamais sur un commit déjà poussé et partagé avec d'autres, sauf si vous êtes seul sur la branche et acceptez de force-push.

Annuler des commits avec reset#

git reset déplace le pointeur de la branche courante. Trois modes :

# --soft : annule le commit, garde les changements indexés (staged)
git reset --soft HEAD~1

# --mixed (par défaut) : annule le commit ET le staging, garde les fichiers modifiés
git reset HEAD~1

# --hard : annule le commit, le staging, ET les modifications du working directory
git reset --hard HEAD~1
# Revenir à un commit précis (attention : perd tout ce qui vient après, en --hard)
git reset --hard abc1234

⚠️ git reset --hard supprime définitivement les changements non commités. Vérifiez git status avant de l'utiliser. Si le commit existait déjà, il reste souvent récupérable via le reflog.

Annuler un commit déjà poussé (git revert)#

Contrairement à reset, revert ne réécrit pas l'historique : il crée un nouveau commit qui annule les changements d'un commit précédent. C'est la méthode sûre pour annuler quelque chose de déjà partagé (main, PR mergée...).

# Annuler un commit précis en créant un commit inverse
git revert abc1234

# Annuler sans ouvrir l'éditeur (message auto-généré)
git revert --no-edit abc1234

# Annuler plusieurs commits (du plus ancien au plus récent)
git revert abc1234..def5678

# Annuler un merge commit (il faut préciser quel parent garder, en général -m 1)
git revert -m 1 <hash-du-merge-commit>

Cas pratique : un bug part en production dans le dernier commit sur main. La correction n'est pas prête, il faut annuler vite :

git log --oneline -5          # repérer le hash du commit fautif
git revert abc1234            # crée un nouveau commit qui annule abc1234
git push origin main          # déploie l'annulation, sans réécrire l'historique

Nettoyer les fichiers non suivis (git clean)#

# Voir ce qui serait supprimé, SANS rien supprimer (toujours faire ça d'abord)
git clean -nd

# Supprimer les fichiers non suivis
git clean -fd

# Supprimer aussi les fichiers ignorés (.gitignore), ex: node_modules
git clean -fdx

⚠️ git clean supprime des fichiers définitivement (pas de corbeille). Utilisez toujours -n (dry-run) avant -f.

Rebase

Intermédiaire

Rebase simple sur une PR, rebase interactif, conflits, force-push sécurisé.

Qu'est-ce qu'un rebase ?#

Un rebase "rejoue" les commits d'une branche par-dessus une autre base, comme si vous aviez commencé votre travail à partir de ce nouveau point de départ. Contrairement à merge, il ne crée pas de commit de fusion : l'historique reste linéaire.

Avant :                        Après rebase sur main :

main:    A---B---C              main:    A---B---C
              \                                    \
ma-branche:    D---E             ma-branche:         D'---E'

D' et E' sont de nouveaux commits (nouveaux hash) qui contiennent les mêmes changements que D et E.

Cas pratique : rebase simple sur une PR (mettre sa branche à jour avec main)#

C'est le cas d'usage le plus courant : votre PR a du retard sur main et vous voulez la mettre à jour proprement avant qu'elle soit mergée.

# 1. Se placer sur sa branche de PR
git switch ma-feature

# 2. Récupérer les derniers commits de main (sans les fusionner encore)
git fetch origin

# 3. Rejouer les commits de ma-feature par-dessus origin/main
git rebase origin/main

# 4. Si tout se passe bien, pousser la branche mise à jour
#    --force-with-lease est nécessaire car l'historique a changé
git push --force-with-lease

C'est exactement l'équivalent de cliquer sur "Update branch" sur GitHub, mais fait en local et avec un historique linéaire (pas de merge commit "Merge branch main into ma-feature").

Résoudre les conflits pendant un rebase#

Un rebase rejoue les commits un par un : si un commit est en conflit, Git s'arrête dessus.

git rebase origin/main
# CONFLICT (content): Merge conflict in src/app.js
# error: could not apply d34db33... Ajoute la validation email
# 1. Ouvrez les fichiers en conflit, résolvez (cherchez <<<<<<<, =======, >>>>>>>)
# 2. Indiquez que le conflit est résolu
git add src/app.js

# 3. Continuez le rebase (rejoue le commit suivant)
git rebase --continue

# Si un commit devient inutile après résolution (changements déjà présents)
git rebase --skip

# À tout moment, pour tout annuler et revenir à l'état d'avant le rebase
git rebase --abort

Astuce : en cas de conflits répétitifs sur plusieurs commits, git rerere peut mémoriser vos résolutions et les réappliquer automatiquement.

Rebase interactif (git rebase -i)#

Le rebase interactif permet de réécrire l'historique de vos propres commits avant de les partager : regrouper, réordonner, reformuler, supprimer.

# Réécrire les 3 derniers commits
git rebase -i HEAD~3

# Réécrire tous les commits depuis la création de la branche (depuis main)
git rebase -i main

Git ouvre un éditeur avec la liste des commits :

pick a1b2c3 Ajoute le formulaire de connexion
pick d4e5f6 Corrige un typo
pick g7h8i9 Ajoute les tests

# Commandes :
# p, pick   = garder le commit tel quel
# r, reword = garder le commit, modifier le message
# e, edit   = garder le commit, s'arrêter pour le modifier
# s, squash = fusionner avec le commit précédent, cumuler les messages
# f, fixup  = comme squash, mais sans garder le message
# d, drop   = supprimer le commit

Cas pratique : squasher des commits avant de merger une PR#

Vous avez 5 commits "wip", "fix typo", "oups"... et vous voulez n'en garder qu'un seul avant la review.

git rebase -i HEAD~5

Dans l'éditeur, gardez pick pour le premier commit et changez les autres en squash (ou fixup pour ne pas garder leurs messages) :

pick a1b2c3 Ajoute le formulaire de connexion
fixup d4e5f6 wip
fixup g7h8i9 fix typo
fixup j1k2l3 oups
fixup m4n5o6 ça marche enfin

Sauvegardez, Git fusionne tout en un seul commit. Terminez par un push forcé :

git push --force-with-lease

Réordonner ou renommer des commits#

Dans l'éditeur du rebase interactif, changez simplement l'ordre des lignes pour réordonner les commits, ou remplacez pick par reword pour ne modifier que le message :

reword a1b2c3 Ajoute le formulaire de connexion
pick   g7h8i9 Ajoute les tests

Git vous demandera le nouveau message pour chaque commit marqué reword.

Force-push en sécurité : --force-with-lease#

Après un rebase, l'historique de votre branche a changé : un git push classique sera refusé. Il faut forcer, mais jamais avec --force seul sur une branche partagée.

# Dangereux : écrase le travail des autres sans vérification
git push --force

# Sûr : échoue si quelqu'un d'autre a poussé sur cette branche entre-temps
git push --force-with-lease

--force-with-lease vérifie que la branche distante n'a pas bougé depuis votre dernier fetch avant d'écraser quoi que ce soit — cela évite d'effacer par accident le travail d'un collègue qui aurait poussé sur la même branche.

Rebase vs Merge : quand utiliser quoi ?#

SituationRecommandation
Mettre à jour une branche de PR pas encore partagée avec d'autresrebase (historique propre)
Branche déjà partagée / plusieurs personnes dessusmerge (évite de réécrire l'historique des autres)
Intégrer une feature terminée dans mainmerge (souvent via "Squash and merge" sur GitHub)
Nettoyer ses propres commits avant une reviewrebase -i
Règle d'orNe jamais rebaser une branche que d'autres ont déjà récupérée, sauf accord explicite de l'équipe

Stash

Intermédiaire

Mettre de côté des modifications temporairement.

Mettre de côté ses modifications#

git stash range temporairement vos modifications non commitées pour retrouver un working directory propre, sans avoir à commiter du travail inachevé.

# Mettre de côté les fichiers modifiés + indexés
git stash

# Inclure aussi les fichiers non suivis (nouveaux fichiers)
git stash -u

# Ajouter un message descriptif (fortement recommandé si vous en avez plusieurs)
git stash push -m "wip: formulaire de connexion"

Cas pratique : vous êtes en plein travail sur une fonctionnalité quand on vous demande de corriger un bug urgent sur main. Pas envie de commiter du code inachevé :

git stash -m "wip: en cours"
git switch main
# ... corriger le bug, commiter, pousser ...
git switch ma-feature
git stash pop

Lister les stashs#

git stash list
# stash@{0}: On ma-feature: wip: formulaire de connexion
# stash@{1}: On main: wip: refacto css

Réappliquer un stash#

# Réapplique le dernier stash ET le supprime de la liste
git stash pop

# Réapplique le dernier stash mais le GARDE dans la liste
git stash apply

# Réappliquer un stash précis (par son index)
git stash apply stash@{1}
git stash pop stash@{1}

Voir le contenu d'un stash#

# Résumé des fichiers modifiés dans le dernier stash
git stash show

# Diff complet
git stash show -p stash@{0}

Stash d'un seul fichier#

# Ne mettre de côté qu'un fichier précis
git stash push -m "message" -- src/app.js

Créer une branche à partir d'un stash#

Utile si le stash entre en conflit avec l'état actuel de votre branche.

git stash branch nouvelle-branche stash@{0}

Supprimer un stash#

# Supprimer un stash précis
git stash drop stash@{0}

# Vider tous les stashs
git stash clear

Tags

Intermédiaire

Marquer des versions (releases) dans l'historique.

Créer un tag léger#

Un simple pointeur nommé vers un commit, sans métadonnées.

git tag v1.0.0

# Tagger un commit précis (pas forcément le dernier)
git tag v1.0.0 abc1234

Créer un tag annoté (recommandé pour les releases)#

Un tag annoté est un objet Git complet : auteur, date, message — comme un commit. C'est la forme recommandée pour marquer des versions publiques.

git tag -a v1.0.0 -m "Première version stable"

Lister les tags#

git tag

# Filtrer avec un motif
git tag -l "v1.*"

# Voir les infos d'un tag annoté
git show v1.0.0

Pousser les tags#

Par défaut, git push ne pousse pas les tags.

# Pousser un tag précis
git push origin v1.0.0

# Pousser tous les tags
git push origin --tags

Cas pratique : créer une release à partir du dernier commit de main#

git switch main
git pull origin main
git tag -a v1.2.0 -m "Ajoute l'export PDF"
git push origin v1.2.0

Revenir sur un tag (état à cette version)#

# Consulter le code à cette version sans changer de branche (detached HEAD)
git checkout v1.0.0

# Créer une branche à partir d'un tag pour y travailler
git switch -c hotfix-v1.0.1 v1.0.0

Supprimer un tag#

# Supprimer localement
git tag -d v1.0.0

# Supprimer sur le dépôt distant
git push origin --delete v1.0.0

Explorer l'historique

Avancé

log avancé, blame, cherry-pick, bisect, reflog.

git log avancé#

# Format compact avec graphe et branches
git log --oneline --graph --all --decorate

# Voir les fichiers modifiés dans chaque commit
git log --stat

# Voir le diff complet de chaque commit
git log -p

# Filtrer par plage de dates
git log --since="2 weeks ago" --until="yesterday"

# Filtrer par auteur
git log --author="Alice"

# Format personnalisé
git log --pretty=format:"%h %ad %s" --date=short

Rechercher du contenu dans l'historique#

# Trouver les commits qui ont ajouté/supprimé une chaîne précise dans le code
git log -S "nomDeFonction" --oneline

# Idem, mais avec une regex
git log -G "function\s+handle.*Click" --oneline

# Chercher dans les messages de commit
git log --grep="fix bug"

Trouver qui a modifié une ligne (git blame)#

# Affiche, pour chaque ligne d'un fichier, le dernier commit qui l'a modifiée
git blame src/app.js

# Limiter à une plage de lignes
git blame -L 20,40 src/app.js

# Ignorer les commits de reformatage (ex: passage à Prettier), pour remonter au vrai auteur
git blame --ignore-rev <hash-du-commit-de-formatage> src/app.js

Cherry-pick : appliquer un commit précis d'une autre branche#

# Applique le commit abc1234 sur la branche courante, sans toucher au reste
git cherry-pick abc1234

# Cherry-pick plusieurs commits
git cherry-pick abc1234 def5678

# Cherry-pick une plage de commits (exclut abc1234, inclut def5678)
git cherry-pick abc1234..def5678

# En cas de conflit : résoudre, puis
git add .
git cherry-pick --continue

# Annuler un cherry-pick en cours
git cherry-pick --abort

Cas pratique : un correctif urgent a été commité sur main, et vous devez aussi l'appliquer sur la branche release-1.x sans tout fusionner :

git switch release-1.x
git log main --oneline -5        # repérer le hash du fix
git cherry-pick abc1234
git push origin release-1.x

Bisect : trouver le commit qui a introduit un bug#

git bisect fait une recherche dichotomique dans l'historique pour identifier automatiquement le commit fautif.

# Démarrer la recherche
git bisect start

# Indiquer un commit où le bug est présent (souvent HEAD)
git bisect bad

# Indiquer un commit où le bug n'existait pas encore (ex: le dernier tag stable)
git bisect good v1.0.0

# Git se place sur un commit "au milieu" : testez, puis répondez
git bisect good   # si le bug n'est pas présent ici
git bisect bad    # si le bug est présent ici

# Répétez jusqu'à ce que Git identifie le commit fautif exact

# Terminer et revenir à votre branche de départ
git bisect reset
# Version automatisée : Git exécute un script/test à chaque étape
git bisect start HEAD v1.0.0
git bisect run npm test

Reflog : récupérer un commit "perdu"#

Le reflog enregistre tous les déplacements de HEAD (commits, resets, checkouts, rebases...), même ceux qui ne sont plus visibles dans git log. C'est le filet de sécurité de Git.

# Voir l'historique des déplacements de HEAD
git reflog
# abc1234 HEAD@{0}: commit: Ajoute la validation
# def5678 HEAD@{1}: reset: moving to HEAD~1
# ghi9012 HEAD@{2}: commit: wip

Cas pratique : vous avez fait git reset --hard HEAD~1 par erreur et perdu un commit qui n'était pas poussé.

git reflog
# repérez la ligne juste avant le reset, ex: ghi9012 HEAD@{2}: commit: wip

git reset --hard ghi9012
# ou, plus prudent, créer une branche à partir de ce commit pour ne rien écraser :
git branch recuperation ghi9012

Le reflog garde généralement 90 jours d'historique local (gc.reflogExpire) — largement de quoi rattraper une erreur récente.

Réécrire l'historique en profondeur (mention)#

Pour des réécritures massives (supprimer un fichier sensible de tout l'historique, changer un email sur des milliers de commits...), git rebase -i n'est pas adapté. L'outil recommandé est git filter-repo (successeur officiel de filter-branch, à installer séparément) :

# Exemple : supprimer un fichier de TOUT l'historique
git filter-repo --path secrets.txt --invert-paths

⚠️ Ces opérations réécrivent tous les hash de commits. À utiliser en connaissance de cause, jamais sur un dépôt partagé sans prévenir toute l'équipe.

Workflow Pull Request

Intermédiaire

Le cycle complet feature branch + PR, du premier commit au merge.

Le workflow classique feature branch + PR#

# 1. Partir d'un main à jour
git switch main
git pull origin main

# 2. Créer une branche dédiée à la tâche
git switch -c feature/export-pdf

# 3. Travailler, commiter régulièrement
git add .
git commit -m "Ajoute le bouton d'export"
git commit -m "Génère le PDF côté serveur"

# 4. Pousser la branche et ouvrir une PR
git push -u origin feature/export-pdf
# puis ouvrir la Pull Request sur GitHub/GitLab

Convention utile : préfixer les branches par type (feature/, fix/, chore/, hotfix/) aide toute l'équipe à s'y retrouver rapidement.

Mettre à jour sa branche de PR avec la dernière version de main#

Deux approches valables, selon la convention de votre équipe :

# Option A — rebase : historique linéaire, nécessite un force-push ensuite
git fetch origin
git rebase origin/main
git push --force-with-lease

# Option B — merge : plus simple, garde un commit de fusion, pas de force-push
git fetch origin
git merge origin/main
git push

Voir la page Rebase pour le détail du cas "rebase simple sur une PR".

Squash and merge : garder un historique propre sur main#

Beaucoup d'équipes activent l'option "Squash and merge" sur GitHub : tous les commits d'une PR sont regroupés en un seul commit sur main, quel que soit le nombre de commits "wip" dans la branche. Cela évite d'avoir à nettoyer soi-même l'historique avant de merger.

# Équivalent en local si vous mergez manuellement en squash
git switch main
git merge --squash feature/export-pdf
git commit -m "Ajoute l'export PDF"

Résoudre les conflits d'une PR#

Si GitHub affiche "This branch has conflicts that must be resolved", résolvez en local (plus fiable que l'éditeur web pour des conflits complexes) :

git switch feature/export-pdf
git fetch origin
git merge origin/main       # ou "git rebase origin/main"

# Résoudre les conflits fichier par fichier
git add fichier-resolu.js
git commit                  # nécessaire seulement après un merge, pas après un rebase --continue

git push                    # ou "git push --force-with-lease" après un rebase

Répondre à une review : ajouter des commits ou amender#

# Option A — ajouter de nouveaux commits (recommandé : garde l'historique de review lisible)
git add .
git commit -m "Prend en compte les retours de review : renomme les variables"
git push

# Option B — amender le dernier commit (si le retour porte sur ce commit précis, pas encore review sérieusement)
git add .
git commit --amend --no-edit
git push --force-with-lease

Astuce : sur la plupart des projets, mieux vaut ajouter des commits pendant une review en cours (le reviewer peut voir "ce qui a changé depuis son dernier passage"), puis squash au moment du merge final.

Supprimer sa branche après merge#

# Une fois la PR mergée, supprimer localement
git switch main
git pull origin main
git branch -d feature/export-pdf

# Supprimer côté distant (souvent automatique sur GitHub après merge)
git push origin --delete feature/export-pdf

Fonctionnalités avancées

Avancé

worktree, submodules, hooks, alias, signature de commits, rerere.

Git worktree : travailler sur plusieurs branches en parallèle#

Un worktree permet d'avoir plusieurs branches extraites simultanément, dans des dossiers différents, sans avoir à stash ou committer pour changer de branche.

# Créer un nouveau worktree pour une branche existante, dans un dossier voisin
git worktree add ../projet-hotfix hotfix/urgent

# Créer un worktree avec une nouvelle branche
git worktree add -b feature/x ../projet-feature-x

# Lister les worktrees actifs
git worktree list

# Supprimer un worktree terminé
git worktree remove ../projet-hotfix

Cas pratique : vous êtes en plein milieu d'une fonctionnalité (fichiers modifiés, pas envie de stash) quand un bug critique tombe. Plutôt que de jongler avec git stash, ouvrez un second dossier de travail :

git worktree add ../projet-hotfix main
cd ../projet-hotfix
# corriger, commiter, pousser le hotfix, sans jamais toucher à votre travail en cours

Submodules : inclure un autre dépôt Git#

# Ajouter un dépôt externe comme sous-dossier suivi séparément
git submodule add https://github.com/org/librairie.git libs/librairie

# Cloner un projet qui contient des submodules
git clone --recurse-submodules git@github.com:org/projet.git

# Si déjà cloné sans les submodules, les initialiser après coup
git submodule update --init --recursive

# Mettre à jour tous les submodules à leur dernière version référencée
git submodule update --remote

Les submodules ont la réputation d'être délicats à manier en équipe. Pour partager du code entre projets, évaluez aussi les alternatives : packages npm privés, monorepo, git subtree.

Hooks : automatiser des actions Git#

Les hooks sont des scripts exécutés automatiquement à certains moments (avant un commit, avant un push...). Ils vivent dans .git/hooks/ (non versionnés par défaut — utilisez un outil comme Husky pour les partager via le dépôt).

# Exemple : .git/hooks/pre-commit (rendre le fichier exécutable)
#!/bin/sh
npm run lint
chmod +x .git/hooks/pre-commit

Hooks courants :

HookSe déclenche
pre-commitAvant la création du commit (lint, formatage)
commit-msgAprès la saisie du message (valider un format de message)
pre-pushAvant un push (lancer les tests)
post-checkoutAprès un changement de branche

Alias utiles#

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.last "log -1 HEAD"
git config --global alias.graph "log --oneline --graph --all --decorate"
git config --global alias.undo "reset --soft HEAD~1"

Utilisation ensuite :

git st
git graph

Signer ses commits (GPG/SSH)#

Signer un commit prouve cryptographiquement qu'il vient bien de vous (GitHub affiche un badge "Verified").

# Configurer une clé GPG comme signature par défaut
git config --global user.signingkey <ID_DE_LA_CLE>
git config --global commit.gpgsign true

# Ou signer avec une clé SSH (plus simple si vous avez déjà une clé SSH GitHub)
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

# Signer un commit ponctuellement (si la signature n'est pas automatique)
git commit -S -m "Commit signé"

git rerere : réutiliser des résolutions de conflits#

rerere ("reuse recorded resolution") mémorise comment vous avez résolu un conflit pour l'appliquer automatiquement si le même conflit réapparaît — très utile lors de rebases répétés sur une longue branche.

# Activer rerere (une fois, globalement)
git config --global rerere.enabled true

# Une fois activé, il agit automatiquement : rien à faire de plus.
# Vérifier les résolutions mémorisées :
git rerere status