link command for api-only operations#50
link command for api-only operations#50skarim wants to merge 5 commits intoskarim/ignore-merged-prsfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new gh stack link command intended for “API-first” stacked PR operations—linking existing PRs and/or branches into a GitHub stack without creating gh-stack local tracking state—along with full documentation and test coverage.
Changes:
- Introduces
gh stack linkCLI command and wires it into the root command. - Implements link flow: push branch args, resolve/create PRs, fix base-branch chaining, then create/update the remote stack (additive-only).
- Adds extensive unit tests plus updates README, CLI reference docs, FAQ, and the gh-stack skill guide.
Show a summary per file
| File | Description |
|---|---|
| skills/gh-stack/SKILL.md | Updates agent rules and adds a new section documenting gh stack link. |
| docs/src/content/docs/reference/cli.md | Adds CLI reference documentation for gh stack link. |
| docs/src/content/docs/faq.md | Updates FAQ to recommend gh stack link for external stacking tools. |
| cmd/root.go | Registers LinkCmd in the CLI. |
| cmd/link.go | Implements the link command (push/resolve/create/fix-base/upsert-stack). |
| cmd/link_test.go | Adds comprehensive tests for PR-number, branch-name, and mixed workflows. |
| README.md | Documents gh stack link usage and flags. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 7/7 changed files
- Comments generated: 5
32de12d to
ced6b5e
Compare
260a40b to
d4c7e0c
Compare
ced6b5e to
a2c7a18
Compare
d4c7e0c to
08df626
Compare
a2c7a18 to
0386acd
Compare
| Long: `Create or update a stack on GitHub from branch names or PR numbers. | ||
|
|
||
| This command does not rely on gh-stack local tracking state. It is | ||
| designed for users who manage branches with external tools (e.g. jj) |
There was a problem hiding this comment.
maybe provide other examples here?
| designed for users who manage branches with external tools (e.g. jj) | |
| designed for users who manage branches with external tools (e.g. jj, Sapling, ghstack, git-town, etc...) |
| if err := pushBranchArgs(cfg, opts, args); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Phase 2: Find existing PRs for all args (don't create yet) | ||
| found, err := findExistingPRs(cfg, client, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Phase 3: Pre-validate the stack — check that adding these PRs won't | ||
| // conflict with existing stacks before creating any new PRs. | ||
| // Also fetches stacks for reuse in the upsert phase. | ||
| knownPRNumbers := make([]int, 0, len(found)) | ||
| for _, r := range found { | ||
| if r != nil { | ||
| knownPRNumbers = append(knownPRNumbers, r.prNumber) | ||
| } | ||
| } | ||
| stacks, err := listStacksSafe(cfg, client) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if len(knownPRNumbers) > 0 { | ||
| if err := prevalidateStack(cfg, stacks, knownPRNumbers); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| // Phase 4: Create PRs for branches that don't have one yet | ||
| resolved, err := createMissingPRs(cfg, client, opts, args, found) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Phase 5: Fix base branches for existing PRs with wrong bases | ||
| fixBaseBranches(cfg, client, opts, resolved) | ||
|
|
||
| // Phase 6: Upsert the stack (reuse stacks from phase 3) | ||
| prNumbers := make([]int, len(resolved)) | ||
| for i, r := range resolved { | ||
| prNumbers[i] = r.prNumber | ||
| } |
There was a problem hiding this comment.
Do you think we should add any console logging info to the user while these events are happening so it appears less like it's timing out or freezing?
There was a problem hiding this comment.
Good call, added additional logging in 499fdb8
| // createMissingPRs creates PRs for args that don't have one yet. | ||
| // Returns the fully resolved list with all args mapped to PRs. |
There was a problem hiding this comment.
Should this be branches instead of args?
| // createMissingPRs creates PRs for args that don't have one yet. | |
| // Returns the fully resolved list with all args mapped to PRs. | |
| // createMissingPRs creates PRs for branches that don't have one yet. | |
| // Returns the fully resolved list with all branches mapped to PRs. |
Lukeghenco
left a comment
There was a problem hiding this comment.
A couple of copy changes that we may or may not want to make, but the code flows look correct.
gh stack link— stacks API-wrapper for external tool usersUsers who manage branches with external tools (jj, Sapling, ghstack, git-town) currently need to run
init --adopt+submitto create GitHub stacks — but that creates local tracking state they don't want. This adds a lightweightgh stack linkcommand that works entirely via the GitHub API without creating/modifying any local state.Usage:
Arguments can be branch names, PR numbers, or a mix. Branches are pushed automatically, PRs are created for branches that don't have them, and the PRs are linked as a stack on GitHub.
How it works:
FindPRByNumberfirst; if not found, fall through to branch lookup viaFindPRForBranchListStacksand checks that the operation won't remove existing PRs from a stack (additive-only safety). Fails early before creating any new PRs--base, subsequent branches chain off the previous)BaseRefNameon existing PRs whose base doesn't match the expected chain (skips newly created PRs)Safety features:
FindPRByNumberAPI errors are treated as fatal (not silently falling through to branch-name lookup)ListStackscall — pre-validation and upsert share the same API responseFlags:
--base <branch>main)--draft--remote <name>