Проблема: велику фічу розбили на маленькі PR, але кожен наступний залежить від попереднього. Чекати злиття першого, щоб почати другий, - повільно; складати все в один PR - втрачаємо переваги маленьких рев'ю.
Stacked PRs - ланцюжок залежних pull request-ів, де кожен спрямований не в main, а в гілку попереднього:
feat/ui → PR #3 (base: feat/api) ← верх
feat/api → PR #2 (base: feat/schema)
feat/schema → PR #1 (base: main) ← низ
main
Кожен PR показує лише свій шар змін: рецензент дивиться міграцію окремо від API і окремо від інтерфейсу. Принцип: якщо код залежить від іншого коду, залежність має бути в тій самій гілці чи нижче.
Головна складність - rebase. Після виправлення в нижній гілці всі верхні треба перебазувати. Вручну це втомливо, але Git уміє оновлювати весь ланцюжок одразу:
git switch feat/ui
git rebase --update-refs main
--update-refs під час rebase верхньої гілки пересуває й проміжні гілки (feat/schema, feat/api) на переписані коміти. Можна увімкнути за замовчуванням: git config rebase.updateRefs true.
Після злиття нижнього PR наступний треба переспрямувати на main і перебазувати. Якщо низ вливали через squash, у верхніх гілках лишаються «старі» коміти, яких у main вже немає за SHA, - тут допомагає git rebase --onto main feat/schema feat/api.
Інструменти: GitHub має нативну підтримку стеків (на момент написання - у публічному попередньому перегляді) з каскадним rebase на сервері і розширенням gh stack; є також сторонні інструменти на кшталт Graphite чи ghstack.
Коли варто: довга фіча, яку природно ділити на шари, і швидкий потік рев'ю. Коли ні: незалежні зміни - їм не потрібен стек, достатньо окремих PR від main; повільне рев'ю - стек з п'яти PR, що висять тиждень, перетворюється на постійний rebase.