9.9 KiB
Graphite Integration
Tool-specific patterns for integrating Graphite (gt) stack visualization and PR management into status reports.
Overview
Graphite provides stack-aware version control with visual branch hierarchies and integrated PR management. Status reports should leverage stack structure for context-rich presentation.
Core Commands
Stack Visualization
# Get visual tree of stacked branches
gt log
# Output includes:
# - Branch hierarchy (parent/child relationships)
# - PR status per branch
# - Commit counts
# - Current branch indicator (◆)
# - Branch states (needs restack, needs submit, ready to merge)
Example Output:
◆ feature/auth-refactor (3) - #123 ✓ Ready to merge
├─ feature/add-jwt (2) - #122 ⏳ In progress
└─ feature/update-middleware (1) - #121 ⏸ Draft
Branch State
# Get current stack state as JSON
gt stack --json
# Returns:
# - Branch metadata (name, parent, children)
# - PR associations
# - Commit SHAs and messages
# - Sync status (ahead/behind trunk)
PR Submission Status
# Check if branches need submission
gt stack
# Shows branches with:
# - "needs submit" → changes not pushed to PR
# - "needs restack" → parent branch updated
# - "ready to merge" → approved, passing CI
Data Gathering
Stack Structure
Extract hierarchical branch relationships:
interface StackNode {
branch: string;
prNumber?: number;
prStatus?: 'draft' | 'open' | 'ready' | 'merged';
commitCount: number;
parent?: string;
children: string[];
isCurrent: boolean;
needsRestack: boolean;
needsSubmit: boolean;
}
async function getStackStructure(): Promise<StackNode[]> {
// Parse gt log output or gt stack --json
const output = await exec('gt log');
// Extract:
// - Branch names and hierarchy
// - PR numbers (from "#123" markers)
// - Status indicators (✓ ⏳ ⏸)
// - Commit counts (from "(N)" markers)
// - Current branch (◆ marker)
return parseStackTree(output);
}
PR Integration
Graphite automatically links branches to PRs:
// Get PR metadata for stack
async function getStackPRs(branches: string[]): Promise<PRMetadata[]> {
// Option 1: Parse from gt log (includes basic status)
// Option 2: Query GitHub directly with PR numbers
// Option 3: Use gt pr status --json (if available)
const prNumbers = branches
.map(b => extractPRNumber(b))
.filter(Boolean);
// Fetch details from GitHub (see github.md)
return fetchPRDetails(prNumbers);
}
Time Filtering
Graphite doesn't natively support time filtering, so filter results:
async function getRecentStackActivity(since: string): Promise<StackActivity> {
// Get full stack
const stack = await getStackStructure();
// Parse time constraint
const cutoff = parseTimeConstraint(since); // "-24h" → Date
// Filter by git commit timestamps
for (const node of stack) {
const commits = await exec(`git log ${node.branch} --since="${cutoff}" --format="%H %s %cr"`);
node.recentCommits = parseCommits(commits);
}
// Only show branches with activity
return stack.filter(n => n.recentCommits.length > 0);
}
Presentation Templates
Stack Tree Format
📊 GRAPHITE STACK
{current_branch_name}
{tree visualization from gt log}
Stack Summary:
Branches: {total} ({open} with PRs)
Ready to merge: {ready_count}
Needs attention: {needs_restack + needs_submit}
Stack-Aware PR Grouping
Organize PRs by stack position (bottom to top):
🔀 PULL REQUESTS (Stack-Aware)
Stack: {stack_name}
├─ PR #123: [feature/auth-refactor] Refactor authentication
│ CI: ✓ 3/3 passing | Reviews: ✓ 2 approved
│ Updated: 3 hours ago
│ └─ Ready to merge ✓
│
├─ PR #122: [feature/add-jwt] Add JWT token support
│ CI: ⏳ 2/3 passing | Reviews: 👀 1 change requested
│ Updated: 5 hours ago
│ └─ Depends on: PR #121
│
└─ PR #121: [feature/update-middleware] Update auth middleware
CI: ✗ 1/3 failing | Reviews: ⏸ No reviews
Updated: 1 day ago
└─ Blocker: CI failing ◆◆
Attention Indicators
Highlight stack-specific issues:
⚠️ STACK ATTENTION NEEDED
◆◆ PR #121: Blocking entire stack (failing CI)
◆ Branch feature/add-jwt: Needs restack (parent updated)
◇ Branch feature/auth-refactor: Needs submit (local changes)
Cross-Referencing
Link Stack to Issues
Match issue IDs in PR titles/bodies:
function linkStackToIssues(stack: StackNode[], issues: Issue[]): void {
for (const node of stack) {
// Extract issue references from PR title
// Pattern: "BLZ-123: Feature title" or "[BLZ-123] Feature title"
const issueKeys = extractIssueKeys(node.prTitle);
// Find matching issues
node.relatedIssues = issues.filter(i => issueKeys.includes(i.key));
}
}
Dependency Tracking
Show blocked/blocking relationships:
interface StackDependencies {
branch: string;
blockedBy: string[]; // Parent branches not merged
blocking: string[]; // Child branches waiting
}
function analyzeStackDependencies(stack: StackNode[]): StackDependencies[] {
return stack.map(node => ({
branch: node.branch,
blockedBy: node.parent && !isReadyToMerge(node.parent) ? [node.parent] : [],
blocking: node.children.filter(child => isReadyToMerge(node) && !isReadyToMerge(child))
}));
}
Best Practices
Efficient Queries
Minimize git/Graphite calls:
- Single
gt logfor stack structure - Single
git log --all --sincefor commit history - Batch PR queries to GitHub (see github.md)
State Caching
Cache stack state to avoid repeated parsing:
interface StackCache {
timestamp: Date;
stack: StackNode[];
ttl: number; // milliseconds
}
function getCachedStack(ttl = 60000): StackNode[] | null {
const cache = loadCache();
if (cache && Date.now() - cache.timestamp.getTime() < ttl) {
return cache.stack;
}
return null;
}
Error Handling
Handle common Graphite errors:
try {
const stack = await exec('gt log');
} catch (error) {
if (error.message.includes('not a git repository')) {
return null; // Gracefully skip Graphite section
}
if (error.message.includes('graphite not initialized')) {
// Suggest: gt repo init
return null;
}
throw error; // Unexpected error
}
Integration Points
With GitHub (see github.md)
Combine Graphite stack structure with GitHub PR details:
async function enrichStackWithGitHub(stack: StackNode[]): Promise<void> {
const prNumbers = stack
.map(n => n.prNumber)
.filter(Boolean);
const prDetails = await fetchGitHubPRs(prNumbers); // See github.md
for (const node of stack) {
const pr = prDetails.find(p => p.number === node.prNumber);
if (pr) {
node.ciStatus = pr.ciStatus;
node.reviewStatus = pr.reviewStatus;
node.updatedAt = pr.updatedAt;
}
}
}
With Linear (see linear.md)
Link Linear issues to stack branches:
async function linkStackToLinear(stack: StackNode[]): Promise<void> {
// Extract all issue keys from PR titles
const issueKeys = stack
.flatMap(n => extractIssueKeys(n.prTitle || ''))
.filter(Boolean);
// Fetch Linear issues
const issues = await fetchLinearIssues({ keys: issueKeys });
// Annotate stack nodes
for (const node of stack) {
const keys = extractIssueKeys(node.prTitle || '');
node.linearIssues = issues.filter(i => keys.includes(i.identifier));
}
}
Common Patterns
Stack Health Score
Calculate stack quality metrics:
interface StackHealth {
score: number; // 0-100
issues: string[];
readyToMerge: number;
needsWork: number;
}
function calculateStackHealth(stack: StackNode[]): StackHealth {
let score = 100;
const issues: string[] = [];
const needsRestack = stack.filter(n => n.needsRestack).length;
const needsSubmit = stack.filter(n => n.needsSubmit).length;
const failingCI = stack.filter(n => n.ciStatus === 'failing').length;
const readyToMerge = stack.filter(n => n.prStatus === 'ready').length;
score -= needsRestack * 10; // -10 per restack needed
score -= needsSubmit * 5; // -5 per submit needed
score -= failingCI * 20; // -20 per failing CI
if (needsRestack) issues.push(`${needsRestack} branches need restack`);
if (needsSubmit) issues.push(`${needsSubmit} branches need submit`);
if (failingCI) issues.push(`${failingCI} PRs with failing CI`);
return {
score: Math.max(0, score),
issues,
readyToMerge,
needsWork: needsRestack + needsSubmit + failingCI
};
}
Stack Timeline
Show activity timeline across stack:
📅 STACK TIMELINE (Last 24 hours)
2 hours ago │ PR #123 approved by @reviewer
3 hours ago │ feature/auth-refactor: Pushed 2 commits
5 hours ago │ PR #122: CI checks passing
1 day ago │ feature/add-jwt: Created PR
CLI Reference
Essential Graphite commands for status reporting:
# Stack visualization
gt log # Visual tree
gt log --short # Compact format
gt log --json # Machine-readable
# Stack state
gt stack # Current stack info
gt stack --json # Structured output
# Branch operations (for context)
gt upstack # Show branches above
gt downstack # Show branches below
# PR operations (for context)
gt pr status # PR status for stack
gt submit --dry-run # Preview what would be submitted
Troubleshooting
Stack Not Showing
# Verify Graphite initialized
gt repo init
# Verify on a branch
git branch
# Check for trunk configuration
gt repo --show
PR Associations Missing
# PRs might not be associated with branches
# Check with:
gt pr status
# Re-associate if needed:
gt pr submit
Performance Issues
Large stacks (>20 branches) can slow down:
- Cache
gt logoutput - Limit depth with
gt log --depth 10 - Filter to relevant branches only
- Consider pagination for display