diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ae2b9a1..a15057b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -136,10 +136,12 @@ below follows from that. NOT be revived, not even for follow-up on the same topic: a merged pull request cannot describe new work, and a closed one was set aside. Follow-up is a new branch, cut fresh from `origin/main`. -* To carry `main`'s progress into an open branch: while the branch is yours - alone, **rebase** it onto `origin/main`; once others may have based work on - it, **merge** `origin/main` in instead. Either keeps the branch current - without rewriting what a collaborator has already pulled. +* To carry `main`'s progress into an open branch, **rebase** it onto + `origin/main`. Never merge `origin/main` into a branch (decision: ADR-0071): + the rebase that lands the branch replays its own commits onto `main`, so that + merge would put back the very shape the linear history removed. If someone + else has already based work on the branch, coordinate the force-push or split + the work in two — do not reach for a merge. * Rewriting a branch's history — a force-push, a `git rebase -i` — is fine while the branch is **yours alone**, and is how a commit message the lint or a reviewer rejected gets fixed, even mid-review: a rejected message cannot be diff --git a/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.fr.md b/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.fr.md index 5442c614..ed82b9b9 100644 --- a/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.fr.md +++ b/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.fr.md @@ -2,8 +2,9 @@ 🌍 🇫🇷 Français (ce fichier) · 🇬🇧 [English](0070-land-pull-requests-by-rebasing-and-keep-main-linear.md) -**Statut :** Proposé +**Statut :** Accepté **Proposé :** 2026-08-10 +**Accepté :** 2026-08-10 **Décideurs :** Reefact ## Contexte diff --git a/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.md b/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.md index 887aea7e..cc40433a 100644 --- a/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.md +++ b/doc/handwritten/for-maintainers/adr/0070-land-pull-requests-by-rebasing-and-keep-main-linear.md @@ -2,8 +2,9 @@ 🌍 🇬🇧 English (this file) · 🇫🇷 [Français](0070-land-pull-requests-by-rebasing-and-keep-main-linear.fr.md) -**Status:** Proposed +**Status:** Accepted **Proposed:** 2026-08-10 +**Accepted:** 2026-08-10 **Decision Makers:** Reefact ## Context diff --git a/doc/handwritten/for-maintainers/adr/0071-refresh-an-open-branch-only-by-rebasing-it.fr.md b/doc/handwritten/for-maintainers/adr/0071-refresh-an-open-branch-only-by-rebasing-it.fr.md new file mode 100644 index 00000000..1f678d46 --- /dev/null +++ b/doc/handwritten/for-maintainers/adr/0071-refresh-an-open-branch-only-by-rebasing-it.fr.md @@ -0,0 +1,93 @@ +# ADR-0071 | Rafraîchir une branche ouverte uniquement en la rebasant sur `main` + +🌍 🇫🇷 Français (ce fichier) · 🇬🇧 [English](0071-refresh-an-open-branch-only-by-rebasing-it.md) + +**Statut :** Accepté +**Proposé :** 2026-08-10 +**Accepté :** 2026-08-10 +**Décideurs :** Reefact + +## Contexte + +L'[ADR-0070](0070-land-pull-requests-by-rebasing-and-keep-main-linear.fr.md) a +rendu l'historique de `main` linéaire et fait du rebase la façon dont une pull +request atterrit. Elle a nommé un point qu'elle ne tranchait pas, en le laissant à +une décision distincte : `CONTRIBUTING.md` (« Branches ») propose **deux** façons +de reporter les avancées de `main` dans une branche ouverte — la rebaser sur +`origin/main` tant qu'elle n'appartient qu'à vous, ou y merger `origin/main` dès +que d'autres ont pu baser du travail dessus. + +La seconde produit exactement les commits que l'ADR-0070 a supprimés. Sur les 401 +commits de merge retirés de `main`, **87 étaient des `Merge branch 'main' into …`**, +créés en appliquant cette règle. Ils n'étaient pas gratuits non plus : 15 d'entre +eux portaient une résolution de conflit faite à la main, et reproduire ces +résolutions a été la partie délicate de la réécriture. + +La règle ne fait d'ailleurs plus ce qu'elle annonce sous la nouvelle stratégie. +Elle a été écrite pour un dépôt à commits de merge, où un merge effectué sur une +branche restait sur la branche et où seul son résultat atteignait `main`. Avec le +*Rebase and merge*, GitHub rejoue les commits propres de la branche sur `main` : +une branche qui a mergé `main` en elle emporte donc ce merge dans ce qui +atterrit. La règle réimporte en silence le bruit que la réécriture a enlevé. + +Ce que la règle protège est réel : rebaser une branche que quelqu'un d'autre a +déjà récupérée détruit son travail. Mais cette situation n'existe pas dans le +modèle de branches de ce dépôt. `CONTRIBUTING.md` énonce qu'une branche porte +**une** pull request, qu'elle est « l'espace de travail jetable d'une pull +request », et que son nom prend la forme `/` — le +propriétaire est nommé dans la ref elle-même. Une branche sur laquelle deux +personnes construisent est déjà hors du modèle, et l'option de merge est la seule +règle qui suppose le contraire. + +## Décision + +Une branche ouverte est mise à jour avec `main` **uniquement en la rebasant sur +`origin/main`**. On ne merge pas `origin/main` dans une branche. + +## Conséquences + +### Le force-push devient la façon ordinaire de rafraîchir une branche + +C'est déjà le cas pour les branches que ce dépôt possède réellement. +`CONTRIBUTING.md` autorise à réécrire l'historique d'une branche tant qu'elle +n'appartient qu'à vous et impose `git push --force-with-lease` plutôt qu'un +`--force` nu ; rafraîchir, c'est la même opération. Rien de nouveau n'est +autorisé, une alternative est retirée. + +### Une branche déjà récupérée par autrui n'a plus d'échappatoire, volontairement + +Si une seconde personne a réellement basé du travail sur une branche ouverte, la +réponse est de coordonner le force-push ou de scinder le travail en deux branches +— pas de placer un commit de merge dans l'historique futur de `main`. Rendre ce +cas visible est précisément le but : sous la règle précédente il se réglait en +silence, par un commit que personne ne relisait. + +### Le coût de la réécriture n'est pas payé une seconde fois + +L'ADR-0070 enregistre que réécrire `main` est une exception unique dont la +fenêtre se referme à la 1.0.0. Laisser l'option de merge ouverte permettrait à la +forme de commit qu'elle a supprimée de réapparaître une branche à la fois, sans +qu'une seconde réécriture soit disponible pour nettoyer. + +## Alternatives considérées + +### Conserver l'option de merge et compter sur GitHub pour l'aplatir + +Envisagée parce que le *Rebase and merge* rejoue bien les commits d'une branche, +et qu'on pourrait attendre qu'un commit de merge disparaisse au passage. Rejetée +parce que le résultat n'est pas quelque chose sur quoi s'appuyer : ce qu'un +rebase fait d'un commit de merge dépend de la forme de la branche, et une règle +dont la sûreté repose sur le comportement d'aplatissement actuel de la plateforme +est une règle qui casse en silence quand ce comportement change. L'historique du +dépôt en est la preuve — 87 commits de cette forme ont atteint `main` sous la +stratégie précédente. + +### Conserver l'option de merge pour les seules branches réellement partagées + +Envisagée parce que la protection offerte est réelle quand elle s'applique. +Rejetée parce qu'elle s'applique à une branche que ce dépôt n'a pas, et parce +qu'une règle assortie d'une exception se lit comme une règle assortie d'une +échappatoire. La doctrine de branche existante — une pull request, un +propriétaire, coupée fraîche, supprimée au merge — est ce qui rend l'exception +inutile ; si cette doctrine change un jour, la présente décision devra être +revue avec elle plutôt qu'affaiblie d'avance pour un cas qui ne se produit pas. diff --git a/doc/handwritten/for-maintainers/adr/0071-refresh-an-open-branch-only-by-rebasing-it.md b/doc/handwritten/for-maintainers/adr/0071-refresh-an-open-branch-only-by-rebasing-it.md new file mode 100644 index 00000000..f108b461 --- /dev/null +++ b/doc/handwritten/for-maintainers/adr/0071-refresh-an-open-branch-only-by-rebasing-it.md @@ -0,0 +1,86 @@ +# ADR-0071 | Refresh an open branch only by rebasing it onto `main` + +🌍 🇬🇧 English (this file) · 🇫🇷 [Français](0071-refresh-an-open-branch-only-by-rebasing-it.fr.md) + +**Status:** Accepted +**Proposed:** 2026-08-10 +**Accepted:** 2026-08-10 +**Decision Makers:** Reefact + +## Context + +[ADR-0070](0070-land-pull-requests-by-rebasing-and-keep-main-linear.md) made +`main`'s history linear and made rebase the way a pull request lands. It named +one thing it did not settle, and left it for a separate decision: `CONTRIBUTING.md` +("Branches") offers **two** ways to carry `main`'s progress into an open branch — +rebase it onto `origin/main` while the branch is yours alone, or merge +`origin/main` into it once others may have based work on it. + +The second option produces exactly the commits ADR-0070 removed. Of the 401 merge +commits deleted from `main`, **87 were `Merge branch 'main' into …`** — created by +following this rule. They were not free either: 15 of them carried a hand-made +conflict resolution, and reproducing those resolutions was the delicate part of +the rewrite. + +The rule also no longer does what it says under the new strategy. It was written +for a merge-commit repository, where a merge performed on a branch stayed on the +branch and only its result reached `main`. With *Rebase and merge*, GitHub +replays the branch's own commits onto `main` — so a branch that merged `main` +into itself carries that merge into what lands. The rule quietly re-imports the +noise the rewrite removed. + +What the rule protects is real: rebasing a branch someone else has already pulled +discards their work. But that situation does not exist in this repository's +branch model. `CONTRIBUTING.md` states that a branch carries **one** pull +request, that it is "the disposable workspace of one pull request", and that its +name takes the form `/` — the owner is named in the +ref itself. A branch two people build on is already outside the model, and the +merge option is the only rule that assumes otherwise. + +## Decision + +An open branch is brought up to date with `main` **only by rebasing it onto +`origin/main`**. Merging `origin/main` into a branch is not done. + +## Consequences + +### Force-push becomes the ordinary way to refresh a branch + +It already is, for the branches this repository actually has. `CONTRIBUTING.md` +permits rewriting a branch's history while it is yours alone and requires +`git push --force-with-lease` rather than a bare `--force`; refreshing is that +same operation. Nothing new is allowed, and one alternative is withdrawn. + +### A branch someone else has pulled has no escape hatch, by design + +If a second person has genuinely based work on an open branch, the answer is to +coordinate the force-push or to split the work into two branches — not to put a +merge commit into `main`'s future history. Making that case visible is the point: +under the previous rule it was resolved silently, by a commit nobody reviewed. + +### The rewrite's cost is not re-incurred + +ADR-0070 records that rewriting `main` is a one-time exception that closes at +1.0.0. Leaving the merge option open would let the exact commit shape it removed +reappear one branch at a time, with no second rewrite available to clean it up. + +## Alternatives Considered + +### Keep the merge option, and rely on GitHub to flatten it + +Considered because *Rebase and merge* does replay a branch's commits, and one +might expect a merge commit to be dropped in the process. Rejected because the +result is not something to rely on: what a rebase does with a merge commit +depends on the branch's shape, and a rule whose safety depends on the platform's +current flattening behaviour is a rule that breaks silently when that behaviour +changes. The repository's own history is the evidence — 87 such commits reached +`main` under the previous strategy. + +### Keep the merge option for genuinely shared branches only + +Considered because the protection it offers is real when it applies. Rejected +because it applies to a branch this repository does not have, and because a rule +with an exception is read as a rule with an escape. The existing branch doctrine — +one pull request, one owner, cut fresh, deleted on merge — is what makes the +exception unnecessary; if that doctrine ever changes, this decision should be +revisited with it rather than pre-weakened for a case that does not occur. diff --git a/doc/handwritten/for-maintainers/adr/README.md b/doc/handwritten/for-maintainers/adr/README.md index 1fcc8a39..7d0d1138 100644 --- a/doc/handwritten/for-maintainers/adr/README.md +++ b/doc/handwritten/for-maintainers/adr/README.md @@ -237,4 +237,5 @@ Optional supporting material: | [ADR-0062](0062-derive-the-build-rule-set-from-the-quality-profile.md) | Derive the build's Sonar rule set from the quality profile: generated membership, hand-written exceptions, weekly drift check | Accepted | | [ADR-0067](0067-treat-the-cli-s-exit-codes-as-a-closed-published-contract.md) | Treat the CLI's exit codes as a closed, published contract | Accepted | | [ADR-0069](0069-consume-justdummies-from-its-own-repository.md) | Consume JustDummies from its own repository | Accepted | -| [ADR-0070](0070-land-pull-requests-by-rebasing-and-keep-main-linear.md) | Land pull requests by rebasing, and keep `main`'s history linear | Proposed | +| [ADR-0070](0070-land-pull-requests-by-rebasing-and-keep-main-linear.md) | Land pull requests by rebasing, and keep `main`'s history linear | Accepted | +| [ADR-0071](0071-refresh-an-open-branch-only-by-rebasing-it.md) | Refresh an open branch only by rebasing it onto `main` | Accepted | diff --git a/doc/handwritten/for-users/CONTRIBUTING.fr.md b/doc/handwritten/for-users/CONTRIBUTING.fr.md index 2517878c..1116cd89 100644 --- a/doc/handwritten/for-users/CONTRIBUTING.fr.md +++ b/doc/handwritten/for-users/CONTRIBUTING.fr.md @@ -148,10 +148,12 @@ Tout ce qui suit en découle. PAS être ravivée, pas même pour un suivi sur le même sujet : une pull request mergée ne peut pas décrire un nouveau travail, et une pull request fermée a été mise de côté. Le suivi passe par une nouvelle branche, coupée fraîchement depuis `origin/main`. -* Pour reporter la progression de `main` dans une branche ouverte : tant que la branche - n’est qu’à vous, **rebasez-la** sur `origin/main` ; dès que d’autres ont pu baser du - travail dessus, **mergez** plutôt `origin/main` dedans. L’un comme l’autre garde la - branche à jour sans réécrire ce qu’un collaborateur a déjà récupéré (pull). +* Pour reporter la progression de `main` dans une branche ouverte, **rebasez-la** sur + `origin/main`. Ne mergez jamais `origin/main` dans une branche (décision : ADR-0071) : + le rebase qui intègre la branche rejoue ses propres commits sur `main`, ce merge y + remettrait donc la forme même que l’historique linéaire a supprimée. Si quelqu’un + d’autre a déjà basé du travail sur la branche, coordonnez le force-push ou scindez le + travail en deux — ne recourez pas à un merge. * Réécrire l’historique d’une branche — un force-push, un `git rebase -i` — est acceptable tant que la branche n’est **qu’à vous**, et c’est ainsi qu’un message de commit rejeté par le lint ou par un relecteur se corrige, même en cours de relecture :