Skip to content

Commit b4d9c08

Browse files
authored
[smart-log] perf: parallelize status check fetches (#42)
* feat: add stack discovery API Add src/api/stack.rs module for discovering PR stacks by walking the base/head branch chain. This enables gh-stack to find related PRs without requiring identifier patterns in titles. New functions: - fetch_pr_by_head: Find PR by head branch name - fetch_prs_by_base: Find all PRs targeting a base branch - fetch_all_open_prs: Get all open PRs in a repo - discover_stack: Walk up/down to find full connected stack - discover_all_stacks: Group all PRs into separate stacks Includes 9 unit tests with mockito for API calls. * feat: add rate limit retry logic Add automatic retry with exponential backoff for GitHub API rate limits. New functionality in src/api/mod.rs: - RateLimitError struct with reset time info and Display impl - parse_rate_limit_headers() to extract X-RateLimit-* headers - is_rate_limited() to detect 429 and 403 with rate limit headers - send_with_retry() for automatic retry with exponential backoff Retries up to 3 times with 1s, 2s, 4s delays before returning a RateLimitError with helpful message showing when to retry. Includes 6 new tests for retry logic. * feat: add identifier detection and prompts Add src/identifier.rs module for trunk branch detection and interactive stack selection prompts. New types and functions: - StackSummary: Display info for a stack (branch, PR count, numbers) - is_trunk_branch(): Check if branch is a trunk (main, master, etc.) - detect_trunk_branch(): Get default branch from git remote - TrunkAction: Enum for user choices when on trunk - prompt_trunk_action(): Interactive menu for trunk branch - prompt_select_stack(): Let user pick from stack list - prompt_identifier(): Manual identifier entry Includes 12 unit tests for trunk detection and summary formatting. * feat: add gh CLI helpers Add src/gh_cli.rs module for GitHub CLI detection and installation hints to help users create PRs when none exist for a branch. New functions: - is_gh_installed(): Check if gh CLI is available - is_gh_authenticated(): Check if gh is logged in - install_instructions(): Platform-specific install commands - suggest_create_pr(): Print gh pr create command with hints - prompt_create_pr(): Interactive PR creation prompt The module handles non-TTY environments gracefully and provides helpful messages when gh is not installed or not authenticated. Includes 3 unit tests. * feat: make log identifier optional with branch inference Enable gh-stack log to work without an explicit identifier by inferring the stack from the current branch's PR chain. New CLI arguments for log command: - --branch, -b: Infer stack from specified branch - --all, -a: List all stacks and select interactively - --ci: Non-interactive mode for CI environments - --trunk: Override trunk branch detection Behavior changes: - Running 'gh-stack log' without args infers from current branch - On trunk branch: prompts to enter identifier or select stack - No PR found: suggests gh pr create command - CI mode requires explicit identifier or --branch flag The identifier argument is now optional but still fully supported for backwards compatibility. * feat: add branch inference to status command Apply the same smart default behavior from log to status command: New CLI arguments for status command: - --branch, -b: Infer stack from specified branch - --all, -a: List all stacks and select interactively - --ci: Non-interactive mode for CI environments - --trunk: Override trunk branch detection The identifier argument is now optional. Running 'gh-stack status' without args infers the stack from the current branch's PR chain. JSON output mode handles errors gracefully with error field in response. * docs: update log documentation for smart defaults Update docs/log.md to document the new smart default behavior: - Stack discovery from current branch - New flags: --branch, --all, --ci, --trunk - CI usage examples - On trunk branch behavior - No PR found behavior with gh pr create suggestion Also add reference to status command in 'See also' section. * docs: update status documentation for smart defaults Update docs/status.md to document the new smart default behavior: - Stack discovery from current branch - New flags: --branch, --all, --ci, --trunk - CI usage examples - Updated options table with new flags - Reorganized examples section * perf: optimize stack discovery with batch fetch Replace sequential API calls with batch-fetch strategy: - Fetch all open PRs in one paginated request (up to 1000 PRs) - Walk the PR chain in-memory using PrIndex lookup structure - Reduces API calls from O(N) to O(1) for stack discovery Changes: - Add pagination support to fetch_all_open_prs (MAX_PAGES=10) - Add PrIndex struct for fast in-memory head/base lookups - Refactor discover_stack to use batch fetch + in-memory walk - Extract discover_stack_from_index for pure in-memory operation - Extract group_into_stacks for reuse between functions Performance improvement: - Before: ~12 sequential API calls for 6-PR stack (~6+ seconds) - After: 1-2 API calls for any stack size (~2 seconds) Enterprise support: - Pagination handles repos with 100+ open PRs - Capped at 1000 PRs (10 pages) for safety Adds 11 new tests for PrIndex, batch fetch, and pagination. * perf: parallelize status check fetches Use futures::join_all to fetch CI checks and mergeable status for all PRs in parallel instead of sequentially. - Add PrCheckData struct to collect PR data in non-async pass - Add fetch_pr_status helper to fetch CI and mergeable in parallel - Refactor build_status_entries to batch fetch all status checks Performance: ~3.3s for 7-PR stack (down from ~8s sequential) * refactor: remove gh CLI dependency Replace gh CLI with native Rust implementations: - Add browser.rs for cross-platform browser opening and URL generation - Add api/create.rs for GitHub API PR creation - Add --create-pr flag for API-based PR creation - Poll for PR after browser-based creation (30s timeout) - Support GitHub Enterprise via remote URL parsing - Add first_commit_message() and get_remote_url() helpers to tree.rs New features: - Interactive mode opens browser to create PR, then polls for it - --create-pr flag creates PR via API with prompts for title/body - CI mode prints PR creation URL Removes: src/gh_cli.rs (no longer needed)
1 parent 249d5c1 commit b4d9c08

11 files changed

Lines changed: 3064 additions & 104 deletions

File tree

docs/log.md

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,32 @@ Visualize your stack's structure and status.
55
## Usage
66

77
```bash
8+
# Infer stack from current branch (recommended)
9+
gh-stack log
10+
11+
# Infer from a specific branch
12+
gh-stack log --branch feat/my-feature
13+
14+
# List all stacks and select interactively
15+
gh-stack log --all
16+
17+
# Search by identifier in PR titles
818
gh-stack log 'STACK-ID'
9-
gh-stack log 'STACK-ID' --short # compact list view
10-
gh-stack log 'STACK-ID' --include-closed # show closed/merged PRs
11-
gh-stack log 'STACK-ID' --no-color # disable colors and unicode
12-
gh-stack log 'STACK-ID' -C /path/to/repo # specify repo path
19+
20+
# CI mode (non-interactive)
21+
gh-stack log --branch $GITHUB_HEAD_REF --ci
1322
```
1423

24+
## Stack Discovery
25+
26+
When run without an identifier, `gh-stack log` automatically discovers your stack by:
27+
28+
1. Finding the PR for your current branch
29+
2. Walking up the PR base chain to find ancestors
30+
3. Walking down to find PRs that build on yours
31+
32+
This works with any PR structure - no special naming required.
33+
1534
## Output
1635

1736
The default tree view shows:
@@ -50,14 +69,68 @@ The `--short` flag shows a compact list:
5069

5170
| Flag | Description |
5271
|------|-------------|
72+
| `--branch`, `-b` | Infer stack from this branch instead of current |
73+
| `--all`, `-a` | List all stacks and select interactively |
74+
| `--ci` | Non-interactive mode for CI environments |
75+
| `--trunk` | Override trunk branch (default: auto-detect or "main") |
5376
| `--short`, `-s` | Compact list format instead of tree |
77+
| `--status` | Show CI, approval, and merge status bits |
5478
| `--include-closed` | Show branches with closed/merged PRs |
5579
| `--no-color` | Disable colors and unicode characters |
5680
| `-C`, `--project` | Path to local repository |
5781
| `-r`, `--repository` | Override repository (owner/repo) |
5882
| `-o`, `--origin` | Git remote name (default: origin) |
5983
| `-e`, `--excl` | Exclude PR by number (repeatable) |
6084

85+
## CI Usage
86+
87+
In CI environments, use `--ci` to disable interactive prompts:
88+
89+
```bash
90+
# Must provide branch explicitly
91+
gh-stack log --branch $GITHUB_HEAD_REF --ci
92+
93+
# Or use identifier
94+
gh-stack log 'STACK-ID' --ci
95+
```
96+
97+
The `--ci` flag will:
98+
- Fail with an error if no identifier or branch is provided
99+
- Fail if on a trunk branch without an identifier
100+
- Never prompt for user input
101+
102+
## On Trunk Branch
103+
104+
When you're on a trunk branch (main, master, etc.), `gh-stack log` will prompt you to:
105+
106+
1. Enter a stack identifier manually
107+
2. Select from detected stacks in the repository
108+
3. Cancel
109+
110+
```
111+
You're on 'main' (trunk branch).
112+
113+
? What would you like to do?
114+
> Enter a stack identifier
115+
Select from detected stacks (3 found)
116+
Cancel
117+
```
118+
119+
## No PR Found
120+
121+
If no PR exists for your current branch:
122+
123+
```
124+
No PR found for branch 'feat/new-feature'.
125+
126+
Create a PR with:
127+
gh pr create --base main --head feat/new-feature
128+
129+
? Create PR now? [Y/n]
130+
```
131+
132+
If you confirm, `gh-stack` will run `gh pr create` for you.
133+
61134
## When to use
62135

63136
- Before rebasing to understand stack structure
@@ -68,4 +141,5 @@ The `--short` flag shows a compact list:
68141
## See also
69142

70143
- [annotate](annotate.md) - Add stack tables to PR descriptions
144+
- [status](status.md) - Show CI and approval status
71145
- [land](land.md) - Merge the stack

docs/status.md

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,35 @@ Show stack status with CI, approval, and merge readiness indicators.
55
## Usage
66

77
```bash
8-
gh-stack status <identifier> [OPTIONS]
9-
# or
10-
gh-stack log --status <identifier> [OPTIONS]
8+
# Infer stack from current branch (recommended)
9+
gh-stack status
10+
11+
# Infer from a specific branch
12+
gh-stack status --branch feat/my-feature
13+
14+
# List all stacks and select interactively
15+
gh-stack status --all
16+
17+
# Search by identifier in PR titles
18+
gh-stack status 'STACK-ID'
19+
20+
# CI mode (non-interactive)
21+
gh-stack status --branch $GITHUB_HEAD_REF --ci
22+
23+
# Use with log command
24+
gh-stack log --status 'STACK-ID'
1125
```
1226

27+
## Stack Discovery
28+
29+
When run without an identifier, `gh-stack status` automatically discovers your stack by:
30+
31+
1. Finding the PR for your current branch
32+
2. Walking up the PR base chain to find ancestors
33+
3. Walking down to find PRs that build on yours
34+
35+
This works with any PR structure - no special naming required.
36+
1337
## Description
1438

1539
The `status` command displays your PR stack with status bits showing:
@@ -68,19 +92,43 @@ Status: [CI | Approved | Mergeable | Stack]
6892

6993
| Flag | Description |
7094
|------|-------------|
71-
| `--no-checks` | Skip fetching CI/approval/conflict status (faster, shows basic tree) |
95+
| `--branch`, `-b` | Infer stack from this branch instead of current |
96+
| `--all`, `-a` | List all stacks and select interactively |
97+
| `--ci` | Non-interactive mode for CI environments |
98+
| `--trunk` | Override trunk branch (default: auto-detect or "main") |
99+
| `--no-checks` | Skip fetching CI/approval/conflict status (faster) |
72100
| `--no-color` | Disable colors and Unicode characters |
73101
| `--help-legend` | Show status bits legend |
74102
| `--json` | Output in JSON format |
75103
| `-C, --project <PATH>` | Path to local repository |
76104
| `-r, --repository <REPO>` | Specify repository (owner/repo) |
77105
| `-o, --origin <REMOTE>` | Git remote to use (default: origin) |
78-
| `-e, --excl <NUMBER>` | Exclude PR by number (can be used multiple times) |
106+
| `-e, --excl <NUMBER>` | Exclude PR by number (repeatable) |
107+
108+
## CI Usage
109+
110+
In CI environments, use `--ci` to disable interactive prompts:
111+
112+
```bash
113+
# Must provide branch explicitly
114+
gh-stack status --branch $GITHUB_HEAD_REF --ci
115+
116+
# Or use identifier
117+
gh-stack status 'STACK-ID' --ci
118+
119+
# JSON output for parsing
120+
gh-stack status --branch $GITHUB_HEAD_REF --ci --json
121+
```
122+
123+
The `--ci` flag will:
124+
- Fail with an error if no identifier or branch is provided
125+
- Fail if on a trunk branch without an identifier
126+
- Never prompt for user input
79127

80128
## JSON Output
81129

82130
```bash
83-
gh-stack status STACK-123 --json
131+
gh-stack status --json
84132
```
85133

86134
```json
@@ -113,41 +161,41 @@ gh-stack status STACK-123 --json
113161
The legend is shown automatically on first run. To see it again:
114162

115163
```bash
116-
gh-stack status STACK-123 --help-legend
164+
gh-stack status --help-legend
117165
```
118166

119167
The legend marker is stored in `~/.gh-stack-legend-seen`.
120168

121169
## Examples
122170

123-
### Basic usage
171+
### Infer from current branch
124172

125173
```bash
126-
gh-stack status JIRA-1234
174+
gh-stack status
127175
```
128176

129-
### Skip API calls for faster output
177+
### Infer from specific branch
130178

131179
```bash
132-
gh-stack status JIRA-1234 --no-checks
180+
gh-stack status --branch feat/my-feature
133181
```
134182

135-
### Specify repository explicitly
183+
### Skip API calls for faster output
136184

137185
```bash
138-
gh-stack status JIRA-1234 -r owner/repo
186+
gh-stack status --no-checks
139187
```
140188

141-
### Output as JSON for scripting
189+
### Specify repository explicitly
142190

143191
```bash
144-
gh-stack status JIRA-1234 --json | jq '.stack[].status.ci'
192+
gh-stack status -r owner/repo
145193
```
146194

147-
### Use with log command
195+
### Output as JSON for scripting
148196

149197
```bash
150-
gh-stack log --status JIRA-1234
198+
gh-stack status --json | jq '.stack[].status.ci'
151199
```
152200

153201
## See Also

0 commit comments

Comments
 (0)