OXYGENHUB
◆ main oxygen / docs / revset_query_language.md
7.7 KiB · Markdown · last changed bookmark: show a bookmark's heads; bookmark() in revsets 1 month ago renderedsource blame history raw

Oxygen (oxy): Revsets Specification

StatusDraft 1.2: the core is implemented (oxy log -r), trust predicates and -r on other commands are next
Date2026-10-04
AudienceImplementers and tooling developers
Depends onspec.md §6 (changes), §8 (dependencies), §9 (conflicts), §10 (states), §11 (checkpoints), §14 (identity, attestations), §22 (profiles)

1. Overview

A revset is an expression that selects a set of changes. Mercurial and Jujutsu users know the idea, so Oxygen keeps the name and much of the syntax. People should not have to learn a new language to ask familiar questions.

Oxygen's model makes revsets more natural than in Git:

  • History is a set of changes, and a state is itself a set: the dependency closure of its heads. Revsets are set algebra over the dependency graph.
  • Changes keep their identity across rebases and transport, so a saved revset means the same thing tomorrow.

Oxygen also has things no other system can query: signatures, delegates, attestations and build receipts. Section 2.4 adds those.

Implemented (2026-10-04, oxygen#65): oxy log -r, oxy show -r, oxy verify -r and oxy abandon -r with the symbols, relations, operators, author, description (substring), date, kind, touches, latest, independent, conflicts and the trust predicates of §2.4 below, saved revsets, and a time budget. Not yet: description(re:…), diff -r, push -r, fold, and the hub's ?r=. Other commands take a single change (a ChangeId prefix, a #hash prefix or @) or a bookmark or tag name; symbols in a revset resolve exactly as they do there.

2. Syntax

oxy log -r 'main..feature & author(andy)'
oxy log -r 'touches("src/**") & date(-7d..)'

2.1 Symbols

Symbols resolve in today's order: bookmark names, then tag names, then change prefixes.

SymbolMeans
main, v0.3.1the heads of that bookmark or tag
bookmark(main), bookmark(origin/main)the heads of that bookmark only; an error if there is none (a bare name falls through to tags and change prefixes)
origin/mainthe heads of a remote-tracking bookmark
8c5ea12athe change with that ChangeId prefix
#fc68459cthe change with that hash prefix
@the working copy's base heads
all(), none()every stored change, no change

2.2 Relations

Each function takes a set and returns a set.

ExpressionReturns
deps(X)the immediate dependencies of X
dependents(X)changes that immediately depend on X
::XX and its whole dependency closure, which is the state X names
X::X and everything that transitively depends on it
X::Ychanges that are both in X:: and in ::Y
X..Y::Y ~ ::X: what state Y has that state X lacks. This is what a proposal from X to Y adds.
heads(X), roots(X)members of X that no other member depends on, and members that depend on no other member
independent(X, IN)changes in IN that neither depend on X nor are depended on by X: what could ship without X. Only Oxygen can answer this, because changes commute.
latest(X, n)the n most recent members of X, by hybrid logical clock

2.3 Content and metadata

ExpressionSelects
author(P)changes whose author identity matches name, email or KeyId P
description(T)changes whose message contains T (re: for a regular expression)
date(RANGE)changes by HLC wall time: 2026-10-01.., -7d.., ..2026-09-30
kind(K)normal, resolution or working-copy
touches(GLOB)changes that edit any node whose path matches GLOB in the evaluation state. Nodes keep their identity through renames, so this follows a file's whole history under any name.
conflicts(S)changes that contributed atoms to a conflict in state S (default: the working copy's base). Conflicts belong to states, not changes.

2.4 Trust: unique to Oxygen

ExpressionSelects
signed_by(K)changes whose signature or seal is by key or identity K (a key prefix, a name, an email, or me), including a delegate such as an agent key
delegated()changes signed by a delegate rather than their author
unsigned()changes with no valid signature or seal
revoked()changes signed by a key now revoked
attested(KIND)changes in states carrying an attestation of that kind: tag, receipt, or ref (states a remote attested, as its tracking bookmarks hold them)
built(JOB)changes in states with a valid receipt for Flow job JOB

For example, release..main & (unsigned() | revoked()) asks: "is anything about to ship without a trustworthy signature?"

2.5 Operators and precedence

From tightest to loosest:

  1. :: and ..
  2. ~ (difference)
  3. & (intersection)
  4. | (union)

Parentheses group. Strings with spaces or operators are quoted. ~ is difference, as in Jujutsu. There is no unary not: write all() ~ X. ..Y is ::Y, and X.. is everything the repository has beyond X. A date() argument is taken as written, so date(-7d..) needs no quotes.

3. Where revsets are used

  • Commands that show history take -r: log, show (several changes), verify, and diff -r (the combined edit of a set).
  • Commands that act on changes take -r too: abandon, the agent workflow's fold, and push -r (push only these heads). Evaluating the revset never changes anything. A command that acts prints the selected set and refuses to act if it is empty. Above a threshold (five, for abandon) it refuses unless given --yes. abandon -r drops the newest first and finds each later member again by its ChangeId, since dropping a change rewrites what depended on it.
  • The hub API takes ?r= on history endpoints, evaluated only over what the caller can read.
  • Saved revsets: oxy config revset.mine 'author(me) & ::@' lets you write mine(). author(me) is the configured identity.

4. Evaluation

  • Read-only. Evaluation never mutates the graph or any reference.
  • On the state index. Relations use the dependency index that oxy log already uses. Unions and intersections stream. Difference, heads and roots need their operands' sets, bounded by the closure being walked.
  • Checkpoints stand for a state's bulk without visiting each change, so a closure walk stops at the newest checkpoint it reaches.
  • Budgets. Evaluation has a time budget (10 s from the command line; Repo::revset_with_budget takes another), and a query that exceeds it fails with a message saying what to narrow. The hub enforces a stricter one, so a query cannot be used to load the server.
  • Predicates range over all() (every change a bookmark, remote bookmark, tag or the working copy reaches), except that in X & p() the predicate is evaluated over X alone, so main.. & touches("src/**") reads only the changes beyond main.
  • Targets. A first page of oxy log -r for a simple revset should appear in under 100 ms, in line with the existing log target. touches() and the trust predicates are slower and are cached by state hash, since a state never changes.

5. Profiles (§22)

  • Full and Lite evaluate locally, within their memory targets.
  • Thin clients (68000-class, 1 MiB RAM) hold no history. A Thin client sends the revset text to its trusted Full-profile proxy, which evaluates it against its graph and returns a page of ChangeIds and metadata.
    • The proxy applies the same budget as the hub.
    • Over ZCT (§15.3) the query and its results travel in plaintext, like everything else on that transport. ZCT stays off by default, and the client warns on every use.