Introduction
Git a deux résultats de merge fondamentalement différents : fast-forward, où la pointe d'une branche est simplement déplacée, et trois-voies, où un nouveau commit avec deux parents est créé. Le choix dépend de si les branches ont divergé.
Fast-forward
Si la pointe de main est un ancêtre de feature, aucun vrai merge n'est nécessaire. Git fait simplement avancer main :
A---B---C feature
/
main
# après git merge feature
A---B---C main, feature
Commande :
git switch main
git merge feature
Pour forcer un vrai merge commit même quand un fast-forward est possible :
git merge --no-ff feature
Merge trois-voies
Quand les deux branches ont divergé, Git trouve la base de merge (l'ancêtre commun le plus récent) et combine les changements des deux côtés :
A---B---C feature
/
A---D---E main
# après git merge feature
A---D---E---M main
\ /
B---C feature
La stratégie de merge par défaut de Git depuis 2.33 est ort ; avant c'était recursive. Les deux calculent un merge trois-voies par fichier en utilisant la base de merge.
Inspecter
git merge-base main feature
git log --oneline --graph --all
git show --stat HEAD # voir le merge commit
Stratégies et options
git merge -X ours feature # préférer notre côté en cas de conflit
git merge -X theirs feature # préférer leur côté
git merge -X ignore-space-change feature
git merge --squash feature # appliquer les changements sans merge commit
--squash crée un commit unique contenant tous les changements de feature, sans lien parent vers elle. Utile pour un historique propre mais perd le détail par commit.
Annuler et défaire
git merge --abort # en plein merge, retour à l'état pré-merge
git reset --hard ORIG_HEAD # après un merge terminé, l'annuler
Configurer la politique fast-forward
git config --global merge.ff false # toujours créer un merge commit
git config --global merge.ff only # seulement fast-forward, refuser sinon
git config --global pull.ff only # idem pour les pulls
Pilotes de merge et attributs
Certains fichiers se mergent mal avec un trois-voies textuel (lockfiles générés par machine, documentation générée). Configurez un pilote de merge personnalisé via .gitattributes et git config :
# .gitattributes
package-lock.json merge=ours
Gemfile.lock merge=union
git config merge.ours.driver true
git config merge.union.name "Line union merge"
git config merge.union.driver "git merge-file --union %A %O %B"
La stratégie union intégrée garde les deux côtés ; ours choisit toujours notre version ; les pilotes personnalisés peuvent exécuter n'importe quel programme. À utiliser avec parcimonie ; un comportement de merge surprenant déroute les reviewers.
Erreurs fréquentes
Croire que le fast-forward est en quelque sorte spécial ou risqué ; il ne l'est pas, c'est juste un déplacement de pointeur. Utiliser --no-ff sur chaque merge dans un projet à évolution rapide, polluant l'historique avec des merge commits vides. Utiliser -X ours alors que vous vouliez --strategy=ours (qui abandonne entièrement l'autre côté) ou vice versa. Enfin, éditer manuellement le message de merge commit après coup et casser la formulation standardisée « Merge branch 'X' » sur laquelle s'appuient les outils ; si vous devez éditer, faites-le avant git commit.