OXYGENHUB
# 47ce8b8e2d4b oxygen / docs / revset_query_language.md
7.2 KiB · Markdown · last changed Revsets: oxy log -r selects changes by set algebra over history 1 month ago renderedsource blame history raw
8ad9dfe1 Andy McCall ↶ 1
# Oxygen (`oxy`): Revsets Specification
2
3
| | |
4
|---|---|
47ce8b8e Andy McCall ↶ 5
| **Status** | Draft 1.2: the core is implemented (`oxy log -r`), trust predicates and `-r` on other commands are next |
6
| **Date** | 2026-10-04 |
8ad9dfe1 Andy McCall ↶ 7
| **Audience** | Implementers and tooling developers |
8
| **Depends on** | spec.md §6 (changes), §8 (dependencies), §9 (conflicts), §10 (states), §11 (checkpoints), §14 (identity, attestations), §22 (profiles) |
9
10
## 1. Overview
11
12
A **revset** is an expression that selects a set of changes. Mercurial
13
and Jujutsu users know the idea, so Oxygen keeps the name and much of
14
the syntax. People should not have to learn a new language to ask
15
familiar questions.
16
17
Oxygen's model makes revsets more natural than in Git:
18
- History is a set of changes, and a state is itself a set: the
19
  dependency closure of its heads. Revsets are set algebra over the
20
  dependency graph.
21
- Changes keep their identity across rebases and transport, so a saved
22
  revset means the same thing tomorrow.
23
24
Oxygen also has things no other system can query: signatures, delegates,
25
attestations and build receipts. Section 2.4 adds those.
26
47ce8b8e Andy McCall ↶ 27
**Implemented (2026-10-04, oxygen#65):** `oxy log -r REVSET` with the
28
symbols, relations, operators, `author`, `description` (substring),
29
`date`, `kind`, `touches` and `latest` below, saved revsets, and a
30
time budget. Not yet: `independent()`, `conflicts()`, `description(re:…)`,
31
the trust predicates of §2.4, `-r` on other commands, and the hub's
32
`?r=`. Other commands still take a single change (a ChangeId prefix, a
33
`#hash` prefix or `@`) or a bookmark or tag name; symbols in a revset
34
resolve exactly as they do there.
8ad9dfe1 Andy McCall ↶ 35
36
## 2. Syntax
37
38
```sh
39
oxy log -r 'main..feature & author(andy)'
40
oxy log -r 'touches("src/**") & date(-7d..)'
41
```
42
43
### 2.1 Symbols
44
45
Symbols resolve in today's order: bookmark names, then tag names, then
46
change prefixes.
47
48
| Symbol | Means |
49
|---|---|
50
| `main`, `v0.3.1` | the heads of that bookmark or tag |
51
| `origin/main` | the heads of a remote-tracking bookmark |
52
| `8c5ea12a` | the change with that ChangeId prefix |
53
| `#fc68459c` | the change with that hash prefix |
54
| `@` | the working copy's base heads |
55
| `all()`, `none()` | every stored change, no change |
56
57
### 2.2 Relations
58
59
Each function takes a set and returns a set.
60
61
| Expression | Returns |
62
|---|---|
63
| `deps(X)` | the immediate dependencies of X |
64
| `dependents(X)` | changes that immediately depend on X |
65
| `::X` | X and its whole dependency closure, which is the *state* X names |
66
| `X::` | X and everything that transitively depends on it |
67
| `X::Y` | changes that are both in `X::` and in `::Y` |
68
| `X..Y` | `::Y ~ ::X`: what state Y has that state X lacks. This is what a proposal from X to Y adds. |
69
| `heads(X)`, `roots(X)` | members of X that no other member depends on, and members that depend on no other member |
70
| `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. |
71
| `latest(X, n)` | the n most recent members of X, by hybrid logical clock |
72
73
### 2.3 Content and metadata
74
75
| Expression | Selects |
76
|---|---|
77
| `author(P)` | changes whose `author` identity matches name, email or KeyId P |
78
| `description(T)` | changes whose message contains T (`re:` for a regular expression) |
79
| `date(RANGE)` | changes by HLC wall time: `2026-10-01..`, `-7d..`, `..2026-09-30` |
80
| `kind(K)` | `normal`, `resolution` or `working-copy` |
81
| `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. |
82
| `conflicts(S)` | changes that contributed atoms to a conflict in state S (default `@`). Conflicts belong to states, not changes. |
83
84
### 2.4 Trust: unique to Oxygen
85
86
| Expression | Selects |
87
|---|---|
88
| `signed_by(K)` | changes whose signature or seal is by key or identity K, including a delegate such as an agent key |
89
| `delegated()` | changes signed by a delegate rather than their author |
90
| `unsigned()` | changes with no valid signature or seal |
91
| `revoked()` | changes signed by a key now revoked |
92
| `attested(KIND)` | changes in states carrying an attestation of that kind, such as `tag` or `receipt` |
93
| `built(JOB)` | changes in states with a valid receipt for Flow job JOB |
94
95
For example, `release..main & (unsigned() | revoked())` asks: "is
96
anything about to ship without a trustworthy signature?"
97
98
### 2.5 Operators and precedence
99
100
From tightest to loosest:
101
102
1. `::` and `..`
103
2. `~` (difference)
104
3. `&` (intersection)
105
4. `|` (union)
106
107
Parentheses group. Strings with spaces or operators are quoted.
108
`~` is difference, as in Jujutsu. There is no unary not: write `all() ~ X`.
47ce8b8e Andy McCall ↶ 109
`..Y` is `::Y`, and `X..` is everything the repository has beyond `X`.
110
A `date()` argument is taken as written, so `date(-7d..)` needs no quotes.
8ad9dfe1 Andy McCall ↶ 111
112
## 3. Where revsets are used
113
114
- **Commands that show history** take `-r`: `log`, `show` (several
115
  changes), `verify`, and `diff -r` (the combined edit of a set).
116
- **Commands that act on changes** take `-r` too: `abandon`, the agent
117
  workflow's `fold`, and `push -r` (push only these heads). Evaluating
118
  the revset never changes anything. A command that acts prints the
119
  selected set and refuses to act if it is empty. Above a threshold it
120
  asks first, or proceeds with `--yes`.
121
- **The hub API** takes `?r=` on history endpoints, evaluated only over
122
  what the caller can read.
123
- **Saved revsets:** `oxy config revset.mine 'author(me) & ::@'` lets
47ce8b8e Andy McCall ↶ 124
  you write `mine()`. `author(me)` is the configured identity.
8ad9dfe1 Andy McCall ↶ 125
126
## 4. Evaluation
127
128
- **Read-only.** Evaluation never mutates the graph or any reference.
129
- **On the state index.** Relations use the dependency index that
130
  `oxy log` already uses. Unions and intersections stream. Difference,
131
  `heads` and `roots` need their operands' sets, bounded by the
132
  closure being walked.
133
- **Checkpoints** stand for a state's bulk without visiting each change,
134
  so a closure walk stops at the newest checkpoint it reaches.
47ce8b8e Andy McCall ↶ 135
- **Budgets.** Evaluation has a time budget (10 s from the command
136
  line; `Repo::revset_with_budget` takes another), and a query that
8ad9dfe1 Andy McCall ↶ 137
  exceeds it fails with a message saying what to narrow. The hub
138
  enforces a stricter one, so a query cannot be used to load the
139
  server.
47ce8b8e Andy McCall ↶ 140
- **Predicates range over `all()`** (every change a bookmark, remote
141
  bookmark, tag or the working copy reaches), except that in `X & p()`
142
  the predicate is evaluated over `X` alone, so `main.. & touches("src/**")`
143
  reads only the changes beyond `main`.
8ad9dfe1 Andy McCall ↶ 144
- **Targets.** A first page of `oxy log -r` for a simple revset should
145
  appear in under 100 ms, in line with the existing log target.
146
  `touches()` and the trust predicates are slower and are cached by
147
  state hash, since a state never changes.
148
149
## 5. Profiles (§22)
150
151
- **Full and Lite** evaluate locally, within their memory targets.
152
- **Thin clients** (68000-class, 1 MiB RAM) hold no history. A Thin
153
  client sends the revset text to its trusted Full-profile proxy, which
154
  evaluates it against its graph and returns a page of ChangeIds and
155
  metadata.
156
  - The proxy applies the same budget as the hub.
157
  - Over **ZCT (§15.3) the query and its results travel in plaintext**,
158
    like everything else on that transport. ZCT stays off by default,
159
    and the client warns on every use.