Skip to content

Git Aide-mémoire

Système de contrôle de version distribué pour suivre les modifications du code source.

01

Configuration et initialisation

Configuration globale et locale

Git stocke la configuration à trois niveaux : système (/etc/gitconfig), global (~/.gitconfig pour l'utilisateur) et local (.git/config par dépôt). Les niveaux inférieurs remplacent les supérieurs. Définissez toujours user.name et user.email avant de committer, sinon les commits utiliseront une identité par défaut peu utile. Définir init.defaultBranch à main évite la valeur master obsolète et s'aligne avec les conventions modernes.

git
# set user identity (required for commits)
git config --global user.name "Alice Lee"
git config --global user.email "[email protected]"

# set default editor and branch name
git config --global core.editor "code --wait"
git config --global init.defaultBranch main

# view all settings and their origin
git config --list --show-origin

# edit config files directly
git config --global --edit        # ~/.gitconfig
git config --edit                 # repo .git/config

Créer et cloner des dépôts

git init crée un dépôt vide en ajoutant un répertoire caché .git qui stocke toutes les données de version. git clone copie un dépôt distant en incluant tout son historique. Utilisez --depth 1 pour un clone superficiel quand vous n'avez besoin que du dernier instantané (par ex. builds CI) — cela réduit considérablement la taille du téléchargement. --single-branch évite de récupérer les branches non liées.

git
# create a new repo from scratch in current dir
mkdir my-app && cd my-app
git init

# clone a remote repository
git clone https://github.com/user/repo.git

# clone into a specific folder name
git clone https://github.com/user/repo.git my-folder

# clone a single branch (saves bandwidth)
git clone -b dev --single-branch https://github.com/user/repo.git

# shallow clone: only the latest commit
git clone --depth 1 https://github.com/user/repo.git

Alias et raccourcis

Les alias permettent de définir des noms plus courts pour les commandes ou séquences de commandes fréquemment utilisées. Ils sont stockés dans la section [alias] de votre gitconfig. Un alias commençant par ! s'exécute comme une commande shell, permettant des workflows complexes. L'alias lg ci-dessus produit un graphe d'historique visuel compact extrêmement utile pour comprendre la structure des branches.

git
# create shortcuts for common commands
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"

# now use them
git co main
git lg

# alias that runs an external command (starts with !)
git config --global alias.unstage "reset HEAD --"
git unstage file.txt

Aide et documentation

Git est livré avec une documentation intégrée complète. git help <command> ouvre la page de manuel dans votre pager. L'option -h donne un résumé rapide d'un écran des options. Les guides (git help -g) incluent un tutoriel, un glossaire des termes et une référence du workflow quotidien — excellents pour les débutants apprenant la terminologie.

git
# open the full manual for a command
git help commit
git commit --help

# show a concise synopsis
git commit -h

# list all git commands
git help -a

# list all guides (tutorial, glossary, etc.)
git help -g
git help glossary

Motifs .gitignore

.gitignore indique à Git quels fichiers exclure du contrôle de version — essentiel pour les artefacts de build, les dépendances et les secrets. Les motifs utilisent la syntaxe glob ; une barre oblique finale correspond aux répertoires. Le préfixe ! négatif un motif, forçant un fichier à être suivi. .gitignore lui-même devrait être commité pour que l'équipe partage les règles d'ignorance. Les fichiers déjà suivis ne sont pas affectés par les nouveaux motifs d'ignorance — supprimez-les d'abord avec git rm --cached.

git
# .gitignore file — patterns for files to skip
node_modules/
*.log
.env
.env.local
dist/
build/

# but track this specific file even if it matches
!important.log

# ignore all .txt files in build/ but not subdirs
build/*.txt

# ignore everything in a folder except one file
secrets/*
!secrets/template.env
02

Staging et commit

Statut et staging

Git utilise un modèle en deux étapes : les modifications vont dans la zone de staging (index) avant d'être commitées. git status montre ce qui est modifié, stagé et non suivi. git add -p vous permet de stager des morceaux individuels d'un patch — inestimable pour diviser une arborescence de travail en désordre en commits ciblés et logiques. Utilisez git restore --staged (la commande moderne) pour unstage sans perdre vos modifications.

git
# see current state of working tree
git status
git status -s              # short format
git status -sb             # short + branch info

# stage changes
git add file.txt           # one file
git add src/               # a directory
git add .                  # all changes in repo
git add -p                 # stage interactively by hunk

# unstage a file (keep changes in working dir)
git restore --staged file.txt
git reset HEAD file.txt    # older syntax

Committer des modifications

Un commit enregistre un instantané des modifications stagées. Écrivez les messages à l'impératif (« add feature » et non « added feature »). L'option -a ne stage que les fichiers déjà suivis — les nouveaux fichiers ont toujours besoin de git add. --amend réécrit le dernier commit ; utilisez-le pour corriger une faute de frappe ou ajouter un fichier oublié, mais n'amendez jamais des commits que vous avez déjà poussés sur une branche partagée.

git
# commit staged changes with a message
git commit -m "feat: add login form"

# multi-line commit message
git commit -m "feat: add login form" -m "Closes #42"

# stage tracked files AND commit in one step
git commit -am "fix: correct typo"

# amend the previous commit (keep message)
git commit --amend --no-edit

# amend and edit the message
git commit --amend -m "feat: add login form (v2)"

Conventions de messages de commit

Conventional Commits est une spécification largement adoptée pour les messages de commit structurés. Le préfixe de type (feat, fix, docs, etc.) permet la génération automatique de changelog et le versionnage sémantique. Le marqueur ! indique un changement cassant. Une ligne vide sépare le sujet (≤50 caractères) du corps, et une autre ligne vide sépare les pieds de page comme « Closes #123 » qui ferment automatiquement les issues sur GitHub.

git
# Conventional Commits format
feat: add user registration endpoint
fix: resolve crash on empty cart
docs: update API reference
style: format login component
refactor: extract validation logic
test: add unit tests for parser
chore: upgrade dependencies

# with scope and breaking change marker
feat(api): add pagination to list endpoint
fix!: change default port (BREAKING CHANGE)

# with body and footer
feat: add dark mode

Implement theme toggle using CSS variables.
Closes #128

Visualiser l'historique avec log

git log est l'outil principal pour explorer l'historique. --oneline --graph --all est la combinaison la plus utile pour comprendre la structure des branches d'un coup d'œil. Vous pouvez filtrer par auteur, plage de dates ou contenu de message avec --grep. --stat montre quels fichiers ont changé et combien de lignes, tandis que --patch montre le diff complet de chaque commit.

git
# full history with details
git log
git log --oneline                 # one line per commit
git log --oneline --graph --all   # visual branch graph
git log -n 5                      # last 5 commits

# filter by author, date, or message
git log --author="Alice"
git log --since="2 weeks ago"
git log --grep="fix"

# show files changed in each commit
git log --stat
git log --patch                   # full diffs

Diff et show

git diff compare des instantanés — sans arguments, il montre les modifications non stagées par rapport à l'index. --staged compare l'index à HEAD. git show affiche les métadonnées et le patch d'un seul commit. La syntaxe HEAD:path vous permet de visualiser le contenu de n'importe quel fichier à n'importe quel commit sans le checker, ce qui est pratique pour récupérer d'anciennes versions ou inspecter l'historique.

git
# compare working tree, index, and commits
git diff                  # unstaged changes
git diff --staged         # staged but uncommitted
git diff HEAD             # all changes vs last commit
git diff main feature     # compare two branches
git diff abc1234 def5678  # compare two commits

# inspect a single commit
git show HEAD             # latest commit diff + metadata
git show abc1234
git show HEAD:file.txt    # view a file at a given commit
03

Branching et merging

Créer et changer de branche

Les branches dans Git sont des pointeurs légers vers un commit — en créer une est presque instantané. git switch (Git 2.23+) est l'alternative moderne et plus sûre à checkout pour changer de branche, réservant checkout à la restauration de fichiers. -d refuse de supprimer une branche non fusionnée (protégeant votre travail) ; -D force la suppression. Supprimez toujours les branches après fusion pour garder la liste des branches propre.

git
# list branches
git branch                 # local branches
git branch -a              # local + remote
git branch -vv             # with tracking info

# create and switch
git branch feature         # create only
git checkout feature       # switch to it
git checkout -b feature    # create + switch (classic)
git switch -c feature      # create + switch (modern)

# delete branches
git branch -d feature      # safe delete (merged only)
git branch -D feature      # force delete

Fusionner des branches

Une fusion en avance rapide (fast-forward) déplace simplement le pointeur de branche vers l'avant quand la cible n'a pas de nouveaux commits — produisant un historique linéaire. --no-ff force un commit de fusion, préservant le fait qu'une branche a existé (utile pour le suivi des fonctionnalités). --squash combine tous les commits de branche en une seule modification stagée que vous commitez ensuite une fois — idéal pour nettoyer un historique de fonctionnalité bruyant avant l'intégration dans main.

git
# merge feature into main
git checkout main
git merge feature

# fast-forward merge (default when possible)
#   main just moves forward to feature's commit

# create a merge commit (preserves branch history)
git merge --no-ff feature -m "Merge feature branch"

# squash all feature commits into one
git merge --squash feature
git commit -m "feat: add feature (squashed)"

# abort a merge with conflicts
git merge --abort

Résoudre les conflits de fusion

Les conflits surviennent quand les mêmes lignes sont modifiées différemment sur deux branches. Git insère des marqueurs de conflit (<<<<<<<, =======, >>>>>>>) montrant les deux côtés. Résolvez en éditant le fichier à l'état final souhaité, puis git add pour marquer comme résolu. Committez pour terminer la fusion. git mergetool lance un outil de diff visuel. Si vous êtes submergé, git merge --abort revient à l'état pré-fusion.

git
# a conflict produces markers in the file
# <<<<<<< HEAD
# my changes (current branch)
# =======
# their changes (incoming branch)
# >>>>>>> feature

# edit the file to resolve, then:
git add resolved-file.txt
git commit                # finalize the merge

# use a merge tool
git mergetool

# see which files conflict
git status

# abandon the merge
git merge --abort

Rebasing

Rebase rejoue les commits de votre branche au-dessus d'une autre branche, produisant un historique linéaire sans commits de fusion. Le rebase interactif (-i) est un outil puissant : squash fusionne les commits, reword édite les messages, drop supprime des commits, edit met en pause pour vous laisser modifier un commit. NE JAMAIS rebaser des commits qui ont été poussés et partagés — cela réécrit l'historique et casse les dépôts des coéquipiers. Utilisez rebase uniquement sur vos propres branches locales.

git
# rebase feature onto main (replay feature commits)
git checkout feature
git rebase main

# interactive rebase — rewrite last 5 commits
git rebase -i HEAD~5
# options: pick, reword, squash, fixup, drop, edit

# continue, skip, or abort during a rebase
git rebase --continue
git rebase --skip
git rebase --abort

# rebase while pulling
git pull --rebase origin main

Cherry-picking et reflog

Cherry-pick applique un commit individuel d'une autre branche sur votre branche courante — utile pour rétroporter un bugfix sans fusionner toute la branche. Le reflog est un journal local de chaque mouvement de HEAD (commits, checkouts, resets) conservé ~90 jours. C'est votre filet de sécurité : même après un reset destructif, vous pouvez trouver l'ancien hash de commit dans le reflog et le récupérer. Les données du reflog sont locales uniquement et jamais poussées.

git
# apply a specific commit onto current branch
git cherry-pick abc1234
git cherry-pick abc1234 def5678   # multiple commits
git cherry-pick abc1234..def5678  # range (exclusive start)

# reflog: record of where HEAD has been
git reflog
git reflog show feature

# recover a 'lost' commit
git reset --hard HEAD@{2}

# view a branch's history of moves
git reflog show main
04

Dépôts distants

Gérer les distants

Un distant est une référence nommée vers un autre dépôt, typiquement origin pour votre fork et upstream pour le projet original. git remote -v montre les URLs de fetch et push. Les forks sur GitHub utilisent le distant upstream pour se synchroniser avec l'original : fetch depuis upstream, merge ou rebase, puis push vers origin. set-url est pratique pour basculer entre l'authentification HTTPS et SSH.

git
# list configured remotes
git remote -v
git remote show origin

# add a remote
git remote add origin https://github.com/user/repo.git

# add an upstream remote (for forks)
git remote add upstream https://github.com/original/repo.git

# rename or remove
git remote rename origin upstream
git remote remove origin

# change a remote URL (e.g. HTTPS to SSH)
git remote set-url origin [email protected]:user/repo.git

Fetch, pull et push

fetch télécharge les données distantes mais laisse votre arborescence de travail intacte — sûr à inspecter avant de fusionner. pull = fetch + merge (ou rebase avec --rebase). Le premier push d'une nouvelle branche a besoin de -u pour configurer le suivi afin que les futurs git push/pull fonctionnent sans arguments. --force-with-lease est l'alternative sûre à --force : elle n'écrase le distant que si personne d'autre n'a poussé entre-temps, empêchant l'écrasement accidentel du travail des coéquipiers.

git
# download remote changes without merging
git fetch origin
git fetch --all --prune      # all remotes, drop deleted branches

# fetch + merge in one step
git pull
git pull --rebase            # rebase instead of merge

# push local commits to remote
git push origin main
git push -u origin feature   # push + set upstream tracking
git push                     # subsequent pushes (uses tracking)

# force push (rewrites remote history — DANGER)
git push --force-with-lease  # safer than --force

Suivi et synchronisation de branches

Les branches de suivi lient une branche locale à une branche distante afin que git pull et git push sachent où fetch/pusher sans le spécifier. -u (raccourci pour --set-upstream-to) le définit au premier push. Quand les coéquipiers suppriment des branches distantes, vos refs de suivi distant locales deviennent obsolètes — git remote prune origin les nettoie. git fetch --prune le fait automatiquement pendant le fetch.

git
# set upstream for current branch
git branch -u origin/main
git branch --set-upstream-to=origin/main

# create a local branch tracking a remote one
git checkout -b feature origin/feature
git switch feature           # auto-detects tracking branch

# delete a remote branch
git push origin --delete feature

# list all remote-tracking branches
git branch -r
git remote prune origin      # remove stale remote refs

Workflow des pull requests

Le flux GitHub standard : créez une branche de fonctionnalité, poussez-la, ouvrez une pull request pour révision, et supprimez la branche après fusion. Pour les forks, le distant upstream vous permet de tirer les modifications du dépôt original. Garder le main de votre fork synchronisé avec upstream régulièrement évite des fusions importantes et douloureuses plus tard. Beaucoup d'équipes activent « auto-delete branch on merge » pour garder la liste des branches ordonnée.

git
# typical feature workflow
git checkout -b feature/login
# ... make changes, commit ...
git push -u origin feature/login

# create PR on GitHub, then after merge:
git checkout main
git pull origin main
git branch -d feature/login

# sync a fork with its upstream
git fetch upstream
git checkout main
git merge upstream/main
git push origin main

Dépôts nus et miroirs

Un dépôt nu n'a pas d'arborescence de travail — il stocke uniquement les données .git. Les dépôts nus sont utilisés sur les serveurs (comme Git auto-hébergé) comme distant central vers lequel plusieurs personnes poussent et duquel elles tirent. --mirror clone tout y compris les refs de suivi distant et est utilisé pour les sauvegardes ou la migration d'un dépôt entre hôtes. --all pousse toutes les branches ; --tags pousse tous les tags.

git
# create a bare repo (no working tree) — for servers
git init --bare project.git

# mirror clone (full copy including all refs)
git clone --mirror https://github.com/user/repo.git

# push all branches and tags
git push --all
git push --tags

# push a specific branch to a specific remote
git push origin local-name:remote-name
05

Tags et releases

Tags légers et annotés

Les tags marquent des commits spécifiques, typiquement pour les releases. Les tags légers sont de simples pointeurs nommés ; les tags annotés sont des objets Git complets stockant le tagger, la date et le message — recommandés pour les releases car ils sont signés et immuables. Par convention, v1.0.0 suit le versionnage sémantique (majeur.mineur.correctif). Utilisez git show <tag> pour visualiser le commit taggé et l'annotation du tag.

git
# lightweight tag (just a pointer to a commit)
git tag v1.0.0

# annotated tag (stores metadata + message)
git tag -a v1.0.0 -m "Release 1.0.0"

# tag a specific commit
git tag -a v0.9.0 abc1234 -m "Old release"

# list and inspect tags
git tag
git tag -l "v1.*"
git show v1.0.0

Pousser et partager des tags

Les tags sont locaux jusqu'à être explicitement poussés — un piège fréquent. --follow-tags ne pousse que les tags annotés atteignables depuis les commits poussés, ce qui est le défaut le plus sûr pour les workflows de release. Checker un tag vous met dans l'état « detached HEAD » (pas sur une branche) — bien pour l'inspection, mais si vous voulez faire des modifications, créez d'abord une branche : git checkout -b fix/v1.0.1 v1.0.0.

git
# tags are NOT pushed by default
git push origin v1.0.0       # push one tag
git push origin --tags       # push all tags

# push tags + branches together
git push origin main --follow-tags

# delete a tag locally and remotely
git tag -d v1.0.0
git push origin --delete v1.0.0

# checkout code at a tag (detached HEAD)
git checkout v1.0.0

Tags signés (GPG)

Les tags et commits signés utilisent GPG (ou des clés SSH dans les versions récentes de Git) pour prouver cryptographiquement l'identité de l'auteur. Cela empêche l'usurpation d'identité — critique pour les releases open-source. Les distributeurs et utilisateurs peuvent vérifier un tag avec git tag -v. GitHub affiche un badge « Verified » sur les commits et tags signés. Définissez tag.gpgsign=true pour toujours signer les tags automatiquement.

git
# sign a tag with your GPG key
git tag -s v1.0.0 -m "Signed release 1.0.0"

# verify a signed tag
git tag -v v1.0.0

# configure signing key
git config --global user.signingkey ABCD1234
git config --global tag.gpgsign true   # sign all tags

# also sign commits
git commit -S -m "signed commit"
git config --global commit.gpgsign true

Versionnage sémantique

Le versionnage sémantique (SemVer) donne du sens aux numéros de version : MAJEUR pour les changements d'API incompatibles, MINEUR pour les nouvelles fonctionnalités rétrocompatibles, CORRECTIF pour les corrections de bugs. Les suffixes de pré-release (-alpha, -beta, -rc) signalent la stabilité. Suivre SemVer permet aux utilisateurs de votre bibliothèque de savoir si une mise à jour est sûre — des outils automatisés peuvent analyser et comparer les chaînes SemVer pour détecter les changements cassants.

git
# semantic versioning: MAJOR.MINOR.PATCH
v1.0.0   # initial stable release
v1.1.0   # new backward-compatible feature  -> MINOR
v1.1.1   # backward-compatible bug fix       -> PATCH
v2.0.0   # breaking change                   -> MAJOR

# pre-release tags
v2.0.0-alpha.1
v2.0.0-beta.1
v2.0.0-rc.1

# git tag with the version
git tag -a v1.2.0 -m "Add export feature"
git push origin v1.2.0

Describe et changelog

git describe trouve le tag le plus récent atteignable depuis un commit et indique combien de commits vous êtes en avance — parfait pour générer des chaînes de version de build comme v1.2.0-3-gabc1234. La syntaxe de plage de log v1.1.0..v1.2.0 montre les commits dans v1.2.0 mais pas dans v1.1.0, ce qui est exactement ce dont vous avez besoin pour compiler les notes de release ou un changelog entre deux releases.

git
# describe the closest tag (great for versioning)
git describe --tags
# output: v1.2.0-3-gabc1234
# meaning: 3 commits after v1.2.0, commit hash abc1234

# use it for build versioning
VERSION=$(git describe --tags --always)
echo "Building $VERSION"

# generate a changelog between two tags
git log v1.1.0..v1.2.0 --oneline
git log v1.1.0..v1.2.0 --pretty=format:"- %s" > CHANGELOG.md
06

Annuler des modifications

Reset : soft, mixed, hard

reset déplace le pointeur de branche courant. --soft garde vos modifications stagées (seul le commit est annulé) — idéal pour re-committer. --mixed (par défaut) unstage mais conserve les modifications de l'arborescence de travail. --hard jette tout définitivement — votre seule récupération est le reflog. N'utilisez jamais --hard sur des commits que vous avez poussés, car cela réécrit l'historique partagé et provoque une divergence.

git
# reset moves HEAD and optionally the index/worktree
git reset --soft HEAD~1     # undo commit, keep staged
git reset --mixed HEAD~1    # undo commit + unstage (DEFAULT)
git reset --hard HEAD~1     # undo commit + discard changes

# reset to a specific commit
git reset --hard abc1234

# reset a single file to HEAD
git reset HEAD file.txt     # unstage
git checkout -- file.txt    # discard working changes

Revert (annulation sûre)

Contrairement à reset (qui réécrit l'historique), revert ajoute un nouveau commit qui inverse le commit cible — sûr pour les branches partagées car l'historique est préservé. C'est la bonne façon d'annuler un changement déjà poussé. Revenir un commit de fusion nécessite -m 1 pour spécifier quelle ligne parente conserver (1 = la branche dans laquelle vous avez fusionné).

git
# revert creates a NEW commit that undoes another
git revert abc1234
git revert HEAD             # undo last commit

# revert a range
git revert HEAD~3..HEAD

# revert without committing (stage the inverse)
git revert --no-commit abc1234

# revert a merge commit
git revert -m 1 abc1234     # -m 1 = keep main branch parent

Restore et clean

git restore (Git 2.23+) est la commande moderne et ciblée pour les opérations sur l'arborescence de travail, séparant les préoccupations de checkout. --staged unstage sans toucher aux modifications de travail. git clean supprime les fichiers non suivis — exécutez toujours avec -n (dry run) d'abord pour prévisualiser ce qui sera supprimé. -x est agressif : il supprime aussi les fichiers gitignored, ce qui est utile pour un build propre mais peut effacer des secrets ou des sorties de build.

git
# discard unstaged changes in a file
git restore file.txt
git checkout -- file.txt    # older syntax

# unstage a file (keep working changes)
git restore --staged file.txt

# restore a file from a specific commit
git restore --source=abc1234 file.txt

# remove untracked files
git clean -n                # dry run (preview)
git clean -fd               # remove untracked files + dirs
git clean -fdx              # also remove ignored files

Amend et fixup

--amend réécrit le dernier commit — pratique pour corriger une faute de frappe ou ajouter un fichier oublié, mais n'amendez jamais des commits poussés. Le workflow fixup est élégant : créez des commits fixup au fur et à mesure que vous remarquez de petits problèmes, puis git rebase -i --autosquash réordonne et squash automatiquement les dans leurs commits cibles. Cela garde l'historique propre tout en vous laissant committer de petites corrections incrémentalement.

git
# amend the last commit (message + content)
git add forgotten-file.txt
git commit --amend --no-edit

# change only the commit message
git commit --amend -m "better message"

# create a fixup commit (for later autosquash)
git commit --fixup=abc1234

# squash fixups during interactive rebase
git rebase -i --autosquash abc1234~1

Récupération via reflog

Le reflog est votre filet de sécurité. Il enregistre chaque commit, checkout, reset et rebase — même les opérations qui « détruisent » des commits. Les entrées persistent ~90 jours. Si vous faites accidentellement reset --hard ou supprimez une branche, trouvez le hash du commit orphelin dans le reflog et reset vers lui ou créez une nouvelle branche pointant vers lui. Le reflog est local uniquement, donc cela fonctionne même hors ligne.

git
# reflog records every HEAD movement
git reflog
# abc1234 HEAD@{0}: reset: moving to abc1234
# def5678 HEAD@{1}: commit: feat: add x
# ghi9012 HEAD@{2}: checkout: moving to feature

# recover a 'lost' commit
git reset --hard def5678

# recover a deleted branch
git branch recovered-feature def5678

# reflog for a specific branch
git reflog show feature
07

Stashing et workflows

Stasher des modifications

Stash met de côté les modifications non commitées pour que vous puissiez changer de branche ou tirer des mises à jour avec une arborescence propre. apply garde le stash dans la liste (utile si vous voulez l'appliquer à plusieurs branches) ; pop applique et le supprime. Les stashes sont une pile LIFO référencée par stash@{N}. Utilisez clear avec prudence — il jette définitivement tous les stashes.

git
# save uncommitted changes (reverts working tree to HEAD)
git stash
git stash push -m "wip: login form"   # with a message

# list, show, apply
git stash list
git stash show -p stash@{0}   # show diff
git stash apply               # apply latest, keep stash
git stash pop                 # apply latest, drop stash

# apply a specific stash
git stash apply stash@{2}

# drop a stash
git stash drop stash@{0}
git stash clear               # drop ALL stashes

Stash partiel et sélectif

Ces options donnent un contrôle fin sur ce qui est stashé. --keep-index est utile quand vous avez stagé un commit logique mais voulez tester uniquement ces modifications — stashez le reste, lancez les tests, puis pop. -u inclut les fichiers non suivis (sinon ils restent dans votre arborescence de travail). -p vous permet de choisir des morceaux spécifiques, reflétant git add -p.

git
# stash only staged changes
git stash --staged

# stash only unstaged changes (keep staged)
git stash --keep-index

# stash interactively by hunk
git stash -p

# stash including untracked files
git stash -u
git stash --include-untracked

# stash everything (even ignored)
git stash -a

Branches de stash et création

git stash branch crée une nouvelle branche au commit parent original du stash et y applique le stash — parfait quand un stash ne s'applique plus proprement à la branche courante à cause de conflits. Vous pouvez aussi exporter un stash comme fichier patch avec show -p pour l'archivage ou le partage. Les stashes sont locaux et jamais poussés, donc ils ne devraient pas être utilisés pour le stockage à long terme.

git
# create a branch from a stash (great for conflicts)
git stash branch feature/wip stash@{0}

# create a commit from a stash without applying
git stash store -m "saved wip" stash@{0}

# apply a stash to a different branch
git checkout other-branch
git stash apply stash@{0}

# inspect stash contents
git stash show stash@{0} --stat
git stash show -p stash@{0} > patch.diff

Modèles de workflow Git

GitHub Flow est le plus simple : une branche main, des branches de fonctionnalité avec PR, déploiement à la fusion — idéal pour le déploiement continu. Git Flow (le modèle de Vincent Driessen) ajoute des branches develop, release et hotfix pour une gestion structurée des releases — adapté aux produits versionnés. Le Trunk-Based Development utilise des branches très éphémères et est privilégié par les équipes DevOps haute performance pour une vitesse d'intégration maximale.

git
# GitHub Flow (simple, popular)
# main is always deployable; feature branches + PRs
git checkout -b feature/x
git push -u origin feature/x
# open PR, review, merge to main, deploy

# Git Flow (structured, with release branches)
# main: production releases
# develop: integration branch
# feature/*: features off develop
# release/*: release prep
# hotfix/*: urgent fixes off main

# Trunk-Based Development
# short-lived branches (1-2 days), frequent rebase on main

Sous-modules

Les sous-modules intègrent un dépôt Git dans un autre — utiles pour partager une bibliothèque entre projets tout en la gardant versionnée indépendamment. Le dépôt parent stocke un pointeur vers un commit spécifique du sous-module. Les clones ne récupèrent pas le contenu des sous-modules par défaut ; --recurse-submodules le fait en une étape. Les sous-modules peuvent être délicats ; pour une gestion de dépendances plus simple, envisagez les Git subtrees ou un gestionnaire de paquets.

git
# add another repo as a submodule
git submodule add https://github.com/user/lib.git libs/lib

# clone a repo with its submodules
git clone --recurse-submodules https://github.com/user/repo.git

# initialize submodules in an existing clone
git submodule update --init --recursive

# update submodules to their latest remote commit
git submodule update --remote

# record a submodule pointer change
git add libs/lib
git commit -m "chore: bump lib submodule"
08

Inspection et débogage

Blame et annotate

git blame (aussi appelé annotate) montre le commit et l'auteur pour chaque ligne d'un fichier — essentiel pour comprendre pourquoi le code a l'air tel qu'il est. -L restreint à une plage de lignes, utile pour les grands fichiers. -w ignore les modifications de pur espace blanc, et -C détecte le code déplacé ou copié depuis un autre fichier, vous donnant un historique plus précis de l'origine réelle des lignes.

git
# show who last changed each line
git blame file.txt
git blame -L 10,20 file.txt        # only lines 10-20
git blame -e file.txt              # show author email
git blame -w file.txt              # ignore whitespace changes
git blame -C file.txt              # detect moved lines across files

# GUI annotation in some editors
git gui blame file.txt

Bisect (recherche binaire de bugs)

bisect effectue une recherche binaire dans l'historique pour localiser le commit exact qui a introduit un bug. Vous marquez un commit connu bon et un connu mauvais ; Git check le point milieu, vous testez, puis marquez bon ou mauvais, divisant la plage à chaque fois. Avec un script, tout le processus est entièrement automatisé — un énorme gain de temps pour les régressions dans les grands historiques.

git
# find the commit that introduced a bug
git bisect start
git bisect bad                 # current commit is broken
git bisect good v1.0.0         # v1.0.0 was working

# Git checks out a midpoint; test it, then:
git bisect good                # or git bisect bad

# automate with a script that exits non-zero on bug
git bisect start HEAD v1.0.0 -- npm test

# finish and return to original branch
git bisect reset

# view the bisect log
git bisect log

Recherche dans le code et l'historique

git grep cherche les fichiers suivis dans l'arborescence de travail — plus rapide que grep -r car il utilise l'index. L'option -S « pickaxe » trouve les commits qui ont ajouté ou supprimé une chaîne spécifique, inestimable pour tracer quand une fonction ou un bug a été introduit. -G est similaire mais correspond à une regex n'importe où dans le diff. Combiné avec les filtres --author et --since, vous pouvez localiser n'importe quel changement dans l'historique.

git
# search working tree for a string
git grep "TODO"
git grep -n "TODO" -- "*.js"
git grep -i "error" src/

# search across all commits (history)
git log -S "functionName" --oneline    # pickaxe: when added/removed
git log -G "functionName.*\(" --oneline  # regex match in diffs

# search commit messages
git log --grep="fix.*login" --oneline
git log --author="Alice" --since="1 week ago"

FSCK et objets pendantes

git fsck (vérification du système de fichiers) vérifie l'intégrité de la base de données d'objets et peut trouver des commits pendantes — utile pour la récupération quand le reflog est insuffisant. git gc réorganise et compresse les objets pour économiser de l'espace disque ; --prune=now supprime immédiatement les objets inaccessibles. Git auto-gc périodiquement, mais l'exécuter manuellement peut réduire un grand dépôt. --aggressive recalcule les deltas pour une compression maximale.

git
# check repository integrity
git fsck --full
git fsck --unreachable

# find dangling (unreachable) commits & blobs
git fsck --lost-found

# garbage collect and prune old objects
git gc
git gc --prune=now
git gc --aggressive

# count objects and disk usage
git count-objects -v

Archive et bundle

git archive exporte un instantané propre d'un commit sans le répertoire .git — idéal pour distribuer des releases ou envoyer du code source à quelqu'un qui n'a pas besoin de l'historique. git bundle empaquette un dépôt (ou une plage de commits) dans un seul fichier qui peut être cloné ou tiré — parfait pour transférer des dépôts à travers des réseaux air-gappés ou par email quand vous ne pouvez pas utiliser un serveur distant.

git
# export a snapshot as a tar/zip (no .git history)
git archive --format=zip --output=app.zip HEAD
git archive --format=tar HEAD | gzip > app.tar.gz
git archive -o release.zip v1.0.0

# bundle a repo (with history) into a single file
git bundle create repo.bundle --all
git bundle create patch.bundle main..feature

# clone from a bundle (offline transfer)
git clone repo.bundle new-repo
git fetch patch.bundle feature
09

Techniques avancées

Hooks

Les hooks sont des scripts qui s'exécutent automatiquement à des points spécifiques. Les hooks côté client (pre-commit, commit-msg, pre-push) appliquent une politique locale comme le linting ou les tests. Les hooks côté serveur (pre-receive, post-receive) s'exécutent sur le distant et peuvent appliquer la protection de branche ou déclencher CI/CD. Le répertoire .git/hooks contient des scripts d'exemple finissant en .sample — renommez pour activer. Des outils comme Husky ou le framework pre-commit gèrent les hooks dans le dépôt lui-même pour la cohérence d'équipe.

git
# client-side hooks live in .git/hooks/
#   pre-commit    runs before a commit is created
#   commit-msg    validates the commit message
#   pre-push      runs before pushing to remote

# sample pre-commit hook (.git/hooks/pre-commit)
#!/bin/sh
npm run lint || exit 1
npm test || exit 1

# server-side hooks (on the remote)
#   pre-receive, update, post-receive

# make a hook executable
chmod +x .git/hooks/pre-commit

Worktrees

Les worktrees vous permettent d'avoir plusieurs arborescences de travail pour un seul dépôt, chacune sur une branche différente — sans cloner. C'est inestimable quand vous devez travailler sur un hotfix tout en gardant l'arborescence de travail de votre branche de fonctionnalité intacte, ou lancer un long build sur une branche tout en éditant une autre. Tous les worktrees partagent la même base de données d'objets .git, donc l'utilisation disque est minimale.

git
# create a second working tree of the same repo
git worktree add ../app-feature feature/x

# list worktrees
git worktree list

# create a worktree with a new branch
git worktree add -b hotfix/y ../app-hotfix main

# remove a worktree
git worktree remove ../app-feature

# prune stale worktree metadata
git worktree prune

Filtrer et réécrire l'historique

Réécrire l'historique est nécessaire quand des secrets ou de gros fichiers ont été commités. git filter-repo est le remplaçant moderne et rapide de git filter-branch. BFG Repo-Cleaner est une alternative conviviale pour supprimer de gros fichiers ou des motifs de mot de passe. Après réécriture, vous devez force-pusher et notifier tous les collaborateurs de re-cloner — les anciens commits restent dans leurs reflogs jusqu'à expiration. Rottez toujours immédiatement tout secret divulgué.

git
# remove a file from ALL of history (sensitive data)
git filter-repo --path secrets.env --invert-paths
# (install git-filter-repo; the old filter-branch is deprecated)

# rewrite author info across all commits
git filter-repo --mailmap mailmap.txt

# split a subdirectory into its own repo
git filter-repo --subdirectory-filter libs/lib

# the simpler BFG Repo-Cleaner (Java tool)
bfg --delete-files *.env
bfg --replace-text passwords.txt

Sparse checkout et partial clone

Le clone partiel (--filter) récupère les commits et arbres mais télécharge les blobs (contenus de fichiers) paresseusement à mesure que vous y accédez — accélérant considérablement les clones de très grands dépôts. Le sparse checkout limite votre arborescence de travail à des répertoires spécifiques, donc vous ne voyez que les parties sur lesquelles vous travaillez. Ensemble, ils rendent les énormes monorepos gérables : le clone est rapide et votre arborescence de travail reste petite.

git
# partial clone: download history on demand
git clone --filter=blob:none https://github.com/user/huge-repo.git

# sparse checkout: only certain directories
git clone --no-checkout https://github.com/user/repo.git
cd repo
git sparse-checkout init --cone
git sparse-checkout set src docs
git checkout main

# add a directory to the sparse set later
git sparse-checkout add tests

# disable sparse checkout (fetch everything)
git sparse-checkout disable

Reflog, refspec et notes

Les refspeccs donnent un contrôle explicite sur la façon dont les refs sont mappées pendant fetch/push — utiles pour les workflows inhabituels comme pousser une branche de fonctionnalité locale vers le main distant. git notes attache des métadonnées à un commit sans le réécrire, ce qui est pratique pour les commentaires de revue ou les liens CI. Les notes sont stockées dans une ref séparée (refs/notes/commits) et doivent être explicitement poussées et tirées.

git
# refspec: explicit fetch/push mapping
git fetch origin "refs/heads/*:refs/remotes/origin/*"
git push origin "refs/heads/feature:refs/heads/main"

# add a note to a commit (without changing it)
git notes add -m "Reviewed by Alice" abc1234
git notes show abc1234
git log --show-notes

# configure where notes are stored
git config --global notes.displayRef "refs/notes/*"

# push notes to a remote
git push origin refs/notes/commits
10

Bonnes pratiques et astuces

Hygiène des commits

Les bons commits sont petits, atomiques et autonomes : un changement logique par commit. Cela facilite la revue de code, accélère le bisect et rend les reverts chirurgicaux. Utilisez git add -p pour ne stager que les morceaux pertinents à une seule préoccupation. Les messages à l'impératif (« add » et non « added ») se lisent comme des instructions au codebase. Rebasez les branches de fonctionnalité sur le dernier main avant de fusionner pour garder l'historique linéaire et intelligible.

git
# commit small, focused, logical units
git add -p               # stage only relevant hunks
git commit -m "fix: handle null user in login"

# write clear, imperative messages
# GOOD: "feat: add password reset endpoint"
# BAD:  "stuff", "fix", "WIP", "asdf"

# one concern per commit
git add login.ts && git commit -m "feat: add login"
git add styles.css && git commit -m "style: format login"

# rebase before merging to keep history clean
git fetch && git rebase origin/main

Protection de branche et revue de code

Les règles de protection de branche empêchent les force-push, exigent des revues de PR et conditionnent les fusions au succès de la CI — essentielles pour la sécurité d'équipe. Exiger un historique linéaire force le rebase ou les fusions squash, gardant l'historique lisible. Les commits signés (GPG ou SSH) prouvent l'auteur et empêchent l'usurpation. Ces paramètres sont configurés sur la plateforme d'hébergement (GitHub/GitLab), pas dans Git lui-même, mais ils appliquent une bonne hygiène Git à l'échelle de l'organisation.

git
# on GitHub/GitLab: protect the main branch
# - require pull request reviews
# - require status checks (CI) to pass
# - require linear history (no merge commits)
# - dismiss stale reviews on push
# - require signed commits

# enforce via config (GitHub CLI)
gh api repos/:owner/:repo/branches/main/protection \
  -X PUT -f required_status_checks[strict]=true

# sign commits and verify on receive
git config --global commit.gpgsign true
git config --global gpg.format ssh

Ignorance et gestion des secrets

Les secrets commités sont l'incident de sécurité Git le plus courant. Une fois poussés, supposez que le secret est compromis — rottez-le immédiatement, même après l'avoir retiré de l'historique, car les clones et forks le conservent. La prévention est la meilleure : .gitignore les secrets, utilisez des variables d'environnement ou un gestionnaire de secrets (Vault, AWS Secrets Manager), et installez git-secrets ou un hook pre-commit qui scanne les clés API et mots de passe avant chaque commit.

git
# never commit secrets — use .gitignore
echo ".env" >> .gitignore
echo "*.pem" >> .gitignore
echo "secrets/" >> .gitignore

# if you accidentally committed a secret:
# 1. rotate/revoke the secret immediately
# 2. remove from history
git filter-repo --path .env --invert-paths
git push --force-with-lease

# use environment files or secret managers
echo "API_KEY=$API_KEY" > .env.local   # gitignored
# load via dotenv in your app

# use git-secrets to scan pre-commit
git secrets --install
git secrets --register-aws

Astuces de performance

Les grands dépôts peuvent être lents. fsmonitor et untrackedcache rendent git status beaucoup plus rapide en mettant en cache l'état du système de fichiers. Les clones superficiels et partiels réduisent le téléchargement initial. Exécutez git gc périodiquement pour compacter les objets. --no-verify saute les hooks pre-commit et commit-msg — utile pour les urgences mais ne devrait pas devenir une habitude, car il contourne les vérifications de qualité de votre équipe.

git
# speed up status on large repos
git config --global core.fsmonitor true
git config --global core.untrackedcache true

# shallow clone for CI
git clone --depth 1 https://github.com/user/repo.git

# partial clone for huge monorepos
git clone --filter=blob:none https://github.com/user/monorepo.git

# periodic garbage collection
git gc --prune=now

# disable slow hooks temporarily
git commit --no-verify

Pièges courants

Évitez ces erreurs classiques : ne jamais force-pusher sur des branches partagées (--force-with-lease est l'option sûre) ; ne jamais rebaser des commits sur lesquels d'autres ont pu baser leur travail ; ne jamais committer sur un detached HEAD sans créer d'abord une branche. Utilisez Git LFS pour les gros binaires — sinon ils gonflent l'historique définitivement. Configurez les fins de ligne (autocrlf) par OS pour éviter les diffs bruyants de pur espace blanc dans les équipes multiplateformes.

git
# PITFALL: force-pushing to shared branches
git push --force origin main   # DON'T — use --force-with-lease

# PITFALL: rebasing pushed commits
# rebase only your local, unpushed branches

# PITFALL: committing on detached HEAD
git checkout v1.0.0
# changes here are not on a branch — create one:
git checkout -b fix/v1.0.1

# PITFALL: large binary files bloating history
# use Git LFS for binaries
git lfs install
git lfs track "*.psd"
git add .gitattributes

# PITFALL: CRLF/LF line endings across OS
git config --global core.autocrlf input   # on Linux/Mac
git config --global core.autocrlf true    # on Windows
11

Workflow Git Flow

Modèle de branches Git Flow

Git Flow est un modèle de branching strict pour les projets basés sur les releases. main contient toujours le code de production ; develop contient le travail d'intégration. Les fonctionnalités branchent depuis develop et fusionnent vers lui. Les releases branchent depuis develop, se stabilisent, puis fusionnent vers main (avec un tag) et develop. Les hotfixes branchent depuis main et fusionnent vers main et develop. Ce modèle convient aux projets avec des releases planifiées (applications desktop, logiciels on-premise). Pour le déploiement continu, GitHub Flow (main + branches de fonctionnalité) est plus simple. L'outil CLI git-flow automatise la danse des branches.

git
# Git Flow uses long-lived branches:
# main      → production-ready code
# develop   → latest delivered development changes
# feature/* → new features (branched from develop)
# release/* → release preparation (branched from develop)
# hotfix/*  → urgent production fixes (branched from main)

# Initialize git-flow (interactive setup)
git flow init

# Start a new feature
git flow feature start my-feature
# Creates feature/my-feature from develop

# Finish a feature (merges to develop, deletes branch)
git flow feature finish my-feature

# Publish a feature to remote
git flow feature publish my-feature

# Start a release
git flow release start 1.2.0
# Creates release/1.2.0 from develop

# Finish a release (merges to main AND develop, tags)
git flow release finish 1.2.0

GitHub Flow (plus simple)

GitHub Flow est le workflow le plus simple : main est toujours déployable, les branches de fonctionnalité sont éphémères, et tout fusionne via des pull requests. Il n'y a pas de branche develop ni de branches de release — main est déployé en continu. Cela fonctionne bien pour les applications web avec déploiement continu. La règle clé : ne jamais committer directement sur main ; utilisez toujours une PR pour la revue. Gardez les branches de fonctionnalité petites et éphémères (des jours, pas des semaines). Supprimez les branches après fusion pour garder le dépôt propre. Ce modèle privilégie la vitesse et la simplicité sur la gestion structurée des releases de Git Flow.

git
# GitHub Flow: only main + feature branches
# 1. Create branch from main
git checkout main
git pull origin main
git checkout -b feature/add-login

# 2. Commit changes
git add .
git commit -m "feat: add login page"

# 3. Push to remote
git push -u origin feature/add-login

# 4. Open Pull Request on GitHub
# 5. Review, discuss, get approval
# 6. Merge to main (via GitHub UI or CLI)
git checkout main
git pull origin main
git branch -d feature/add-login  # delete local branch

# 7. Deploy from main (continuous deployment)

Trunk-Based Development

Le Trunk-Based Development est le plus extrême : les développeurs commitent directement sur main (ou des branches très éphémères fusionnées dans les 24 heures). Cela permet une véritable intégration continue — tout le monde s'intègre constamment. Les fonctionnalités incomplètes utilisent des feature flags (déployées mais masquées) plutôt que des branches longues. Cela nécessite une CI/CD solide, des tests complets et une infrastructure de feature flags. Utilisé par Google, Facebook et Netflix. Avantages : pas d'enfer des fusions, retour rapide, petits changements. Défis : nécessite de la discipline, de la couverture de tests et la gestion des feature flags. Le mieux pour les équipes expérimentées avec une CI/CD robuste.

git
# Trunk-Based: everyone commits to main (trunk)
# Short-lived feature branches optional

# Direct commit to main (small teams)
git checkout main
git pull
# make changes
git add . && git commit -m "fix: typo in header"
git push

# With short-lived branches (larger teams)
git checkout -b quick-fix
git commit -m "fix: validation bug"
git push
# PR merged within 24 hours

# Feature flags enable incomplete code on main
# Code is deployed but hidden behind a flag
if (featureFlag.isEnabled("new-checkout")) {
  showNewCheckout();
} else {
  showOldCheckout();
}

Workflow Fork & Pull

Le workflow Fork & Pull est standard pour l'open source. Les contributeurs forkent le dépôt (créant leur propre copie), poussent des branches vers leur fork, et ouvrent des PR vers le dépôt original (upstream). Le distant upstream vous permet de synchroniser votre fork avec l'original. Créez toujours des branches de fonctionnalité depuis un main à jour. Ce workflow permet à quiconque de contribuer sans accès en écriture. Les mainteneurs revuent les PR et fusionnent. Pour garder votre fork synchronisé, fetch upstream et merge/rebase régulièrement. Certains projets utilisent un modèle « clone, branch, PR » pour les contributeurs internes avec accès direct en push sur les branches de fonctionnalité.

git
# For open-source projects (contributors don't have push access)

# 1. Fork the repo on GitHub (UI button)
# 2. Clone YOUR fork
git clone https://github.com/YOUR-USERNAME/project.git
cd project

# 3. Add upstream (original repo)
git remote add upstream https://github.com/ORIGINAL/project.git

# 4. Keep your fork updated
git fetch upstream
git checkout main
git merge upstream/main  # or: git rebase upstream/main
git push origin main

# 5. Create feature branch
git checkout -b feature/my-contribution

# 6. Push to YOUR fork
git push origin feature/my-contribution

# 7. Open Pull Request from your fork to upstream

Conventions de nommage des branches

Un nommage de branche cohérent améliore la clarté et permet l'automatisation. Préfixes courants : feature, bugfix, hotfix, release, chore, docs, refactor, experiment. Inclure des numéros de ticket (PROJ-123) lie les branches aux issues et permet l'auto-lien. Les barres obliques créent une hiérarchie visuelle dans les interfaces Git. Certaines équipes appliquent le nommage via des hooks Git ou des vérifications CI. La convention devrait être documentée dans CONTRIBUTING.md. Gardez les noms descriptifs mais concis. Évitez les noms personnels (johns-branch) — décrivez le travail, pas l'auteur. Un nommage cohérent rend le nettoyage des branches et la navigation dans l'historique beaucoup plus faciles.

git
# Common naming patterns:
feature/add-user-authentication
feature/PROJ-123-user-profile
bugfix/fix-login-redirect
bugfix/PROJ-456-crash-on-startup
hotfix/security-patch-xss
release/v2.0.0
chore/update-dependencies
docs/api-documentation
refactor/extract-auth-module
experiment/new-algorithm

# Using ticket numbers (Jira, Linear, etc.)
git checkout -b feature/PROJ-123-oauth-login

# Auto-linking in commit messages
git commit -m "PROJ-123: implement OAuth login"
# GitHub auto-links PROJ-123 to the Jira ticket

# Branch naming with slashes creates hierarchy
# in Git GUI tools (GitHub, GitKraken, SourceTree)
12

Plongée dans rebase

Rebase interactif

Le rebase interactif (-i) est l'outil d'édition d'historique le plus puissant. Il vous permet de réécrire, réordonner, combiner, diviser ou supprimer des commits avant de pousser. squash combine un commit avec son parent (fusionnant les messages) ; fixup fait de même mais jette le message du commit (nettoyer les commits « WIP »). edit met en pause le rebase pour que vous puissiez amender le commit (ajouter des fichiers, changer le contenu). reword vous permet de changer uniquement le message. drop supprime un commit. Rebasez toujours avant de pousser pour garder l'historique propre. Ne rebasez jamais des commits que d'autres ont déjà tirés — cela réécrit l'historique partagé.

git
# Rebase last 5 commits interactively
git rebase -i HEAD~5

# Opens editor with:
# pick a1b2c3d First commit
# pick e4f5g6h Second commit
# pick i7j8k9l Third commit
# pick m0n1o2p Fourth commit
# pick q3r4s5t Fifth commit

# Commands:
# pick   = use commit as-is
# reword = use commit, edit message
# edit   = pause to amend the commit
# squash = combine with previous commit
# fixup  = like squash, discard this message
# drop   = remove commit entirely
# exec   = run a shell command
# reorder = move lines to reorder commits

Squasher des commits

Squasher combine plusieurs commits en un, créant un historique propre. C'est idéal pour fusionner une branche de fonctionnalité : squasher 20 commits « WIP » en un seul commit significatif « feat: add login ». L'option --fixup crée un commit spécial que --autosquash place et squashe automatiquement avec sa cible — génial pour corriger des retours de revue sans encombrer l'historique. git reset --soft main suivi d'un seul commit squashe tout d'un coup (plus simple que le rebase interactif pour un squash total). Beaucoup d'équipes configurent les fusions de PR pour auto-squasher (l'option « Squash and merge » de GitHub).

git
# Squash the last 3 commits into one
git rebase -i HEAD~3

# In editor, change:
# pick a1b2c3d First
# squash e4f5g6h Second  (or 'fixup' to discard message)
# squash i7j8k9l Third

# Git prompts for combined commit message

# Squash everything since branching from main
git rebase -i main

# Auto-squash fixup commits
git commit --fixup a1b2c3d  # creates fixup! commit
git rebase -i --autosquash main
# Git automatically reorders and squashes fixup commits

# Squash all commits into one on a branch
git reset --soft main
git commit -m "feat: complete feature X"

Rebase vs merge

Merge préserve l'historique complet de la branche (avec des commits de fusion montrant où les fonctionnalités ont divergé et fusionné). Rebase rejoue les commits au-dessus de la cible, créant un historique linéaire sans commits de fusion. Merge est plus sûr (pas de réécriture d'historique) et montre le contexte de la fonctionnalité. Rebase est plus propre mais réécrit l'historique des commits. Stratégie courante : rebasez votre branche de fonctionnalité sur le dernier main avant de fusionner, puis fusionnez (fast-forward ou avec --no-ff pour un commit de fusion). Cela donne des commits propres ET un commit de fusion marquant la fonctionnalité. Pour les branches publiques/partagées, préférez merge pour éviter les conflits d'historique.

git
# MERGE: preserves complete history with merge commits
git checkout main
git merge feature
# Creates a merge commit, history looks like a graph:
# *   merge commit
# |\
# | * feature commit 2
# | * feature commit 1
# * main commit

# REBASE: linear history, no merge commits
git checkout feature
git rebase main
git checkout main
git merge feature  # fast-forward, no merge commit
# History is linear:
# * feature commit 2
# * feature commit 1
# * main commit

# When to use each:
# Merge: preserve context of feature branch, public repos
# Rebase: clean linear history, before pushing feature branch

Résoudre les conflits de rebase

Les conflits de rebase surviennent en rejouant les commits sur une base modifiée. Résolvez chaque conflit, git add les fichiers résolus, et git rebase --continue. Le rebase traite un commit à la fois, donc vous pouvez rencontrer plusieurs conflits. --skip jette un commit (utilisez s'il devient vide après le rebase). --abort annule tout et revient à l'état pré-rebase — toujours une issue de secours. Pour les conflits complexes, git mergetool lance un outil de fusion visuel (VS Code, Beyond Compare, etc.). La différence clé avec les conflits de fusion : le rebase peut nécessiter de résoudre le même conflit plusieurs fois (une fois par commit rejoué).

git
# During rebase, conflicts pause the process
git rebase main
# CONFLICT in file.txt

# 1. Resolve conflicts manually in the file
#    (look for <<<<<<< ======= >>>>>>> markers)

# 2. Stage resolved files
git add file.txt

# 3. Continue the rebase
git rebase --continue

# Skip a commit (if it's empty after rebase)
git rebase --skip

# Abort the entire rebase (return to original state)
git rebase --abort

# Use a merge tool for conflicts
git mergetool

# After resolving, the rebase continues
# with the next commit automatically

Rebase --onto (avancé)

git rebase --onto est une forme avancée pour une transplantation précise de commits. La syntaxe : rebase --onto NEW-BASE OLD-BASE BRANCH — il prend les commits entre OLD-BASE et BRANCH, et les rejoue sur NEW-BASE. C'est utile pour changer le point de base d'une branche (par ex., votre fonctionnalité était basée sur une autre fonctionnalité qui a été fusionnée ; rebasez sur main pour nettoyer). C'est aussi utilisé pour retirer des commits spécifiques de l'historique (rejouez autour d'eux). C'est une fonctionnalité pour utilisateurs avancés — comprenez d'abord le rebase normal. Ayez toujours une sauvegarde (reflog) avant une réécriture d'historique avancée.

git
# Scenario: feature branch was based on old-branch,
# but you want to rebase onto main instead

# git rebase --onto new-base old-base branch
git rebase --onto main old-branch feature

# This takes commits from old-branch..feature
# and replays them onto main

# Use case: remove commits from the middle
# Remove commit C from branch (replay A,B,D onto base)
#   base -> A -> B -> C -> D
# git rebase --onto base C D
# Result: base -> A -> B -> D'

# Use case: move a branch to a different parent
git rebase --onto main feature/sub-feature feature/main-feature
# Moves main-feature's unique commits from sub-feature to main
13

Cherry-pick et bisect

git cherry-pick

cherry-pick applique un commit spécifique d'une branche à une autre. Cas d'usage : appliquer un bugfix à une branche de release, copier un commit oublié de fusionner, ou porter sélectivement des fonctionnalités. Le commit obtient un nouveau hash (parent différent). Cherry-pick peut causer des conflits si la branche cible a divergé. --no-commit stage les modifications sans committer (utile pour combiner plusieurs cherry-picks). Évitez le cherry-pick excessif — il peut créer des commits en double quand les branches finissent par fusionner. Pour le rétroportage systématique, utilisez des branches de release avec des fusions à la place.

git
# Apply a specific commit to current branch
git cherry-pick a1b2c3d

# Cherry-pick multiple commits
git cherry-pick a1b2c3d e4f5g6h i7j8k9l

# Cherry-pick a range (exclusive start, inclusive end)
git cherry-pick A..E  # commits B, C, D, E

# Cherry-pick without committing (stage only)
git cherry-pick --no-commit a1b2c3d
# or: -n

# Cherry-pick and edit commit message
git cherry-pick --edit a1b2c3d

# Cherry-pick from another branch
git cherry-pick feature-branch~2

# If conflicts occur:
git add . && git cherry-pick --continue
git cherry-pick --abort  # cancel

git bisect (recherche binaire)

git bisect effectue une recherche binaire dans l'historique des commits pour trouver le commit exact qui a introduit un bug. Vous marquez l'état courant comme « bad » et un commit connu fonctionnel comme « good ». Git check le point milieu ; vous testez et marquez good/bad. Chaque étape divise l'espace de recherche par deux — trouver un bug dans 1000 commits prend ~10 étapes. bisect reset revient à votre branche originale. C'est inestimable pour traquer les régressions. Combinez avec git bisect log pour sauvegarder/restaurer des sessions bisect. Le commit coupable révèle souvent immédiatement la cause racine.

git
# Find which commit introduced a bug
git bisect start

# Mark current (bad) commit
git bisect bad

# Mark a known-good commit (last working version)
git bisect good v1.0.0
# or: git bisect good a1b2c3d

# Git checks out a middle commit
# Test it, then mark:
git bisect good  # if this commit works
git bisect bad   # if this commit is broken

# Repeat until Git identifies the culprit:
# "a1b2c3d is the first bad commit"

# When done:
git bisect reset  # return to original branch

# Skip a commit (can't test, e.g., doesn't build)
git bisect skip

Bisect automatisé

Le bisect automatisé exécute un script de test pour chaque commit, éliminant les tests manuels. Le script sort 0 (good), non-zéro (bad), ou 125 (skip — par ex., échec de build). Git marque automatiquement chaque commit et trouve le coupable. C'est extrêmement puissant avec une suite de tests : git bisect run npm test trouve le commit cassant en minutes. Vous pouvez aussi restreindre la recherche à des fichiers spécifiques (git bisect start -- path/to/file) pour accélérer. Le script peut être n'importe quoi : un test, une vérification de build, ou une commande curl vérifiant une API. Sauvegardez le log bisect pour reproduire ou reprendre plus tard.

git
# Auto-bisect with a test script
git bisect start
git bisect bad HEAD
git bisect good v1.0.0

# Git runs this script for each commit
# Exit 0 = good, exit 1 = bad, 125 = skip
git bisect run npm test

# Or a custom script
git bisect run ./scripts/check-bug.sh

# Example check script:
#!/bin/bash
npm run build || exit 125  # skip if build fails
npm test -- --grep "login bug"
# exit 0 if test passes (good), 1 if fails (bad)

# Git automatically finds the bad commit
# without manual testing

# Bisect with a file range (faster)
git bisect start -- src/auth/login.js

git blame et annotate

git blame montre l'auteur et le commit pour chaque ligne d'un fichier — essentiel pour comprendre pourquoi le code existe. -L restreint à une plage de lignes (plus rapide, plus ciblé). -w ignore les modifications de pur espace blanc (montre le véritable auteur du contenu). -M détecte le code déplacé dans le même fichier ; -C détecte le code copié depuis d'autres fichiers (montre l'auteur original, pas le copieur). blame est pour comprendre, pas blâmer — utilisez-le pour trouver le contexte du code, puis lisez le commit complet avec git show. Le bouton « Blame » de GitHub fournit une interface visuelle. Combinez avec git log -S pour trouver quand un texte spécifique a été ajouté.

git
# Show who last modified each line
git blame file.txt

# Blame specific line range
git blame -L 10,20 file.txt

# Show full commit hash (not abbreviated)
git blame -l file.txt

# Show email instead of name
git blame -e file.txt

# Ignore whitespace changes
git blame -w file.txt

# Detect moved lines within file
git blame -M file.txt

# Detect moved lines from other files
git blame -C file.txt

# Blame with commit message
git blame --show-name --show-email file.txt | head

git revert (annulation sûre)

git revert crée un nouveau commit qui annule un commit précédent — c'est la façon sûre d'annuler des modifications sur des branches partagées (contrairement à reset, qui réécrit l'historique). revert est idéal pour les branches de production où vous ne pouvez pas réécrire l'historique. Revenir un commit de fusion nécessite -m 1 (parent mainline) — cela annule la fusion tout en gardant l'historique de la branche. Revenir un revert ré-applique le changement original (courant quand un revert était erroné). Pour plusieurs commits, revenez dans l'ordre inverse (plus récent d'abord) pour minimiser les conflits. Utilisez toujours revert sur les branches partagées/publiques ; utilisez reset uniquement sur les branches locales.

git
# Revert a commit (creates a NEW commit that undoes it)
git revert a1b2c3d

# Revert multiple commits
git revert a1b2c3d e4f5g6h

# Revert a range
git revert A..E

# Revert without committing (stage the reversal)
git revert --no-commit a1b2c3d

# Revert a merge commit
git revert -m 1 a1b2c3d
# -m 1 specifies the mainline parent (parent 1 = the branch
# you merged INTO, parent 2 = the branch you merged FROM)

# If revert conflicts:
git add . && git revert --continue
git revert --abort
14

Reflog et récupération

Bases de git reflog

Le reflog enregistre chaque changement de HEAD et des pointeurs de branche — même les opérations qui « détruisent » des commits (reset --hard, rebase, suppression de branche). C'est votre filet de sécurité : les commits « perdus » sont récupérables via le reflog pendant ~90 jours (par défaut). Le reflog est local (jamais poussé) et montre l'historique chronologique des mouvements de pointeur. Pour récupérer, trouvez le hash du commit dans le reflog et checkout/reset vers lui. La syntaxe @{N} référence les entrées : HEAD@{0} est courant, HEAD@{1} est précédent. Si vous pensez avoir « perdu » du travail, vérifiez le reflog d'abord — il y est presque certainement encore.

git
# View the reflog (reference log)
git reflog
# Shows ALL reference changes, including:
# - commits, checkouts, resets, rebases, merges
# Example output:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: add feature
# i7j8k9l HEAD@{2}: checkout: moving from main to feature

# Reflog for a specific branch
git reflog show feature-branch

# Reflog with dates
git reflog --date=iso

# Recover a "lost" commit
git reflog
# Find the commit hash, then:
git checkout a1b2c3d  # or: git reset --hard a1b2c3d

Récupérer des branches supprimées

Les branches supprimées et les commits de reset sont récupérables via le reflog. Les objets commit existent toujours dans le magasin d'objets de Git jusqu'au garbage collection (par défaut : 90 jours pour les objets inaccessibles). Pour récupérer une branche supprimée, trouvez son commit tip dans le reflog et créez une nouvelle branche pointant vers lui. Pour les erreurs reset --hard, le reflog montre la position précédente de HEAD — reset vers elle. Pour les mauvais rebases, trouvez l'entrée du reflog avant le début du rebase et reset vers elle. La leçon clé : dans Git, presque rien n'est vraiment perdu immédiatement. Vérifiez toujours le reflog avant de paniquer.

git
# Accidentally deleted a branch?
git branch -D feature  # force deleted

# Find it in reflog
git reflog
# e4f5g6h HEAD@{2}: commit: work on feature

# Recreate the branch at that commit
git branch feature e4f5g6h

# Or checkout and create
git checkout -b feature e4f5g6h

# Recover after reset --hard
git reset --hard HEAD~3  # oops, went too far
git reflog
# Find the commit before the reset
git reset --hard HEAD@{1}  # undo the reset

# Recover after a bad rebase
git reflog
git reset --hard HEAD@{5}  # before the rebase started

git fsck (objets pendantes)

git fsck vérifie l'intégrité du dépôt et trouve les objets pendantes — commits, blobs et arbres non référencés par aucune branche ou tag. --lost-found les écrit dans .git/lost-found/. C'est le dernier recours quand le reflog n'a pas ce dont vous avez besoin (les entrées du reflog expirent, ou git gc a tourné). Les commits pendantes sont souvent le résultat d'opérations avortées ou d'entrées de reflog expirées. Inspectez avec git show, puis récupérez en créant une branche. fsck --full vérifie l'intégrité de tous les objets (utile pour détecter la corruption). Exécutez fsck périodiquement sur les dépôts importants pour détecter les problèmes tôt.

git
# Find dangling (unreachable) commits
git fsck --lost-found

# Output:
# dangling commit a1b2c3d...
# dangling blob e4f5g6h...

# Inspect a dangling commit
git show a1b2c3d

# Recover it
git branch recovered a1b2c3d

# Full fsck (check repository integrity)
git fsck --full

# Check for dangling objects only
git fsck --no-reflogs --dangling

# When reflog doesn't have what you need
# (e.g., expired, or gc ran), fsck finds
# ALL dangling objects in the object store

Plongée dans git stash

git stash met temporairement de côté les modifications non commitées. push -m ajoute un message descriptif (essentiel pour gérer plusieurs stashes). -u inclut les fichiers non suivis ; -a inclut aussi les fichiers ignorés. apply ré-applique sans supprimer ; pop applique et supprime. stash branch crée une nouvelle branche depuis le stash (utile si le stash conflite avec la branche courante). Les stashes sont stockés dans une pile (LIFO) — référencez par stash@{N}. show -p affiche le diff. Les stashes persistent à travers les redémarrages mais sont locaux (jamais poussés). Nettoyez les vieux stashes régulièrement ; ils s'accumulent. Pour un travail à long terme, créez une branche au lieu de stasher.

git
# Stash with message
git stash push -m "work in progress on login"

# Stash specific files
git stash push -m "wip" file1.txt file2.txt

# Stash including untracked files
git stash push -u  # or --include-untracked

# Stash including ignored files
git stash push -a  # or --all

# List stashes
git stash list
# stash@{0}: On feature: wip on login
# stash@{1}: On main: quick fix

# Apply a specific stash
git stash apply stash@{1}

# Apply and drop (pop)
git stash pop  # applies stash@{0} and removes it

# Show stash contents
git stash show -p stash@{0}

# Create branch from stash
git stash branch new-branch stash@{0}

# Drop a stash
git stash drop stash@{1}
git stash clear  # drop ALL stashes

Gestion des git tag

Les tags marquent des commits spécifiques comme importants (releases, jalons). Les tags annotés (-a) stockent des métadonnées (tagger, date, message) et sont recommandés pour les releases. Les tags légers sont de simples pointeurs nommés (pas de métadonnées). Les tags signés (-s) utilisent GPG pour la vérification (important pour les releases de sécurité). Les tags ne sont PAS poussés par défaut — utilisez --tags pour les pousser. Le versionnage sémantique (v1.2.3) est la convention de nommage standard. Checker un tag donne un état detached HEAD (pour inspecter ou builder une release). Les releases GitHub sont construites sur les tags — créez un tag, puis publiez une release avec des notes.

git
# Create annotated tag (recommended)
git tag -a v1.0.0 -m "Release 1.0.0"

# Create lightweight tag
git tag v1.0.0

# Tag a specific commit
git tag -a v0.9.0 -m "beta" a1b2c3d

# List all tags
git tag
git tag -l "v1.*"  # filter by pattern

# Push tags to remote
git push origin v1.0.0      # single tag
git push origin --tags       # all tags

# Delete a tag
git tag -d v1.0.0            # local
git push origin --delete v1.0.0  # remote

# Show tag details
git show v1.0.0

# Checkout a tag (detached HEAD)
git checkout v1.0.0

# Signed tags (GPG)
git tag -s v1.0.0 -m "signed release"
15

Worktree et sous-modules

git worktree

git worktree crée des arborescences de travail supplémentaires depuis le même dépôt — pas besoin de cloner. Chaque worktree check une branche différente simultanément. C'est parfait pour : travailler sur un hotfix tout en gardant votre branche de fonctionnalité ouverte, lancer des tests sur une branche tout en codant sur une autre, ou avoir des builds longs sur un worktree. Tous les worktrees partagent le même répertoire .git (objets, refs), donc ils restent synchronisés et économisent de l'espace disque. Vous ne pouvez pas checker la même branche dans deux worktrees (Git l'empêche pour éviter les conflits). Les worktrees sont plus rapides que le clonage pour les workflows multi-branches.

git
# Create a new working directory linked to the repo
git worktree add ../project-hotfix hotfix-branch

# Work in the new directory
cd ../project-hotfix
# This is a full working tree on hotfix-branch
# Shares the same .git, so no cloning needed

# List worktrees
git worktree list
# /path/to/project          main
# /path/to/project-hotfix   hotfix-branch

# Remove a worktree
git worktree remove ../project-hotfix

# Prune stale worktree entries
git worktree prune

# Create worktree at new branch
git worktree add -b feature-x ../project-feature main

Bases de git submodule

Les sous-modules intègrent un dépôt Git dans un autre — utiles pour inclure des bibliothèques partagées ou des dépendances. Le dépôt parent stocke un pointeur (hash de commit) vers le sous-module, pas son contenu. --recurse-submodules est essentiel au clonage (sinon les sous-modules sont vides). La mise à jour des sous-modules (--remote) récupère les derniers commits ; vous devez ensuite committer le nouveau hash dans le dépôt parent. Les sous-modules sont complexes : branches, conflits et mises à jour nécessitent une gestion soigneuse. Pour une gestion de dépendances plus simple, envisagez les Git subtrees, les gestionnaires de paquets (npm, pip) ou les stratégies monorepo. Utilisez les sous-modules quand vous devez suivre un commit externe spécifique.

git
# Add a submodule to your repo
git submodule add https://github.com/user/lib.git libs/lib

# Clone a repo with submodules
git clone --recurse-submodules https://github.com/user/project.git

# If you forgot --recurse-submodules:
git submodule update --init --recursive

# Pull latest changes in all submodules
git submodule update --remote

# Pull specific submodule
git submodule update --remote libs/lib

# After updating submodules, commit the new hash
git add libs/lib
git commit -m "update lib submodule"

# Execute command in all submodules
git submodule foreach 'git status'

Workflows de sous-modules

Travailler dans un sous-module est comme travailler dans un dépôt normal — vous commitez et poussez depuis le répertoire du sous-module. Le dépôt parent suit le hash du commit, donc après avoir modifié un sous-module, vous devez aussi committer dans le parent. Au changement de branche, le contenu du sous-module peut ne pas correspondre — exécutez git submodule update --init --recursive pour synchroniser. Supprimer des sous-modules nécessite trois étapes : deinit (désenregistrer), git rm (retirer du suivi), et suppression manuelle de .git/modules. Les workflows de sous-modules sont sujets aux erreurs ; communiquez toujours avec votre équipe lors de la mise à jour des sous-modules pour éviter les inadéquations de hash.

git
# Change code inside a submodule
cd libs/lib
# Make changes, commit, and push
git add .
git commit -m "fix: bug in lib"
git push origin HEAD:main

# Back in parent repo, update the pointer
cd ../..
git add libs/lib
git commit -m "chore: update lib submodule"

# Switch branches with submodules
git checkout main
git submodule update --init --recursive

# Delete a submodule
git submodule deinit -f libs/lib
git rm libs/lib
rm -rf .git/modules/libs/lib
git commit -m "remove lib submodule"

# Move a submodule
git mv libs/lib libs/new-lib

Hooks Git

Les hooks Git exécutent des scripts à des points spécifiques du cycle de vie Git. Les hooks côté client (pre-commit, pre-push, commit-msg) appliquent des standards locaux. pre-commit est idéal pour le linting/formatage ; pre-push pour lancer les tests ; commit-msg pour appliquer les commits conventionnels. Les hooks ne sont PAS suivis par Git (ils vivent dans .git/hooks/), donc ils ne se synchronisent pas entre les clones. Pour partager les hooks across une équipe, utilisez un outil comme Husky (npm), pre-commit (Python), ou commitez les hooks dans un répertoire vérifié et symlinkz-les. Les hooks côté serveur (pre-receive, post-receive) s'exécutent sur le distant et peuvent appliquer des politiques pour tous les contributeurs.

git
# Hooks live in .git/hooks/ (not tracked by Git)
# Sample hooks are provided with .sample extension

# pre-commit: runs before commit is created
# .git/hooks/pre-commit
#!/bin/bash
npm run lint || exit 1  # block commit if lint fails

# pre-push: runs before pushing
#!/bin/bash
npm test || exit 1  # block push if tests fail

# commit-msg: validate commit message format
#!/bin/bash
# $1 is the path to the commit message file
if ! grep -qE "^(feat|fix|docs|chore):" "$1"; then
  echo "Use conventional commit format: type: message"
  exit 1
fi

# prepare-commit-msg: auto-populate message
#!/bin/bash
echo "# Branch: $(git branch --show-current)" >> "$1"

# Make hooks executable
chmod +x .git/hooks/pre-commit

Git LFS (Large File Storage)

Git LFS remplace les gros fichiers (binaires, vidéos, datasets) par des pointeurs texte dans Git, stockant le contenu réel sur un serveur LFS séparé. Cela garde le dépôt léger — sans LFS, les fichiers binaires gonflent le dépôt définitivement (chaque version est stockée). Suivez les motifs de fichiers avec git lfs track, puis commitez .gitattributes. Après cela, les gros fichiers fonctionnent transparentement. git lfs migrate import convertit rétroactivement les fichiers existants en LFS (réécrit l'historique — coordonnez avec l'équipe d'abord). LFS nécessite un support serveur (GitHub, GitLab, Bitbucket le supportent tous). Note : LFS a des quotas de bande passante/stockage sur les plateformes hébergées. Pour les fichiers vraiment énormes, envisagez un stockage externe avec URLs.

git
# Initialize Git LFS
git lfs install

# Track large file types
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/**"

# This creates/updates .gitattributes
# Commit the .gitattributes file
git add .gitattributes
git commit -m "chore: configure LFS tracking"

# Add large files normally
git add design.psd
git commit -m "add design file"
git push

# View LFS tracked files
git lfs ls-files

# Pull LFS content (if not auto-downloaded)
git lfs pull

# Migrate existing files to LFS (rewrites history)
git lfs migrate import --include="*.psd" --everything

# Check LFS status
git lfs status
16

Stash

Save et pop

git stash met de côté les modifications non commitées pour que vous puissiez changer de branche ou tirer des mises à jour avec une arborescence de travail propre. pop applique et supprime le stash du sommet ; apply le garde. Le stash est une pile LIFO.

git
# Save uncommitted changes (tracked files only)
git stash

# Save including untracked files
git stash -u

# Pop the most recent stash (removes it)
git stash pop

# Apply without removing (keeps stash in list)
git stash apply

Stashes nommés

Passez toujours -m pour étiqueter les stashes — le message par défaut est la branche et le commit, rarement descriptif. stash@{N} référence un stash spécifique par son index.

git
# Save with a descriptive message
git stash push -m "WIP: refactor auth flow"

# List all stashes with messages
git stash list

# Apply a specific stash by index
git stash apply stash@{1}

Branches de stash

git stash branch crée une nouvelle branche depuis le commit où le stash a été originellement fait, puis y applique le stash. C'est la façon la plus propre de récupérer quand un stash ne s'applique plus proprement.

git
# Create a new branch from a stash
git stash branch feature-branch stash@{0}

# Useful when stash conflicts with current branch
# - Creates branch from the commit where stash was made
# - Applies stash on top
# - Drops stash if apply succeeds

Stash partiel

Stashez uniquement ce dont vous avez besoin avec -p (sélection interactive de morceaux) ou en listant des fichiers spécifiques. --keep-index stashe les modifications non stagées mais laisse les modifications stagées en place.

git
# Interactively choose hunks to stash
git stash push -p

# Stash specific files only
git stash push -m "config tweaks" config.yml .env.local

# Stash with keep-index (staged changes stay)
git stash --keep-index

Gestion du stash

git stash show -p affiche le diff complet d'un stash. drop supprime un seul stash ; clear les efface tous (irréversible). Les stashes sont locaux uniquement ; ils ne sont jamais poussés vers les distants.

git
# Show what's in a stash
git stash show -p stash@{0}

# Drop a specific stash
git stash drop stash@{0}

# Clear all stashes (irreversible)
git stash clear

# Show stash list with dates
git stash list --date=relative
17

Rebase

Rebase de base

Rebase déplace les commits de votre branche au-dessus d'une autre branche, produisant un historique linéaire. Contrairement à merge, il réécrit les hashes de commit. Ne rebasez jamais des commits qui ont été poussés et partagés.

git
# Rebase current branch onto main
git checkout feature
git rebase main

# Abort if conflicts are too messy
git rebase --abort

# Continue after resolving conflicts
git rebase --continue

Rebase interactif

Le rebase interactif (-i) vous permet de réécrire l'historique avant de partager : réordonner les commits, squasher les apparentés en un seul commit propre, reword les messages, ou drop les erreurs. Faites toujours cela sur des commits non poussés.

git
# Rebase last 5 commits interactively
git rebase -i HEAD~5

# Commands: pick, reword, edit, squash, fixup, drop
# squash  = combine with previous, keep message
# fixup   = combine with previous, discard message
# reword  = change commit message only

Squash et fixup

--fixup crée un commit marqué comme correction d'un autre. --autosquash pendant le rebase place automatiquement les commits fixup! et squash! à côté de leurs cibles. Cela simplifie le workflow « committer tôt, nettoyer plus tard ».

git
# Create a fixup commit targeting an earlier commit
git commit --fixup a1b2c3

# Autosquash during rebase (auto-reorders fixups)
git rebase -i --autosquash HEAD~5

Rebase --onto

--onto est un rebase chirurgical : il déplace une plage de commits d'une base vers une autre. Utilisez-le pour re-parenter une branche, ou pour retirer les premiers commits d'une branche.

git
# Move commits from one base to another
# git rebase --onto <new-base> <old-base> <branch>
git rebase --onto main feature-old feature-new

# Drop the first N commits of a branch
git rebase --onto main HEAD~3

Conflits de rebase

Pendant le rebase, les conflits s'arrêtent à chaque commit. Résolvez, git add, puis --continue pour continuer. --skip jette le commit en conflit entièrement. --abort revient à l'état pré-rebase.

git
# During rebase, conflicts pause the process
git rebase main
# CONFLICT (content): Merge conflict in file.js

# Resolve in editor, then:
git add file.js
git rebase --continue

# Skip a commit that conflicts (drop it)
git rebase --skip

# Give up entirely
git rebase --abort
18

Cherry-Pick

Cherry-picker un commit

cherry-pick applique un commit spécifique d'une autre branche sur votre branche courante, créant un nouveau commit avec les mêmes modifications. Le nouveau commit a un hash différent car le parent est différent.

git
# Apply a specific commit to current branch
git cherry-pick a1b2c3d

# Cherry-pick multiple commits
git cherry-pick a1b2c3d e4f5g6h

# Cherry-pick a range (exclusive of start, inclusive of end)
git cherry-pick A..E

Cherry-pick sans commit

--no-commit (-n) stage les modifications cherry-pickées sans créer de commit. Cela vous permet de combiner plusieurs cherry-picks en un commit, ou de modifier les modifications avant de committer.

git
# Apply changes to working tree without committing
git cherry-pick --no-commit a1b2c3d

# Stage the changes yourself
git add -p
git commit -m "Custom message"

Conflits de cherry-pick

Les conflits pendant le cherry-pick mettent en pause l'opération. Résolvez, git add, et --continue. --skip abandonne le commit courant. --abort annule le cherry-pick et restaure la branche.

git
git cherry-pick a1b2c3d
# error: could not apply a1b2c3d...

# Resolve conflicts, then:
git add resolved-file.js
git cherry-pick --continue

# Skip this commit
git cherry-pick --skip

# Abort the entire cherry-pick
git cherry-pick --abort

Cherry-pick depuis une autre branche

Le workflow hotfix classique : corrigez un bug sur une branche de maintenance, puis cherry-pickez le même commit sur main (et les autres branches actives). Cela évite de fusionner du travail de fonctionnalité non lié.

git
# Find the commit hash on another branch
git log --oneline feature-branch

# Switch to target branch
git checkout main

# Cherry-pick the commit
git cherry-pick <hash>

# Useful for hotfixes: fix on a feature branch,
# then cherry-pick the fix to main

Stratégie de cherry-pick

-X theirs/ours biaise la résolution de conflit. -x ajoute une ligne enregistrant le hash du commit original — essentiel pour les pistes d'audit lors du cherry-pick de hotfixes à travers les branches.

git
# Use a different merge strategy
git cherry-pick -X theirs a1b2c3d   # prefer their changes on conflict
git cherry-pick -X ours a1b2c3d     # prefer our changes on conflict

# Preserve original commit author
git cherry-pick -x a1b2c3d          # adds "(cherry picked from ...)" to message

# Edit commit message before committing
git cherry-pick -e a1b2c3d
19

Bisect

Bisect de base

git bisect effectue une recherche binaire dans l'historique des commits pour trouver quel commit a introduit un bug. Vous marquez le commit courant comme bad et un commit connu good comme good. Après ~log2(N) étapes, git nomme le commit coupable.

git
# Start bisecting
git bisect start

# Mark current commit as bad (has the bug)
git bisect bad

# Mark a known-good commit (older)
git bisect good v1.0.0

# Git checks out the midpoint. Test, then mark:
git bisect good   # or
git bisect bad

# Exit bisect mode
git bisect reset

Log et replay de bisect

git bisect log enregistre chaque décision good/bad. Si vous marquez mal un commit (une erreur courante), reset et rejouez le log, puis corrigez la mauvaise étape.

git
# Record what happens during bisect
git bisect log > bisect.log

# If you make a mistake, replay from the log
git bisect reset
git bisect replay bisect.log

# Visualize the bisect state
git bisect visualize

Bisect automatisé

git bisect run automatise la recherche : git check chaque candidat, exécute votre script, et marque le commit selon le code de sortie. C'est considérablement plus rapide que les tests manuels.

git
# Auto-bisect: git runs a test script and marks good/bad itself
git bisect start HEAD v1.0.0 --     # bad=HEAD, good=v1.0.0
git bisect run npm test             # exit 0=good, 1-124=bad, 125=skip

# Or with a custom script
git bisect run ./scripts/check-bug.sh

Bisect par fichier

Passer un chemin à git bisect start restreint la recherche aux commits qui ont modifié ce fichier. Cela saute des centaines de commits non pertinents et cible le fichier où le bug réside probablement.

git
# Limit bisect to changes in a specific file
git bisect start -- path/to/file.js

# Combine with bad/good markers
git bisect bad HEAD
git bisect good v1.0.0

# Git only considers commits that touched that file

Bisect reset

Exécutez toujours git bisect reset quand vous avez fini — il vous renvoie à votre branche originale et nettoie l'état bisect. Sans reset, votre arborescence de travail reste sur le dernier commit testé.

git
# Exit bisect mode and return to original branch
git bisect reset

# Reset to a specific branch/commit instead
git bisect reset <branch>

# View current bisect state
git bisect status
20

Sous-modules

Ajouter un sous-module

Les sous-modules intègrent un dépôt Git dans un autre — utiles pour vendorer des bibliothèques partagées. git submodule add enregistre le sous-module dans .gitmodules. Les nouveaux clones ont besoin de --recurse-submodules.

git
# Add a submodule at a specific path
git submodule add https://github.com/user/lib.git libs/lib

# This creates:
# - libs/lib/ (the submodule checkout)
# - .gitmodules (tracks submodule URLs and paths)

# Commit the new submodule
git commit -m "Add lib submodule"

# Clone a repo with submodules included
git clone --recurse-submodules https://github.com/user/repo.git

Mettre à jour les sous-modules

Un sous-module est épinglé à un commit spécifique. git submodule update --remote récupère le dernier commit sur la branche suivie et met à jour le pointeur. Vous devez committer ce changement de pointeur dans le dépôt parent.

git
# Pull latest changes in all submodules
git submodule update --remote

# Update a specific submodule
git submodule update --remote libs/lib

# Then commit the updated pointer
git add libs/lib
git commit -m "Bump lib submodule"

# Initialize submodules after cloning
git submodule update --init --recursive

Submodule foreach

foreach exécute une commande shell dans chaque répertoire de sous-module — utile pour les opérations en masse comme vérifier le statut, tirer des mises à jour, ou builder. --recursive descend dans les sous-modules imbriqués.

git
# Run a command in every submodule
git submodule foreach 'git status'

# With recursion into nested submodules
git submodule foreach --recursive 'git checkout main'

# Use --quiet to suppress the "Entering..." messages
git submodule foreach --quiet 'git pull'

Deinit et remove

Supprimer un sous-module est un processus multi-étapes : deinit le désenregistre, rm -rf .git/modules/... supprime les données Git du sous-module, et git rm supprime l'arborescence de travail. Oublier le nettoyage .git/modules laisse des données orphelines.

git
# Deinit a submodule (unregisters, keeps files)
git submodule deinit libs/lib

# Fully remove a submodule
git submodule deinit -f libs/lib
rm -rf .git/modules/libs/lib
git rm -f libs/lib
git commit -m "Remove lib submodule"

Branches de sous-module

Par défaut, les sous-modules sont en detached HEAD. Définir submodule.<name>.branch permet à --remote de suivre cette branche. Pour faire des modifications dans un sous-module, cd dedans, checkout une branche, commitez, et poussez.

git
# Configure a submodule to track a branch
git config -f .gitmodules submodule.libs/lib.branch main

# Now --remote updates from that branch
git submodule update --remote

# Work inside a submodule like a normal repo
cd libs/lib
git checkout feature-branch
git push
cd ../..
git add libs/lib
git commit -m "Update lib to feature-branch"
21

Hooks

Hooks courants

Les hooks Git sont des scripts dans .git/hooks/ qui s'exécutent automatiquement à des points spécifiques. Les hooks côté client s'exécutent sur votre machine et peuvent bloquer des actions. Les hooks côté serveur s'exécutent sur le distant et appliquent la politique. Les hooks ne sont pas versionnés par défaut — utilisez Husky pour les partager.

git
# Client-side (run on your machine):
#   pre-commit        - runs before commit is created
#   prepare-commit-msg - can edit commit message
#   commit-msg        - validates commit message
#   pre-push          - runs before pushing
#   post-merge        - runs after git merge

# Server-side (run on the remote):
#   pre-receive       - runs before refs are updated
#   update            - runs once per ref
#   post-receive      - runs after refs are updated

Hook pre-commit

pre-commit s'exécute avant que le commit ne soit créé ; une sortie non-zéro annule le commit. Usages courants : lint les fichiers stagés, lancer des tests ciblés, formater le code. Gardez-le rapide (<5 secondes) ou les développeurs le contourneront avec --no-verify.

git
#!/bin/sh
# .git/hooks/pre-commit

# Run linter on staged files
npm run lint-staged
if [ $? -ne 0 ]; then
  echo "Linting failed. Fix errors and try again."
  exit 1
fi

# Run tests
npm test -- --findRelatedTests
if [ $? -ne 0 ]; then
  echo "Tests failed."
  exit 1
fi

exit 0

Hook commit-msg

commit-msg reçoit le chemin vers le fichier de message de commit temporaire comme $1. Il peut valider ou réécrire le message. Une sortie non-zéro rejette le commit. C'est la façon standard d'appliquer les Conventional Commits.

git
#!/bin/sh
# .git/hooks/commit-msg

# Enforce Conventional Commits format
pattern='^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.*\))?: .{1,72}$'

if ! grep -qE "$pattern" "$1"; then
  echo "Invalid commit message format."
  echo "Use: <type>(<scope>): <description>"
  exit 1
fi

Configuration Husky

Husky installe les hooks Git depuis un répertoire .husky/ versionné, donc chaque membre de l'équipe obtient les mêmes hooks après npm install. lint-staged exécute des commandes uniquement sur les fichiers stagés, gardant pre-commit rapide.

git
# Install Husky
npm install --save-dev husky
npx husky init

# Add a pre-commit hook
echo 'npm run lint-staged' > .husky/pre-commit

# Add a commit-msg hook
echo 'npx --no-install commitlint --edit $1' > .husky/commit-msg

# package.json
{
  "scripts": { "prepare": "husky" },
  "lint-staged": {
    "*.{js,ts}": ["eslint --fix", "prettier --write"]
  }
}

Hook pre-push

pre-push s'exécute avant que les refs ne soient poussées ; une sortie non-zéro annule. Il lit les pushes proposés depuis stdin. Usages courants : bloquer les pushes vers les branches protégées, lancer la suite de tests complète.

git
#!/bin/sh
# .husky/pre-push

# Reads stdin: <local ref> <local sha> <remote ref> <remote sha>
while read local_ref local_sha remote_ref remote_sha; do
  if [ "$remote_ref" = "refs/heads/main" ]; then
    echo "Direct push to main is not allowed."
    exit 1
  fi
done

npm test
if [ $? -ne 0 ]; then
  echo "Tests failed. Push aborted."
  exit 1
fi
22

Worktrees

Ajouter un worktree

Un worktree est une arborescence de travail séparée liée au même dépôt. Vous pouvez avoir plusieurs branches checkées simultanément dans des répertoires différents — pas de stash pour changer de contexte.

git
# Create a worktree at a path, on a new branch
git worktree add ../project-feature feature-branch

# Worktree on an existing branch
git worktree add ../project-hotfix hotfix-branch

# Worktree at a specific commit (detached HEAD)
git worktree add --detach ../project-inspect v1.0.0

# List all worktrees
git worktree list

Workflow de worktree

Les worktrees brillent pour le changement de contexte : un bug urgent arrive alors que vous êtes plongé dans une fonctionnalité. Au lieu de stasher et perdre l'état de votre IDE, créez un worktree sur main, corrigez le bug là, et revenez.

git
# Scenario: working on feature, urgent bug comes in
git worktree add ../project-hotfix main

cd ../project-hotfix
git checkout -b fix-urgent
# ... fix the bug, commit, push ...

cd ../project-feature
# Your feature work is untouched

# Clean up when done
git worktree remove ../project-hotfix

Remove et prune

remove supprime un répertoire de worktree et ses métadonnées d'administration. La branche sur laquelle il était reste. Si le worktree a des modifications non commitées, remove refuse sauf si vous passez --force.

git
# Remove a worktree (must be clean or use --force)
git worktree remove ../project-feature

# Force remove even with uncommitted changes
git worktree remove --force ../project-feature

# Prune worktree admin files for deleted directories
git worktree prune

# Show what would be pruned
git worktree prune --dry-run -v

Avantages des worktrees

Les worktrees résolvent plusieurs points de douleur : pas de stash pour les changements de contexte, builds/tests parallèles, node_modules isolés par branche, et comparaison de branches côte à côte. La base de données d'objets partagée signifie une surcharge disque minimale.

git
# 1. No stashing needed for context switches
# 2. Run long builds/tests in one worktree while
#    continuing work in another
# 3. Each worktree has its own node_modules
# 4. Compare two branches side-by-side in two
#    editor windows
# 5. Shared object database = minimal disk overhead

# Typical layout:
# ~/projects/
#   main-repo/          (main branch)
#   main-repo-feature/  (feature worktree)
#   main-repo-hotfix/   (hotfix worktree)

Worktrees verrouillés

Verrouillez un worktree quand il contient du travail qui ne devrait pas être dérangé (builds longs, debugger attaché). Les worktrees verrouillés survivent à prune. move relocalise un worktree ; repair répare les fichiers d'administration.

git
# Lock a worktree (prevents it from being pruned)
git worktree lock ../project-feature --reason "Running long build"

# Unlock when done
git worktree unlock ../project-feature

# Move a worktree to a new path
git worktree move ../project-feature ../new-location/project-feature

# Repair after moving manually
git worktree repair ../new-location/project-feature
23

Reflog

Visualiser le reflog

Le reflog enregistre chaque changement de HEAD et des tips de branche — commits, checkouts, resets, rebases. C'est un filet de sécurité local : même après une opération destructive, les commits sont encore dans le reflog.

git
# Show reflog for HEAD
git reflog
# a1b2c3d HEAD@{0}: commit: Fix bug
# e4f5g6h HEAD@{1}: checkout: moving to feature
# 789abc0 HEAD@{2}: reset: moving to HEAD~1

# Show reflog for a specific branch
git reflog show feature

# Show reflog with dates
git reflog --date=iso

Récupérer des commits perdus

Après un hard reset ou un rebase, les commits « perdus » sont encore atteignables via le reflog. Trouvez le hash dans git reflog, puis reset --hard ou cherry-pick pour récupérer. C'est pourquoi Git est sûr — presque rien n'est vraiment parti.

git
# Find the commit you lost (e.g., after a hard reset)
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: Important work  <-- this one

# Reset back to the lost commit
git reset --hard e4f5g6h

# Or cherry-pick it onto your current branch
git cherry-pick e4f5g6h

Reflog et reset

ORIG_HEAD est une ref de commodité qui pointe vers le HEAD précédent après des opérations destructives (reset, merge, rebase). git reset --hard ORIG_HEAD annule la dernière telle opération en une commande.

git
# Undo a git reset --hard
git reflog
git reset --hard HEAD@{1}

# Undo a rebase
git reflog
git reset --hard HEAD@{5}

# Undo a merge
git reset --hard ORIG_HEAD
# ORIG_HEAD is set by merge, rebase, reset, and cherry-pick

Expire et nettoyage

Les entrées du reflog s'accumulent et consomment de l'espace disque. expire supprime les anciennes entrées ; --expire-unreachable cible uniquement les entrées non atteignables depuis aucune ref. Après expiration, exécutez git gc --prune=now pour supprimer les objets inaccessibles.

git
# Expire reflog entries older than 30 days
git reflog expire --expire=30.days

# Expire entries unreachable from current branches
git reflog expire --expire-unreachable=30.days

# Delete a specific reflog entry
git reflog delete HEAD@{2}

# Dry run (see what would be expired)
git reflog expire --dry-run --expire=now --all

Reflog pour les branches

Chaque branche maintient son propre reflog. Si vous supprimez une branche avec git branch -D, les commits sont encore dans le reflog — trouvez le hash et recréez la branche avec git branch <name> <hash>.

git
# Each branch has its own reflog
git reflog show main
# a1b2c3d main@{0}: commit: Update docs
# e4f5g6h main@{1}: pull origin main: Fast-forward

# Recover a deleted branch
git reflog  # find the hash the branch pointed to
git branch recovered-branch e4f5g6h

# Compare current state with a past reflog entry
git diff HEAD@{1} HEAD
24

LFS

Installer et suivre

Git LFS stocke les gros fichiers (images, vidéos, binaires) en dehors du dépôt Git, les remplaçant par des fichiers pointeurs dans les commits. git lfs track enregistre les motifs dans .gitattributes. Commitez toujours .gitattributes avant d'ajouter de gros fichiers.

git
# Install Git LFS (once per user)
git lfs install

# Track large file types
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/*"

# View tracking rules
cat .gitattributes

# Commit the .gitattributes file
git add .gitattributes
git commit -m "Configure LFS tracking"

Ajouter et committer des fichiers LFS

Une fois un motif de fichier suivi, git add stage le fichier via LFS automatiquement. Le commit stocke un petit fichier pointeur (~130 octets) au lieu du binaire. Le contenu réel est téléversé vers le serveur LFS au push.

git
# After configuring tracking, add files normally
git add design.psd
git commit -m "Add design file"

# Verify LFS is handling the file
git lfs ls-files
# a1b2c3d4 * design.psd

# The commit contains a pointer, not the actual file
git show HEAD -- design.psd
# version https://git-lfs.github.com/spec/v1
# oid sha256:...
# size 12345678

Cloner et tirer

Cloner un dépôt avec LFS télécharge d'abord les fichiers pointeurs, puis le contenu réel. Si le téléchargement LFS échoue, git lfs pull réessaie. git lfs fetch télécharge les objets sans les écrire dans l'arborescence de travail.

git
# Clone a repo with LFS files (downloads them automatically)
git clone https://github.com/user/repo.git

# If LFS files are missing, fetch them
git lfs pull

# Fetch LFS objects without checking them out
git lfs fetch

# Checkout LFS files for specific paths
git lfs checkout assets/

Migrer vers LFS

git lfs migrate import convertit rétroactivement les gros fichiers de l'historique en pointeurs LFS. Cela réécrit tous les hashes de commit — chaque collaborateur doit re-cloner. Exécutez --dry-run d'abord pour voir l'impact.

git
# Convert existing large files to LFS (rewrites history!)
git lfs migrate import --include="*.psd" --include-ref=refs/heads/main

# Migrate all branches
git lfs migrate import --include="*.psd" --everything

# Check what would be migrated (dry run)
git lfs migrate import --include="*.psd" --include-ref=refs/heads/main --dry-run

Gestion LFS

git lfs status montre les modifications LFS en attente. git lfs env affiche la configuration. git lfs fsck vérifie que tous les objets LFS référencés par les fichiers pointeurs sont présents et non corrompus.

git
# View LFS status
git lfs status

# List all LFS files in the repo
git lfs ls-files

# Check LFS configuration
git lfs env

# Untrack a file type (does not remove existing LFS objects)
git lfs untrack "*.zip"

# Verify LFS objects are present and valid
git lfs fsck
25

Intégration CI/CD

GitHub Actions de base

GitHub Actions exécute des workflows sur push/PR. actions/checkout récupère le dépôt ; fetch-depth: 0 obtient l'historique complet. npm ci installe depuis le lockfile (plus rapide, plus strict que install). Mettez en cache les dépendances avec la clé cache.

git
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm test
      - run: npm run build

Workflows conditionnels

Filtrez les workflows par branche ou événement avec on: push: branches. Utilisez des conditions if: au niveau du job pour sauter des jobs selon le contexte. needs: crée des dépendances entre jobs — deploy ne s'exécute que si test passe.

git
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh

Hooks Git en CI

Les pipelines CI reflètent souvent les hooks locaux : lint d'abord (échec rapide), puis test. needs: lint garantit que les tests ne s'exécutent que si le linting passe, économisant les minutes CI. Diviser les jobs permet des runners parallèles pour la vitesse.

git
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck

  test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test -- --coverage

Secrets et environnement

Stockez les secrets (clés API, tokens) dans les paramètres du dépôt et référencez-les via ${{ secrets.NAME }}. Ils ne sont jamais affichés dans les logs. Les environnements peuvent exiger une approbation manuelle avant le déploiement, ajoutant une barrière.

git
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # requires manual approval
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}
          DB_URL: ${{ secrets.DATABASE_URL }}
        run: ./scripts/deploy.sh "$API_KEY" "$DB_URL"

Builds matriciels

Les builds matriciels exécutent le même job à travers plusieurs combinaisons OS/langage en parallèle. Cela détecte tôt les bugs spécifiques à une plateforme. Utilisez fail-fast: false pour exécuter toutes les combinaisons même si l'une échoue. Gardez les matrices raisonnables pour contrôler les minutes CI.

git
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node-version: ['18', '20', '22']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

Was this helpful?

Learning path

Learn from scratch

Learn this language from the ground up with structured lessons.