Chapitre 514 minDébutant
Les branches Git : créer, changer, fusionner
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- expliquer ce qu'est une branche et pourquoi l'utiliser ;
- créer une branche, changer de branche et la nommer correctement ;
- fusionner avec
git merge, en avance rapide ou non ; - supprimer une branche et sortir d'un HEAD détaché.
4 exercices corrigés2 schémas
Une branche Git est une ligne de travail indépendante : tu y développes une fonctionnalité ou une correction sans toucher à la branche principale, puis tu fusionnes ton travail quand il est prêt. Techniquement, une branche n'est qu'une étiquette qui pointe vers un commit, ce qui la rend instantanée à créer. Ce chapitre montre comment créer une branche, changer de branche, fusionner avec git merge (en avance rapide ou avec un commit de fusion), puis renommer et supprimer des branches.
Pourquoi travailler sur des branches
Sans branche, tout le monde commite sur main. Lina commence la route d'export, Hugo corrige un bug d'affichage, et main contient en permanence du travail à moitié fini : impossible de livrer la correction de Hugo sans livrer aussi l'export inachevé de Lina.
Avec des branches, chaque sujet vit à part :
maincontient toujours une version qui fonctionne, celle qu'on peut déployer ou montrer à la commission ;- chaque fonctionnalité ou correction se développe sur sa propre branche, où l'on peut commiter du travail en cours sans gêner personne ;
- quand le travail est terminé et vérifié, on le fusionne dans
main.
C'est aussi ce qui te permet d'essayer une idée sans risque : si elle ne mène nulle part, tu supprimes la branche, et main n'a jamais rien su.
Une branche est un pointeur
Une branche est un simple fichier qui contient l'empreinte d'un commit : le dernier commit de la branche. Quand tu commites sur une branche, Git crée le commit et avance l'étiquette sur lui. HEAD, lui, pointe vers la branche sur laquelle tu te trouves.
Créer une branche ne copie aucun fichier : Git écrit une étiquette de 41 octets. C'est pour cela qu'on crée des branches sans hésiter, une par sujet.
Créer une branche et changer de branche
git branch # liste les branches locales, * devant la branche courante
git switch -c feature/export # crée la branche à partir de HEAD et y va
Switched to a new branch 'feature/export'
| Commande | Effet |
|---|---|
git branch | Liste les branches locales |
git branch -v | Les liste avec leur dernier commit |
git branch -a | Ajoute les branches de suivi des dépôts distants (remotes/origin/…) |
git branch feature/export | Crée la branche sans y aller |
git switch feature/export | Va sur une branche existante |
git switch -c feature/export | Crée la branche et y va |
git switch -c hotfix/total main | Crée la branche à partir de main, quelle que soit la branche courante |
git switch - | Revient à la branche précédente |
Quand tu changes de branche, Git remplace les fichiers de ton répertoire de travail par ceux du dernier commit de la branche visée. Tes modifications non commitées te suivent si elles ne gênent pas ; si un fichier modifié diffère entre les deux branches, Git refuse le changement pour ne pas l'écraser. Commite ou range-les avec git stash.
Nommer ses branches
Un nom de branche décrit le sujet, en minuscules, avec des tirets, et commence souvent par un préfixe qui donne la nature du travail :
| Préfixe | Usage | Exemple |
|---|---|---|
feature/ ou feat/ | Nouvelle fonctionnalité | feature/export-csv |
fix/ ou bugfix/ | Correction de bug | fix/total-ligne-vide |
hotfix/ | Correction urgente en production | hotfix/connexion-impossible |
docs/ | Documentation | docs/guide-installation |
refactor/ | Restructuration sans changement de comportement | refactor/service-indemnites |
Beaucoup d'équipes ajoutent le numéro du ticket : fix/12-filtre-date. Évite les espaces, les accents et les noms personnels (branche-hugo) : une branche porte un sujet, pas une personne.
Fusionner deux branches avec git merge
La fonctionnalité est terminée sur feature/export. Pour l'intégrer, place-toi sur la branche qui reçoit, puis fusionne la branche qui apporte :
git switch main
git pull # main à jour avant de fusionner
git merge feature/export
Le nom qui suit merge est la branche qu'on fait entrer, pas la destination. Selon l'historique, Git fusionne de deux façons.
L'avance rapide (fast-forward). Si main n'a reçu aucun commit depuis la création de feature/export, il n'y a rien à combiner : Git avance simplement l'étiquette main jusqu'au dernier commit de la branche.
Updating 0f0c3b3..f7e293d
Fast-forward
export.js | 1 +
1 file changed, 1 insertion(+)
create mode 100644 export.js
La fusion à trois points. Si main a avancé de son côté, les deux historiques ont divergé. Git compare trois versions : le dernier commit de chaque branche et leur ancêtre commun. Il combine les deux séries de modifications et crée un commit de fusion, qui a deux parents.
Merge made by the 'ort' strategy.
filtre.js | 1 +
1 file changed, 1 insertion(+)
create mode 100644 filtre.js
ort est le nom de l'algorithme de fusion par défaut de Git depuis 2021. Git ouvre ton éditeur avec le message Merge branch 'feature/filtre' : tu peux le garder tel quel.
Pour visualiser le résultat :
git log --oneline --graph --all
* 4398d2d (HEAD -> main) Merge branch 'feature/filtre'
|\
| * 2eb48ca (feature/filtre) Ajoute le filtre par date
* | b14293e Documente l'installation
|/
* f7e293d (feature/export) Ajoute l'export CSV
* 0f0c3b3 Ajoute le point d'entrée de l'API
* 24b02c7 Initialise le projet
Chaque * est un commit, les traits dessinent les branches. On lit de bas en haut : feature/filtre est partie de f7e293d, main a reçu b14293e entre-temps, et le commit de fusion 4398d2d réunit les deux lignes.
Forcer ou interdire l'avance rapide
Certaines équipes veulent garder la trace de chaque fonctionnalité dans l'historique, même quand l'avance rapide était possible. L'option --no-ff force un commit de fusion :
git merge --no-ff feature/export
À l'inverse, git merge --ff-only feature/export refuse de fusionner si l'avance rapide est impossible : pratique pour vérifier que ta branche est bien à jour. Et git merge --squash feature/export prépare toutes les modifications de la branche en un seul bloc, sans commit de fusion : tu les commites ensuite en un seul commit. C'est ce que fait le bouton Squash and merge de GitHub (chapitre 9).
Si deux branches modifient les mêmes lignes d'un même fichier, Git ne sait pas laquelle garder et s'arrête sur un conflit. Le chapitre 6 le traite en détail.
Supprimer et renommer des branches
Une branche fusionnée ne sert plus. Supprime-la pour garder une liste lisible :
git branch -d feature/export
Deleted branch feature/export (was f7e293d).
Les commits restent dans l'historique de main : seule l'étiquette disparaît. -d refuse de supprimer une branche dont le travail n'est fusionné nulle part, pour te protéger :
error: The branch 'feature/plafond' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature/plafond'.
-D force la suppression ; les commits de la branche ne sont alors plus accessibles que par le reflog, pendant 30 jours.
| Commande | Effet |
|---|---|
git branch -d nom | Supprime une branche fusionnée |
git branch -D nom | Supprime une branche, fusionnée ou non |
git push origin --delete nom | Supprime la branche sur le dépôt distant |
git branch -m nouveau-nom | Renomme la branche courante |
git branch -m ancien nouveau | Renomme une autre branche |
git branch --merged main | Liste les branches déjà fusionnées dans main, donc supprimables |
Pour renommer une branche déjà poussée, renomme-la en local, pousse le nouveau nom, puis supprime l'ancien sur le serveur :
git branch -m fix/filtre fix/12-filtre-date
git push -u origin fix/12-filtre-date
git push origin --delete fix/filtre
HEAD détaché
Tu veux voir le projet tel qu'il était il y a dix commits, pour vérifier quand un bug est apparu :
git switch --detach a1b2c3d
HEAD is now at a1b2c3d Ajoute le calcul des indemnités
HEAD pointe maintenant directement sur un commit, et non plus sur une branche : c'est l'état HEAD détaché (detached HEAD). Tu peux lire, compiler, tester. Si tu commites dans cet état, tes commits ne sont rattachés à aucune branche : dès que tu changes de branche, ils ne sont plus accessibles que par le reflog. Pour les garder, crée une branche avant de partir : git switch -c essai-ancien-calcul. Pour revenir, git switch main.
Tu tombes aussi en HEAD détaché quand tu fais git switch --detach v1.0 pour tester une version étiquetée, ou au milieu d'un rebase.
Exercices
Exercice 1 sur 4 Avance rapide ou commit de fusion ? Réussi À revoir
Pour chaque situation, dis si git merge fait une avance rapide ou crée un commit de fusion.
- Lina crée
feature/exportdepuismain, y fait trois commits, puis revient surmain, où personne n'a commité, et fusionne. - Même départ, mais Hugo a poussé un commit sur
mainentre-temps, et Lina a faitgit pullsurmainavant de fusionner. - Même départ qu'en 1, mais Lina lance
git merge --no-ff feature/export. - Situation 2, avec
git merge --ff-only feature/export.
Voir la réponseMasquer la réponse de l'exercice 1
- Avance rapide :
mainn'a pas bougé, son dernier commit est l'ancêtre du dernier commit defeature/export. L'étiquette avance de trois commits. - Commit de fusion : les deux historiques ont divergé (le commit de Hugo d'un côté, les trois commits de Lina de l'autre).
- Commit de fusion, forcé par
--no-ff, alors que l'avance rapide était possible. - Aucune fusion : Git refuse avec
fatal: Not possible to fast-forward, aborting.Il faudrait d'abord mettrefeature/exportà jour avecmain(par un rebase, chapitre 7).
Tu avais trouvé ?
Exercice 2 sur 4 Lire un graphe Réussi À revoir
Voici la sortie de git log --oneline --graph --all :
* c81d2e0 (HEAD -> main) Merge branch 'fix/arrondi'
|\
| * 77ab310 (fix/arrondi) Arrondit le total au centime
* | 4e0f9a2 (feature/pdf) Ajoute l'export PDF
|/
* 1b6c5d8 Ajoute la liste des notes
* 0a2e4f7 Initialise le projet
- Sur quelle branche est-on ?
- De quel commit les deux branches sont-elles parties ?
- Comment
feature/pdfa-t-elle été intégrée àmain? - Combien de parents a le commit
c81d2e0, et lesquels ? - Quelles branches peut-on supprimer avec
git branch -d?
Voir la réponseMasquer la réponse de l'exercice 2
- Sur
main:HEAD -> main. - De
1b6c5d8(« Ajoute la liste des notes »), là où les deux lignes se séparent. - Par avance rapide :
4e0f9a2est sur la ligne demain, sans commit de fusion propre àfeature/pdf.mainétait sur1b6c5d8, la fusion defeature/pdfa avancé l'étiquette sur4e0f9a2. - Deux :
4e0f9a2(le dernier commit demainavant la fusion) et77ab310(le dernier commit defix/arrondi). - Les deux :
feature/pdfetfix/arrondisont entièrement contenues dansmain.
Tu avais trouvé ?
Exercice 3 sur 4 Enchaîner les commandes Réussi À revoir
Tu es sur main, à jour. Écris les commandes pour :
- créer la branche
fix/12-filtre-dateet y aller ; - commiter le fichier
routes/notes.jscorrigé ; - revenir sur
mainet y intégrer la correction ; - supprimer la branche devenue inutile.
Puis réponds : à l'étape 3, quel message affiche git merge si personne n'a touché à main ?
Voir la réponseMasquer la réponse de l'exercice 3
git switch -c fix/12-filtre-date
git add routes/notes.js
git commit -m "Corrige le filtre par date des notes (ticket 12)"
git switch main
git merge fix/12-filtre-date
git branch -d fix/12-filtre-date
main n'a pas bougé : la fusion est une avance rapide, et Git affiche Updating <ancien>..<nouveau> (par exemple Updating 1b6c5d8..9e1c4a7), puis Fast-forward avec le récapitulatif des fichiers modifiés.
Tu avais trouvé ?
Exercice 4 sur 4 Le commit fantôme Réussi À revoir
Hugo a fait git switch --detach 1b6c5d8 pour tester une ancienne version. Il a corrigé une faute dans README.md et commité, puis il est revenu sur main. Son commit n'apparaît plus dans git log.
- Pourquoi ?
- Comment le retrouver et le rattacher à une branche ?
Voir la réponseMasquer la réponse de l'exercice 4
- En HEAD détaché, le commit n'était rattaché à aucune branche. En revenant sur
main, plus aucune étiquette ne pointe dessus :git logne le montre plus. Git l'a d'ailleurs signalé au moment dugit switch main(Warning: you are leaving 1 commit behind, not connected to any of your branches). git reflogaffiche l'empreinte du commit (lignecommit: …juste avant le retour surmain). Puisgit branch fix/readme <empreinte>crée une branche dessus. Il peut ensuite la fusionner ou en récupérer le commit avecgit cherry-pick(chapitre 7).
Tu avais trouvé ?