Skip to content

Where structure comes from

Every command that selects a stack asks one question first: which source describes this branch?

adopted into g2g's store → g2g's own graph
tracked by Graphite → Graphite
neither → refused, with the remedy

Adoption wins because recording an edge is you saying you want g2g to own the branch. Graphite is the fallback for branches g2g has not adopted.

The answer is worked out per branch, every time, and never stored. Moving a branch between sources is therefore just g2g track or g2g untrack, in either direction, and there is no ownership record to go stale.

It is per branch rather than per stack because a stack can legitimately span sources: main, then a branch Graphite tracks, then one adopted into g2g above it. A rule for the whole stack would have to give one of them the wrong answer. It is never stored because a stored owner goes stale through actions g2g never sees, such as someone running gt track on a branch that joins two trees.

Source Expresses Unpublished branches Trees Shared
g2g’s store intent yes yes no
Graphite intent yes yes no
pull request bases effect no yes yes

g2g’s store and Graphite say what was meant. Pull request bases say what GitHub will do on merge. That is not a weaker authority, it is authority over a different question, and both answers are worth having.

push, submit and github link work on a stack g2g owns, with no Graphite installed. And g2g will not run Graphite in a repository that does not already use it. Graphite’s discovery creates state, so being asked whether it applies must not be what enrols you. In such a repository g2g stays local. See Graphite for keeping the two records in step where you do use it.

Reading composes across sources: a branch can sit in g2g’s graph, be tracked by Graphite, and appear in a GitHub stack all at once, and nothing here removes it from any of them. Authority is not exclusive.

restack is the exception, deliberately. It needs a fork point, which only g2g’s own store records, so it refuses a Graphite-owned branch and says to g2g track it first. land likewise needs the stack in g2g’s own graph, because it replays and forgets there. See Land.

Terminal window
g2g status --from graphite # Graphite's record, drawn in g2g's format
g2g github status --from graphite # and its pull requests, as Graphite groups them
g2g push --from g2g

Once a branch is adopted there is otherwise no way to ask Graphite what it thinks of it, and comparing the two views is what you want before reconciling them. Nothing is recorded.

status --from offers only the offline records, g2g and graphite. It draws shape and no state: needing a restack or having moved off a parent is worked out from recorded fork points, which only g2g’s store has, so another record can say where branches sit but not whether their contents have drifted.

Pull request bases are a third source, github, read only when you name it:

Terminal window
g2g github status --from github --scope stack

It shows a repository’s published branches as a tree with nothing recorded locally, and says what GitHub will merge rather than what you intended. It is never consulted by precedence, because reading a base invokes gh, and push must never do that.

Asking for it makes its two limits an informed reading rather than a silent one:

  • It describes published branches only. No pull request, no edge.
  • GitHub retargets a child when its base branch is deleted on merge, so right after a parent lands its children point at the trunk. It is never wrong about what a merge will do, and no longer a record of what the stack was.

g2g github adopt records what it describes. See GitHub.

GitHub’s native stack is not a source at all. Nothing defines a stack by editing it: branches and bases are edited, and the native stack is written from them.

A trunk is never inferred from a branch’s name.

  • On a Graphite-described stack, g2g infers the only Graphite-declared trunk on the selected ancestry and shows it prominently. If that ancestry has several declared trunks, it fails closed and requires --trunk <branch>, and the branch you name must be both declared by Graphite and an ancestor of the selected branch.
  • A g2g-owned path has exactly one root, so --trunk can only confirm it. Naming any other branch is refused rather than ignored, because silently using a different base than the one asked for is how a stack gets pushed at the wrong thing.

design-docs/source-resolution.md records the reasoning in full.