git merge : fusionner des branches et résoudre les conflits
git merge intègre l'historique d'une autre branche dans la branche courante : si celle-ci n'a pas avancé, Git déplace simplement le pointeur (fast-forward), sinon il crée un commit de merge à deux parents. Placez-vous sur la branche cible et lancez git merge nom-de-branche ; en cas de conflit, corrigez les fichiers, faites git add et git commit, ou annulez tout avec git merge --abort.
Ce que fait git merge
La commande git merge prend les changements de la branche indiquée et les intègre dans la branche où vous vous trouvez. Retenez bien le sens : on fusionne « vers soi ». Pour intégrer feature/login dans main, on bascule d'abord sur main, puis on lance la fusion.
git switch main
git pull # récupérer la version à jour de main depuis le serveur
git merge feature/login
Git ne copie rien ligne par ligne « à la main ». Il trouve l'ancêtre commun des deux branches (la merge base), compare les deux versions à cet ancêtre et combine les changements. Si chaque côté a modifié des endroits différents, le résultat est automatique. Si c'est le même endroit, un conflit apparaît et c'est à vous de trancher.
Deux modes : fast-forward et fusion à trois voies
Fast-forward — quand aucun commit de merge n'est nécessaire
Supposons que vous ayez créé feature depuis main, fait trois commits, et que personne n'ait rien ajouté à main entre-temps. L'historique de main n'est alors que le début de celui de feature. Git avance le pointeur main jusqu'au dernier commit de feature, sans créer de nouveau commit.
$ git merge feature
Updating 3f2a1c4..9b7e0d2
Fast-forward
src/auth.js | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
L'historique reste linéaire, mais la trace « il y avait ici une branche feature » disparaît. C'est parfois souhaitable, parfois non — voir plus bas la section sur --no-ff.
Fusion à trois voies et commit de merge
Si les deux branches ont avancé depuis l'ancêtre commun, on ne peut plus simplement déplacer le pointeur. Git effectue une fusion à trois voies : la base, « notre » côté (la branche courante) et « leur » côté (la branche fusionnée). Le résultat est enregistré dans un commit de merge distinct, qui a deux parents.
$ git merge feature
Merge made by the 'ort' strategy.
src/auth.js | 42 ++++++++++++++++++++
src/router.js | 3 +-
2 files changed, 44 insertions(+), 1 deletion(-)
Git ouvre l'éditeur avec un message du type Merge branch 'feature'. Vous pouvez le garder ou préciser pourquoi la fusion a été faite. Pour voir la « bifurcation » obtenue :
git log --oneline --graph --all
La stratégie ort est utilisée par défaut depuis Git 2.34 ; auparavant, c'était recursive. Au quotidien, inutile de changer de stratégie.
Options utiles de git merge
| Option | Ce qu'elle fait | Quand l'utiliser |
|---|---|---|
--no-ff | Crée toujours un commit de merge, même si un fast-forward est possible | Vous voulez voir dans l'historique quels commits viennent d'une même fonctionnalité |
--ff-only | Fusionne uniquement par avance rapide, sinon échoue | Mettre à jour une branche locale sans commit de merge accidentel |
--squash | Place tous les changements de la branche dans l'index, sans commiter ni créer de lien avec la branche | Transformer une fonctionnalité en un seul commit propre |
--no-commit | Effectue la fusion mais s'arrête avant le commit | Vérifier le résultat ou lancer les tests avant d'enregistrer |
-m "texte" | Définit le message du commit de merge | Fusion dans des scripts ou sans éditeur |
--abort | Annule la fusion en cours et restaure l'état antérieur | Trop de conflits, il faut recommencer |
--continue | Termine la fusion après résolution des conflits | Alternative à git commit à la dernière étape |
-X ours / -X theirs | Sur les fragments en conflit, prend automatiquement notre version ou la leur | Vous savez d'avance quelle version est la bonne (prudence !) |
--no-ff : conserver les limites d'une fonctionnalité
Beaucoup d'équipes ne fusionnent les fonctionnalités qu'avec --no-ff. Chaque tâche apparaît alors comme une « bulle » dans le graphe et peut être annulée d'un seul git revert -m 1 <commit-de-merge>. Avec un fast-forward, ce point n'existe pas, et il faudrait annuler chaque commit séparément.
--squash : un commit au lieu de dix
git switch main
git merge --squash feature/login
git commit -m "Connexion par e-mail et mot de passe"
Après un --squash, Git ne considère pas la branche comme fusionnée : git branch -d feature/login refusera de la supprimer avec l'avertissement « not fully merged ». Supprimez-la avec git branch -D une fois sûr que tout est en place. Et ne continuez pas à travailler sur la même branche après un squash — la fusion suivante réapporterait les changements déjà intégrés et provoquerait presque à coup sûr des conflits.
Conflit de fusion : comment le résoudre
Un conflit est une situation de travail normale, pas une catastrophe. Il signifie simplement que deux branches ont modifié les mêmes lignes (ou que l'une a supprimé un fichier que l'autre modifiait) et que Git refuse de deviner quelle version est la bonne.
$ git merge feature/login
Auto-merging src/config.js
CONFLICT (content): Merge conflict in src/config.js
Automatic merge failed; fix conflicts and then commit the result.
Étape 1. Voir ce qui est en conflit
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/config.js
Étape 2. Modifier le fichier
Dans le fichier en conflit, Git insère des marqueurs :
<<<<<<< HEAD
const TIMEOUT = 5000;
=======
const TIMEOUT = 10000;
>>>>>>> feature/login
La partie du haut correspond à votre branche courante (HEAD), celle du bas à la branche fusionnée. Gardez la bonne variante (ou combinez les deux), supprimez les trois lignes de marqueurs et enregistrez le fichier. Erreur fréquente : oublier un marqueur au milieu du fichier ; avant de commiter, cherchez-les : git diff --check signale les marqueurs restants.
Étape 3. Marquer la résolution et terminer la fusion
git add src/config.js
git commit # ou git merge --continue
Si le conflit se règle entièrement en choisissant un côté, inutile d'éditer le fichier à la main :
git checkout --ours src/config.js # garder la version de la branche courante
git checkout --theirs src/config.js # prendre la version de la branche fusionnée
git add src/config.js
Plus confortable dans un éditeur
VS Code, les IDE JetBrains et d'autres éditeurs affichent le conflit en trois colonnes avec des boutons « accepter l'actuel / l'entrant / les deux ». On peut aussi brancher un outil externe via git mergetool. Réglage utile : git config --global merge.conflictStyle zdiff3 — les marqueurs incluent alors aussi la version d'origine de l'ancêtre commun, et l'on voit qui a changé quoi.
Vous changez d'avis — annuler la fusion
git merge --abort
La commande remet le répertoire de travail et l'index dans l'état d'avant le git merge. C'est pourquoi il vaut mieux fusionner avec un arbre de travail propre : commitez ou mettez de côté les modifications en cours via git stash, sinon il sera difficile de les distinguer du résultat de la fusion.
Annuler un merge déjà effectué
Si le commit de merge n'a pas encore été poussé, le plus simple est de ramener la branche en arrière :
git reset --hard ORIG_HEAD
Git enregistre ORIG_HEAD avant les opérations risquées, dont la fusion : c'est la position de la branche avant le merge. L'option --hard jette les modifications non commitées, vérifiez donc git status.
Si la fusion est déjà dans le dépôt partagé, on ne réécrit pas l'historique. On crée un commit inverse :
git revert -m 1 a1b2c3d
L'option -m 1 indique que le parent « principal » est le premier (la branche dans laquelle on a fusionné). Attention au piège : après ce revert, Git considère les commits de la fonctionnalité comme déjà intégrés. Si vous voulez plus tard fusionner à nouveau la fonctionnalité corrigée, il faudra d'abord annuler le commit de revert lui-même.
Merge dans le travail en équipe
git pull = fetch + merge
Un git pull classique télécharge les changements et fusionne la branche distante dans la branche locale. Si un collègue et vous avez commité en parallèle, un commit « Merge branch 'main' of ... » apparaît. Pour éviter ces fusions techniques, configurez git config --global pull.ff only (le pull échoue si l'avance rapide est impossible, et vous décidez vous-même) ou utilisez git pull --rebase.
Pull request et bouton Merge
Sur GitHub et GitLab, la fusion passe généralement par une pull/merge request. Le bouton « Create a merge commit » équivaut à git merge --no-ff, « Squash and merge » à une fusion avec compression, « Rebase and merge » à un rebase. Le choix du mode est une convention d'équipe ; le mécanisme est le même qu'en console.
Intégrer main dans votre branche
Une branche de longue durée diverge de main et les conflits s'accumulent. Récupérez régulièrement la branche principale chez vous :
git switch feature/login
git fetch origin
git merge origin/main
Ainsi, les conflits se règlent par petites portions au fil de l'eau, et la fusion finale dans main se passe sans accroc.
Erreurs fréquentes
- Fusion dans le mauvais sens. Vous avez lancé
git merge mainen étant surmain, ou fusionné une fonctionnalité dans une autre. Vérifiez la branche courante avecgit branch --show-currentavant de fusionner. - Fusion avec une branche périmée. Vous avez fusionné un
mainlocal pas mis à jour depuis une semaine. D'abordgit pull, ensuite merge. - « Your local changes would be overwritten by merge ». Le répertoire de travail contient des modifications non commitées dans les mêmes fichiers. Commitez-les ou mettez-les dans le stash.
- « refusing to merge unrelated histories ». Les branches n'ont pas d'ancêtre commun — par exemple, le dépôt local a été créé avec
git initalors que le distant contient déjà un README. Si la fusion est vraiment voulue, ajoutez--allow-unrelated-histories. - Marqueurs de conflit commités. Le build échoue sur
<<<<<<<. Vérifiezgit diff --checket lancez les tests avant de commiter.
Conclusion
git merge est une façon sûre de réunir des branches : il ne réécrit pas les commits existants, il en ajoute un nouveau (ou avance le pointeur). Fusionnez dans la branche courante, gardez l'arbre de travail propre, utilisez --no-ff si les limites des fonctionnalités comptent et --squash si vous voulez un seul commit. Un conflit se résout en trois étapes : modifier le fichier, git add, git commit — et git merge --abort vous ramène toujours au point de départ. La fusion de branches et la résolution de conflits sont pratiquées en détail dans le cours Git.
Questions fréquentes
Quelle différence entre git merge et git pull ?
git pull télécharge d'abord les changements du serveur (git fetch), puis fusionne la branche distante dans la branche courante via git merge ou, si configuré, via rebase. git merge ne travaille qu'avec ce qui est déjà présent en local.
Peut-on fusionner plusieurs branches à la fois ?
Oui, git merge feature-a feature-b effectue une fusion « octopus » à plusieurs parents, mais seulement en l'absence de conflits. En pratique, on fusionne généralement les branches une par une.
Comment voir quelles branches sont déjà fusionnées dans la branche courante ?
git branch --merged liste les branches entièrement intégrées à la branche courante, et git branch --no-merged celles qui ne le sont pas encore. Les branches fusionnées peuvent être supprimées sans risque avec git branch -d.
Comment voir à l'avance ce qu'apportera une fusion ?
Lancez git log main..feature pour voir les commits absents de main, et git diff main...feature pour voir les changements de la branche par rapport à l'ancêtre commun.
Qu'est-ce que ORIG_HEAD après un merge ?
C'est une référence que Git enregistre avant la fusion : elle pointe sur la position de la branche avant le merge. git reset --hard ORIG_HEAD permet d'annuler une fusion locale tant qu'elle n'a pas été poussée.
Ce sujet est traité en détail avec de la pratique dans le cours « Git de zéro à développeur confirmé ».
- Branches : la réalité parallèle de votre code
- Conflits de fusion : ne paniquez pas - comprenons et résolvons