I spent today refining my Git workflow for managing PR stacks with Graphite. The goal was to establish a clean, repeatable process for handling merged PRs and their associated worktrees.

Git Worktrees: Parallel Development Made Easy

Git worktrees let you check out multiple branches simultaneously in separate directories. Instead of stashing work or committing half-done changes to switch contexts, each branch lives in its own folder.

Why this matters: while PR #123 is in code review, I can start working on PR #124 in a different directory. No context switching, no stashing, no waiting. The reviewer can take their time—I’m not blocked.

# Main repo on develop
cd ~/projects/myapp
git checkout develop
 
# First feature: create the command infrastructure
git worktree add ../create-claude-command -b issue-123-claude-command develop
cd ../create-claude-command
gt track  # Tell Graphite to track this branch
 
# Now I have two directories:
# ~/projects/myapp                   → develop branch
# ~/projects/create-claude-command   → issue-123-claude-command branch

Graphite: Stacking PRs Without Blocking

Graphite takes this further by managing PR stacks—sequential PRs that build on each other. The first PR can stay in review while I build the next feature on top of it.

The key insight: code review stops being a bottleneck. Reviewers interact with small, focused PRs. Meanwhile, I keep making progress on dependent work.

# Second feature: add skill support (depends on first)
git worktree add ../create-claude-skill -b issue-124-claude-skill issue-123-claude-command
cd ../create-claude-skill
gt track  # Graphite knows this stacks on issue-123
 
# Third feature: add hook support (depends on second)
git worktree add ../create-claude-hook -b issue-125-claude-hook issue-124-claude-skill
cd ../create-claude-hook
gt track

Now I have a stack:

◯ issue-125-claude-hook
│
◯ issue-124-claude-skill
│
◯ issue-123-claude-command
│
◯ develop

Each PR is reviewable independently, but they stay in sync.

Working with Graphite

The daily workflow is straightforward:

# After upstream changes or PR merges, sync with remote
gt sync
 
# Rebase the stack when base branches change
gt restack
 
# Submit all PRs in the stack
gt submit

gt sync fetches from remote and detects merged PRs. gt restack rebases dependent branches when their parents change. gt submit pushes and creates/updates PRs.

One command rebases and pushes an entire stack with multiple PRs. No manual rebasing, no force-pushing each branch individually. The stack stays coherent.

The Problem: Cleanup After Merges

After several PRs merged, my workspace was littered with stale worktrees. The cleanup process was confusing:

  • Which worktrees needed to be pruned after their PRs were merged?
  • Does Graphite support worktrees well, or should I prune worktrees first?

The manual process involved juggling git worktree remove, git branch -d, gt sync, and gt stack restack commands in unclear sequences. Each step could fail in different ways, and the error messages didn’t make the recovery path obvious.

What I Learned

Graphite and Worktrees Don’t Mix Well

Graphite’s gt sync command detects merged PRs and offers to delete branches. But it can’t delete branches checked out in worktrees, leading to confusing warnings and prompts.

The insight: remove worktrees first, then let Graphite work unblocked.

Building the /pr-stacks Command

Rather than memorizing complex grep/sed pipelines, I created a /pr-stacks Claude Code command that:

  1. Runs git fetch --prune to update remote state
  2. Parses gt log output to show PR status
  3. Filters to show only merged PRs with /pr-stacks merged
  4. Cleans up worktrees with /pr-stacks clean

You can see this command in my dotme avatar where I recently started tracking my tools preferences and Claude Code commands: pr-stacks.md.

The command handles common failures gracefully:

  • Checks that develop branch is clean before starting
  • Verifies worktrees are clean before removing them
  • Stops with clear messages if manual intervention is needed

Example output:

$ /pr-stacks merged
 
 issue-125-claude-hook
 PR #125 (Merged)
 
 issue-124-claude-skill
 PR #124 (Merged)
 
 issue-123-claude-command
 PR #123 (Merged)

The Clean Workflow

The final workflow simplified to two steps:

  1. /pr-stacks clean - removes merged worktrees and syncs with Graphite
  2. gt stack restack && gt stack submit - rebases dependent branches and pushes

This is teachable to team members because each step has a clear purpose and the command handles edge cases.

Key Takeaways

  • Git worktrees let you work on multiple branches without context switching
  • Graphite stacks PRs so code review never blocks development
  • Remove worktrees before running gt sync - Graphite can’t clean up branches checked out elsewhere
  • Automate complex workflows with Claude Code commands - they’re easier to maintain than shell scripts

See Also