This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Stackinator is a minimal CLI tool for managing stacked branches and syncing them to GitHub Pull Requests. It uses git config to track parent-child relationships between branches, with no external state files.
# Build binary
go build -o stack
# Or use convenience scripts
./scripts/build # Build binary
./scripts/install # Build and symlink to ~/bin
./scripts/test # Run tests
./scripts/clean # Clean build artifacts
# Run tests
go test ./...
# Run the tool locally
./stack <command>-
Stack Tracking via Git Config: Parent relationships are stored in git config as
branch.<name>.stackparent. This is the single source of truth for stack structure. -
No External State: Unlike other stack tools, Stackinator intentionally avoids state files, databases, or JSON files. Everything lives in git config.
-
Three Main Operations:
stack new: Create new branch and record parent in git configstack status: Build tree from git config and displaystack sync: Topological sort, rebase each branch onto parent, update PR bases
cmd/: Cobra CLI commands (root, new, status, sync)internal/git/: Git operations wrapper with dry-run and verbose supportinternal/github/: GitHub CLI (gh) wrapper for PR operationsinternal/stack/: Core stack logic including topological sort and tree buildinginternal/spinner/: Loading spinner for slow operations (disabled in verbose mode)
Topological Sort (internal/stack/stack.go:TopologicalSort):
- Builds dependency graph from parent relationships
- Performs Kahn's algorithm to order branches from base to tips
- Critical for
stack syncto rebase in correct order
Merged PR Detection (cmd/sync.go:runSync):
- Fetches PRs for relevant branches in parallel
- If parent PR is merged, updates child's parent to grandparent
- If branch's own PR is merged, removes from stack tracking
Tree Building (internal/stack/stack.go:BuildStackTree):
- Constructs visual tree from parent relationships
- Handles multiple independent stacks in same repo
- Used by
stack statuscommand
Both git and github packages support:
DryRun: Print what would happen without executing mutationsVerbose: Show all git/gh commands being executed
The spinner package also respects the verbose flag:
Enabled: When false (verbose mode), spinners are hidden to avoid visual conflicts with command output
These are set via persistent flags on root command.
- cobra: CLI framework
- git: Required in PATH
- gh (GitHub CLI): Required in PATH for PR operations
Base branch can be configured per-repo:
git config stack.baseBranch develop # Default is "main"Always use ./scripts/test to run tests (handles CGO_ENABLED=0 for macOS compatibility).
Test patterns:
- Use table-driven tests for topological sort and tree building
- Mock git/gh command execution for unit tests using
testutil.MockGitClientandtestutil.MockGitHubClient - Consider integration tests that use temporary git repos
IMPORTANT: When testing git operations (creating branches, stashing, etc.), always use ./tests/test-repo directory, NOT the main repository. This keeps the main repo clean and prevents pollution from test branches.
-
Stash Handling:
stack syncauto-stashes uncommitted changes usinggit rebase --autostashand manual stash/pop for safety. -
Force Push Safety: Uses
--force-with-leaseto prevent overwriting remote changes from other sources. -
PR Base Updates: When parent relationships change (due to merged PRs), PR bases are automatically updated via
gh pr edit --base. -
Error Handling: Commands return early on git errors. For rebase conflicts, user must resolve manually and re-run
stack sync. -
Branch Existence: Parent branches may not exist locally if they were merged and deleted. Code handles missing parents gracefully.
Releases are automated via GitHub Actions. When PRs are merged to main, a release is created automatically. Do not create releases manually.
At the start of a session, read decision-log.md in the repo root for past architectural and design decisions.
When making an architectural or design decision, add an entry to decision-log.md with:
- Date (YYYY-MM-DD)
- Decision — what was decided
- Context — why it came up, what the problem was
- Resolution — what was done and how