OXYGENHUB
# fbda43e7d8ed oxygen / docs / guide.md
14.8 KiB · Markdown · last changed Docs: bookmarks in the guide, spec status table, plan 1 month ago renderedsource blame history raw
1
# Oxygen user guide
2
3
This guide is organised by task. It assumes the `oxy` binary is on your
4
path (`cargo build --release` puts it in `target/release/`). The design
5
behind every command is in [spec.md]spec.md; this guide only tells you
6
what to type and what to expect.
7
8
Oxygen tracks *changes* rather than snapshots: a change is the set of
9
edits you made, it commutes with unrelated changes, and conflicts are
10
recorded in history instead of blocking you. Two ideas recur below:
11
12
- A **bookmark** (default `main`) names a *state*: the set of changes it
13
  contains. A state can have several **heads** when independent changes
14
  have not been related to each other; that is normal, not a problem.
15
- The **working copy** is itself a change that Oxygen rewrites every time
16
  it looks at your files. `oxy commit` turns it into a real change.
17
18
Output uses colour and glyphs on a terminal; pipe it, pass `--plain`, or set
19
`NO_COLOR` for plain ASCII.
20
21
## 1. Set yourself up
22
23
```
24
oxy identity create --name "Ada Lovelace" --email [email protected]
25
oxy config --user core.sign change
26
```
27
28
`identity create` generates a composite Ed25519 + ML-DSA-65 key pair, keeps
29
the secret in `~/.config/oxy/keys/`, and records you as the default author
30
in `~/.config/oxy/config.toml`. With `core.sign = change` every commit is
31
signed; without it, changes are sealed in batches when you push (also
32
authenticated, just later). Use `--local` to record the identity in one
33
repository only.
34
35
`oxy identity show` prints the key, its expiry, delegates, and whether the
36
secret is available. `oxy identity export` writes the public identity to a
37
file to hand to whoever manages a trust policy (section 8).
38
39
## 2. Start a repository
40
41
```
42
mkdir project && cd project
43
oxy init
44
oxy status
45
```
46
47
`oxy init` creates `.oxy/`. Add `.oxyignore` (gitignore syntax) for files
48
that should never be tracked; `/target` and `*.tmp` are typical entries.
49
50
To take over an existing Git project, see section 12.
51
52
## 3. Everyday work
53
54
```
55
oxy status                 # what changed since the last commit
56
oxy diff                   # the edits, file by file
57
oxy commit -m "Add retry loop"
58
oxy log                    # newest first; -n 10, -v for full messages
59
oxy show 658990aa          # one change by ChangeId prefix, #hash prefix, or @
60
```
61
62
`status --json` prints the same for tools, with every conflict's kind, path and
63
contributing ChangeIds. On a terminal, `status` lists each path with a mark: `✚ added`, `✎ modified`, `✖ removed`,
64
`➜ renamed`, `⚙ mode`. Renames are detected: a removed file and an added
65
file of the same kind whose contents are at least half alike are recorded
66
as a move of the same file, so identity survives a rename.
67
68
Files containing NUL bytes are stored whole ("opaque"); text files are
69
stored line by line and merge line by line. `.oxyattributes` overrides the
70
guess (section 14).
71
72
Every change has two names: a **ChangeId** (`658990aa…`) that survives
73
rewrites, and a content hash (`#7469d16c…`) that changes when the change is
74
edited. Use whichever you have; prefixes work.
75
76
## 3a. Bookmarks: branches without merges
77
78
A bookmark names a state; each workspace works on one (`main` to begin
79
with). Because changes commute, "merging" is the union of two bookmarks'
80
heads, with no merge change and any real conflict recorded as in section 6.
81
82
```
83
oxy bookmark create feature        # at the working copy's base
84
oxy bookmark switch feature        # check it out; commits now go here
85
oxy commit -m "Feature work"
86
oxy bookmark switch main
87
oxy merge feature                  # main now holds both; working copy follows if clean
88
oxy bookmark list                  # ● current, heads, ↑n/↓n against origin
89
oxy push -b feature                # publish a bookmark other than the current one
90
oxy bookmark set feature 658990aa  # move it; oxy bookmark delete feature to drop it
91
```
92
93
`oxy checkout feature` materialises a bookmark without switching to it, and
94
a bookmark name works wherever a change is expected when it has one head.
95
Each workspace remembers its own bookmark, so two directories can sit on
96
two bookmarks of one repository.
97
98
## 4. Fix up history
99
100
Oxygen keeps its own record of every operation, so all of these are safe to
101
try and to undo.
102
103
```
104
oxy describe -m "Better message"        # reword the head the working copy sits on
105
oxy describe 658990aa -m "..."          # reword any change; dependents are rewritten
106
oxy amend                               # fold the working copy into the head
107
oxy amend --into 658990aa               # fold it into an older change
108
oxy split 658990aa src/parser.rs        # split a head: those paths become a separate first change
109
oxy abandon 658990aa                    # drop a change; its content stays on disk
110
oxy undo                                # undo the last operation, whatever it was
111
oxy op log                              # the operation log
112
```
113
114
Rewriting a change that others depend on rewrites them too, keeping their
115
ChangeIds. Where a dependent referred to a line that no longer exists, the
116
reference is re-anchored to the nearest surviving line and `oxy abandon`
117
prints a note about it.
118
119
## 5. Move around
120
121
```
122
oxy checkout                    # the bookmark's heads
123
oxy checkout 658990aa           # the state of one change and its dependencies
124
oxy checkout v1.2               # a tag
125
oxy checkout --discard ...      # throw away uncommitted work first
126
```
127
128
Checking out a single older change gives you the files that change and its
129
dependencies touch, not a full snapshot from that date: in Oxygen a change
130
depends only on what it actually builds on.
131
132
## 6. Conflicts
133
134
A conflict never stops a commit, fetch, or pull. It is recorded in the
135
state and shown by `oxy status`, which exits with status 2 while any
136
conflict remains. Conflicted text files are written with markers:
137
138
```
139
<<<<<<< oxy conflict 1 of 2, side 1: change k7m2p9 "Add retry loop"
140
... lines ...
141
======= oxy conflict 1 of 2, side 2: change q3x8w1 "Refactor client"
142
... lines ...
143
>>>>>>> oxy conflict 1 of 2 end
144
```
145
146
Edit the file to what it should be (keep one side, both, or neither) and
147
remove the markers. Tree conflicts, such as two files wanting the same
148
name, appear as `<name>.oxy-conflict-<n>` candidates: keep the one you
149
want under the right name and delete the rest.
150
151
```
152
oxy resolve      # which conflicts your edits resolve, which still have markers
153
oxy commit -m "Resolve retry vs refactor"
154
```
155
156
The commit is recorded as a *resolution*, a change that depends on every
157
side of the conflict; anyone who fetches it gets the conflict resolved.
158
159
## 7. Work with others
160
161
Run a server (any machine with the repositories under one directory):
162
163
```
164
oxy serve --root /srv/oxy --bind 0.0.0.0:8890 --token SECRET \
165
          --tls-cert cert.pem --tls-key key.pem
166
```
167
168
Without `--tls-*` the server speaks plain HTTP; keep that on a loopback
169
address or behind a TLS terminator. `--token` makes pushes require a bearer
170
token; reads stay open.
171
172
On your machine:
173
174
```
175
oxy clone https://host:8890/project
176
cd project
177
oxy config remote.origin.token SECRET   # or export OXY_TOKEN
178
oxy config remote.origin.ca my-ca.pem   # self-signed server: extra roots to trust (or OXY_CA)
179
oxy push
180
oxy pull
181
```
182
183
- `oxy fetch` updates `origin/main` without touching your bookmark.
184
- `oxy pull` fetches, then makes your bookmark the union of both sides.
185
  There is no merge commit; unrelated changes simply coexist, and a real
186
  conflict is recorded as in section 6. The working copy follows when it was
187
  clean.
188
- `oxy push` sends your changes and moves the remote bookmark. If the remote
189
  moved since you last fetched, the push is refused: pull and push again.
190
  `--force` overrides the compare-and-swap.
191
- `oxy remote add|remove|list` manages remotes; the clone source is `origin`.
192
193
A repository can also be cloned from a local path, which is handy for tests
194
and for a bare "hub" on a shared disk.
195
196
## 8. Decide who may publish: the trust policy
197
198
Without a policy a repository is *unverified*: everything is accepted and
199
every remote command says so. A policy names root keys and the keys allowed
200
to update each bookmark; servers and clients enforce it.
201
202
```
203
oxy policy init                    # your key becomes the only root; push publishes it
204
oxy policy show
205
oxy policy grant alice.identity    # alice.identity came from her `oxy identity export`
206
oxy policy grant ci.identity --pattern 'release/*'
207
oxy policy revoke 9458b656         # by KeyId prefix
208
oxy verify                         # signature status of every change in the log
209
```
210
211
Several people can hold root keys:
212
213
```
214
oxy policy root add bob.identity
215
oxy policy root threshold 2        # future updates need two root signatures
216
```
217
218
Once the threshold is above one, an update you make alone becomes a
219
*proposal file* (`policy-<serial>.proposal`, or `--out`). Send it to another
220
root holder, who runs `oxy policy sign policy-7.proposal`; whoever completes
221
the threshold applies it, and the next push publishes it. Everyone else
222
picks it up on fetch, verifying each step from the policy they already
223
trust.
224
225
## 9. Keys over time
226
227
```
228
oxy identity rotate                   # fresh key pair; old signatures stay valid
229
oxy identity renew --expires 365      # same keys, new expiry in days (0 = never)
230
oxy identity delegate add ci.identity # let another key sign on your behalf
231
oxy identity delegate remove 3f2a…
232
oxy identity revoke                   # retire your own key
233
oxy identity revoke 3f2a… --reason "laptop stolen"   # any key, if you hold a root
234
```
235
236
Rotation, renewal, and delegation all issue a successor identity that names
237
the previous one. If a policy names your key, it is updated to the successor
238
in the same step (as a proposal when more root signatures are needed).
239
Changes signed before the rotation remain authorised; a revoked key
240
authorises nothing. `oxy verify` shows signatures by revoked keys as valid
241
but revoked.
242
243
## 10. Tags and workspaces
244
245
```
246
oxy tag create v1.2 -m "First release"   # the current heads; or name changes
247
oxy tag list
248
oxy workspace add ../project-hotfix      # a second checkout sharing the repository
249
oxy workspace list
250
oxy workspace forget ../project-hotfix
251
```
252
253
A tag is an immutable state, signed when you have a key, and travels with
254
push and fetch. Workspaces have their own working copy and bookmark
255
(`workspace.bookmark`) but share objects and history.
256
257
## 11. Big repositories: partial, sparse, shallow
258
259
```
260
oxy clone URL --max-blob 1M          # files above 1 MiB are fetched on first read
261
oxy clone URL --sparse src --sparse docs   # materialise only these subtrees
262
oxy clone URL --depth 50             # only the last 50 levels of history
263
oxy sparse src docs tests            # change the filter later
264
oxy deepen 100                       # fetch more history below the boundary
265
```
266
267
Lazy blobs and sparse trees do not affect commits: what you do not have is
268
left exactly as it was. A shallow clone starts from a *checkpoint* of the
269
state, so status and commit work at full speed from the first minute.
270
271
## 12. Git interoperability
272
273
```
274
oxy git import ../project.git ./project-oxy --branch main   # convert a Git history
275
oxy git fetch https://github.com/you/project.git            # bring new Git commits in
276
oxy git push  https://github.com/you/project.git            # publish your changes to Git
277
oxy git export ../mirror.git                                # a plain Git mirror of the bookmark
278
```
279
280
Import turns each commit into one change (merge commits become
281
resolutions), keeps authors and hashes as `Git-Author`/`Git-Commit`
282
trailers, detects renames, and skips submodules. `git fetch`/`git push` keep
283
a bare mirror under `.oxy/git/`; a push is refused until the remote's tip
284
has been fetched, so it always fast-forwards. Exported commits carry
285
`Change-Id` and `Oxy-Hash` trailers.
286
287
## 13. Automate: hooks
288
289
Executables in `.oxy/hooks/<event>` (never cloned) receive one JSON object
290
on stdin. `pre-commit`, `pre-push` and other `pre-*` hooks veto by exiting
291
non-zero, and their stderr becomes the error message. Events:
292
`pre-commit`, `post-commit`, `pre-push`, `post-fetch`, `post-checkout`, and
293
`semantic-deps`, which runs at commit when `hooks.semantic-deps = true` and
294
answers `{"deps": ["<hash>", …]}` to make the change depend on more than it
295
textually references.
296
297
## 14. Per-path attributes, ignores, locks
298
299
`.oxyattributes` uses gitattributes syntax:
300
301
```
302
*.png     binary
303
*.txt     text eol=lf
304
*.psd     lockable nocompress
305
```
306
307
`binary`/`text` override content sniffing, `eol=lf|crlf` normalises line
308
endings in the store and on checkout, `nocompress` stores chunks raw, and
309
`lockable` files are checked out read-only until you take the local lock:
310
311
```
312
oxy lock assets/logo.psd
313
oxy unlock assets/logo.psd
314
```
315
316
Locks are advisory and local; server-side locks are planned.
317
318
## 15. Configuration
319
320
Two files, both `key = "value"` lines: `~/.config/oxy/config.toml`
321
(`oxy config --user KEY VALUE`) and `.oxy/config` (`oxy config KEY VALUE`).
322
The repository file wins. `OXY_HOME` relocates the user directory.
323
324
| Key | Meaning |
325
|---|---|
326
| `author.name`, `author.email`, `author.key` | who you are; `key` is set by `identity create` |
327
| `core.sign` | `change` to sign every commit; otherwise seals at push |
328
| `core.op-retention` | days for which old operations keep their states alive (90) |
329
| `core.gc-grace` | days an unreachable object survives (14) |
330
| `core.checkpoint-interval` | changes between automatic checkpoints (256) |
331
| `filter.max-blob`, `filter.paths` | lazy blobs and sparse subtrees (section 11) |
332
| `hooks.semantic-deps` | run the semantic-deps hook at commit |
333
| `workspace.bookmark` | this workspace's bookmark (`main`) |
334
| `remote.<name>.url`, `.token`, `.ca` | remotes; token and CA fall back to `OXY_TOKEN` and `OXY_CA` |
335
336
## 16. Shell prompt
337
338
`oxy prompt` prints `main 658990aa*+2` (bookmark, working copy, `*` dirty,
339
`!` conflicted, ahead/behind origin) in a few milliseconds and records
340
nothing. See [prompt.md]prompt.md for bash, oh-my-bash, zsh, and fish
341
snippets and the `--format` placeholders.
342
343
## 17. Housekeeping
344
345
```
346
oxy gc              # pack loose objects, prune unreachable ones past the grace period
347
oxy gc --now        # ignore the grace period (only when nothing else is writing)
348
oxy checkpoint      # serialise the bookmark's state so large histories load instantly
349
```
350
351
Both run automatically: packing when loose objects pile up, checkpoints
352
every `core.checkpoint-interval` changes.
353
354
## 18. If something looks wrong
355
356
- **"the remote has change … which you do not; pull first"**: someone
357
  pushed since your last fetch. `oxy pull`, then push.
358
- **"policy needs N root signature(s)"** on fetch: the server's policy was
359
  updated in a way your copy cannot verify. Ask a root holder whether the
360
  chain of updates was published; a fresh clone trusts the current policy.
361
- **`oxy status` exits 2**: conflicts remain; see section 6.
362
- **Many heads on `main`**: independent changes that do not reference one
363
  another. Nothing to fix; the state is their union.
364
- **Undo anything**: `oxy undo` reverts the last operation, including
365
  pulls, checkouts, and rewrites.