The Situation: Lost Changes in Stacked PRs

Recently, our team implemented code quality improvements using a stacked PR approach:

  1. First PR (feature/one): Added foundational changes
  2. Second PR (feature/two): Built on the first PR, adding additional functionality

When the first PR merged to main, our second PR disappeared into the void! Despite showing as "merged," the changes weren't in main. This left us with a confusing situation - changes that appeared merged but weren't actually deployed.

What Went Wrong?

In a stacked PR workflow, the second PR was targeting the first PR's branch rather than main. When the first PR merged, the second PR merged into an orphaned branch state, not into main as intended.

main       o---o---o---A---o      (A = merge of PR1)
            \           /
feature/one  \-o---o---/          (PR1)
               \
feature/two     \-o---o---B       (PR2 merged here, not to main!)

The Original Branch Setup

# Create the first feature branch
git checkout -b feature/one main
# Make changes...
git add .
git commit -m "Add foundational changes"
git push origin feature/one

# Create the second feature branch based on the first
git checkout -b feature/two feature/one
# Make additional changes...
git add .
git commit -m "Add additional functionality"
git push origin feature/two

The Solution: Direct Branch Merge

After several failed approaches, we got our changes into main by:

# 1. Ensure main is up-to-date
git checkout main
git pull origin main

# 2. Merge the feature branch directly
git merge feature/two

# 3. If there are conflicts, resolve them
# Example conflict in a file
<<<<<<< HEAD
function mainImplementation() {
  // Latest implementation from main
}
=======
function improvedImplementation() {
  // Your implementation from feature/two
}
>>>>>>> feature/two

# After manually fixing conflicts
git add .
git commit -m "Merge feature/two into main"
git push origin main

Best Practices for Stacked PRs

To avoid this issue in the future:

  1. Update PR targets after merges - When your base PR merges, immediately update your second PR to target main
# After feature/one is merged to main
git checkout feature/two
git rebase main
git push origin feature/two --force  # Warning: Force push changes history!
  1. Use GitHub's "Edit" button - Change the target branch in GitHub's PR interface
Pull Request #123: feature/two → feature/one

[Edit button]
  ↓
Base repository: yourorg/project
Base: main         ← Change this from feature/one to main
  1. Consider linear workflows - For complex changes, consider whether linear PRs might be clearer
# Linear approach with separate branches from main
git checkout -b feature/logging main
# Work on logging...
git checkout -b feature/linting main
# Work on linting separately...
  1. Clear communication - Let teammates know when merging PRs that have other PRs stacked on them

Conclusion

Stacked PRs are powerful but require careful management. By updating PR targets after each merge and communicating clearly, we can avoid code disappearing into merged-but-not-deployed states. Our cleanup process was straightforward once we understood the issue, and we now have our improvements successfully integrated into the main codebase.