mirror of
https://github.com/github/gh-stack.git
synced 2026-09-14 20:26:28 +08:00
68ce60c760
* 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
37 lines
2.4 KiB
Markdown
37 lines
2.4 KiB
Markdown
# gh-stack: Copilot Instructions
|
|
|
|
A Go CLI extension (`gh stack`) for managing stacked branches and pull requests. Uses Cobra for commands, bubbletea/lipgloss for TUI, and `stretchr/testify` for tests.
|
|
|
|
## Build and validate
|
|
|
|
```sh
|
|
go mod download # install deps
|
|
go build -o gh-stack . # build
|
|
go vet ./... # static analysis. Always run before tests.
|
|
go test -race -count=1 ./... # tests with race detection
|
|
```
|
|
|
|
No Makefile, no code generation, no external linter config. Standard Go toolchain only.
|
|
|
|
## Project layout
|
|
|
|
- `cmd/`: One Cobra command per file. Each exports `<Name>Cmd(cfg *config.Config)` with logic in `run<Name>()`.
|
|
- `internal/git/`: `Ops` interface (52 methods) wrapping git CLI. `MockOps` for tests. Package-level functions delegate to swappable `ops` variable.
|
|
- `internal/github/`: `ClientOps` interface (18 methods) for GitHub API. `MockClient` for tests. Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`); merges use the async merge API (`/repos/{owner}/{repo}/pulls/{n}/merge-async`) with an explicit `merge_action` (`direct_merge` or `merge_queue`) chosen from the base branch's merge-queue detection. `merge_action` is optional — omitting it (or sending `default`) lets the server auto-route (merge queue if one is configured, else direct merge) — but the CLI sends it explicitly so a wrong detection fails loudly instead of silently merging directly.
|
|
- `internal/config/`: `Config` struct passed to all commands. Holds I/O, colors, and test hooks (`SelectFn`, `ConfirmFn`, `InputFn`, `GitHubClientOverride`).
|
|
- `internal/stack/`: Stack file (`.git/gh-stack`, JSON) management with file locking.
|
|
- `internal/tui/`: bubbletea views (`stackview`, `modifyview`).
|
|
|
|
## Coding conventions
|
|
|
|
- Return typed `ExitError` sentinels (codes 1-10 in `cmd/utils.go`) from `RunE`. Never call `os.Exit()` directly.
|
|
- Check errors with `var exitErr *ExitError; errors.As(err, &exitErr)`.
|
|
- Table-driven tests with `t.Run()` subtests.
|
|
- Use `config.NewTestConfig()` for test configs with captured I/O.
|
|
- Mock git: `restore := git.SetOps(&git.MockOps{...}); defer restore()`. Always defer restore.
|
|
- Mock GitHub: `cfg.GitHubClientOverride = &github.MockClient{...}`.
|
|
- Mock prompts: set `cfg.SelectFn`, `cfg.ConfirmFn`, or `cfg.InputFn`.
|
|
- Load stack files with `stack.Load(dir)` after writing to get correct checksums.
|
|
|
|
For full architecture details, see [AGENTS.md](../AGENTS.md) in the repository root.
|