Comment fonctionnent les pull requests empilées sur GitHub
Découvrez les pull requests empilées sur GitHub : créer une pile avec gh-stack, examiner ses niveaux, rebaser les modifications et fusionner les PR dépendantes.
Les pull requests empilées (stacked pull requests) de GitHub découpent une modification volumineuse en une chaîne ordonnée de petites pull requests. Chacune cible la branche de la pull request située juste en dessous, et GitHub relit, rebase et fusionne la chaîne comme un tout.
La plupart des équipes connaissent l’alternative. Vous développez une fonctionnalité en trois parties dépendantes, vous ouvrez trois PR, et la deuxième affiche les modifications de la première en plus des siennes. Les relecteurs abandonnent, ou bien vous regroupez tout dans une PR gigantesque que personne ne lit vraiment.
Cet article décrit de bout en bout le fonctionnement des piles natives de GitHub : comment créer une pile, comment se déroule la revue, ce qui se passe lorsqu’une PR au milieu de la pile est modifiée, et ce que fait chaque option de fusion. Si vous avez besoin d’une mise à niveau sur les PR classiques, consultez comment créer des pull requests depuis votre terminal.
Points clés
- Dans une pile GitHub, la base de chaque pull request est la branche de la PR située en dessous, de sorte que chaque diff ne contient que les modifications de cette couche.
- GitHub a annoncé les pull requests empilées en préversion publique le 30 juillet 2026. La fonctionnalité est susceptible d’évoluer.
- D’après le guide de démarrage rapide de GitHub, l’extension CLI gh-stack nécessite GitHub CLI 2.90.0 ou une version ultérieure, ainsi que Git 2.20 ou une version ultérieure.
- Si une couche intermédiaire est modifiée, toutes les branches au-dessus restent sur un commit obsolète. Ces branches doivent être rebasées de bas en haut, puis faire l’objet d’un push forcé.
- Fusionner la PR prête la plus haute intègre celle-ci et toutes les PR non fusionnées situées en dessous en une seule fusion. Si la fusion échoue en cours de route, les PR déjà intégrées restent fusionnées et les autres restent ouvertes. Chaque PR est vérifiée selon les règles de la branche de base de la pile.
Le problème des pull requests dépendantes
Les pull requests dépendantes posent problème lors de la revue, car GitHub compare chaque PR à sa branche de base. Imaginons que vous ouvriez trois PR ciblant toutes main : auth-middleware, puis api-routes, puis ui-login. Le diff de la deuxième PR contient aussi tout le code du middleware, et celui de la troisième contient l’ensemble. Impossible de savoir quelles lignes une PR donnée introduit réellement.
Sans piles natives, les développeurs contournent généralement le problème de deux façons. Soit ils regroupent tout dans une seule grosse PR, ce qui ralentit la revue et masque les erreurs. Soit ils font pointer manuellement chaque PR vers la branche située en dessous, puis rebasent et reciblent eux-mêmes toute la chaîne à chaque modification.
Que sont les PR empilées de GitHub ?
Une pull request empilée sur GitHub est un maillon d’une chaîne ordonnée. Chaque PR utilise comme base la branche de la PR située en dessous, de sorte que son diff ne montre que les modifications apportées dans cette couche. La présentation des pull requests empilées par GitHub explique que la PR du bas cible le tronc (trunk) de la pile, généralement main, mais il peut s’agir de n’importe quelle branche, par exemple une branche de release. Toutes les branches d’une pile doivent se trouver dans le même dépôt : une pile ne peut donc pas s’étendre sur plusieurs forks.
Voici à quoi ressemble l’exemple à trois couches une fois empilé :
main
└─ auth-middleware PR → main (diff: middleware only)
└─ api-routes PR → auth-middleware (diff: routes only)
└─ ui-login PR → api-routes (diff: login UI only)
Le modèle repose toujours sur des branches et des bases, des notions que Git maîtrise déjà. GitHub ajoute le suivi par-dessus : il connaît l’ordre des PR, les relie entre elles et les traite comme un groupe.
Créer une pile avec la CLI gh
L’extension gh-stack permet de créer et de gérer des piles depuis le terminal. Le guide de démarrage rapide de GitHub indique comme prérequis GitHub CLI 2.90.0 ou ultérieure et Git 2.20 ou ultérieure. La référence des commandes CLI et le README de l’extension mentionnent un minimum plus bas, gh 2.0 : la valeur du guide de démarrage rapide est donc la plus sûre à suivre. Vous devez également être authentifié et disposer d’un accès en écriture (push) au dépôt. Si vous découvrez gh, commencez par créer des pull requests depuis le terminal.
gh --version # 2.90.0 or later
git --version # 2.20 or later
gh auth login
gh extension install github/gh-stack
Construisez ensuite la pile, une couche à la fois :
gh stack init # prompts for the first branch name, e.g. auth-middleware
git add -A
git commit -m "Auth middleware"
gh stack add api-routes
git add -A
git commit -m "API routes"
gh stack add ui-login
git add -A
git commit -m "Login UI"
gh stack push # pushes branches only
gh stack submit # opens PRs with correct bases and links them
gh stack view # branches, PR links, statuses, latest commits
gh stack init initialise le suivi local de la pile et crée la première branche. Sauf si vous passez l’option --base, le tronc est la branche par défaut du dépôt. La référence des commandes CLI souligne que push se contente d’envoyer les branches et ne touche jamais aux pull requests. C’est submit qui ouvre les PR, attribue à chacune la bonne branche de base et les relie en une pile.
Vous pouvez aussi passer par l’interface web. Sur github.com, vous pouvez créer une pile en ouvrant une PR dont la base est la branche d’une PR précédente, puis en sélectionnant Create stack. Pour agrandir une pile existante, ouvrez n’importe quelle PR de celle-ci et choisissez Add to stack dans l’en-tête. Si vos PR ouvertes forment déjà une chaîne, GitHub affiche une bannière proposant de les transformer en pile. Les piles sont utilisables depuis l’application mobile GitHub ainsi que par des agents de codage comme Copilot via le skill gh-stack. GitHub Desktop ne les prend pas en charge.
Relire une pile couche par couche
Les relecteurs peuvent approuver chaque couche d’une pile séparément. Plusieurs relecteurs peuvent travailler sur différentes couches en parallèle pendant que l’auteur continue de construire par-dessus. Le principal avantage des pull requests empilées est que la revue porte sur de petits diffs, et qu’il n’est pas nécessaire d’attendre que toute la fonctionnalité soit terminée pour la lancer.
Lorsque vous ouvrez une PR de la pile, vous ne voyez que le diff de cette couche. La zone de fusion contient une carte de la pile qui répertorie toutes les PR de la chaîne avec leur statut, et le chiffre affiché sur l’icône de pile en haut de la page indique la couche ouverte.
Des couches de petite taille laissent également un historique plus exploitable sur main. Chaque commit intégré est ciblé, si bien qu’une session git bisect peut pointer vers une modification précise plutôt que vers une fusion d’un millier de lignes.
Mettre à jour une PR au milieu de la pile
Lorsqu’un nouveau commit arrive sur une branche au milieu d’une pile, toutes les branches au-dessus pointent toujours vers l’ancien commit. Ces branches supérieures doivent être rebasées sur la couche mise à jour, puis faire l’objet d’un push forcé. Dans l’exemple, une correction issue de la revue sur api-routes laisse ui-login en retard. auth-middleware n’est pas concernée.
Avec une chaîne gérée à la main, vous effectuez cette opération vous-même en Git pur (consultez 10 commandes Git pour les bases du rebase) :
# plain Git, no gh-stack
git checkout ui-login
git rebase api-routes # resolve any conflicts
git push --force-with-lease
# repeat for every branch above, bottom to top
Avec gh-stack, le tutoriel sur l’empilement de modifications de code réduit l’opération à deux commandes :
gh stack checkout api-routes
git add -A
git commit -m "Fix API routes"
gh stack rebase --upstack # rebases only the branches above api-routes
gh stack push # per-branch --force-with-lease
Si le rebase s’interrompt sur un conflit, corrigez les fichiers concernés, ajoutez-les avec git add, puis exécutez gh stack rebase --continue. Pour abandonner, gh stack rebase --abort remet chaque branche dans l’état où elle se trouvait avant le rebase.
Sur github.com, un bouton Rebase stack apparaît dans la zone de fusion dès que la pile n’est plus linéaire. D’après le guide de gestion des pull requests empilées, un clic sur ce bouton rejoue les branches sur la version la plus récente du tronc, en partant du bas, et effectue un push forcé de chacune. Les commits créés de cette manière sur les serveurs de GitHub ne sont pas signés : les dépôts qui exigent des commits signés doivent donc plutôt rebaser en local avec gh stack rebase.
Le rebase réécrit les branches supérieures. Si un collègue a extrait l’une de ces branches, il doit s’arrêter et se synchroniser avec le dépôt distant avant tout nouveau commit. Sinon, son prochain push entrera en conflit avec l’historique réécrit.
Fusionner une pile
Fusionner la pull request prête la plus haute l’intègre avec toutes les PR non fusionnées situées en dessous, en une seule fusion. En cas d’échec en cours de route, par exemple un conflit inattendu, la fusion s’arrête à cette PR : les PR inférieures déjà fusionnées restent sur la branche de base, tandis que la PR en échec et toutes celles situées au-dessus restent ouvertes. Si vous ne fusionnez que les couches inférieures, les PR au-dessus restent ouvertes, et GitHub les rebase en remplaçant leur base par la branche de base de la pile. Le guide de fusion exige que la pile ait un historique linéaire avant que la moindre partie puisse être fusionnée.
| Vous fusionnez | Ce qui arrive sur main | Ce qu’il advient du reste |
|---|---|---|
La PR prête la plus haute (ui-login) | Les trois PR | Plus aucune PR ne reste ouverte |
La PR du bas (auth-middleware) | Cette PR uniquement | Les PR supérieures sont rebasées et reciblées sur main |
La PR du milieu (api-routes) | Cette PR et toutes celles situées en dessous | ui-login reste ouverte et est reciblée sur main |
Une PR située au milieu d’une pile GitHub ne peut pas être fusionnée seule. Elle est toujours intégrée avec les PR situées en dessous. D’après la référence des pull requests empilées de GitHub, les piles fonctionnent avec les commits de fusion (merge commits), le squash et le rebase. Avec le squash, chaque PR devient son propre commit squashé : fusionner trois PR ajoute donc trois commits sur main.
Les règles de fusion proviennent de la branche de base de la pile, et non de la branche directement ciblée par chaque PR. Les protections de branche et les vérifications obligatoires continuent donc de déterminer ce qui atteint main. Les piles fonctionnent également avec les files d’attente de fusion (merge queues), qui reçoivent les PR dans le bon ordre. Le changelog du 30 juillet 2026 annonçait un déploiement progressif de la prise en charge des files d’attente de fusion, et le guide de fusion de GitHub indique désormais qu’elles sont entièrement prises en charge.
L’empilement avant la prise en charge native
Avant que GitHub ne prenne en charge les piles nativement, il fallait un outil distinct pour gérer la chaîne de branches. Graphite, spr et git-branchless assuraient chacun cette gestion en dehors du modèle de pull requests propre à GitHub.
Les pull requests empilées natives de GitHub fonctionnent avec les revues, les vérifications obligatoires et les protections de branche que vous utilisez déjà : l’équipe n’a donc pas besoin d’adopter un outil supplémentaire. Pour les équipes qui s’étaient tournées vers ces outils uniquement pour contourner des PR dépendantes illisibles, cela supprime la principale raison de les utiliser.
Conclusion
Une pile GitHub est une chaîne de pull requests dans laquelle chacune utilise comme base la branche située en dessous, GitHub se chargeant du rebase, du reciblage et de la fusion. Comme chaque couche est de petite taille, les relecteurs peuvent l’examiner correctement, et une modification au milieu de la pile n’impose de rebaser que les branches situées au-dessus. Pour essayer, installez github/gh-stack et découpez votre prochaine fonctionnalité en plusieurs parties sur trois couches.
FAQ
La fusion automatique fonctionne-t-elle avec les pull requests empilées de GitHub ?
Non. La documentation de GitHub sur la fusion précise que la fusion automatique (auto-merge) n'est pas prise en charge pour les pull requests empilées. Les outils qui fusionnent via l'API doivent utiliser l'API de fusion asynchrone pour les piles, car les anciens endpoints de fusion de pull requests ne peuvent pas fusionner une pile. Pour intégrer automatiquement une pile une fois les vérifications réussies, utilisez plutôt une file d'attente de fusion : les piles prennent en charge les merge queues, et la file traite les PR dans l'ordre de la pile.
Que se passe-t-il lorsqu'une pull request empilée est retirée d'une file d'attente de fusion ?
Toutes les pull requests situées au-dessus d'elle dans la pile quittent également la file. Pour préserver l'intégrité d'une pile, la file d'attente de fusion autorise un groupe de fusion à dépasser jusqu'à 50 % de sa taille maximale configurée. Si une pile est trop volumineuse pour cette marge supplémentaire, GitHub la répartit sur plusieurs groupes de fusion consécutifs.
Comment mettre à jour ma pile locale après la fusion de la pull request du bas ?
Exécutez gh stack sync. En une seule étape, cette commande récupère les dernières modifications, fait avancer le tronc, rebase les branches restantes par-dessus, les pousse et actualise le statut de chaque pull request. Ajoutez --prune pour supprimer les branches locales des pull requests fusionnées. Si sync rencontre un conflit, la commande remet chaque branche dans son état initial : exécutez alors gh stack rebase pour résoudre le conflit, puis gh stack push.
Comment réordonner, supprimer ou renommer des couches dans une pile existante ?
Utilisez gh stack modify, une interface interactive en terminal permettant de supprimer, fusionner (fold), insérer, réordonner et renommer des branches. Elle nécessite un répertoire de travail propre, un historique linéaire, aucun rebase en cours et aucune pull request en attente de fusion. Vous ne pouvez pas réordonner des branches et, dans la même session, en supprimer, en fusionner, en insérer ou en renommer. Vos modifications ne s'appliquent qu'à l'enregistrement, et gh stack modify --abort restaure la pile dans l'état où elle se trouvait avant de commencer. Exécutez ensuite gh stack submit pour mettre à jour GitHub.