OXYGENHUB
◆ v0.2.0 oxygen / docs / guide.md
26.8 KiB · Markdown · last changed The operation log says what each operation did; op restore, op revert and --at-op; commands say where they left you (closes #51, closes #52, closes #53) 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, per
48
directory, negations with `!`) for files that should never be tracked;
49
`/target` and `*.tmp` are typical entries. As in Git, ignore rules only
50
affect untracked files: a file already in history stays tracked until you
51
delete it. A project's `.gitignore` files are honoured too, so moving a Git
52
project to Oxygen does not commit its build output; `.oxyignore` comes
53
first, and a `!` rule there brings back what `.gitignore` leaves out.
54
55
To take over an existing Git project, see section 12.
56
57
## 3. Everyday work
58
59
```
60
oxy status                 # what changed since the last commit
61
oxy diff                   # the edits, file by file
62
oxy commit -m "Add retry loop"
63
oxy log                    # newest first; -n 10, -v for full messages
64
oxy show 658990aa          # one change by ChangeId prefix, #hash prefix, or @
65
```
66
67
`oxy log` draws the history as a graph, newest first, two lines per change:
68
69
```
70
◉  2f8518c0 Andy McCall just now
71
│  (working copy, clean)
72
●  1b5888b1 Andy McCall 2 minutes ago main #cc23b999 ✔ signed
73
│  Edit b
74
│ ●  d6f52eca Andy McCall 3 minutes ago main #ec6a7dc4 ✔ signed
75
├─╯  Edit a
76
○  c0593116 Andy McCall 5 minutes ago #eeac7940 ✔ signed
77
   Both files
78
```
79
80
The bright start of each ID is the shortest prefix that names it. `●` marks
81
a head, `◉` the working copy. Bookmarks and tags show beside their heads,
82
unless they have more than three. A lane follows the change each one most
83
directly builds on, its newest dependency. Two edits that build on the same
84
change take two lanes, which join above it, so independent work shows side
85
by side. `~` means the change builds on something further down than the
86
graph draws, or not shown; `-v` lists every dependency.
87
88
Commit part of what changed with `oxy commit PATH.. -m "..."` (only those
89
paths; the rest stays as uncommitted work) or `oxy commit -i -m "..."`, which
90
offers each hunk in turn like `git add -p`; a hunk that another chosen hunk
91
needs, such as the directory a new file lives in, comes along by itself.
92
93
`status --json` prints the same for tools, with every conflict's kind, path and
94
contributing ChangeIds. On a terminal, `status` lists each path with a mark: `✚ added`, `✎ modified`, `✖ removed`,
95
`➜ renamed`, `⚙ mode`. Renames are detected: a removed file and an added
96
file of the same kind whose contents are at least half alike are recorded
97
as a move of the same file, so identity survives a rename.
98
99
Files containing NUL bytes are stored whole ("opaque"); text files are
100
stored line by line and merge line by line. `.oxyattributes` overrides the
101
guess (section 14).
102
103
Every change has two names: a **ChangeId** (`658990aa…`) that survives
104
rewrites, and a content hash (`#7469d16c…`) that changes when the change is
105
edited. Use whichever you have; prefixes work.
106
107
## 3a. Bookmarks: branches without merges
108
109
A bookmark names a state; each workspace works on one (`main` to begin
110
with). Because changes commute, "merging" is the union of two bookmarks'
111
heads, with no merge change and any real conflict recorded as in section 6.
112
113
```
114
oxy bookmark create feature        # at the working copy's base
115
oxy bookmark switch feature        # check it out; commits now go here
116
oxy commit -m "Feature work"
117
oxy bookmark switch main
118
oxy merge feature                  # main now holds both; working copy follows if clean
119
oxy bookmark list                  # ● current, heads, ↑n/↓n against origin
120
oxy push -b feature                # publish a bookmark other than the current one
121
oxy bookmark set feature 658990aa  # move it; oxy bookmark delete feature to drop it
122
```
123
124
`oxy checkout feature` materialises a bookmark without switching to it, and
125
a bookmark name works wherever a change is expected when it has one head.
126
Each workspace remembers its own bookmark, so two directories can sit on
127
two bookmarks of one repository.
128
129
**A state is its heads, not a history.** If you are coming from Git, this
130
is the one thing to know. A bookmark holds its heads and what they depend
131
on, and a change depends only on the changes it builds on (the files it
132
touched), not on everything recorded before it. So on a bookmark with
133
several heads, a new bookmark at one change can be a much older-looking
134
tree. `oxy bookmark create NAME CHANGE` refuses when that would leave out
135
earlier work and lists what it would lack. Then pick one:
136
137
```
138
oxy bookmark create logo 745307b4 --upto  # everything here up to that change, as a Git branch would be
139
oxy bookmark create logo 745307b4 --only  # just that change and what it depends on
140
oxy bookmark create logo                  # the whole current state, usually what you want
141
```
142
143
`oxy bookmark set NAME CHANGE` asks the same when the move would lose
144
changes the bookmark holds that were recorded before CHANGE, and takes
145
`--upto` and `--only` too. After merging a proposal's target into its
146
bookmark to resolve conflicts, `oxy bookmark set NAME CHANGE --upto`
147
keeps everything the bookmark held.
148
149
`oxy bookmark switch` says when the bookmark you switch to lacks changes
150
that the one you left has.
151
152
**A remote's bookmarks.** A clone fetches every change, checks out the
153
remote's default bookmark, and names the others `origin/NAME`: use them
154
anywhere a bookmark is expected (`oxy log origin/develop`,
155
`oxy diff main origin/develop`). `oxy bookmark list --all` shows them.
156
`oxy bookmark switch develop` makes a local `develop` from `origin/develop`
157
when there is none, and `oxy pull` then keeps every local bookmark up to
158
date with the remote's of the same name, not only the current one. Pulling
159
is a union, so this never loses work.
160
161
```
162
oxy clone https://oxygenhub.io/owner/game --recursive   # subrepos too
163
oxy bookmark list --all
164
oxy bookmark switch develop        # from origin/develop
165
oxy pull                           # updates develop, main and the rest
166
```
167
168
### A develop / feature flow
169
170
Names with `/` work everywhere, pushing included:
171
172
```
173
oxy bookmark create feature/42-login          # at the working copy's base
174
oxy bookmark switch feature/42-login --carry  # started already? take the edits along
175
oxy commit -F msg.txt                         # a long message from a file
176
oxy push -b feature/42-login
177
oxy bookmark switch develop
178
oxy merge feature/42-login                    # the working copy follows when it was clean
179
oxy push                                      # develop, with the feature in it
180
oxy bookmark delete feature/42-login          # oxy push --delete feature/42-login on the remote
181
```
182
183
`bookmark switch` stops when the working copy has uncommitted edits, so
184
nothing is lost by accident. There are two ways past it:
185
- `--carry` takes the edits along. It is refused only when the target
186
  bookmark has other versions of the files they touch.
187
- `--discard` drops the edits.
188
189
`oxy describe` names what the working copy holds, for build stamps:
190
- a tag, when a tag holds exactly this state;
191
- otherwise the nearest tag with the distance from it and the change;
192
- or, with no tag behind it, the bookmark and the change;
193
- with `-dirty` added when there are uncommitted edits.
194
195
## 4. Fix up history
196
197
Oxygen keeps its own record of every operation, so all of these are safe to
198
try and to undo.
199
200
```
201
oxy describe -m "Better message"        # reword the head the working copy sits on
202
oxy describe 658990aa -m "..."          # reword any change; dependents are rewritten
203
oxy amend                               # fold the working copy into the head
204
oxy amend --into 658990aa               # fold it into an older change
205
oxy split 658990aa src/parser.rs        # split a head: those paths become a separate first change
206
oxy abandon 658990aa                    # drop a change; its content stays on disk
207
oxy squash 658990aa --into 9df9ac29     # fold a change into one it depends on
208
oxy rebase 658990aa --onto feature      # re-express a change against another base
209
oxy rebase 658990aa --onto develop --into-working-copy   # conflicted? resolve it here, then commit
210
oxy undo                                # undo the last operation, whatever it was
211
oxy op log                              # every operation, what it changed, and its command
212
```
213
214
### The operation log
215
216
Every command that changes the repository records an operation: the
217
bookmarks, tags and working copy as they were after it. `oxy op log` lists
218
them newest first, each with what it changed and the command that did it;
219
working-copy snapshots fold into one row.
220
221
```
222
@  13adb0d0b8bf just now
223
│  main: 1 → 2 heads; working copy now 91ab4d50
224
│  oxy commit -m "Third"
225
○  fa21727d70da just now
226
│  created bookmark test (1 head)
227
│  oxy bookmark create test
228
○  2d15930afdef … 830168a3ddcd just now
229
│  2 working-copy snapshots
230
```
231
232
Any operation can be looked at, gone back to, or undone alone:
233
234
```
235
oxy --at-op fa21727d log                # the log as it was after that operation; any read-only command works
236
oxy op restore fa21727d                 # bookmarks and working copy as they were then
237
oxy op revert fa21727d                  # undo just that operation, keeping everything after it
238
oxy undo                                # takes back the last command, a restore included
239
```
240
241
`op restore` and `op revert` are operations themselves, so `oxy undo` takes
242
them back. `op revert` puts back each bookmark the operation moved, unless
243
it has moved again since. Tags and the trust policy never go backwards.
244
`undo` brings the files back as well when nothing uncommitted would be
245
lost; an undone commit's edits stay on disk as uncommitted work.
246
247
Commands that move the working copy end by saying where it is:
248
249
```
250
* Committed 95245a21 #35bbf5097e0c  Third
251
  Working copy  91ab4d50
252
  Bookmark      main: 2 heads, newest 95245a21 Third
253
```
254
255
Rewriting a change that others depend on rewrites them too, keeping their
256
ChangeIds. Because unrelated changes already coexist, `rebase` is rarely
257
needed; its job is to change what a change is said to build on, for instance
258
to drop a dependency on something you are about to abandon. Where a dependent referred to a line that no longer exists, the
259
reference is re-anchored to the nearest surviving line and `oxy abandon`
260
prints a note about it.
261
262
## 4a. Who wrote this, and what broke it
263
264
```
265
oxy blame src/parser.rs          # every line: the change that introduced it, author, age
266
oxy blame src/parser.rs --json
267
```
268
269
Blame is exact: each line is an atom that remembers the change that
270
introduced it, so renames cost nothing and there is no heuristic.
271
272
```
273
oxy bisect start
274
oxy bisect good v1.2             # a change, bookmark or tag known to be fine
275
oxy bisect bad main              # and one known to be broken
276
oxy bisect run make test         # exit 0 good, 125 skip, anything else bad
277
oxy bisect reset                 # back to where you were
278
```
279
280
Without `run`, each step checks out a state to test and you answer with
281
`oxy bisect good`, `bad`, or `skip`. The candidates are the changes the bad
282
state has that the good one lacks, ordered so every tested state is
283
well-formed; the answer is the first change whose inclusion breaks things.
284
285
## 5. Move around
286
287
```
288
oxy checkout                    # the bookmark's heads
289
oxy checkout 658990aa           # the state of one change and its dependencies
290
oxy checkout v1.2               # a tag
291
oxy checkout --discard ...      # throw away uncommitted work first
292
```
293
294
Checking out a single older change gives you the files that change and its
295
dependencies touch, not a full snapshot from that date: in Oxygen a change
296
depends only on what it actually builds on.
297
298
## 6. Conflicts
299
300
A conflict never stops a commit, fetch, or pull. It is recorded in the
301
state and shown by `oxy status`, which exits with status 2 while any
302
conflict remains. Conflicted text files are written with markers:
303
304
```
305
<<<<<<< oxy conflict 1 of 2, side 1: change k7m2p9 "Add retry loop"
306
... lines ...
307
======= oxy conflict 1 of 2, side 2: change q3x8w1 "Refactor client"
308
... lines ...
309
>>>>>>> oxy conflict 1 of 2 end
310
```
311
312
Edit the file to what it should be (keep one side, both, or neither) and
313
remove the markers. Tree conflicts, such as two files wanting the same
314
name, appear as `<name>.oxy-conflict-<n>` candidates: keep the one you
315
want under the right name and delete the rest.
316
317
```
318
oxy resolve      # which conflicts your edits resolve, which still have markers
319
oxy commit -m "Resolve retry vs refactor"
320
```
321
322
The commit is recorded as a *resolution*, a change that depends on every
323
side of the conflict; anyone who fetches it gets the conflict resolved.
324
325
## 7. Work with others
326
327
Run a server (any machine with the repositories under one directory):
328
329
```
330
oxy serve --root /srv/oxy --bind 0.0.0.0:8890 --token SECRET \
331
          --tls-cert cert.pem --tls-key key.pem
332
```
333
334
Without `--tls-*` the server speaks plain HTTP; keep that on a loopback
335
address or behind a TLS terminator. `--token` makes pushes require a bearer
336
token; reads stay open.
337
338
On your machine:
339
340
```
341
echo SECRET | oxy auth login https://host:8890 --with-token --ca my-ca.pem
342
oxy clone https://host:8890/project
343
cd project
344
oxy push
345
oxy pull
346
```
347
348
`--ca` is only for a server with a self-signed certificate. On a hub such as
349
OxygenHub, `oxy auth login https://oxygenhub.io` signs in through the
350
browser instead: it shows a code to approve on the hub, and the hub issues a
351
token that lasts 90 days past its last use.
352
353
### Signing in
354
355
`oxy auth login HUB` keeps one token per hub (scheme, host and port) in
356
`~/.config/oxy/credentials.toml`, readable by you only, and every repository
357
on that hub uses it: clone, fetch, push and subrepos alike. Nothing secret is
358
written into a checkout. `oxy auth status` lists the hubs you are signed in
359
to and checks each token with its hub; `oxy auth logout HUB` forgets the token
360
and revokes it on the hub.
361
362
To keep tokens in the system keyring instead, name a Git credential helper:
363
`oxy config --user auth.helper libsecret` (Linux), `osxkeychain` (macOS) or
364
`manager` (Windows). The file then keeps only the account and CA.
365
366
A token is found in this order: the remote's own `remote.<name>.token`, then
367
`$OXY_TOKEN` (for CI), then the store, then a remote on the same hub in an
368
enclosing checkout (so a subrepo uses its parent's). oxy sends a token over
369
`https` only, or over plain `http` to this machine; it is never sent to
370
another host after a redirect, and a credentials file that others can read
371
is ignored.
372
373
- `oxy fetch` updates `origin/main` without touching your bookmark.
374
- `oxy pull` fetches, then makes your bookmark, and every other local
375
  bookmark the remote also has, the union of both sides.
376
  There is no merge commit; unrelated changes simply coexist, and a real
377
  conflict is recorded as in section 6. The working copy follows when it was
378
  clean.
379
- `oxy push` sends your changes and moves the remote bookmark. If the remote
380
  moved since you last fetched, the push is refused: pull and push again.
381
  `--force` overrides the compare-and-swap.
382
- `oxy remote add|remove|list` manages remotes; the clone source is `origin`.
383
384
A repository can also be cloned from a local path, which is handy for tests
385
and for a bare "hub" on a shared disk.
386
387
## 8. Decide who may publish: the trust policy
388
389
Without a policy a repository is *unverified*: everything is accepted and
390
every remote command says so. A policy names root keys and the keys allowed
391
to update each bookmark; servers and clients enforce it.
392
393
```
394
oxy policy init                    # your key becomes the only root; push publishes it
395
oxy policy show
396
oxy policy grant alice.identity    # alice.identity came from her `oxy identity export`
397
oxy policy grant ci.identity --pattern 'release/*'
398
oxy policy revoke 9458b656         # by KeyId prefix
399
oxy verify                         # signature status of every change in the log
400
```
401
402
Several people can hold root keys:
403
404
```
405
oxy policy root add bob.identity
406
oxy policy root threshold 2        # future updates need two root signatures
407
```
408
409
Once the threshold is above one, an update you make alone becomes a
410
*proposal file* (`policy-<serial>.proposal`, or `--out`). Send it to another
411
root holder, who runs `oxy policy sign policy-7.proposal`; whoever completes
412
the threshold applies it, and the next push publishes it. Everyone else
413
picks it up on fetch, verifying each step from the policy they already
414
trust.
415
416
## 9. Keys over time
417
418
```
419
oxy identity rotate                   # fresh key pair; old signatures stay valid
420
oxy identity renew --expires 365      # same keys, new expiry in days (0 = never)
421
oxy identity delegate add ci.identity # let another key sign on your behalf
422
oxy identity delegate remove 3f2a…
423
oxy identity revoke                   # retire your own key
424
oxy identity revoke 3f2a… --reason "laptop stolen"   # any key, if you hold a root
425
```
426
427
Rotation, renewal, and delegation all issue a successor identity that names
428
the previous one. If a policy names your key, it is updated to the successor
429
in the same step (as a proposal when more root signatures are needed).
430
Changes signed before the rotation remain authorised; a revoked key
431
authorises nothing. `oxy verify` shows signatures by revoked keys as valid
432
but revoked.
433
434
## 10. Tags and workspaces
435
436
```
437
oxy tag create v1.2 -m "First release"   # the current heads; or name changes
438
oxy tag list
439
oxy workspace add ../project-hotfix      # a second checkout sharing the repository
440
oxy workspace list
441
oxy workspace forget ../project-hotfix
442
```
443
444
A tag is an immutable state, signed when you have a key, and travels with
445
push and fetch. Workspaces have their own working copy and bookmark
446
(`workspace.bookmark`) but share objects and history.
447
448
### Receipts from CI
449
450
A runner such as OxygenFlow signs a receipt for every job it runs: what ran,
451
in what environment, on which files, and the result. Receipts are kept with
452
the state they ran on.
453
454
```
455
oxy receipt list                         # receipts for the bookmark's state
456
oxy receipt show 3f2a9c                  # one in full, with its signature checked
457
oxy receipt export 3f2a9c build.receipt  # hand it to someone
458
oxy receipt verify build.receipt         # check one without keeping it
459
oxy receipt import build.receipt         # verify and keep it
460
```
461
462
## 11. Big repositories: partial, sparse, shallow
463
464
```
465
oxy clone URL --max-blob 1M          # files above 1 MiB are fetched on first read
466
oxy clone URL --sparse src --sparse docs   # materialise only these subtrees
467
oxy clone URL --depth 50             # only the last 50 levels of history
468
oxy sparse src docs tests            # change the filter later
469
oxy deepen 100                       # fetch more history below the boundary
470
```
471
472
Lazy blobs and sparse trees do not affect commits: what you do not have is
473
left exactly as it was. A shallow clone starts from a *checkpoint* of the
474
state, so status and commit work at full speed from the first minute.
475
476
## 11a. Nested repositories (submodules)
477
478
A subrepo is another repository checked out at a path and pinned to a state,
479
Oxygen or Git. The pin is versioned like a file, so two people pinning
480
different states get an ordinary conflict.
481
482
```
483
oxy submodule add https://host/lib.git libs/lib     # Git or Oxygen URL; records .oxymodules
484
oxy commit -m "Add lib"                              # the pin is committed with the parent
485
oxy submodule status                                 # pin vs what is on disk
486
oxy submodule update --init                          # after a clone: fetch nested repositories
487
oxy submodule remove libs/lib                        # drop the node; the directory stays
488
```
489
490
Committing the parent records whatever the nested repository is at, and
491
checking out an older parent state moves the nested repository back, unless
492
it has uncommitted changes, which are never overwritten. An absent nested
493
repository keeps its pin until you run `update --init`. Git submodules
494
survive `oxy git import` as Git pins and go back out as gitlinks on export.
495
496
## 12. Git interoperability
497
498
```
499
oxy git import ../project.git ./project-oxy --branch main   # convert a Git history
500
oxy git fetch https://github.com/you/project.git            # bring new Git commits in
501
oxy git push  https://github.com/you/project.git            # publish your changes to Git
502
oxy git export ../mirror.git                                # a plain Git mirror of the bookmark
503
```
504
505
Import turns each commit into one change (merge commits become
506
resolutions), keeps authors and hashes as `Git-Author`/`Git-Commit`
507
trailers, detects renames, and skips submodules. `git fetch`/`git push` keep
508
a bare mirror under `.oxy/git/`; a push is refused until the remote's tip
509
has been fetched, so it always fast-forwards. Exported commits carry
510
`Change-Id` and `Oxy-Hash` trailers.
511
512
## 13. Automate: hooks
513
514
Executables in `.oxy/hooks/<event>` (never cloned) receive one JSON object
515
on stdin. `pre-commit`, `pre-push` and other `pre-*` hooks veto by exiting
516
non-zero, and their stderr becomes the error message. Events:
517
`pre-commit`, `post-commit`, `pre-push`, `post-fetch`, `post-checkout`, and
518
`semantic-deps`, which runs at commit when `hooks.semantic-deps = true` and
519
answers `{"deps": ["<hash>", …]}` to make the change depend on more than it
520
textually references.
521
522
## 14. Per-path attributes, ignores, locks
523
524
`.oxyattributes` uses gitattributes syntax:
525
526
```
527
*.png     binary
528
*.txt     text eol=lf
529
*.psd     lockable nocompress
530
```
531
532
`binary`/`text` override content sniffing, `eol=lf|crlf` normalises line
533
endings in the store and on checkout, `nocompress` stores chunks raw, and
534
`lockable` files are checked out read-only until you take the local lock:
535
536
```
537
oxy lock assets/logo.psd
538
oxy unlock assets/logo.psd
539
```
540
541
Locks are advisory and local; server-side locks are planned.
542
543
## 15. Configuration
544
545
Two files, both `key = "value"` lines: `~/.config/oxy/config.toml`
546
(`oxy config --user KEY VALUE`) and `.oxy/config` (`oxy config KEY VALUE`).
547
The repository file wins. `OXY_HOME` relocates the user directory. `OXY_TIMINGS=1` prints how long each phase of a snapshot takes (loading the base state, rendering it, reading the working copy, building the change) to standard error, for finding out where a slow command spends its time. Built with `cargo build --release --features alloc-stats`, oxy also reports each phase's live and peak memory and what it allocated, and a summary at the end.
548
549
| Key | Meaning |
550
|---|---|
551
| `author.name`, `author.email`, `author.key` | who you are; `key` is set by `identity create` |
552
| `core.sign` | `change` to sign every commit; otherwise seals at push |
553
| `core.op-retention` | days for which old operations keep their states alive (90) |
554
| `core.gc-grace` | days an unreachable object survives (14) |
555
| `core.checkpoint-interval` | changes between automatic checkpoints (256) |
556
| `filter.max-blob`, `filter.paths` | lazy blobs and sparse subtrees (section 11) |
557
| `hooks.semantic-deps` | run the semantic-deps hook at commit |
558
| `workspace.bookmark` | this workspace's bookmark (`main`) |
559
| `remote.<name>.url`, `.token`, `.ca` | remotes; token and CA fall back to `OXY_TOKEN` and `OXY_CA`, then `oxy auth login`'s store |
560
| `auth.helper` (user file) | a Git credential helper to keep `oxy auth` tokens in, such as `libsecret` |
561
562
## 16. Shell prompt
563
564
`oxy prompt` prints `main 658990aa*+2` (bookmark, working copy, `*` dirty,
565
`!` conflicted, ahead/behind origin) in a few milliseconds and records
566
nothing. See [prompt.md]prompt.md for bash, oh-my-bash, zsh, and fish
567
snippets and the `--format` placeholders.
568
569
## 17. Housekeeping
570
571
```
572
oxy gc              # prune unreachable objects past the grace period, repack into one pack
573
oxy gc --now        # ignore the grace period (only when nothing else is writing)
574
oxy fsck            # check every object, the op log, references, signatures, checkpoints
575
oxy doctor          # check identity, config and remotes, with what to do about each problem
576
oxy checkpoint      # serialise the bookmark's state so large histories load instantly
577
```
578
579
Both run automatically: packing when loose objects pile up, checkpoints
580
every `core.checkpoint-interval` changes.
581
582
## 17a. Scripting and editors
583
584
Every reporting command takes `--json` (`status`, `log --path`, `show`,
585
`diff`, `hunks`, `blame`, `resolve`, `op log`, `bookmark list`, `remote list`,
586
`fetch`, `pull`, `push`, `submodule status`, `bisect status`, `verify`,
587
`policy show`, `root`, `version`). `oxy cat PATH -r CHANGE` prints a file as
588
it was, `oxy discard PATH..` restores files to the base, and `oxy hunks`
589
numbers the working copy's hunks for `commit --hunks` and `discard --hunks`.
590
591
## 18. If something looks wrong
592
593
- **"the remote has change … which you do not; pull first"**: someone
594
  pushed since your last fetch. `oxy pull`, then push.
595
- **"policy needs N root signature(s)"** on fetch: the server's policy was
596
  updated in a way your copy cannot verify. Ask a root holder whether the
597
  chain of updates was published; a fresh clone trusts the current policy.
598
- **`oxy status` exits 2**: conflicts remain; see section 6.
599
- **Many heads on `main`**: independent changes that do not reference one
600
  another. Nothing to fix; the state is their union.
601
- **Undo anything**: `oxy undo` reverts the last operation, including
602
  pulls, checkouts, and rewrites.
603
604
## Shell completions and man pages
605
606
```sh
607
oxy completions bash > ~/.local/share/bash-completion/completions/oxy   # bash
608
oxy completions zsh > "${fpath[1]}/_oxy"                                 # zsh
609
oxy completions fish > ~/.config/fish/completions/oxy.fish               # fish
610
oxy man --dir ~/.local/share/man/man1                                    # then: man oxy-commit
611
```
612
613
The oh-my-bash plugin in `contrib/oh-my-bash` loads the bash completions itself.