The forest
g2g status, g2g adopt, g2g track and g2g untrack maintain a branch
forest g2g owns itself. They read Git and nothing else: no Graphite, no GitHub,
no network.
This is the structure that exists for branches you have not pushed yet, and it is the only place a fork can live. GitHub native stacks are linear, and a pull request base cannot describe a branch that has no pull request.
Why it has to exist
Section titled “Why it has to exist”Git supplies ancestry, but it does not keep the intended parent once a branch moves or its parent is squash-merged. Pull request bases describe only published work, and they describe what a merge will do rather than what you meant. Graphite can express a tree, but it may not be there.
So g2g records the one small, local fact every later operation needs: which branch is the intended parent of which.
Its shape
Section titled “Its shape”Every branch has at most one parent, a parent may have many children, and a repository may have several roots, because it may have several trunks:
main├─ synthetic-auth│ ├─ synthetic-login│ └─ synthetic-session└─ synthetic-billing └─ synthetic-invoiceTrees are the model even though GitHub is linear. To publish onto a GitHub native stack, one path from the trunk to a tip is selected and projected. The tree is neither flattened nor rejected for containing a fork. See Scope for how a selection is narrowed to a path.
Where the edges come from
Section titled “Where the edges come from”Parents are inferred from commit ancestry, which needs no network and works for branches that have never been pushed. For a branch, the candidates are the local branches its commits sit on top of, ordered nearest first, plus the roots already recorded, because a trunk that has moved on is no longer an ancestor of the branches built from it.
track never chooses a parent when the answer is ambiguous. Without
--parent it previews the candidates and blocks. The nearest ancestor is
usually right, and “usually” is not a basis for writing down structure every
later command trusts. adopt follows the same rule: you assert the trunk, and
where ancestry cannot order two branches it refuses and names them. See
When a command refuses for what that looks like, and
Shape the stack for the commands.
A recorded parent whose tip is no longer an ancestor of its child is reported as needs restack, not silently reparented. A parent that moved is not the same thing as no parent, and treating the two alike would quietly move a stale child onto the trunk.
What is stored, and what is not
Section titled “What is stored, and what is not”The fork point is stored. Each edge records the parent’s tip at the moment the edge was written. That answers which commits are mine: everything from the fork point to the branch. Without it a restack cannot work out what to replay, and it is also what lets a restack survive its parent being deleted after a merge. It changes only on structural events, such as adopting an edge or restacking, never on a commit or a force push.
A branch’s own tip is not stored. It moves with every ordinary commit, so recording it would make routine work look like the structure had changed.
Graph identity is derived, not stored. A graph is a connected component of the recorded edges, which is a computation rather than a record, so there is no identifier to generate and no merge or split to handle when two components join. Renaming a branch is a rewrite of its key rather than a migration.
Trunks
Section titled “Trunks”A trunk is a branch nothing is recorded under. g2g makes one on its own when
you stack something on a branch it does not know, which is why create refuses
to do that from anything but the repository’s default branch. A second trunk,
or one that lands somewhere later, is declared out loud with
track --as-trunk. See Shape the stack.
Where it lives
Section titled “Where it lives”The forest is stored under the repository’s Git common directory. Linked worktrees share it, it never appears in a diff or dirties a checkout, and it is neither pushed nor shared between clones. A fresh clone starts empty, which matches the unpublished branches those edges describe. See Compatibility and storage.
Further reading
Section titled “Further reading”design-docs/g2g-owned-graphs.md describes the model, the storage decisions, and what is deliberately left out.