Omnigraph

Branches, Commits, and History

A branch is an isolated, durable graph history. Use branches to prepare and

A branch is an isolated, durable graph history. Use branches to prepare and review a multi-step change without exposing intermediate results on main.

Branch workflow

# Create an isolated branch from main.
omnigraph branch create review/add-benchmark --from main --store graph.omni

# Write and inspect it.
omnigraph load --data benchmark.jsonl --mode append \
  --branch review/add-benchmark graph.omni
omnigraph query sources_for_claim --query queries.gq \
  --params '{"claim":"lower-latency"}' \
  --branch review/add-benchmark --store graph.omni

# Publish its result to main, then remove the source branch.
omnigraph branch merge review/add-benchmark --into main --delete-branch \
  --store graph.omni

List and delete branches with:

omnigraph branch list --store graph.omni
omnigraph branch delete review/abandoned --store graph.omni

Creating a branch defaults to main when --from is omitted. A load can create a missing target branch by combining --branch <name> with --from <base>.

Each branch operation is also a GQ statement, so a client that already sends .gq source needs no second transport. The three control writes go to POST /mutate and the listing to POST /query, each with no request target, name, or parameters:

branch create "review/add-benchmark" from main
branch merge "review/add-benchmark" into main
branch delete "review/abandoned"
branch list

from defaults to main and into defaults to main. A name outside the identifier alphabet (a lowercase letter or _, then letters, digits, or _), such as one containing /, -, or ., is quoted, as above; main, b0, and _x are bare. A control write answers a ChangeOutput whose outcome.kind is created, deleted, or merged (outcome.merge holds the merge result), with zero affected counts; a merge that publishes a commit reports it in commit. branch list answers one row per branch in column name, sorted by name. Each statement runs under the same policy check as its HTTP route (POST /branches, DELETE /branches/{branch}, POST /branches/merge, GET /branches), and branch list is a read, so it is refused on POST /mutate as a control write is refused on POST /query.

Branches are cheap until written: unchanged data remains shared with the source. A branch remains after a normal merge, so prefer --delete-branch or delete it when review is complete. Live branches retain the history they depend on and can prevent omnigraph cleanup from reclaiming old data.

Branch names may contain /, but live names must not be path prefixes of one another. For example, review and review/alice cannot coexist. main is reserved. A parent branch can be deleted while descendants remain. A path segment may not end in . followed by 26 upper-case letters and digits: that shape is reserved for OmniGraph's internal per-life branch identity. Ordinary dotted names such as release.1.2 are fine.

Deleting a branch and creating another with the same name yields a fresh branch lifetime. Readers that captured the deleted branch fail with a typed error rather than seeing the replacement. Deletion retires the logical name and leaves native manifest history and table storage for explicit omnigraph cleanup; it starts no background table-reclaim work and does not wait for table-fork reclamation. Cleanup preserves data still needed by other branches and their underlying histories.

Branch-control operations are safe across handles in one writer process. Do not run branch create/delete control concurrently from separate writer processes against the same graph.

Atomicity model

OmniGraph does not provide connection-scoped BEGIN and ROLLBACK.

ScopeGuarantee
One mutation queryAll statements publish as one commit or none become visible.
One load requestThe complete batch publishes as one commit or none becomes visible.
Several commands on a branchEach command that publishes a change is a durable branch commit. Earlier commands are not rolled back if a later one fails.
Branch mergeThe resulting source state becomes visible on the target in one atomic commit.

Deleting an abandoned branch discards that workspace from normal access, but it is an explicit lifecycle action rather than transaction rollback.

Commits

Every write that publishes a change records a graph commit. A successful mutation that matches no entities publishes no commit. A commit includes its id, parent, branch, actor when known, and timestamp. Merge commits have two parents.

omnigraph commit list graph.omni --branch main
omnigraph commit show <commit-id> --uri graph.omni

commit list is newest first. Omitting --branch shows history reachable from main; selecting a branch includes the history inherited at its fork plus its own commits.

Historical reads

Every read targets either a live branch or an immutable graph commit. Take a graph_commit_id from commit list --json and pass it as the snapshot:

omnigraph query sources_for_claim --query queries.gq \
  --params '{"claim":"lower-latency"}' \
  --snapshot <graph-commit-id> --store graph.omni

A query stays on one snapshot for its entire lifetime. Historical reads can eventually fail after destructive cleanup removes the versions that commit needs. Branch deletion can likewise end access to branch-only history.

Changes and feeds

Inspect the logical changes made by one commit:

omnigraph commit changes <commit-id> --store graph.omni --json

For continuous consumption, omnigraph changes poll follows complete commits on one branch and returns an opaque resume cursor. If cleanup has reclaimed required history, create a coherent snapshot and new cursor with omnigraph changes baseline.

See Changes and Change Feeds for filters, pagination, cursor checkpointing, and 410 retention-gap recovery.

See Merging Branches for merge outcomes and conflict handling.

On this page