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 Ctrl+C is pressed during a prompter interaction, the CLI now prints a friendly 'Received interrupt, aborting operation' message instead of ugly wrapped errors like 'failed to read prefix: could not prompt: interrupt'. Changes: - Add isInterruptError(), printInterrupt(), and errInterrupt sentinel to cmd/utils.go for centralized interrupt detection - Update all 8 prompt sites (init, push, checkout, utils) to detect survey's terminal.InterruptErr and exit cleanly - Update callers of resolveStack, pickRemote, and ensureRerere to propagate interrupt without double-printing errors - Change ensureRerere signature to return error so callers can abort on interrupt - Add tests for interrupt detection helpers Closes td-746bdc Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fdce698 to
426ad11
Compare
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>
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.