Tags: github/gh-stack
Tags
Create/update stack on remote during sync (#156) * Create/update the remote stack on sync and fix false "Stack synced" `gh stack sync` reported "Stack synced" even when it had not created or updated the stack object on GitHub. After running `gh stack init` to adopt existing branches and then opening PRs outside the CLI, `gh stack sync` detected the open PRs and printed "Stack synced" — but no stack had ever been created on the server. There were two distinct bugs: 1. Sync never reconciled the remote stack object. `runSync` called `syncStackPRs`, which only *reads* PR state and links PRs to local branches; it never called the create/update path. So the branches were rebased and pushed and the PRs were detected, but the stack on GitHub was never created. 2. The final message was unconditional. `runSync` always printed "Stack synced", which is supposed to mean "the stack object on GitHub now reflects the local stack" — something that can only be true when two or more open PRs exist and the remote stack was actually created/updated. Fix Reconcile the remote stack from sync, and make the closing message reflect what actually happened. * cmd/sync.go - Add a reconciliation step (5b) after PR-state sync: when the stack has two or more open PRs, link them into a stack on GitHub via the new `syncRemoteStack` helper. It inspects existing stacks first and: - short-circuits quietly when a remote stack already lists exactly these PRs (records the ID, prints "Stack already up to date on GitHub") so routine syncs don't issue a redundant, misleading update; - otherwise delegates to `syncStack` to create a new stack, adopt an untracked one, or update a partially-formed one. Sync never opens PRs — that remains `gh stack submit`'s job. - Replace the unconditional "Stack synced" with a result-driven message: "Stack synced" when the remote stack object was created/updated/in sync, otherwise "Branches synced" (fewer than two PRs, stacked PRs unavailable, a cross-stack divergence, or no GitHub client). - Update the command's long description to document the stack-object step and the two possible closing messages. * cmd/submit.go - Thread a `synced bool` return through the existing, tested stack helpers so sync can tell whether the remote stack object now matches local: `syncStack`, `createNewStack`, and `updateStack` now return `bool`; `adoptRemoteStack` returns `(handled, synced)`; and `handleCreate422` returns `bool` (true only when the PRs are already stacked together). Extract the shared `stackPRNumbers` helper. - This is additive: submit's single call site ignores the new return value, so submit's behavior, output, and tests are unchanged. Reusing these helpers (instead of duplicating the 404/422 handling in sync) keeps the create/adopt/update logic in one tested place. Tests * cmd/sync_test.go — six new cases covering the reconciliation matrix: - TestSync_CreatesRemoteStackWhenPRsExist: open PRs but no remote stack -> CreateStack is called and the new ID is persisted to the stack file; output contains "Stack created on GitHub" and "Stack synced". - TestSync_AdoptsExistingEqualRemoteStack: a matching remote stack -> no create/update, ID recorded, "Stack synced". - TestSync_UpdatesPartialRemoteStack: a subset stack -> UpdateStack with the full PR list, "Stack synced". - TestSync_FewerThanTwoPRs_BranchesSynced: one PR -> no stack API calls, "Branches synced", not "Stack synced". - TestSync_StacksUnavailable_BranchesSynced: 404 on create -> warns, "Branches synced". - TestSync_PRsSpanMultipleStacks_BranchesSynced: PRs across two stacks -> divergence warning, no create/update, "Branches synced". Docs Document the new stack-object step and the "Stack synced" vs "Branches synced" distinction in: - README.md - docs/src/content/docs/reference/cli.md - skills/gh-stack/SKILL.md - docs/src/content/docs/introduction/overview.md - docs/src/content/docs/guides/stacked-prs.md - docs/src/content/docs/guides/workflows.md * Address PR review: one ListStacks per sync, command-neutral guidance Two follow-ups from the #156 review (both flagged optional / non-blocking). 1. Remove the redundant ListStacks round-trip on sync's create path. syncRemoteStack fetched the stack list for its already-up-to-date short-circuit, then delegated to syncStack -> adoptRemoteStack, which listed the stacks again — two GETs on the first-sync-create and membership-changed paths. Refactor adoptRemoteStack into a list-accepting reconcileUntrackedStack(cfg, client, s, prNumbers, stacks): syncStack now fetches the list once and passes it down, and syncRemoteStack reuses the list it already fetched. Net: exactly one ListStacks per sync. This also drops the (handled, synced) tuple. Submit's behavior is unchanged. 2. Make the divergence / dropped-PR guidance command-neutral. The shared helper emitted submit-specific wording ("reconcile them before submitting", "...then `gh stack submit`") that is now reachable from `gh stack sync`. Reword to "reconcile them first" and drop the trailing `gh stack submit` so it reads correctly from either command. Tests: assert exactly one ListStacks on the create path and that the divergence guidance is not submit-specific. * increment skill file version * Simplify sync reconciliation: reuse syncStack instead of a parallel path Review feedback noted the change felt heavier than the fix warranted. The weight came from `syncRemoteStack` (cmd/sync.go), a near-duplicate of submit's `syncStack` — same <2-PR guard, ListStacks, and update/create dispatch — that existed only to add an "already up to date" short-circuit. That one optimization is what spawned the second entry point, the pre-fetched-list threading, and the double-ListStacks it then required. Collapse it to a single reconciliation path: - Remove `syncRemoteStack`; `gh stack sync` now calls the shared `syncStack` directly. One path, one ListStacks per sync. - Fold `createNewStack` into `reconcileUntrackedStack` (renamed from `adoptRemoteStack`) so it returns a single `synced bool` instead of a `(handled, synced)` tuple and owns its own ListStacks again. - Inline `stackPRNumbers` back into `syncStack` (it was only extracted to share with the now-removed `syncRemoteStack`). - Drop the now-unused `strconv`/`github` imports from cmd/sync.go. Behavior note: a routine re-sync of an already-tracked stack now prints "Stack updated on GitHub with N PRs" instead of "Stack already up to date on GitHub". This is accurate (sync does PUT the current state) and matches submit. The "Stack synced" / "Branches synced" summary is unchanged, and submit's behavior is unchanged.
trunk command (#108) * Add `gh stack trunk` navigation command Add a new navigation command that checks out the trunk branch of the current stack. The command is stack-aware: it requires the user to be on a branch that is part of a stack, loads the stack metadata, and checks out `s.Trunk.Branch`. If the user is already on the trunk branch, it prints a message and exits without calling git checkout. New files: - cmd/trunk.go: TrunkCmd (cobra command) + runTrunk implementation - cmd/trunk_test.go: 7 test cases covering happy path, already on trunk, from top of stack, not in a stack, checkout failure, custom trunk branch name, and positional argument rejection Modified files: - cmd/root.go: register TrunkCmd in the "nav" command group - README.md: add `gh stack trunk` to the Navigation section - docs/src/content/docs/reference/cli.md: add `gh stack trunk` reference section * address review comments * increment skill version
prune merged branches (#94) * prune merged branches * interactively prompt for prune * delete remote tracking ref too * disable selecting merged branches in TUIs * include full list (including merged PRs) in PUT request to stacks API * add prune to docs * addressing review comments * increment skill file version