Introduction
Dans Git, une branche n'est qu'une référence mobile vers un commit. Créer une branche, c'est créer un fichier de 41 octets. Comparé aux systèmes centralisés où les branches sont des copies complètes de répertoires, le modèle de Git rend le branching assez peu coûteux pour être utilisé avec désinvolture.
Ce qu'est réellement une branche
cat .git/refs/heads/main
# a1b2c3d4e5f6...
git rev-parse main
Ce hachage est la pointe de la branche. La « branche » elle-même est la chaîne de commits atteignables depuis la pointe via les pointeurs parents.
Créer des branches
git branch feature/login # créer, ne pas changer
git switch -c feature/login # créer et changer
git switch -c hotfix v1.2.3 # depuis un tag
git branch fixup HEAD~3 # depuis une ref relative
Déplacer la pointe
Chaque commit sur la branche courante avance sa pointe d'un cran. Les commandes qui déplacent les pointes incluent commit, merge, rebase, reset et cherry-pick.
git commit -m "Work" # main avance
git reset --hard HEAD~2 # main recule ; les commits deviennent inatteignables
Branches et atteignabilité
Un commit est « vivant » tant qu'une ref l'atteint. Supprimez chaque branche et tag pointant vers un commit et il devient dangling ; le garbage collector de Git finit par le supprimer.
git fsck --unreachable
git gc
Suivre l'upstream
Une branche locale peut être liée à une branche de tracking distant :
git branch --set-upstream-to=origin/main main
git branch -u origin/main
git status # affiche avance/retard d'après le lien
Maintenance des branches
git branch --merged main # branches entièrement mergées
git branch --no-merged main # branches avec travail non mergé
git branch -d feature/old # supprimer mergée
git branch -D feature/discarded # supprimer de force
Comparer les branches
git log main..feature/login --oneline
git diff main...feature/login
git range-diff main..@{u} main..HEAD
range-diff est excellent pour comparer deux versions d'une branche rebase.
Politiques de branches sur les hôtes
La plupart des plateformes d'hébergement vous permettent de protéger les branches par motif de nom : exiger des pull requests, des commits signés, une CI passante ou des reviewers spécifiques avant qu'un push soit accepté. Le côté Git de cela est implémenté via les hooks pre-receive ; le côté plateforme est de la configuration. Depuis votre clone local, la protection de branche est invisible jusqu'à ce que votre push soit rejeté, moment où le serveur explique pourquoi. Pour inspecter la protection en ligne de commande, utilisez la CLI de l'hôte (gh, glab) ou son API.
gh api repos/owner/repo/branches/main/protection
Erreurs fréquentes
Imaginer les branches comme des conteneurs de commits. Elles ne le sont pas ; les commits existent indépendamment et de nombreuses branches peuvent atteindre le même commit. Supprimer une branche « feature » et supposer que les commits sont partis ; ils ne sont partis qu'après être devenus inatteignables et que le reflog ait expiré (90 jours par défaut). Utiliser de longs noms de branche d'un seul mot qui entrent en collision avec des noms de fichiers ; les séparateurs -- évitent l'ambiguïté. Enfin, travailler directement sur main dans un dépôt partagé ; faites toujours une branche pour le travail non trivial, même si vous comptez faire un fast-forward plus tard.