Status and doctor
Two commands answer “where am I?” before anything else has to happen. Both are
read-only and both ask nothing of the network: not GitHub, not Graphite, not
the remote. status is the full picture of one stack; doctor is the opposite
trade, every recorded stack but only what is wrong with it.
Neither repairs anything. Where they find something, they name the command that puts it right, phrased as a next step — the same advice a refused command gives, described in When a command refuses.
status
Section titled “status”g2g status # this whole stackg2g status --scope subtree # the branch and its descendantsg2g status --branch synthetic-login --scope trunk # every stack on that trunkg2g status --scope all # every stack in the repositoryg2g status --remote synthetic-fork # compare with another remoteg2g status --from graphite # Graphite's record, in this formatg2g status draws the stack you are on from g2g’s own graph and says where
each branch stands, the way git status does for one branch. Because it asks
nothing of the network, it is the command to run before deciding whether
anything else needs to happen. Pull requests are
g2g github status’s business, because reading one
invokes gh.
It defaults to --scope stack: reading is free, so it shows where you are,
ancestors and descendants both. The other values, and what each selects, are
in Scope. --from graphite draws Graphite’s record of
the branch in the same format, which is how you compare the two views before
reconciling them; status --from offers only the offline records, g2g and
graphite. See Where structure comes from.
Reading the graph
Section titled “Reading the graph”A chain is drawn as the same flat column every other command uses, because a chain has no structure that indentation would add:
Target synthetic-login · --branch
○ synthetic-main trunk │ ● synthetic-auth ● synthetic-login ← target
Scope stack · 3 branches · /synthetic/repo/.git/g2g/graph.jsonA fork is drawn with connectors. Here the selection is --scope trunk, so the
second stack on the same trunk is drawn too:
Target synthetic-login · --branch
○ synthetic-main trunk ├─● synthetic-auth │ ├─● synthetic-login ← target │ └─● synthetic-session └─● synthetic-billing
Scope trunk · 5 branches · /synthetic/repo/.git/g2g/graph.jsonThe last line gives the scope, how many branches it covers, and where the graph is stored.
Where each branch stands
Section titled “Where each branch stands”status reports what it finds and repairs none of it:
- needs restack — the recorded parent moved underneath the branch.
- moved off parent — the branch is no longer built on its recorded parent, which is what a manual rebase looks like.
- parent missing — the recorded parent is no longer a local branch, which is what a squash-merged and deleted parent looks like.
- landed — the branch’s own work is already in the trunk, by content.
- no commits of its own, fork point unresolvable, and branch missing (the recorded branch was deleted or renamed with plain Git).
Compared with the remote
Section titled “Compared with the remote”Each branch is also compared with what its remote last held here: the
remote-tracking ref a push or git fetch left, and the ref under
refs/g2g/remotes/ that g2g pull fetches
into. Where one descends from the other, the descendant is the later knowledge;
where they are not in order, the remote-tracking ref wins, because it is what a
push moves and what git status compares with. Nothing is fetched, so the
answer is exactly as current as the last fetch or push, and the output ends by
saying so.
| Mark | Means |
|---|---|
origin✓ |
the remote holds what is here |
N ahead |
commits here the remote does not have · g2g push |
N behind |
the remote has work this branch does not · g2g pull |
replayed since pushed |
restacked: nothing is missing on either side, and it needs pushing |
diverged · N here, M there |
both have moved · g2g pull shows the ways to reconcile |
on a commit not here |
the remote’s tip was never fetched · g2g pull fetches it |
not on origin |
never published |
Commits are counted by content and bounded to the branch’s own, as
push counts them, so a branch replayed onto a trunk
that moved on reads as replayed rather than as diverged with every trunk commit
counted against it. Beneath the stack come the next steps, in git status’s
manner: which branches to push, which to pull. A branch that was not compared
says nothing rather than reading as up to date.
--remote picks the remote and defaults to origin. A repository with no
origin is ordinary and simply draws no marks, which is how this stack from a
scratch repository reads:
Target session · current Git branch
○ main trunk │ ● auth ● login ● session ← target
Scope stack · 4 branches · /work/app/.git/g2g/graph.jsonA remote you name that does not exist is an error.
doctor
Section titled “doctor”g2g doctor # every recorded stack, and only what is wrongg2g doctor --remote synthetic-forkdoctor reads every recorded stack, offline, and reports only what is not as
it should be, each with the command that puts it right. Most of what it finds
broke outside g2g — a branch deleted with plain Git, a parent rebased by hand,
a force push from somewhere else — which is why it is a command to run when
something feels off rather than a mode of status.
| Finding | Way out |
|---|---|
| a restack stopped part-way | g2g restack --continue |
| its parent moved underneath it | g2g restack --branch <branch> |
| no longer built on its parent, its parent is no longer a local branch, or no tracked parent | g2g track --branch <branch> |
| its recorded fork point is gone | g2g track --branch <branch> --parent <parent> |
| recorded, and no longer a local branch | g2g untrack --branch <branch> |
| already landed | g2g prune --branch <branch> |
| diverged from the remote | g2g pull --branch <branch> |
A branch with no commits of its own is not a finding: it is as likely to be a branch nobody has started as one that is finished.
When nothing is wrong, it says so and nothing else:
Target every recorded stack · repository
Nothing needs putting right across 3 recorded branches.doctor exits 0 when it finds nothing, 1 when it finds something, and 2
when it could not tell, so a script can ask the question and read the status.
The 1 follows diff and grep: a script asking whether anything is wrong
wants the answer as a status. See
Exit status.