Skip to content

Push and submit

push and submit publish a stack. They sit at the top level beside status and restack, although they reach a remote, because publishing a stack is part of managing it. push touches only Git; submit also opens pull requests, and so invokes gh.

Both take a linear selection. A GitHub native stack is linear, so --scope accepts only stack or path, and a selection that forks is refused with the remedy rather than g2g choosing a line for you. Selecting a leaf is that remedy and needs no flag: a leaf has no descendants, so stack collapses to an ordered path by itself. See Scope.

A branch is identified by its single open pull request. Closed and merged pull requests left on a reused branch name are treated as history: they never block github link, github retarget, or github status, and submit creates a replacement rather than skipping the branch. Two or more open pull requests for one branch is the only ambiguity, and it fails closed: a person has to close all but one.

Terminal window
g2g push --branch feature/top # preview; full-stack expansion is the default
g2g push --branch feature/top --apply # every selected ref advances, or none do
g2g push --remote upstream --apply # another name from git remote, not a branch

g2g push is deliberately narrow: it publishes the selected linear path in one git push --atomic with a --force-with-lease per branch, and never invokes GitHub, submits, or restacks. Every selected non-trunk branch is pushed bottom-to-top. The path may come from g2g’s own graph or from Graphite, and --from g2g or --from graphite pins which (see Sources). --remote defaults to origin and must name a configured remote.

The preview shows the path, what each branch would publish, and the exact command:

Target synthetic-top
○ synthetic-main trunk
│
● synthetic-lower 2 commits to publish
● synthetic-top new branch on the remote ← target
Command to run
git push --atomic --force-with-lease origin synthetic-lower synthetic-top
Atomic push: all selected refs advance together or none do.

Each lease is pinned to the remote tip the preview observed. A bare --force-with-lease takes its baseline from the remote-tracking ref, and any fetch between the preview and the push would disarm it.

A branch replayed since it was published, which is the ordinary state after a restack, is shown as rewritten and pushed: the remote’s version is compared by content, so a commit that is here under a new id is not mistaken for somebody else’s. A commit the remote has that this checkout does not, by content, is refused rather than dropped. The way out is to fetch and reconcile first, or to run git push --force-with-lease <remote> <branch> yourself if you mean to replace what is published.

A remote that does not support atomic pushes, or a rejected lease, fails the push. There is no non-atomic or unsafe-force fallback.

push asks the remote itself, because a lease has to be pinned to what is there now. g2g status makes the same comparison from what the remote last held here, without a network, so the two agree up to whatever has happened on the remote since the last fetch.

push never calls gh, so it never touches pull requests, their bases, or the stack comments.

g2g submit is a preview-first publication path for a resolved linear stack. With --apply it:

  1. validates the complete spec,
  2. revalidates immediately before mutating,
  3. performs one atomic, lease-protected push, as push does,
  4. creates only the missing pull requests, bottom-to-top, as drafts,
  5. preserves existing pull requests, and
  6. keeps the stack comment on each pull request.

It never invokes gt submit, restacks Graphite, or retargets an existing pull request. Moving a base that a restack left stale is g2g github retarget, which has its own preview.

Terminal window
g2g submit # preview
g2g submit --edit --apply # write titles and bodies in $EDITOR, then publish

Generate a reusable spec outside the repository, fill in each title, validate it, then apply it:

Terminal window
spec_dir="$(mktemp -d)"
g2g submit --write-spec "$spec_dir"
g2g submit --spec "$spec_dir/submission.json"
g2g submit --spec "$spec_dir/submission.json" --apply

The spec is one JSON document with ordered branch, title, body and reviewer entries. Complex Markdown bodies are preserved exactly. If apply fails, the spec remains in place and the error gives the exact repair, validation, and retry commands.

g2g submit --edit creates one temporary submission.json document and opens $EDITOR; it never opens a buffer per pull request. Add --apply to continue after editing. The temporary spec is deleted only after a successful --edit --apply, and --keep-spec retains it even then. Validation, editor, interruption, and GitHub failures always retain it.

Missing pull requests are opened as drafts. There is no --draft flag, because a draft is the default and can be marked ready at any time. --ready is how you ask for the thing that cannot be undone, since opening a pull request ready for review notifies reviewers immediately.

--write-spec records the choice in the document, so an --apply that reads it back does not silently drop it, and --no-ready overrules a spec that asks for ready. The preview names what it is about to open and echoes --ready into the command it suggests, so what you read is what runs.

Repository pull request templates are detected from GitHub’s conventional locations. One template pre-fills generated bodies. Multiple templates require an explicit --template <name> or --no-template; g2g never guesses. Explicit bodies in the spec win over templates.

Once the pull requests are opened, submit keeps the stack comment on each of them as its last act, because it has changed which pull requests the stack is made of. The preview says so, and --no-comment skips it. If keeping the comments fails, the pull requests that were opened stay opened, and the command exits 3 and names g2g github comment --apply. See github comment for what the comment says and how it is kept.

--link also links the complete stack as a GitHub native stack. That is opt-in, because GitHub will not merge a linked pull request through gh pr merge, which is how land merges each one. land refuses a linked stack and names the g2g github unlink that clears it.

An interrupted submit can leave refs pushed and some pull requests created. A mutation that runs out of time says so and states what may already have happened; re-running with the same spec creates only what is missing. For a slow network or a deep stack, raise the ceiling with the root --timeout flag:

Terminal window
g2g --timeout 3m submit --spec "$spec_dir/submission.json" --apply

See Timeouts.

Terminal window
g2g github retarget --apply # fix bases a restack left stale
g2g github status # check each pull request against the stack

GitHub covers both, and Land takes a finished stack down onto its trunk.