Skip to content

When a command refuses

A blocked preview names the command that repairs the state rather than leaving the reader to work it out. status, github status and doctor give the same advice, phrased as a next step.

Refusing is how g2g avoids guessing. Where there is more than one reasonable answer, or an answer would quietly change something you did not ask about, it stops, says why, and lists the ways out.

The reason comes first, on its own line, then one way out per line, each command drawn as a command. Here create is asked to start auth on main in a repository with no remote, so the graph does not record main and nothing says it is the default branch:

Target auth · new branch
● main untracked
● auth new ← target
Apply blocked
main is not in the g2g graph and no default branch is known here, so recording auth under it would make main a trunk
g2g adopt --branch main record the stack main is on first
pass --parent with a branch the graph records
git remote set-head origin --auto if main is origin's default branch, record that here · create then accepts it
if main is a trunk, start its first branch with git switch -c and record it with g2g track --parent main
No changes were made. Apply would refuse until that is resolved.

create refuses because recording a child under a branch the graph does not know would quietly make that branch a trunk. It accepts a parent the graph already records, or the repository’s default branch, and the refusal says when no default branch is known and how to record one. See Shape the stack.

And here track is asked to record synthetic-login without being told its parent. It lists the candidates, nearest first, and blocks:

Target synthetic-login · --branch
● synthetic-login untracked ← target
Apply blocked: no parent chosen
Nearest ancestor: synthetic-auth (1 commit behind)
Then: synthetic-main (1 commit behind)
g2g track --parent synthetic-auth record just this edge
g2g adopt record the whole ancestry at once
Scope branch · 1 branch · /synthetic/repo/.git/g2g/graph.json
No changes were made. Apply would refuse until that is resolved.

The nearest ancestor is usually right, and “usually” is not a basis for writing down structure every later command trusts. See The forest.

State Way out
A pull request has merged g2g pull for a stack g2g records, gt sync for one Graphite declares; none for one read from pull request bases, which nothing here records
A branch’s work has already landed g2g prune, or gt sync for a Graphite-declared stack
A branch has no pull request, or one closed without merging g2g submit
A pull request is open on the wrong base g2g github retarget
Two open pull requests for one branch none: close all but one; a person has to choose, and the preview says so
The remote has moved on a branch push would publish fetch and reconcile first, or git push --force-with-lease <remote> <branch> to replace what is published
A branch and its published version (the remote’s copy) have both moved g2g pull --take published, bounded with --through, or reconcile it yourself
prune would strand a child Git does not show sitting on the branch below g2g pull --prune, or g2g track --branch <child> --parent <branch> for each child
A tracked branch was deleted with plain Git g2g untrack --branch <branch>
A branch was rebased by hand (moved off its parent) re-record it with g2g track
A branch that has to move is checked out in another worktree switch that worktree away or close it, or select less with --branch or --scope
fold into a parent that has moved on since the branch was stacked on it g2g restack --branch <branch>, then fold
fold into a trunk g2g land --branch <branch>: a branch joins its trunk through its pull request
delete, fold or rename of a branch the g2g graph does not record g2g track --branch <branch>, or plain git branch, which is all it would do

Some states deliberately have no command. Two open pull requests for one branch is left to a person, because nothing here can tell which one you meant. A structure read from pull request bases gets no command for a merged pull request, because nothing here records it, and naming no command is better than naming a wrong one.

--json carries a refusal as two fields. blocked is the reason. repair is the ways out, each carried as a command and what running it achieves, so a consumer does not have to find the command inside the sentence. A way out with no command is a whole answer that is not a thing to run, such as “fetch and reconcile first”, and a refusal that nothing here fixes carries no repair at all.

repair is also reported where nothing is blocked and there is still something to do: a branch no source describes is a state, not a refusal. See Output and Working with agents.