Integrating dependent feature branches Jump to heading

A familiar situation: you start a feature, and halfway through realise it needs a change a colleague is making on their own branch — a new API, a schema change, a refactored helper. You cannot wait for their pull request to merge, so you merge their branch into yours. Now your branch contains their unreviewed work, your pull request’s diff shows both changes, and when they rebase their branch after review, yours contains the old version of their commits and conflicts with the new one. Dependent branches are normal; handling them by ad hoc merging is what causes the pain. This page compares the three clean options — stacking, landing the dependency first, and extracting the shared part — and shows how to keep both branches mergeable while the dependency is in review, within feature branch isolation.

When to use this approach Jump to heading

  • Your branch needs code that exists only on another unmerged branch.
  • You merged a colleague’s branch into yours and the pull request diff now shows both changes.
  • A dependency branch was rebased or squash-merged and yours now conflicts with it.
  • You are planning a feature in pieces and want each piece reviewable on its own.

Step 1 — Choose a strategy before you depend Jump to heading

Three options cover nearly every case. The right one depends on how big the shared piece is and how soon the dependency can merge.

How to depend on unmerged workIf the shared piece is small and self-contained, extract it into its own small pull request and merge it first. If the dependency branch will merge soon, base your branch on it and stack your pull request. If the dependency will take long, wait or build behind an interface so your branch does not need it yet.What does your branch need from the other?a small shared pieceExtract + merge firsttiny PR, lands todaythe whole branch, soonStack on itPR targets their branchthe whole branch, laterDecoupleinterface or flagmerging their branch into yours ad hoc is the option missing from this tree How to depend on unmerged workIf the shared piece is small and self-contained, extract it into its own small pull request and merge it first. If the dependency branch will merge soon, base your branch on it and stack your pull request. If the dependency will take long, wait or build behind an interface so your branch does not need it yet.What does your branch need from the other?a small shared pieceExtract + merge firsttiny PR, lands todaythe whole branch, soonStack on itPR targets their branchthe whole branch, laterDecoupleinterface or flagmerging their branch into yours ad hoc is the option missing from this tree

Step 2 — Option A: extract the shared piece and land it first Jump to heading

Often the dependency is one helper, one migration or one interface. Move just that into a small pull request off main, get it reviewed and merged quickly, then both feature branches build on main.

# Take the shared change out of the colleague's branch onto a fresh branch from main
git switch -c chore/PAY-812-add-schedule-model origin/main
git checkout origin/feat/PAY-801-scheduler -- src/scheduling/model.py migrations/0042_schedule.py
git commit -m "feat(scheduling): add schedule model and migration (PAY-812)"
git push -u origin HEAD && gh pr create --fill

Once it merges, both branches rebase or merge main and no longer depend on each other. This is the cleanest option and usually the fastest overall, because a small pull request is reviewed in hours.

Step 3 — Option B: stack your branch on theirs Jump to heading

When you need the whole dependency branch, base your branch on it and open your pull request against their branch, not main. Reviewers then see only your changes. When theirs merges, retarget yours to main.

git switch -c feat/PAY-812-export-ui origin/feat/PAY-801-scheduler
# … work …
git push -u origin HEAD
gh pr create --base feat/PAY-801-scheduler --title "Export UI (stacked on #801)" --fill
A stacked branch and its retargetThe dependency branch has commits D1 and D2 on main. Your branch builds Y1 and Y2 on top of D2 and its pull request targets the dependency branch, so the diff shows only Y1 and Y2. After the dependency merges, your branch is moved onto main and the pull request retargeted.review Y alone; land D firstmainMM + DdependencyMD1D2your branchD2Y1 Y2after retargetY1' Y2'if the dependency is squash-merged, move your branch with rebase --onto A stacked branch and its retargetThe dependency branch has commits D1 and D2 on main. Your branch builds Y1 and Y2 on top of D2 and its pull request targets the dependency branch, so the diff shows only Y1 and Y2. After the dependency merges, your branch is moved onto main and the pull request retargeted.review Y alone; land D firstmainMM + DdependencyMD1D2your branchD2Y1 Y2after retargetY1' Y2'if the dependency is squash-merged, move your branch with rebase --onto

When the dependency is squash-merged, its original commits are not on main, so a plain rebase replays them and conflicts. Move only your commits:

git fetch origin
git rebase --onto origin/main origin/feat/PAY-801-scheduler feat/PAY-812-export-ui
gh pr edit "$(gh pr view --json number --jq .number)" --base main
git push --force-with-lease

The mechanics are in rebasing onto a new base with --onto and, for several levels, updating stacked branches with --update-refs.

Step 4 — Option C: decouple so you do not depend yet Jump to heading

If the dependency will take weeks, depending on it ties your schedule to theirs. Build against an interface or a stub, behind a flag, and connect the real implementation when it lands.

# Your branch codes against an interface the dependency will implement
class Scheduler(Protocol):
    def schedule(self, account_id: str, cron: str) -> str: ...

scheduler: Scheduler = get_scheduler()   # returns a stub until the real one merges

This is branch by abstraction at small scale; the larger form is in branch by abstraction for large changes.

Step 5 — If you already merged their branch in, untangle it Jump to heading

If you merged the colleague’s branch into yours, your pull request now carries their commits. Untangle by rebasing only your commits onto either main (if theirs has merged) or their current branch tip (if not).

# List your own commits, excluding everything from their branch
git log --oneline --no-merges origin/feat/PAY-801-scheduler..HEAD
# Recreate your branch with only those commits, on their latest tip
git rebase --onto origin/feat/PAY-801-scheduler "$(git merge-base HEAD origin/feat/PAY-801-scheduler)" --no-rebase-merges

⚠️ SAFETY WARNING: This rewrites your branch. Push with --force-with-lease, and check the result with git range-diff against the old tip before pushing. If anything goes wrong, the previous state of your branch is in git reflog and ORIG_HEAD.

Ad hoc merging against a deliberate strategyMerging a colleague's branch into yours shows their changes in your pull request and conflicts when they rebase. Extracting, stacking or decoupling keeps each pull request's diff to its own change and survives the dependency being rebased or squash-merged.Merge theirs into yoursExtract / stack / decoupleyour PR's diffboth changesyours onlythey rebase or squashconflicts, duplicates--onto, cleanreviewtangledindependentthe strategies cost a few minutes up front and save hours at merge time Ad hoc merging against a deliberate strategyMerging a colleague's branch into yours shows their changes in your pull request and conflicts when they rebase. Extracting, stacking or decoupling keeps each pull request's diff to its own change and survives the dependency being rebased or squash-merged.Merge theirs into yoursExtract / stack / decoupleyour PR's diffboth changesyours onlythey rebase or squashconflicts, duplicates--onto, cleanreviewtangledindependentthe strategies cost a few minutes up front and save hours at merge time

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Can my pull request merge before the one it depends on? Jump to heading

Not if it is stacked: its base is the dependency branch, so merging it would merge into that branch, not main. Land dependencies first, or decouple.

What if the dependency changes significantly during review? Jump to heading

Rebase your stacked branch onto its new tip as it changes — little and often. With rebase.updateRefs, one rebase updates the whole stack.

Should reviewers review both pull requests together? Jump to heading

Review them separately, in dependency order. The stacked pull request’s diff shows only its own change, which is the point of stacking.