Skip to content

v0.2.24: GraphQL PR fetch + ez track#23

Merged
rohoswagger merged 2 commits into
mainfrom
perf/graphql-pr-statuses
May 13, 2026
Merged

v0.2.24: GraphQL PR fetch + ez track#23
rohoswagger merged 2 commits into
mainfrom
perf/graphql-pr-statuses

Conversation

@rohoswagger
Copy link
Copy Markdown
Owner

@rohoswagger rohoswagger commented May 13, 2026

Summary

Two features bundled for 0.2.24.

1. ez sync/ez list GraphQL PR fetch — 122s → 1.8s on a 10k-PR repo

get_all_pr_statuses paginated every PR in the repo (98 round-trips for 9.8k PRs in onyx) to look up status for a handful of local branches. Replaced with a single GraphQL request that asks for just the branches we have, one aliased pullRequests(headRefName: ...) field per branch.

Repo Before After
onyx-dot-app/onyx (9,799 PRs) 122s 1.8s
ez-stack (small) 1.7s 1.4s
  • github::get_pr_statuses_for(remote, branches) — single GraphQL round-trip.
  • git::remote_url + local URL parser — derive owner/repo from .git/config (~10ms) instead of gh repo view (~400ms). Falls back to gh repo view for unparseable URLs.
  • Wired into cmd/sync.rs and cmd/list.rs. cmd/adopt.rs keeps the full scan since it needs the global PR graph.

Behavior note: GraphQL returns state: "MERGED" for merged PRs where REST returned "CLOSED" + mergedAt. The PR badge in ez list/ez log now shows MERGED (purple) instead of CLOSED for merged PRs — more accurate. PrInfo.merged is set correctly for cleanup logic.

2. ez track — adopt a raw-git branch into the stack

Local-only counterpart to ez adopt. Pure metadata write — no rebase, no network, no PR graph.

ez track                        # current branch, infer parent
ez track feat/orphan            # specific branch, infer parent
ez track --parent feat/base     # explicit parent

Parent inference walks merge-base(candidate, target) across trunk + tracked branches and picks the strictly deepest descendant. Trunk wins on ties. parent_head is set to merge_base(parent, target) so the next ez sync correctly restacks onto the current parent tip.

EzError::BranchNotInStack now hints at ez track instead of just ez log.

Tests

15 new tests, 215 total pass:

GraphQL (11):

  • URL parser covering SSH, HTTPS, SCP-style, and unrecognized forms.
  • Query builder: alias ordering + JSON escape for special characters.
  • Response parser: matched/missing branches, malformed nodes, empty data fallback.
  • MERGED vs CLOSED state distinction.
  • Integration test: exactly one GraphQL request for 25 branches.

ez track (4):

  • Pure unit tests for parent selection, decoupled from git I/O.
  • Trunk-as-default when nothing closer, tracked branch wins when its merge-base is strictly deeper, deepest wins across 3-level chain regardless of HashMap order, sibling branches don't beat trunk.

Test plan

  • cargo test — 215 pass
  • cargo fmt --all -- --check clean
  • cargo clippy -- -D warnings clean on lib code
  • ez sync smoke test in onyx (1.8s) and ez-stack (1.4s)
  • ez track happy path + 4 error paths (trunk, missing branch, already-tracked, unknown --parent)

Replace the global paginated REST scan in get_all_pr_statuses with a
targeted GraphQL query that asks for just the branches we have locally.
On repos with thousands of historical PRs (e.g. 10k+) this drops ez sync
from ~120s to ~2s.

- github::get_pr_statuses_for(remote, branches) uses aliased
  pullRequests(headRefName: ...) fields in a single GraphQL query.
- Parse owner/repo from the local git remote URL (10ms) instead of
  shelling out to gh repo view (400ms), with a gh fallback for
  unparseable URLs.
- Wire sync.rs and list.rs to the new function. adopt.rs stays on the
  full scan because it needs the global PR graph.
- 11 new unit tests covering URL parsing, query construction
  (alias ordering + JSON escape), response parsing (MERGED vs CLOSED),
  and an integration test asserting exactly one GraphQL request is
  sent for 25 branches.

Bumps version to 0.2.24.
Local-only counterpart to ez adopt: pure metadata write that makes an
existing branch tracked by ez without rebasing, network, or PR graph.

Parent inference: pick the candidate (trunk + tracked branches) whose
merge-base with the target is the strictly deepest descendant. Trunk
wins on ties so users get the expected default when no closer ancestor
is tracked. parent_head is set to merge_base(parent, target) so the
next ez sync correctly restacks onto current parent tip.

- src/cmd/track.rs: command + infer_parent + 4 pure unit tests for the
  selection rule (decoupled from git I/O).
- src/cli.rs / src/main.rs: Track command and dispatch.
- src/error.rs: BranchNotInStack hints at ez track now.
- src/git.rs: rev_list_count helper for the commits-ahead reporting.

Updates CLAUDE.md 0.2.24 entry to cover both this and the GraphQL change.
@rohoswagger rohoswagger changed the title perf: fetch PR statuses via single GraphQL request (v0.2.24) v0.2.24: GraphQL PR fetch + ez track May 13, 2026
@rohoswagger rohoswagger merged commit 3953fe9 into main May 13, 2026
4 checks passed
@rohoswagger rohoswagger deleted the perf/graphql-pr-statuses branch May 13, 2026 00:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant