Skip to content

Add reorder, move and init use cases - #41

Merged
DomBlack merged 8 commits into
reshape-enginefrom
reshape-usecases
Oct 15, 2026
Merged

DomBlack merged 8 commits into
reshape-enginefrom
reshape-usecases

Conversation

@DomBlack

@DomBlack DomBlack commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

The three use cases behind the new commands, built on the reshape driver from the PR below.

Reorder takes the new order bottom to top, insists it names every branch of the stack once, leaves the branches below the first change alone and restacks the rest. Move takes a branch (and everything above it, unless told otherwise) onto the top of another stack, or onto a trunk as a new stack; a move within the same stack is a reorder and says so, and a branch can't go under another stack's branches because gh stack keeps stacks as a single line. Init tracks branches you already have as a new stack, recording merge bases and rebasing nothing, so it works from any branch without the checkout flip gh stack's own init does.

Submit does the GitHub side. A reorder or move clears the stack's id locally and queues it; the next submit dissolves each queued stack with gh stack's unstack (remote only, since the number is no longer tracked here) before gh stack creates the submitted stack again, and the notice says which stack was dissolved and, when it isn't the one being submitted, to submit from it. Reviewers will see a new stack number after a reorder, the same as with gh stack modify.

@DomBlack
DomBlack added this pull request to stack #43 October 15, 2026 14:56
@DomBlack DomBlack changed the title reshape usecases Add reorder, move and init use cases Oct 15, 2026
Copilot AI balanced review requested due to automatic review settings October 15, 2026 15:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Reordering a stack has meant leaving git-stack for gh stack's interactive modify, then restacking. gh stack keeps a stack as a plain list, so a reorder is a permutation of that list followed by a restack, and we have both halves now.

Reorder takes the new order bottom to top (or asks a picker for it), insists it names every branch of the stack once, leaves the branches below the first change alone, and runs the shared reshape: the metadata is rewritten, each moved branch keeps the base that marks its own commits, and the restack replays them onto the new parents. A conflict stops the usual way and abort puts the old order back along with the refs.
Moving a branch to another stack, or out onto trunk as a stack of its own, was not something git-stack could do; you had to untrack, rebase by hand and track again. With gh stack's flat stacks a move is cutting a run of branches out of one list and appending it to another, so it rides on the same reshape as reorder.

The destination has to be a trunk or the top of another stack (a within stack move is a reorder, and gh stack has no way to put a branch under another one's children, so both get a clear refusal). By default the branch takes everything above it; with --only it goes alone and the branches left behind are restacked onto its old parent as a second segment of the same operation, so continue and abort cover both.
Branches made by hand, or a stack somebody else pushed, had to be adopted with gh stack init, which also wants to check the top branch out and refuses from inside another stack. Adopting is only a metadata change, so it now goes through our own metadata editor: one new stack on the trunk, each branch's base set to the merge base with the one below, nothing rebased and nothing checked out.

The trunk is --base, else the only trunk the repository has, else its default branch. Branches that aren't on their parent are recorded as they are and reported, with git stack restack as the next step.
GitHub's stacks API can create a stack and append to it, nothing else, so after a reorder or a move gh stack submit would push the branches and fix each pull request's base but leave the stack on GitHub in its old order with a warning, and an append of pull requests still held by their old stack would be rejected.

A reshape now records the stale stack and clears its id locally; submit works through that list first, dissolving each stack on GitHub with gh stack's own unstack (remote only, since the number is no longer tracked here) before gh stack creates it afresh from the local list. A dissolve that fails stays on the list and is retried next time, with a notice either way.
After a move or reorder, submit dissolves the stale GitHub stack but only creates the one you submitted from. The notice said the stack would be created again in its new order, which isn't true when it now lives somewhere else (e.g. after moving a branch onto another stack and submitting from there). The pull requests just sat ungrouped with nothing saying what to do about it.

The notice now depends on where the dissolved stack lives: created again below if it is the one being submitted, "submit from X to group them again" if it still starts its own stack, and a plain note if its pull requests moved into another stack.

While in there, move now says "can't be moved onto itself" and "is already on X" for the source's own parent instead of the misleading same stack message, and init refuses while an interrupted reorder or move is waiting to be continued or aborted, like the other commands do.
A handful of small gaps turned up in review. A reorder could silently drop a branch added to the stack while it ran, init accepted a stacked branch as its base, and submit would happily push in the middle of a stopped reorder, then dissolve a GitHub stack that an abort would have put back. Unstacking also treated any "not in a stack" exit from gh as "already gone", and an unreadable pending file gave a notice that didn't say where it was.

Reorder now refuses if the stack changed under it, and hands back its own copy of the order. Init refuses a stacked branch as --base. Submit checks for a saved restack first, and the unreadable file notice names the file and says deleting it clears the list. Unstack checks that gh stack is installed, and only counts the stack as gone when gh says it was not found.
After a move or a reorder, submit dissolves the old stack on GitHub so it
can be grouped again in its new order. But gh stack only creates the stack
you're submitting, so a moved branch's old stack (or a reordered stack you
then submitted from somewhere else) stayed ungrouped on GitHub until someone
happened to submit from it.

So the forge can now create a stack from a list of pull request numbers, and
once the backend submit is done, submit creates each stack it dissolved that
still exists and isn't the one being submitted. The new id and number are
recorded on that stack so later submits treat it like any other. A stack with
fewer than two pull requests has nothing to group, and a create GitHub refuses
just becomes a notice; submitting from that stack later creates it as before.
Submit now creates a dissolved stack again on GitHub, but a couple of cases
left you stuck. With no forge set up nothing gets created, yet the notice had
lost its "submit from it" hint, so there was no hint at all. And a branch in
the middle with no open pull request breaks the chain GitHub needs, so the
create was bound to be refused, with GitHub's reason wrapped in a second error
and an HTTP status tacked on the end (or just "Validation Failed" when the
real reason was in the response body).

So without a forge the dissolve notice keeps saying to submit from the stack
to group it again. Merged branches at the bottom are still left out, but the
first branch above them without an open pull request now stops the create,
and the notice names it. When GitHub refuses, its own message is used as is,
taken from the response body when that has the details.
Copilot AI balanced review requested due to automatic review settings October 15, 2026 15:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@DomBlack
DomBlack merged commit 17383cb into main Oct 15, 2026
5 of 6 checks passed
@DomBlack
DomBlack deleted the reshape-usecases branch October 15, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants