Stacks CLI prototype with core operations - #1
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces an initial gh stack CLI prototype, including local stack state persistence in .git/gh-stack, basic stack operations (init/add/view/update/navigation), and GitHub PR create/update plumbing with placeholders for stack-aware APIs.
Changes:
- Adds local stack state model + persistence and a git helper layer used by commands.
- Implements core CLI commands (
init,add,view,update,push, navigation) plus placeholder commands (checkout,merge, hidden stubs). - Wires the extension entrypoint to the Cobra command tree and adds dependencies for CLI UX and GitHub API access.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| main.go | Switches entrypoint to execute the Cobra CLI. |
| go.mod | Updates module path and adds required CLI/GitHub dependencies. |
| go.sum | Adds checksums for new dependencies. |
| cmd/root.go | Defines the stack root command and registers subcommands. |
| cmd/init.go | Initializes/adopts a stack and persists state in .git/gh-stack. |
| cmd/add.go | Adds a new branch on top of the current stack. |
| cmd/navigate.go | Adds stack navigation commands (up/down/top/bottom). |
| cmd/view.go | Displays stack state (short/full) and optionally opens PRs in browser. |
| cmd/update.go | Implements fetch + cascading rebase with conflict state persistence. |
| cmd/push.go | Pushes branches and creates/updates PRs (stack-linking is a stub). |
| cmd/checkout.go | Placeholder for future stack checkout workflow. |
| cmd/merge.go | Placeholder for future stack merge workflow. |
| cmd/placeholder.go | Hidden placeholder commands for planned features. |
| internal/stack/stack.go | Stack data structures + JSON load/save in .git/gh-stack. |
| internal/git/git.go | Git helper wrapper around cli/v2/git plus custom helpers. |
| internal/config/config.go | Shared config for terminal IO and colorized output. |
| internal/github/github.go | GitHub API client wrapper (GraphQL PR find/create/update). |
Comments suppressed due to low confidence (2)
cmd/update.go:327
abortUpdateignores errors from checkout/reset for each branch, which can leave the repo partially restored while still reporting success. Consider handling errors (at least collecting/reporting them and returning a non-zero error) so users know when a restore did not fully succeed.
for branch, sha := range state.OriginalRefs {
_ = git.CheckoutBranch(branch)
_ = git.ResetHard(sha)
}
cmd/update.go:339
saveRebaseStatediscards JSON marshal/write errors. If the state file can’t be written,--continue/--abortwon’t work but the user won’t know why. Return an error fromsaveRebaseStateand surface it where it’s called (especially on the first conflict).
func saveRebaseState(gitDir string, state *rebaseState) {
data, _ := json.MarshalIndent(state, "", " ")
_ = os.WriteFile(filepath.Join(gitDir, rebaseStateFile), data, 0644)
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR turns gh-stack into a functional GitHub CLI extension prototype by introducing the core command set, local stack persistence, and supporting git/GitHub API wrappers.
Changes:
- Replace the previous
main.goprototype with a Cobra-basedgh stackcommand tree (init/add/view/rebase/push/sync/unstack + placeholders). - Add local stack persistence (
.git/gh-stack) with a JSON schema and associated stack model/helpers. - Introduce internal wrappers for git operations and GitHub API calls to support PR discovery/creation and rebasing workflows.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| main.go | Switches entrypoint to execute the Cobra CLI. |
| go.mod | Updates module path and adds dependencies for CLI + TUI + GitHub operations. |
| go.sum | Adds/upgrades dependency checksums consistent with new imports. |
| README.md | Documents installation, concepts, and the new command set/workflows. |
| internal/stack/stack.go | Implements stack persistence model + load/save helpers for .git/gh-stack. |
| internal/stack/schema.json | Defines JSON schema for the stack state file. |
| internal/github/github.go | Adds GitHub client wrapper (GraphQL/REST) for PR operations. |
| internal/git/git.go | Adds git wrapper utilities for branching, pushing, rebasing, and conflict detection. |
| internal/config/config.go | Adds shared config for IO, terminal detection, and styled output. |
| cmd/root.go | Adds root Cobra command and wires up subcommands. |
| cmd/init.go | Implements local stack initialization (interactive/explicit/adopt). |
| cmd/add.go | Implements adding a new branch at the top of the current stack. |
| cmd/view.go | Implements stack visualization (short/full) + pager + open-in-browser. |
| cmd/rebase.go | Implements cascading rebase with conflict state persistence and resume/abort. |
| cmd/push.go | Implements pushing stack branches and creating/updating chained PRs. |
| cmd/sync.go | Implements fetch → trunk FF → cascade rebase → push → PR sync workflow. |
| cmd/unstack.go | Implements removing a stack from local tracking and (placeholder) GitHub deletion. |
| cmd/navigate.go | Implements stack navigation commands (up/down/top/bottom). |
| cmd/utils.go | Adds stack disambiguation + PR metadata syncing helpers. |
| cmd/placeholder.go | Adds hidden placeholder commands for planned features. |
| cmd/checkout.go | Adds placeholder for stack checkout workflow. |
| cmd/merge.go | Adds placeholder for stack merge workflow. |
| cmd/feedback.go | Adds command to open a Discussions feedback form. |
Comments suppressed due to low confidence (2)
cmd/utils.go:33
- This message includes a trailing newline, but
Warningfalready prints one. This will produce an extra blank line in the output; remove the\nfrom the format string.
cfg.Warningf("Branch %q is the trunk of multiple stacks\n", branch)
cmd/view.go:306
runViewalready synced PR state before invokingviewWeb, butviewWebstill performs a GitHub API lookup for every branch (FindPRForBranch). This is redundant and can significantly slow down--webfor large stacks. Prefer openingbr.PullRequest.URLwhen present, and only querying when it’s missing.
opened := 0
for _, br := range s.Branches {
pr, err := client.FindPRForBranch(br.Branch)
if err != nil || pr == nil {
continue
}
url := fmt.Sprintf("https://github.com/%s/%s/pull/%d", repo.Owner, repo.Name, pr.Number)
if err := b.Browse(url); err != nil {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
Warning This is an internal experiment to assess Copilot's ability to auto-approve PRs. Please 👍 this comment if the assessment below is correct and 👎 if not. Feedback in #f-ccr-auto-approve is appreciated! Copilot thinks this PR is not ready to merge — see review comments for details. |
When a rebase conflict was detected during 'gh stack sync', the function continued to push branches and reported 'Stack synced' with exit code 0. Now it persists PR state and returns ErrConflict immediately, preventing the confusing post-conflict push and success message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
* merge cmd
* Refine the merge TUI and simplify the async-merge client
Follow-up polish for `gh stack merge` (the command itself landed in the
previous commit). These changes refine the interactive wizard, enrich the
PR picker, and replace the merge client's bespoke HTTP handling with the
standard go-gh REST client.
Wizard and stepper:
- Redesign the top stepper as a segmented bar: completed steps are green,
the active step is the brightest, and upcoming steps are dimmed. Steps
are separated by a Powerline arrow that blends into the shading, with a
graceful fallback to abutting segments on terminals that lack the glyph
(e.g. Apple Terminal). Set GH_STACK_POWERLINE=1/0 to override detection.
- Show the stack number in the header ("Merge stack #123").
- Hide the header and stepper once the merge is submitted so the live
progress view stands on its own.
PR picker:
- Render each pull request on two lines: the title (white/black, a touch
bolder when selected) above its "#number • branch" (gray, fainter when
deselected). Titles are fetched in one batched GraphQL query (PRTitles)
and fall back to the branch name.
- Scroll long stacks in a fixed 10-item window with persistent "N more"
indicators, so the list no longer jumps as those hints appear and
disappear. Add shift+up / shift+down to jump to the top or bottom.
Progress and outcome:
- Always render a status line ("Submitting merge request...") so it does
not pop in later and shift the view, and normalize messages to end in an
ellipsis.
- Print the final result from the command layer rather than the TUI: a
success line that includes the merge commit SHA
("Merged #1, #2 into main (abc1234)"), an atomic-rollback note on
failure, a distinct message when the user stops watching an in-flight
merge, and "Cancelled operation, nothing merged" on cancel.
- Clamp every rendered line to the terminal width so resizing no longer
leaves duplicated header lines behind, and make truncation ANSI-aware.
Async-merge client:
- Use the go-gh REST client (c.rest.Put / c.rest.Get) for both the submit
and poll endpoints, removing the bespoke http.Client, base-URL helper,
and manual response decoding. The REST client discards non-2xx bodies,
but that only costs the rare 400 message and 409 UUID: real merge
failures still surface through the 200 poll body, and the in-range PRs
are validated open, non-draft, and non-merged before submitting.
- Add classifyAsyncMergeError to map status codes to clear errors (404
unavailable, 409 already exists, 400 no longer mergeable) and drop the
now-unused AsyncMergeResult.StatusCode field. Rework the client tests to
drive the REST client through a stub http.RoundTripper.
* warn merge queue unsupported
* update for new status field from api
* merge cmd docs
* more helpful error msgs
* update to support merge queue
* addressing review comments
* hide merge method step for merge queue
* set merge action explicitly
* address review comments to clarify docs on merge/api behavior
gh stackCLI extensionA GitHub CLI extension for managing stacked branches and pull requests. Breaks large changes into small, reviewable layers with automated branch management, cascading rebases, and PR creation.
Local stack management
init [branches...]— Initialize a new stack. Supports interactive (prompts for branch names), explicit (creates branches from args), and adopt (--adopt) modes. Auto-detects trunk fromorigin/HEAD. Enablesgit rererefor conflict reuse. Prevents initializing inside an existing stack. Persists state to.git/gh-stack. Supports--prefixto set a branch name prefix and--numbered(with--prefix) for auto-incrementing branch names (prefix/01,prefix/02, …). In interactive mode, prompts for an optional prefix.add [branch]— Add a new branch on top of the current stack. Must be run from the topmost branch. Supports staging and committing as part of the add flow:-Astages all changes (including untracked),-ustages tracked files only, and-mcreates a commit with the given message. When-mis provided without an explicit branch name, the branch name is auto-generated (numbered format if the stack uses--numbered, otherwise date+slug format). On a branch with no commits yet,add -Amcommits directly on it rather than creating a new one.unstack [branch]— Remove a stack from local tracking and optionally from GitHub. With--local, only removes local metadata.Viewing and navigation
view— Display the stack structure with branch status, PR links, and last commit info. Status indicators:✓merged,⚠needs rebase,○open PR. Pipes through pager (GIT_PAGER/PAGER/less -R). Supports--short(compact branch list) and--json(machine-readable output).checkout [<pr-or-branch>]— Check out a locally tracked stack from a PR number or branch name. Interactive mode shows a menu of all locally available stacks. Currently limited to stacks created locally viagh stack init; server-side stack discovery is not yet implemented.up [n],down [n],top,bottom— Navigate between branches in the stack. Clamps to bounds — moving past the top or bottom is a no-op with a message.Rebasing
rebase [branch]— Cascading rebase of all branches in the stack. Fetches from origin first. Detects squash-merged PRs and usesgit rebase --ontoto avoid duplicate-patch conflicts. Scoping:--downstack(trunk to current),--upstack(current to top). Conflict handling: pauses and prints conflicted files with line numbers; resume with--continue, undo with--abort(restores all branches to pre-rebase SHAs). Leveragesgit rerereto auto-resolve previously seen conflicts. Supports--remoteto specify which remote to fetch from.Remote operations & PR workflow
push— Push all branches and create/update pull requests. Uses--force-with-leaseby default for safety after rebases. Creates PRs with branch-chaining (each PR targets the branch below it). Prompts for PR titles on new PRs (press Enter for branch name default, or use--autoto skip prompting). Supports--draft,--skip-prs(push branches without creating/updating PRs), and--remote.sync— All-in-one fetch → fast-forward trunk → cascade rebase → push → sync PR state. Performs each step safely: fast-forward is skipped if trunk has diverged, rebase only runs if trunk moved, push uses--force-with-leaseafter rebase. On conflict, aborts and restores all branches, directing the user togh stack rebasefor manual resolution. Supports--remote.Planned (not yet implemented)
merge <pr>— Merge a stack of PRs. Currently prints a notice that it is not yet implemented.Other
feedback [title]— Opens the gh-stack GitHub Discussions page to submit feedback.Abbreviated workflow
With
--prefix+--numberedon init and-Amon add, the typical workflow collapses to minimal keystrokes — no need to name branches, rungit add, or rungit commitseparately:Internals
.git/gh-stack(schema version 1). Tracks repository context (host:owner/repo), trunk, branches with merge-base SHAs, and PR metadata (number, GraphQL ID, URL, merged status). Rebase state stored in.git/gh-stack-rebase-state.cli/cli/v2/git.Client. Custom support for--force-with-leasepush, three-argumentrebase --onto, rerere auto-continue, conflict marker detection, and fast-forward merge.FindPRForBranch,CreatePR,UpdatePRBase). REST fallback. Auth via GitHub CLI defaults.Successf,Errorf,Warningf,Infof).--ontomode for subsequent rebases to replay only unique commits.