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.omniList and delete branches with:
omnigraph branch list --store graph.omni
omnigraph branch delete review/abandoned --store graph.omniCreating 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 listfrom 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.
| Scope | Guarantee |
|---|---|
| One mutation query | All statements publish as one commit or none become visible. |
| One load request | The complete batch publishes as one commit or none becomes visible. |
| Several commands on a branch | Each command that publishes a change is a durable branch commit. Earlier commands are not rolled back if a later one fails. |
| Branch merge | The 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.omnicommit 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.omniA 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 --jsonFor 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.