0053 — Merge commit imposé sur `main` (abandon du squash)
Contexte
Section intitulée « Contexte »Une pull request (proposition de fusion d’une branche dans une autre,
revue avant intégration) peut être intégrée à main de trois façons sur
GitHub :
- merge commit — un commit de fusion relie les deux branches ; tous les commits de la branche sont conservés tels quels dans l’historique ;
- squash — les commits de la branche sont écrasés en un seul commit
posé sur
main; l’historique fin de la branche disparaît ; - rebase — les commits sont rejoués un à un au sommet de
main, sans commit de fusion ; les SHA changent.
L’ADR 0016 avait acté le
squash comme stratégie, au nom d’un « historique linéaire ». L’usage a
révélé le prix de ce choix : le squash réécrit les commits. Une branche
mergée par squash a, sur main, un commit de SHA et de contenu agrégés
différents de ses commits d’origine. Conséquences observées :
- une branche locale conservée après merge paraît « en avance » alors que son
travail est déjà intégré — git ne peut plus le reconnaître, puisque les
SHA diffèrent (cas rencontré sur
docs/appliquer-charte, dont les deux commits se sont révélés vides au report surmain) ; - le message de commit détaillé rédigé sur la branche (corps, justifications) est aplati dans la description de la PR, moins durable que le commit ;
- le découpage en commits atomiques — pensé à la rédaction — est perdu à
l’intégration, ce qui appauvrit
git bisectetgit blame.
Le bénéfice affiché (« linéarité ») est en partie illusoire : un merge
commit donne un historique tout aussi lisible via git log --first-parent,
tout en préservant les commits d’origine.
Décision
Section intitulée « Décision »Sur
main, le merge commit est la seule stratégie d’intégration. Le squash et le rebase sont retirés des options proposées.
La contrainte est portée par la ruleset GitHub main (jeu de règles
appliqué à la branche, prioritaire sur les réglages globaux du dépôt) : sa
règle pull_request restreint allowed_merge_methods à ["merge"]. C’est
elle qui gouverne le bouton « Merge » des PR vers main.
Les réglages du dépôt sont alignés autant que l’API l’autorise : merge
commit activé, squash désactivé. GitHub interdit toutefois de désactiver
à la fois squash et rebase au niveau du dépôt (erreur
protected_branch_policy : « il faut autoriser squash ou rebase, ou les
deux »). Le rebase reste donc activé dans les réglages du dépôt comme
soupape imposée — sans effet sur main, où la ruleset prime et n’autorise
que le merge commit. Cette case ne s’appliquerait qu’à une branche non
couverte par une ruleset (il n’y en a pas aujourd’hui).
Deuxième piège : deux systèmes de protection superposés. main est
protégée à la fois par une ruleset (système récent) et par une branch
protection classique (système hérité, configuré par
l’ADR 0016). La branch
protection classique portait required_linear_history = true — exigence d’un
historique linéaire, vestige du choix « squash » de 0016. Or un merge
commit crée par nature un historique non linéaire : cette règle
interdisait donc le merge commit (« This branch must not contain merge
commits »), en contradiction directe avec la ruleset. Il a fallu désactiver
required_linear_history dans la branch protection classique pour débloquer
le merge. Les autres protections de 0016 (status checks, force-push bloqué,
bypass admin) y sont conservées intactes.
Appliquée le 2026-06-10.
Accepted (2026-06-10). Amende l’ADR 0016 sur le seul point de la stratégie de merge (la phrase « historique linéaire — squash-merge des PR » ne tient plus) ; les autres protections de 0016 (status checks requis, force-push bloqué, bypass admin) restent en vigueur.
Conséquences
Section intitulée « Conséquences »Bénéfices. Les commits rédigés sur une branche — découpage atomique,
messages détaillés — arrivent intacts sur main. Une branche mergée est
reconnue comme intégrée par git (mêmes SHA), ce qui supprime les fausses
« avances » et les reports à vide. git bisect et git blame retrouvent leur
granularité. La lisibilité « linéaire » reste accessible via
git log --first-parent.
Prix à payer. L’historique gagne des commits de fusion et un graphe non
strictement linéaire. La discipline de découpage se déplace en amont : la
qualité de l’historique de main dépend désormais de la propreté des commits
de chaque branche (le squash la masquait jusqu’ici). Une branche bavarde ou
brouillonne pollue maintenant main — d’où l’importance des
Conventional Commits
et des hooks lefthook.
Garde-fous.
- La source de vérité est la ruleset
main(allowed_merge_methods = ["merge"]), pas les réglages du dépôt. Toute revue de configuration vérifie la ruleset, pas seulement l’onglet « General » du dépôt. - Le
rebaserésiduel dans les réglages du dépôt est un artefact de la contrainte GitHub, pas une autorisation : il n’a d’effet que hors couverture d’une ruleset. Si une branche protégée s’ajoute, lui appliquer la même ruleset (merge seul). - Vérifier les deux systèmes de protection.
maincumule une ruleset et une branch protection classique. Un réglage de l’un peut contredire silencieusement l’autre — icirequired_linear_history(classique) interdisait le merge commit imposé par la ruleset. Toute revue de configuration inspecte les deux ;required_linear_historydoit rester désactivé tant que le merge commit est la stratégie. - Avant d’ouvrir une PR, rebaser/nettoyer la branche localement : sur
main, ses commits seront conservés tels quels. - Revue à la cadence d’audit transverse (ADR 0039), en même temps que les règles de protection de 0016.