返回 Skill 列表
extension
分类: 开发与工程无需 API Key

Stacked Diffs Workflow

该代理实现了堆叠差异(堆叠PR),将大的变更分解为可审查的小块。在处理复杂功能、管理依赖变更或优化代码审查流程时使用。

person作者: jakexiaohubgithub

Stacked Diffs Workflow

Purpose

Stacked diffs (also known as stacked PRs or stacked branches) is a code review workflow pioneered at Meta (Facebook) that enables:

  • Breaking large changes into small, reviewable chunks
  • Parallel code review of dependent changes
  • Faster iteration without waiting for reviews
  • Cleaner git history with logical commits
  • Reduced context switching for reviewers

Meta engineers use stacked diffs extensively with their internal tools, and this practice has spread to companies like Uber, Airbnb, and many others.

Features

| Feature | Description | Benefit | |---------|-------------|---------| | Dependent PRs | Chain of PRs building on each other | Logical separation | | Parallel Review | Review all PRs simultaneously | Faster feedback | | Easy Updates | Update any commit in the stack | Flexible iteration | | Automatic Rebase | Tools handle rebase complexity | Less manual work | | Merge Automation | Merge entire stacks efficiently | Cleaner history |

Stacked Diffs vs Traditional PRs

Traditional PR Workflow

Main ─────────────────────────────────────────────►
       \
        └── Feature Branch (1000 lines) ──────────►
                        │
                        └── Single large PR
                            - Hard to review
                            - Long review time
                            - All-or-nothing merge

Stacked Diffs Workflow

Main ────────────────────────────────────────────────►
       \
        └── PR 1: Add data model (100 lines)
             \
              └── PR 2: Add API endpoint (150 lines)
                   \
                    └── PR 3: Add frontend (200 lines)
                         \
                          └── PR 4: Add tests (150 lines)

Each PR:
- Small and focused
- Can be reviewed in parallel
- Merged independently (in order)

Tools

Graphite (Recommended)

# Install Graphite CLI
npm install -g @withgraphite/graphite-cli

# Authenticate with GitHub
gt auth

# Create first branch in stack
gt branch create feature/add-data-model
# ... make changes ...
git add . && gt commit -m "Add user data model"

# Create dependent branch
gt branch create feature/add-api
# ... make changes ...
git add . && gt commit -m "Add user API endpoints"

# Create another dependent branch
gt branch create feature/add-frontend
# ... make changes ...
git add . && gt commit -m "Add user frontend components"

# View your stack
gt log

# Submit all PRs in stack
gt stack submit

# After PR 1 is approved and merged, restack remaining
gt stack restack

# Sync with remote
gt repo sync

ghstack (GitHub)

# Install ghstack
pip install ghstack

# Configure
ghstack config set github.com

# Create stack from commits
# First, make commits on a local branch
git checkout -b feature/user-system
git commit -m "Add user data model"
git commit -m "Add user API"
git commit -m "Add user frontend"

# Submit stack to GitHub
ghstack submit

# Update a commit in the middle
git rebase -i HEAD~3
# Edit the commit
ghstack submit

# After reviews, land the stack
ghstack land

git-branchless

# Install git-branchless
cargo install git-branchless

# Initialize
git branchless init

# Create commits (each becomes a "branch")
git commit -m "Add data model"
git commit -m "Add API"
git commit -m "Add frontend"

# View stack
git smartlog

# Submit to GitHub
git branchless submit

# Rebase and update
git branchless reword HEAD~2  # Edit middle commit
git branchless submit         # Update all PRs

Sapling (Meta's Tool)

# Install Sapling
brew install sapling

# Clone repo
sl clone https://github.com/org/repo

# Create commits in stack
sl commit -m "Add data model"
sl commit -m "Add API"
sl commit -m "Add frontend"

# View stack
sl smartlog

# Submit for review
sl pr submit

# Amend a commit in the middle
sl goto <commit-hash>
# make changes
sl amend

# Rebase dependent commits
sl rebase -d <base>

Workflow Patterns

Creating a Stack

# Start from main
git checkout main && git pull

# Create first logical change
gt branch create feat/step-1-models
# Make changes for data models
git add . && gt commit -m "feat: add user and post models"

# Create second logical change (builds on first)
gt branch create feat/step-2-api
# Make changes for API layer
git add . && gt commit -m "feat: add REST API for users"

# Create third logical change (builds on second)
gt branch create feat/step-3-frontend
# Make changes for frontend
git add . && gt commit -m "feat: add user management UI"

# Submit entire stack
gt stack submit

# Result: 3 PRs created, each targeting the previous

Updating Middle Commits

# Scenario: Reviewer requests changes to PR 2 (API layer)

# Check out the branch for PR 2
gt branch checkout feat/step-2-api

# Make the requested changes
# ... edit files ...

# Amend the commit
git add . && git commit --amend

# Restack all dependent branches
gt stack restack

# Update all PRs
gt stack submit

Handling Merge Conflicts

# When PR 1 is merged and conflicts arise

# Sync with remote
gt repo sync

# Restack your branches on top of main
gt stack restack --onto main

# If conflicts occur, resolve them
# ... resolve conflicts ...
git add . && git rebase --continue

# Update PRs
gt stack submit

Merging a Stack

# Option 1: Merge from bottom up
# After PR 1 approved:
gh pr merge 123 --squash  # PR 1
gt repo sync
gt stack restack

# After PR 2 approved:
gh pr merge 124 --squash  # PR 2
gt repo sync
gt stack restack

# Continue until all merged

# Option 2: Use Graphite's merge queue
gt stack merge  # Merges all approved PRs in order

Best Practices

1. Keep PRs Small and Focused

## Good Stack Structure

PR 1: Database migrations and models
- Add User table migration
- Add Post table migration
- Add UserPost junction table

PR 2: Repository layer
- Add UserRepository with CRUD
- Add PostRepository with CRUD

PR 3: Service layer
- Add UserService with business logic
- Add PostService with business logic

PR 4: API controllers
- Add /users endpoints
- Add /posts endpoints

PR 5: Integration tests
- Add API integration tests
- Add E2E user flow tests

2. Write Clear Stack Descriptions

## PR Description Template for Stacks

### Stack Overview (in first PR)
This stack implements user authentication:
1. **PR 1** (this PR): Database schema for users
2. PR 2: Authentication service
3. PR 3: Login/Register endpoints
4. PR 4: JWT middleware
5. PR 5: Frontend auth components

### This PR
Adds the database schema and migrations for the user authentication system.

### Dependencies
- Base: `main`
- Next in stack: PR #124

### Testing
- [ ] Unit tests pass
- [ ] Migration tested locally

### Review Notes
Please review the schema design first. Later PRs depend on this structure.

3. Coordinate with Reviewers

// PR naming convention for stacks
const prNamingConvention = {
  format: '[Stack: <stack-name>] <n>/<total>: <description>',
  examples: [
    '[Stack: user-auth] 1/5: Add user database schema',
    '[Stack: user-auth] 2/5: Add authentication service',
    '[Stack: user-auth] 3/5: Add login endpoints',
    '[Stack: user-auth] 4/5: Add JWT middleware',
    '[Stack: user-auth] 5/5: Add frontend components'
  ]
};

4. Handle Reviews Efficiently

# Respond to review comments efficiently

# If change affects only one PR
gt branch checkout feat/affected-branch
# make changes
git add . && gt commit --amend
gt stack submit

# If change affects multiple PRs
# Start from the earliest affected PR
gt branch checkout feat/earliest-affected
# make changes
git add . && gt commit --amend
gt stack restack  # Propagates changes up the stack
gt stack submit

Anti-Patterns

| Anti-Pattern | Problem | Solution | |--------------|---------|----------| | Huge PRs in stack | Still hard to review | Keep each PR < 400 lines | | Unrelated changes | Confuses reviewers | Each PR should be cohesive | | Too many PRs | Overhead exceeds benefit | 3-7 PRs is ideal | | Unclear dependencies | Hard to merge correctly | Document stack structure | | No tests per PR | Can't verify independently | Include relevant tests | | Force pushing shared branches | Breaks collaborators | Use amend + restack |

Git Commands for Manual Stacking

When tools aren't available, you can manage stacks manually:

# Create a stack manually
git checkout main
git checkout -b stack/feature-1
# make changes, commit

git checkout -b stack/feature-2
# make changes, commit

git checkout -b stack/feature-3
# make changes, commit

# Create PRs targeting each other
gh pr create --base main --head stack/feature-1
gh pr create --base stack/feature-1 --head stack/feature-2
gh pr create --base stack/feature-2 --head stack/feature-3

# After feature-1 merges, rebase the rest
git checkout stack/feature-2
git rebase main
git push --force-with-lease

git checkout stack/feature-3
git rebase stack/feature-2
git push --force-with-lease

# Update PR targets
gh pr edit 124 --base main  # feature-2 now targets main

Interactive Rebase for Stack Edits

# Edit a commit in the middle of a stack
git rebase -i main

# In the editor, change 'pick' to 'edit' for the commit to modify
# pick abc123 Add data model
# edit def456 Add API endpoints  <-- edit this one
# pick ghi789 Add frontend

# Make your changes
git add . && git commit --amend

# Continue rebase
git rebase --continue

# Force push all branches
git push --force-with-lease origin stack/feature-1
git push --force-with-lease origin stack/feature-2
git push --force-with-lease origin stack/feature-3

IDE Integration

VS Code with Graphite

// .vscode/settings.json
{
  "graphite.enabled": true,
  "graphite.showStackInStatusBar": true,
  "git.branchPrefix": "stack/"
}

JetBrains IDEs

# Use terminal integration
# Add to .zshrc or .bashrc
alias gs='gt stack'
alias gb='gt branch'
alias gc='gt commit'

Use Cases

1. Large Feature Development

## Stack: E-commerce Checkout Redesign

1. **PR 1**: Cart data model updates
   - Add discount fields
   - Add shipping options

2. **PR 2**: Cart service layer
   - Discount calculation logic
   - Shipping cost estimation

3. **PR 3**: Checkout API endpoints
   - POST /checkout/initiate
   - POST /checkout/complete

4. **PR 4**: Payment integration
   - Stripe checkout session
   - Webhook handlers

5. **PR 5**: Checkout UI components
   - Cart summary
   - Payment form
   - Order confirmation

6. **PR 6**: E2E tests
   - Full checkout flow tests

2. Refactoring Safely

## Stack: Extract Service from Monolith

1. **PR 1**: Add new service interface
   - Define contracts
   - Add feature flag

2. **PR 2**: Implement new service
   - Core logic extraction
   - Unit tests

3. **PR 3**: Add service client
   - HTTP client implementation
   - Circuit breaker

4. **PR 4**: Migrate callers (batch 1)
   - Update user service
   - Update order service

5. **PR 5**: Migrate callers (batch 2)
   - Update remaining services

6. **PR 6**: Remove old code
   - Delete deprecated methods
   - Remove feature flag

3. Cross-Team Collaboration

# When multiple developers work on a stack

# Developer A creates base
gt branch create feature/base
git add . && gt commit -m "feat: add base infrastructure"
gt stack submit

# Developer B branches from A's work
gt branch checkout feature/base
gt branch create feature/extension
git add . && gt commit -m "feat: extend with new capability"
gt stack submit

# When A updates their PR
gt repo sync
gt stack restack  # B's changes are rebased automatically

Metrics and Benefits

Before Stacked Diffs

| Metric | Value | |--------|-------| | Average PR size | 1,200 lines | | Review turnaround | 3-5 days | | Merge conflicts | Frequent | | Review quality | Lower |

After Stacked Diffs

| Metric | Value | |--------|-------| | Average PR size | 150-300 lines | | Review turnaround | Same day | | Merge conflicts | Rare | | Review quality | Higher |

Related Skills

  • methodology/finishing-development-branch - Branch completion
  • methodology/requesting-code-review - Code review best practices
  • devops/github-actions - CI/CD for stacks
  • devops/feature-flags - Trunk-based development

Think Omega. Build Omega. Be Omega.