Stacked pull requests : créer et examiner une pile sur GitHub
Les pull requests empilées (stacked pull requests) divisent un gros changement en petites couches. Voici comment GitHub les gère et comment les relire.
En bref. Les pull requests empilées (stacked pull requests) découpent un gros changement en petites pull requests qui s’appuient les unes sur les autres. GitHub a ouvert la fonctionnalité en préversion publique le 30 juillet 2026, puis en disponibilité générale le 6 octobre 2026. Le 14 octobre 2026, nous avons lu les annonces de GitHub, sa documentation et le guide de l’extension gh stack. Nous y ajoutons ce que nous avons appris en montant une pile à la main dans notre propre dépôt, avant l’arrivée de la prise en charge native.
- Chaque couche est une pull request distincte. Celle du bas cible la branche principale, les autres la branche de celle du dessous ; chacune n’affiche que le diff de sa propre couche.
- À chaque couche, les règles sont celles de la branche principale. Les approbations requises, les vérifications requises et CODEOWNERS s’appliquent même si la pull request ne cible pas directement
main. - Le merge se fait de bas en haut. La pull request choisie est fusionnée en une seule opération avec celles du dessous ; celles qui restent au-dessus sont rebasées automatiquement et ciblent la branche principale.
- Contrôlez vous-même sur quel commit les vérifications ont tourné. Dans la pile que nous avons montée à la main, les vérifications vertes appartenaient à un ancien commit ; après un rebase, comparez ce commit à la pointe de la branche.
Qu’est-ce qu’une pile, et à quoi sert-elle ?
Quand vous ouvrez une grosse fonctionnalité en une seule pull request, le relecteur voit des centaines de lignes d’un coup. Les pull requests empilées (stacked pull requests) découpent le même travail en une suite ordonnée de petites pull requests. Cette suite s’appelle une pile (stack), et chaque pull request qu’elle contient, une couche (layer).
Selon la définition de GitHub, une pile est formée de deux pull requests ou plus dans le même dépôt. Celle du bas cible la branche principale de la pile (trunk) ; c’est le plus souvent main, mais ce peut être une branche de release. Chaque pull request suivante cible la branche de celle du dessous. La branche que cible une pull request s’appelle sa base.

Chaque pull request n’affiche que le diff de sa propre couche : la différence entre sa branche et la branche du dessous. Pour l’ordre, la règle est simple : le code dont une couche dépend se trouve dans la même branche ou dans une branche plus basse. Les types partagés et le schéma de la base de données vont en bas, les endpoints d’API et l’interface qui les utilisent vont en haut.
Cela présente deux avantages. Selon la documentation de GitHub, une petite pull request est examinée plus vite, et elle risque moins d’être lue en diagonale, de vieillir ou d’entrer en conflit. Et si votre prochain changement repose sur un travail pas encore fusionné, inutile de l’attendre : vous ouvrez une nouvelle pull request par-dessus. Si c’est un agent d’IA qui écrit le code, l’ordre vient tout seul : une couche par tâche.
Chaîner des branches à la main n’a rien de nouveau ; ce qui est nouveau, c’est que GitHub reconnaît la chaîne. La fonctionnalité est entrée en préversion publique le 30 juillet 2026 et en disponibilité générale le 6 octobre 2026 ; selon l’annonce, elle est disponible sur tous les plans de github.com.
Ce que fait la fonctionnalité, nous le rapportons d’après la documentation de GitHub elle-même, telle que nous l’avons lue le 14 octobre 2026. Nous développons diffox, un outil qui examine les pull requests ; avant l’arrivée de la prise en charge native, nous avons monté une pile à la main dans notre propre dépôt. Ce que nous y avons appris fait l’objet d’une section à part, plus bas.
Comment monter une pile sur GitHub
Pour la ligne de commande, GitHub CLI dispose de l’extension gh stack. Elle crée les branches dans le bon ordre, fait les rebase et les push, ouvre les pull requests et les relie entre elles en une pile. Une pile de trois couches se monte ainsi :
gh extension install github/gh-stack
gh stack init auth-layer # première branche, sur la branche principale
git add .
git commit -m "Couche d’authentification"
gh stack add api-endpoints # deuxième branche, sur la première
git add .
git commit -m "Endpoints d’API"
gh stack add frontend # troisième branche
git add .
git commit -m "Interface"
gh stack submit # fait le push, ouvre les pull requests, les relie en pile
gh stack view # affiche les branches, les pull requests et leur étatgh stack submit ouvre chaque pull request avec la bonne base : la première branche cible main, chaque branche suivante celle du dessous. Si votre branche principale n’est pas main, tapez gh stack init --base release auth-layer. L’extension n’est pas obligatoire ; vous pouvez aussi monter la même pile autrement :
- L’interface web. Ouvrez la première pull request comme d’habitude. Donnez à la deuxième la branche de la première comme base et choisissez l’option « Create stack ». Si vous avez déjà des pull requests ouvertes dont les bases forment une chaîne, GitHub le reconnaît et affiche un bandeau qui propose de les convertir en pile.
- Un agent de codage.
gh skill install github/gh-stackinstalle la compétence (skill) qui apprend à l’agent à travailler avecgh stack; GitHub cite Copilot en exemple. - D’autres outils. Si vous gérez vos branches avec Jujutsu, Sapling ou git-town, la commande
gh stack linkouvre ces mêmes branches en pile sur GitHub.
Sur la page d’une pull request qui fait partie d’une pile apparaît la carte de la pile (stack map). Elle montre chaque pull request de la pile et son état, et vous mène en un clic à la couche voulue ; la branche principale est tout en bas de la carte.
Protection des branches et CI dans une pile
Le point faible d’une chaîne montée à la main, c’étaient les règles. Les couches supérieures ne ciblent pas main mais une branche de fonctionnalité ; la documentation de GitHub écrit d’ailleurs que, sans prise en charge native, les règles de protection des branches et les vérifications de CI ne se déclenchent souvent que sur la pull request du bas.
Dans une pile native, chaque pull request est évaluée selon les règles de la branche principale de la pile, quelle que soit la branche qu’elle cible directement. La page de référence énumère quatre règles : les approbations de review requises, les vérifications d’état requises, CODEOWNERS et le code scanning.
GitHub Actions suit la même logique. Un workflow déclenché par les événements pull_request qui ciblent main tourne pour chaque pull request de la pile ; vous n’avez pas à modifier le fichier du workflow.
Surveillez en revanche la consommation de CI : comme le workflow tourne une fois par pull request, une grande pile la multiplie. D’après la documentation, les expressions de workflow donnent accès aux informations de la pile, pour que vous puissiez limiter les tâches coûteuses à certaines couches : sous github.event.pull_request.stack se trouvent la taille de la pile (size) et la position de la pull request (position). Si les deux sont égales, vous êtes sur la couche du haut.
Comment fusionner une pile
Une pile se fusionne de bas en haut. Vous pouvez en fusionner plusieurs d’un coup, à condition qu’elles se suivent à partir de la pull request la plus basse pas encore fusionnée. Une pull request du milieu ne peut pas être fusionnée seule : celles du dessous sont fusionnées avec elle.

Selon la documentation, trois conditions sont requises pour qu’une pull request puisse être fusionnée : elle satisfait toutes les exigences de protection de la branche principale, chaque pull request du dessous les satisfait aussi, et l’historique entre les branches est linéaire. Si un nouveau commit est arrivé sur une branche inférieure ou si la branche principale a avancé, l’historique n’est plus linéaire. Le bouton « Rebase stack » apparaît alors dans la zone de merge ; son équivalent en ligne de commande est gh stack rebase, suivi de gh stack push.
Les trois méthodes de merge sont prises en charge : merge commit, squash et rebase. Avec squash, chaque pull request devient un seul commit sur la branche principale : trois pull requests, trois commits. Après le merge, la pull request suivante est rebasée automatiquement et cible directement la branche principale. En local, gh stack sync --prune met les branches à jour : il avance la branche principale, replace les branches restantes dessus et supprime les branches locales déjà fusionnées.
Dans les dépôts qui utilisent une merge queue (file d’attente de fusion), les pull requests de la pile entrent dans la file dans le bon ordre. Si l’une d’elles est retirée de la file, celles du dessus le sont aussi.
En montant la pile à la main, nous avons buté à trois endroits
Avant la prise en charge native, en août 2026, nous avons monté à la main une pile de deux couches dans notre propre dépôt. Notre dépôt fusionne en squash. Une fois la pull request du bas fusionnée, nous avons dû faire trois choses à la main pour celle du haut.

La première était le rebase. Le squash transforme les commits de la branche inférieure en un seul nouveau commit ; les anciens commits ne figurent pas dans l’historique de la branche principale. Comme la branche supérieure portait encore ces commits, un merge simple avec la branche principale a produit des conflits. C’est git rebase --onto qui a résolu le problème, en ne déplaçant que les commits propres à la pull request du haut. La deuxième était la base : la pull request du haut continuait de cibler la branche inférieure, et nous l’avons basculée à la main vers la branche principale.
La troisième était silencieuse. Ni le force-push ni le changement de base n’ont lancé la CI sur la pointe rebasée. Les coches vertes de la page appartenaient à des vérifications qui avaient tourné sur la pointe précédente. C’est en fermant puis en rouvrant la pull request que la CI s’est lancée.
# la pull request du bas a été fusionnée en squash ; vous êtes sur la branche du haut
git fetch origin
git rebase --onto origin/main <ancienne-pointe-de-la-branche-du-bas>
git push --force-with-lease
# basculez la base de la pull request du haut vers la branche principale
gh pr edit 123 --base main
# le commit des vérifications est-il bien la pointe de la branche ?
gh pr view 123 --json headRefOid
gh run list --branch api-endpoints --json headSha,workflowName,conclusionNous sommes tombés sur un piège du même genre dans une autre pull request. Quand la pull request est entrée en conflit avec sa base, GitHub n’a pas créé le commit de merge sur lequel tournent les workflows, et dix des onze vérifications n’ont jamais démarré. La page affichait pourtant « All checks have passed · 1 successful check », parce que la seule vérification qui avait tourné n’avait pas besoin de ce commit. La règle est écrite dans la documentation de GitHub : sur une pull request en conflit, les workflows pull_request ne tournent pas.
Ce que la pile native fait à votre place
En mettant la documentation de GitHub en regard de ces étapes, on obtient ce tableau.
| Sujet | Chaîne de branches montée à la main | Pile native de GitHub |
|---|---|---|
| Branche du haut, une fois la couche du bas fusionnée | Rebase à la main ; après un squash, git rebase --onto est nécessaire | Rebase automatique |
| Base de la pull request du haut | Basculée à la main vers la branche principale | Revient d’elle-même sur la branche principale |
| Règles de protection des branches et CI | Souvent déclenchées sur la seule pull request du bas | Évaluées à chaque couche selon la branche principale |
| Vérifications après un rebase | Chez nous, pas relancées ; le résultat de l’ancien commit restait affiché | Relancées après « Rebase stack » et gh stack push |
Dans la colonne « Chaîne de branches montée à la main », le rebase, la base et les vérifications viennent de notre propre expérience ; la ligne des règles vient de la documentation de GitHub. Colonne « Pile native de GitHub » : documentation de GitHub, 14 octobre 2026.
Selon la documentation, le rebase et la base sont désormais l’affaire de GitHub. Pour le squash, la documentation de gh stack elle-même décrit ce que nous avons vu : après un squash, les anciens commits ne sont pas dans l’historique de la branche principale ; gh stack sync reconnaît cette situation et fait le rebase avec --onto.
Pour les vérifications, la documentation décrit clairement deux cas : la CI est relancée après le bouton « Rebase stack » et après gh stack push. Pour le rebase automatique qui suit un merge partiel, nous n’avons rien trouvé d’équivalent dans la documentation le 14 octobre 2026. Avant de fusionner une couche supérieure, il est donc prudent de regarder ces quatre points.

Comment examiner une pile
Avec une pile, on n’examine plus un seul gros diff, mais plusieurs petits, chacun par rapport à la couche du dessous. Pour le relecteur, cela change trois choses.
- Commencez par le bas. GitHub recommande, quand les couches sont étroitement liées, de demander la review en commençant par le bas : une erreur en bas se propage à chaque branche au-dessus. Si des personnes différentes examinent les couches, elles peuvent aussi avancer en parallèle.
- Ne cherchez pas le code de la couche inférieure dans le diff de la couche supérieure. Si une fonction appelée par la troisième couche est définie dans la première, elle n’apparaît pas dans le diff, et elle n’est pas encore dans
mainnon plus. La définition se trouve dans la branche même que vous examinez ; la carte de la pile vous mène à la pull request de cette couche. - Écrivez le constat sur la couche à laquelle il appartient. Selon la documentation, la correction se fait dans la branche à laquelle appartient le changement, puis les branches du dessus sont rebasées. Si le diff n’a pas changé, le rebase ne fait pas tomber les approbations existantes.
gh stack checkout auth-layer # la couche à laquelle appartient la correction
git add .
git commit -m "Gérer le cas du token expiré"
gh stack rebase --upstack # replace les branches du dessus sur ce commit
gh stack push # envoie les branches avec --force-with-leaseCes règles valent autant pour un humain que pour un outil de revue de code par IA : les deux lisent le diff de la couche. Approuver une couche n’est pas approuver celles du dessous ; chaque pull request porte ses propres approbations requises et ses propres vérifications.
Tous les changements n’ont pas besoin d’une pile
Une pile sert aux travaux qui s’appuient les uns sur les autres et sont trop gros pour être examinés d’une traite. Dans les cas suivants, une seule pull request ou des pull requests indépendantes conviennent mieux :
- Si le changement est petit. Découper un diff qui se lit d’une traite oblige le relecteur à ouvrir des pages en plus.
- Si les travaux ne dépendent pas les uns des autres. Une pile est une seule chaîne linéaire : chaque branche a une branche en dessous et au plus une au-dessus. Une correction sans rapport est une pull request à part.
- Si la contribution vient d’un fork. Toutes les branches doivent être dans le même dépôt ; les piles entre forks ne sont pas prises en charge.
- Si la CI coûte cher. Chaque couche lance ses propres vérifications ; limitez d’abord les tâches coûteuses à certaines couches.
La documentation que nous avons lue le 14 octobre 2026 pose trois autres limites. GitHub Desktop ne prend pas en charge les piles. Les outils qui font le merge via l’API doivent passer à l’API de merge asynchrone. Pour l’auto-merge, deux pages disent des choses différentes : la documentation sur le merge écrit qu’il n’est pas pris en charge dans une pile, l’annonce du 6 octobre qu’il arrivera dans les semaines à venir.
diffox examine le diff de la pull request qu’on lui confie. Dans une pile, chaque couche est une pull request distincte : chacune est donc examinée séparément, et les constats sont écrits sur la pull request de cette couche.
Questions fréquentes
Qu’est-ce qu’une stacked pull request ?
Les stacked pull requests (pull requests empilées) sont une façon de découper un gros changement en petites pull requests qui s’appuient les unes sur les autres. La pull request du bas cible la branche principale, le plus souvent main ; chaque pull request suivante cible la branche de celle du dessous. Chacune n’affiche que le diff de sa propre couche et est examinée séparément.
Comment créer une stacked pull request sur GitHub ?
En ligne de commande, installez l’extension avec gh extension install github/gh-stack. gh stack init crée la première branche, gh stack add les branches qui viennent au-dessus ; gh stack submit ouvre les pull requests et les relie en pile. Dans l’interface web, il suffit de donner la branche de la première pull request comme base de la deuxième et de choisir l’option « Create stack ». Toutes les branches doivent être dans le même dépôt.
Qu’est-ce que gh stack ?
gh stack est l’extension de GitHub CLI publiée par GitHub pour gérer les piles. Elle crée les branches dans le bon ordre, fait les rebase en cascade et les push, ouvre les pull requests et permet de passer d’une couche à l’autre. Elle n’est pas obligatoire ; une pile se monte aussi depuis l’interface web.
Les stacked pull requests sont-elles toujours en public preview ?
Non. GitHub a annoncé la fonctionnalité en public preview (préversion publique) le 30 juillet 2026 et l’a ouverte en disponibilité générale le 6 octobre 2026. Selon l’annonce, elle est disponible sur tous les plans de github.com et sera intégrée à une prochaine version de GitHub Enterprise Server.
Que deviennent les pull requests du dessus quand celle du bas est fusionnée ?
Selon la documentation de GitHub, elles restent ouvertes ; la pull request suivante est rebasée automatiquement et cible directement la branche principale. Les trois méthodes de merge sont prises en charge, squash compris. gh stack sync met à jour vos branches locales ; il reconnaît la situation qui suit un squash et fait le rebase avec --onto.
Comment fonctionnent la CI et les règles de protection des branches avec les stacked pull requests ?
Chaque pull request de la pile est évaluée selon les règles de la branche principale de la pile, quelle que soit la branche qu’elle cible directement : les approbations requises, les vérifications requises et CODEOWNERS s’appliquent à chaque couche. Les workflows pull_request qui ciblent main tournent pour chaque couche. Pour qu’une couche puisse être fusionnée, toutes les couches du dessous doivent aussi remplir les conditions.
diffox examine-t-il les pull requests empilées ?
diffox examine le diff de la pull request qu’on lui confie. Chaque couche d’une pile étant une pull request distincte, chacune est examinée avec son propre diff. diffox n’applique aucun traitement propre aux piles, et aucun réglage n’est nécessaire.