Skip to content

feat: sr land — merge a stack's PRs into trunk #11

Description

@amustafa

Summary

Add sr land: a top-level operation that merges a stack's PRs into trunk, bottom-first, reusing the stack's already-green CI so it stays fast even under squash-merge. Named land (not merge) to avoid colliding with git merge, sr fold ("merge into parent"), and sr pull-meta. See ADR-0014 and the Land entry in codev/UBIQUITOUS_LANGUAGE.md.

Scope & ordering

  • Default target: the downstack (current branch + ancestors), merged bottom-first into trunk. Upstack untouched.
  • --stack: extend to the upstack too (whole stack), landing in topological order (parent before child). Open question flagged: --stack --train on a forked upstack can't linearize into one diff — resolve in spec (likely --train is downstack-only, or collapses each root→leaf path separately).

Modes

  • Default --per-pr: squash-merge each PR bottom-up in rapid succession, reusing each PR's green checks (one squash commit per PR on trunk). Depends on branch protection not requiring "branches up to date before merging"; if that setting is on, error up front and point to --train.
  • --train: retarget the top target PR to trunk, squash-merge once, mark intermediate PRs merged-by-reference (one combined commit). Robust fallback, no dependence on the up-to-date setting.

Readiness gate (why it's efficient)

Proceed only if the entire target set is ready — GitHub would accept each merge (not draft, mergeable, checks green, approved) and the stack is restacked. A restacked stack means the top branch already is trunk + all downstack changes, so existing green checks already validate the combined result → no fresh CI run. If anything isn't ready, abort and print a per-PR status report; merge nothing.

Interaction

  • Print the ordered plan with per-PR readiness, then a single Land these N PRs? [y/N] confirm.
  • --dry-run previews; --yes skips the prompt for scripts.

Safety / mid-run failure

  • Pin trunk's SHA at start; re-check before each individual merge. If trunk moved or a merge is rejected: stop, report what landed vs didn't, and restack the survivors onto the new trunk. Never un-merge trunk.

Post-merge (success path)

  • Full auto-sync tail: fetch trunk → prune landed branches from the graph (remote deletion if the repo is configured for it) → restack surviving upstack onto new trunk → check out trunk (or lowest survivor).

Merge method

  • Squash by default (team preference); respect repo config / a flag where relevant.

Acceptance criteria

  • sr land on a ready, restacked downstack lands all PRs bottom-first with no new CI run and leaves a clean, restacked stack.
  • Not-ready downstack → aborts with per-PR status, merges nothing.
  • Dirty (un-restacked) stack → refuses (or offers to restack).
  • --train collapses the target into one squash commit and marks intermediates merged.
  • --per-pr on an "up-to-date-required" repo → errors up front, no partial landing.
  • --stack lands upstack in topological order.
  • Mid-run trunk race → stops, reports, restacks survivors.
  • --dry-run / --yes behave as specified.

Design settled via a grill-me session. See ADR-0014 (codev/adr/0014-land-reuses-stacked-ci.md) and the Land glossary entry.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions