|
|
… |
30 unchanged lines |
| 31 |
31 |
|
enum Cmd { |
| 32 |
32 |
|
/// Create a repository in PATH (default: current directory) |
| 33 |
33 |
|
Init { |
|
34 |
+ |
/// Directory to create the repository in (default: the current directory) |
| 34 |
35 |
|
path: Option<PathBuf>, |
| 35 |
36 |
|
/// No working copy: an object store and references only, for hosting |
| 36 |
37 |
|
#[arg(long)] |
|
|
… |
1 unchanged lines |
| 38 |
39 |
|
}, |
| 39 |
40 |
|
/// Clone a repository from a local path or an http(s) URL |
| 40 |
41 |
|
Clone { |
|
42 |
+ |
/// Local path or http(s) URL of the repository |
| 41 |
43 |
|
url: String, |
|
44 |
+ |
/// Directory to clone into (default: the last part of the URL) |
| 42 |
45 |
|
dest: Option<PathBuf>, |
| 43 |
46 |
|
/// Leave blobs larger than this (e.g. 1M) to be fetched on first read |
| 44 |
47 |
|
#[arg(long)] |
|
|
… |
7 unchanged lines |
| 52 |
55 |
|
}, |
| 53 |
56 |
|
/// Fetch more history below a shallow clone's boundary |
| 54 |
57 |
|
Deepen { |
|
58 |
+ |
/// Levels of history to add below the boundary |
| 55 |
59 |
|
#[arg(default_value_t = 10)] |
| 56 |
60 |
|
depth: u32, |
|
61 |
+ |
/// Remote to fetch the history from |
| 57 |
62 |
|
#[arg(long, default_value = "origin")] |
| 58 |
63 |
|
remote: String, |
| 59 |
64 |
|
}, |
| 60 |
65 |
|
/// Show or set the sparse filter (paths to materialize); re-checks out the bookmark |
| 61 |
|
- |
Sparse { paths: Vec<String> }, |
|
66 |
+ |
Sparse { |
|
67 |
+ |
/// Paths to materialize; none prints the current filter |
|
68 |
+ |
paths: Vec<String>, |
|
69 |
+ |
}, |
| 62 |
70 |
|
/// Show the working copy: changed paths and conflicts |
| 63 |
71 |
|
Status { |
| 64 |
72 |
|
/// Machine-readable output: paths, and every conflict with its kind and contributing changes |
|
|
… |
14 unchanged lines |
| 79 |
87 |
|
}, |
| 80 |
88 |
|
/// Record the working copy as a change on the current bookmark |
| 81 |
89 |
|
Commit { |
|
90 |
+ |
/// The change's message: a subject line, then optionally a blank line and a body |
| 82 |
91 |
|
#[arg(short, long)] |
| 83 |
92 |
|
message: String, |
| 84 |
93 |
|
/// Extra explicit dependencies (ChangeId prefix, #hash prefix) |
|
|
… |
10 unchanged lines |
| 95 |
104 |
|
}, |
| 96 |
105 |
|
/// Fold CHANGE into one of its dependencies (keeps that change's id) |
| 97 |
106 |
|
Squash { |
|
107 |
+ |
/// The change to fold (ChangeId prefix, #hash prefix) |
| 98 |
108 |
|
change: String, |
|
109 |
+ |
/// The dependency that absorbs it |
| 99 |
110 |
|
#[arg(long)] |
| 100 |
111 |
|
into: String, |
| 101 |
112 |
|
}, |
| 102 |
113 |
|
/// Re-express CHANGE against another base state |
| 103 |
114 |
|
Rebase { |
|
115 |
+ |
/// The change to re-express (ChangeId prefix, #hash prefix) |
| 104 |
116 |
|
change: String, |
| 105 |
117 |
|
/// The changes, bookmark or tag whose state becomes the new base |
| 106 |
118 |
|
#[arg(long, required = true, num_args = 1..)] |
|
|
… |
1 unchanged lines |
| 108 |
120 |
|
}, |
| 109 |
121 |
|
/// Show which change introduced each line of a file |
| 110 |
122 |
|
Blame { |
|
123 |
+ |
/// File to annotate, relative to the repository root |
| 111 |
124 |
|
path: String, |
|
125 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 112 |
126 |
|
#[arg(long)] |
| 113 |
127 |
|
json: bool, |
| 114 |
128 |
|
}, |
|
|
… |
13 unchanged lines |
| 128 |
142 |
|
/// Only changes touching this path (renames followed) |
| 129 |
143 |
|
#[arg(long)] |
| 130 |
144 |
|
path: Option<String>, |
|
145 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 131 |
146 |
|
#[arg(long)] |
| 132 |
147 |
|
json: bool, |
| 133 |
148 |
|
}, |
|
|
… |
1 unchanged lines |
| 135 |
150 |
|
Diff { |
| 136 |
151 |
|
/// A change instead of the working copy |
| 137 |
152 |
|
change: Option<String>, |
|
153 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 138 |
154 |
|
#[arg(long)] |
| 139 |
155 |
|
json: bool, |
| 140 |
156 |
|
}, |
| 141 |
157 |
|
/// Print a file as it is in a state (default: the working copy's base) |
| 142 |
158 |
|
Cat { |
|
159 |
+ |
/// File to print, relative to the repository root |
| 143 |
160 |
|
path: String, |
| 144 |
161 |
|
/// A change, bookmark, or tag |
| 145 |
162 |
|
#[arg(short = 'r', long)] |
|
|
… |
1 unchanged lines |
| 147 |
164 |
|
}, |
| 148 |
165 |
|
/// Print the repository root, workspace, and bookmark |
| 149 |
166 |
|
Root { |
|
167 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 150 |
168 |
|
#[arg(long)] |
| 151 |
169 |
|
json: bool, |
| 152 |
170 |
|
}, |
| 153 |
171 |
|
/// Print the version of oxy, its repository format, and protocol |
| 154 |
172 |
|
Version { |
|
173 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 155 |
174 |
|
#[arg(long)] |
| 156 |
175 |
|
json: bool, |
| 157 |
176 |
|
}, |
| 158 |
177 |
|
/// Restore paths to the working copy's base, or undo chosen hunks |
| 159 |
178 |
|
Discard { |
|
179 |
+ |
/// Paths to restore; none with --hunks, or everything with neither |
| 160 |
180 |
|
paths: Vec<String>, |
| 161 |
181 |
|
/// Hunk indices as `oxy hunks` numbers them, comma separated |
| 162 |
182 |
|
#[arg(long, value_delimiter = ',')] |
|
|
… |
1 unchanged lines |
| 164 |
184 |
|
}, |
| 165 |
185 |
|
/// List the working copy's hunks, numbered as commit --hunks expects |
| 166 |
186 |
|
Hunks { |
|
187 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 167 |
188 |
|
#[arg(long)] |
| 168 |
189 |
|
json: bool, |
| 169 |
190 |
|
}, |
| 170 |
191 |
|
/// Change the message of a head (default: the head the working copy sits on) |
| 171 |
192 |
|
Describe { |
|
193 |
+ |
/// The new message |
| 172 |
194 |
|
#[arg(short, long)] |
| 173 |
195 |
|
message: String, |
|
196 |
+ |
/// The head to describe (default: the one the working copy sits on) |
| 174 |
197 |
|
change: Option<String>, |
| 175 |
198 |
|
}, |
| 176 |
199 |
|
/// Fold the working copy into the head it sits on, or into CHANGE with --into |
| 177 |
200 |
|
Amend { |
|
201 |
+ |
/// Replace the message too |
| 178 |
202 |
|
#[arg(short, long)] |
| 179 |
203 |
|
message: Option<String>, |
| 180 |
204 |
|
/// Amend an older change; the edits must touch only lines it already knows |
|
|
… |
1 unchanged lines |
| 182 |
206 |
|
into: Option<String>, |
| 183 |
207 |
|
}, |
| 184 |
208 |
|
/// Split a head: the named paths become a separate first change |
| 185 |
|
- |
Split { change: String, paths: Vec<String> }, |
|
209 |
+ |
Split { |
|
210 |
+ |
/// The head to split |
|
211 |
+ |
change: String, |
|
212 |
+ |
/// Paths whose edits become the first change |
|
213 |
+ |
paths: Vec<String>, |
|
214 |
+ |
}, |
| 186 |
215 |
|
/// Drop a head from the bookmark; its content stays on disk as uncommitted work |
| 187 |
|
- |
Abandon { change: String }, |
|
216 |
+ |
Abandon { |
|
217 |
+ |
/// The head to drop (ChangeId prefix, #hash prefix) |
|
218 |
+ |
change: String, |
|
219 |
+ |
}, |
| 188 |
220 |
|
/// Show one change (ChangeId prefix, #hash prefix, or @) |
| 189 |
221 |
|
Show { |
|
222 |
+ |
/// ChangeId prefix, #hash prefix, or @ for the working copy |
| 190 |
223 |
|
change: String, |
|
224 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 191 |
225 |
|
#[arg(long)] |
| 192 |
226 |
|
json: bool, |
| 193 |
227 |
|
}, |
| 194 |
228 |
|
/// Move the working copy to the state formed by CHANGE... (default: the bookmark heads) |
| 195 |
229 |
|
Checkout { |
|
230 |
+ |
/// Changes whose state to check out (default: the bookmark heads) |
| 196 |
231 |
|
changes: Vec<String>, |
| 197 |
232 |
|
/// Throw away uncommitted working-copy changes |
| 198 |
233 |
|
#[arg(long)] |
|
|
… |
1 unchanged lines |
| 200 |
235 |
|
}, |
| 201 |
236 |
|
/// Report which conflicts of the working copy's base are resolved by the current files |
| 202 |
237 |
|
Resolve { |
|
238 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 203 |
239 |
|
#[arg(long)] |
| 204 |
240 |
|
json: bool, |
| 205 |
241 |
|
}, |
|
|
… |
1 unchanged lines |
| 207 |
243 |
|
Undo, |
| 208 |
244 |
|
/// Write a checkpoint of the current bookmark's state now |
| 209 |
245 |
|
Checkpoint, |
|
246 |
+ |
/// Print a shell completion script: `oxy completions bash > ~/.local/share/bash-completion/completions/oxy` |
|
247 |
+ |
Completions { |
|
248 |
+ |
/// bash, zsh, fish, elvish or powershell |
|
249 |
+ |
shell: clap_complete::Shell, |
|
250 |
+ |
}, |
|
251 |
+ |
/// Write man pages (oxy.1 and one per command) into DIR, or print oxy.1 |
|
252 |
+ |
Man { |
|
253 |
+ |
/// Directory to write every page into, e.g. ~/.local/share/man/man1 |
|
254 |
+ |
#[arg(long)] |
|
255 |
+ |
dir: Option<PathBuf>, |
|
256 |
+ |
}, |
| 210 |
257 |
|
/// Check the identity, configuration and remotes, and say what to do about any problem |
| 211 |
258 |
|
Doctor { |
| 212 |
259 |
|
/// Do not contact the remotes |
| 213 |
260 |
|
#[arg(long)] |
| 214 |
261 |
|
offline: bool, |
|
262 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 215 |
263 |
|
#[arg(long)] |
| 216 |
264 |
|
json: bool, |
| 217 |
265 |
|
}, |
| 218 |
266 |
|
/// Check the repository's integrity: objects, operation log, references, signatures, checkpoints |
| 219 |
267 |
|
Fsck { |
|
268 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 220 |
269 |
|
#[arg(long)] |
| 221 |
270 |
|
json: bool, |
| 222 |
271 |
|
}, |
|
|
… |
10 unchanged lines |
| 233 |
282 |
|
cmd: OpCmd, |
| 234 |
283 |
|
}, |
| 235 |
284 |
|
/// Take the advisory local lock on a lockable file, making it writable |
| 236 |
|
- |
Lock { path: String }, |
|
285 |
+ |
Lock { |
|
286 |
+ |
/// The lockable file |
|
287 |
+ |
path: String, |
|
288 |
+ |
}, |
| 237 |
289 |
|
/// Release the local lock and make the file read-only again |
| 238 |
|
- |
Unlock { path: String }, |
|
290 |
+ |
Unlock { |
|
291 |
+ |
/// The locked file |
|
292 |
+ |
path: String, |
|
293 |
+ |
}, |
| 239 |
294 |
|
/// Workspaces: extra checkout directories sharing this repository |
| 240 |
295 |
|
Workspace { |
| 241 |
296 |
|
#[command(subcommand)] |
|
|
… |
5 unchanged lines |
| 247 |
302 |
|
cmd: BookmarkCmd, |
| 248 |
303 |
|
}, |
| 249 |
304 |
|
/// Union another bookmark's heads into the current one (no merge change) |
| 250 |
|
- |
Merge { bookmark: String }, |
|
305 |
+ |
Merge { |
|
306 |
+ |
/// The bookmark whose heads to add |
|
307 |
+ |
bookmark: String, |
|
308 |
+ |
}, |
| 251 |
309 |
|
/// Subrepositories: nested repositories pinned to a state |
| 252 |
310 |
|
Submodule { |
| 253 |
311 |
|
#[command(subcommand)] |
|
|
… |
11 unchanged lines |
| 265 |
323 |
|
}, |
| 266 |
324 |
|
/// Fetch every bookmark of a remote into its tracking references |
| 267 |
325 |
|
Fetch { |
|
326 |
+ |
/// Remote name (default: origin) |
| 268 |
327 |
|
remote: Option<String>, |
|
328 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 269 |
329 |
|
#[arg(long)] |
| 270 |
330 |
|
json: bool, |
| 271 |
331 |
|
}, |
| 272 |
332 |
|
/// Fetch, then union the remote bookmark into the local one (no merge change) |
| 273 |
333 |
|
Pull { |
|
334 |
+ |
/// Remote name (default: origin) |
| 274 |
335 |
|
remote: Option<String>, |
|
336 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 275 |
337 |
|
#[arg(long)] |
| 276 |
338 |
|
json: bool, |
| 277 |
339 |
|
}, |
| 278 |
340 |
|
/// Push the current bookmark to a remote |
| 279 |
341 |
|
Push { |
|
342 |
+ |
/// Remote name (default: origin) |
| 280 |
343 |
|
remote: Option<String>, |
| 281 |
344 |
|
/// Allow dropping heads the remote has |
| 282 |
345 |
|
#[arg(long)] |
|
|
… |
1 unchanged lines |
| 284 |
347 |
|
/// Push this bookmark instead of the workspace's current one |
| 285 |
348 |
|
#[arg(short = 'b', long)] |
| 286 |
349 |
|
bookmark: Option<String>, |
|
350 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 287 |
351 |
|
#[arg(long)] |
| 288 |
352 |
|
json: bool, |
| 289 |
353 |
|
}, |
|
|
… |
2 unchanged lines |
| 292 |
356 |
|
/// Directory with one repository per subdirectory |
| 293 |
357 |
|
#[arg(long, default_value = ".")] |
| 294 |
358 |
|
root: PathBuf, |
|
359 |
+ |
/// Address and port to listen on |
| 295 |
360 |
|
#[arg(long, default_value = "127.0.0.1:8890")] |
| 296 |
361 |
|
bind: String, |
| 297 |
362 |
|
/// Bearer token allowed to push (repeatable); none means open |
|
|
… |
13 unchanged lines |
| 311 |
376 |
|
}, |
| 312 |
377 |
|
/// Seal changes with your key: named ones, or every change on the current bookmark you can authenticate but have not |
| 313 |
378 |
|
Seal { |
|
379 |
+ |
/// Changes to seal (default: every change on the bookmark you can authenticate and have not sealed) |
| 314 |
380 |
|
changes: Vec<String>, |
| 315 |
381 |
|
/// Seal every change of a hub proposal and push the seal (your approval) |
| 316 |
382 |
|
#[arg(long, conflicts_with = "changes")] |
| 317 |
383 |
|
proposal: Option<String>, |
|
384 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 318 |
385 |
|
#[arg(long)] |
| 319 |
386 |
|
json: bool, |
| 320 |
387 |
|
}, |
|
|
… |
4 unchanged lines |
| 325 |
392 |
|
}, |
| 326 |
393 |
|
/// Verify the signatures on a change (default: every change in the log) |
| 327 |
394 |
|
Verify { |
|
395 |
+ |
/// The change to verify (default: every change in the log) |
| 328 |
396 |
|
change: Option<String>, |
|
397 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 329 |
398 |
|
#[arg(long)] |
| 330 |
399 |
|
json: bool, |
| 331 |
400 |
|
}, |
|
|
… |
9 unchanged lines |
| 341 |
410 |
|
}, |
| 342 |
411 |
|
/// Set a configuration key (author.*, core.sign, core.op-retention, core.gc-grace, core.checkpoint-interval, filter.max-blob, filter.paths, workspace.bookmark, remote.<name>.url|token|ca) |
| 343 |
412 |
|
Config { |
|
413 |
+ |
/// The key, such as author.name or remote.origin.token |
| 344 |
414 |
|
key: String, |
|
415 |
+ |
/// The value to set |
| 345 |
416 |
|
value: String, |
| 346 |
417 |
|
/// Write to the user file (~/.config/oxy/config.toml) instead of this repository |
| 347 |
418 |
|
#[arg(long)] |
|
|
… |
3 unchanged lines |
| 351 |
422 |
|
|
| 352 |
423 |
|
#[derive(Subcommand)] |
| 353 |
424 |
|
enum OpCmd { |
|
425 |
+ |
/// List operations, newest first |
| 354 |
426 |
|
Log { |
|
427 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 355 |
428 |
|
#[arg(long)] |
| 356 |
429 |
|
json: bool, |
| 357 |
430 |
|
}, |
|
|
… |
2 unchanged lines |
| 360 |
433 |
|
#[derive(Subcommand)] |
| 361 |
434 |
|
enum WorkspaceCmd { |
| 362 |
435 |
|
/// Create a workspace at PATH checked out on CHANGE... (default: the bookmark heads) |
| 363 |
|
- |
Add { path: PathBuf, changes: Vec<String> }, |
|
436 |
+ |
Add { |
|
437 |
+ |
/// Directory for the new workspace (must be empty or absent) |
|
438 |
+ |
path: PathBuf, |
|
439 |
+ |
/// Changes whose state to check out there |
|
440 |
+ |
changes: Vec<String>, |
|
441 |
+ |
}, |
|
442 |
+ |
/// List workspaces and their working-copy changes |
| 364 |
443 |
|
List { |
|
444 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 365 |
445 |
|
#[arg(long)] |
| 366 |
446 |
|
json: bool, |
| 367 |
447 |
|
}, |
| 368 |
448 |
|
/// Stop tracking a workspace (by id prefix or path); its files stay |
| 369 |
|
- |
Forget { workspace: String }, |
|
449 |
+ |
Forget { |
|
450 |
+ |
/// Workspace id prefix or path |
|
451 |
+ |
workspace: String, |
|
452 |
+ |
}, |
| 370 |
453 |
|
} |
| 371 |
454 |
|
|
| 372 |
455 |
|
#[derive(Subcommand)] |
|
|
… |
1 unchanged lines |
| 374 |
457 |
|
/// Begin; remembers where the working copy was |
| 375 |
458 |
|
Start, |
| 376 |
459 |
|
/// Mark CHANGE... (or the state under test) as good |
| 377 |
|
- |
Good { changes: Vec<String> }, |
|
460 |
+ |
Good { |
|
461 |
+ |
/// Changes known to be good (default: the state under test) |
|
462 |
+ |
changes: Vec<String>, |
|
463 |
+ |
}, |
| 378 |
464 |
|
/// Mark CHANGE... (or the state under test) as bad |
| 379 |
|
- |
Bad { changes: Vec<String> }, |
|
465 |
+ |
Bad { |
|
466 |
+ |
/// Changes known to be bad (default: the state under test) |
|
467 |
+ |
changes: Vec<String>, |
|
468 |
+ |
}, |
| 380 |
469 |
|
/// The state under test cannot be tested; try another |
| 381 |
470 |
|
Skip, |
| 382 |
471 |
|
/// Run a command for each step: exit 0 good, 125 skip, else bad |
| 383 |
472 |
|
Run { |
|
473 |
+ |
/// Command and arguments to run at each step |
| 384 |
474 |
|
#[arg(required = true, trailing_var_arg = true)] |
| 385 |
475 |
|
command: Vec<String>, |
| 386 |
476 |
|
}, |
| 387 |
477 |
|
/// Where the search stands |
| 388 |
478 |
|
Status { |
|
479 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 389 |
480 |
|
#[arg(long)] |
| 390 |
481 |
|
json: bool, |
| 391 |
482 |
|
}, |
|
|
… |
4 unchanged lines |
| 396 |
487 |
|
#[derive(Subcommand)] |
| 397 |
488 |
|
enum BookmarkCmd { |
| 398 |
489 |
|
/// Create a bookmark at CHANGE... (default: the working copy's base) |
| 399 |
|
- |
Create { name: String, changes: Vec<String> }, |
|
490 |
+ |
Create { |
|
491 |
+ |
/// Name of the new bookmark |
|
492 |
+ |
name: String, |
|
493 |
+ |
/// Its heads (default: the working copy's base) |
|
494 |
+ |
changes: Vec<String>, |
|
495 |
+ |
}, |
| 400 |
496 |
|
/// Point an existing bookmark at CHANGE... |
| 401 |
|
- |
Set { name: String, changes: Vec<String> }, |
|
497 |
+ |
Set { |
|
498 |
+ |
/// The bookmark to move |
|
499 |
+ |
name: String, |
|
500 |
+ |
/// Its new heads |
|
501 |
+ |
changes: Vec<String>, |
|
502 |
+ |
}, |
| 402 |
503 |
|
/// Remove a bookmark; its changes stay |
| 403 |
|
- |
Delete { name: String }, |
|
504 |
+ |
Delete { |
|
505 |
+ |
/// The bookmark to remove |
|
506 |
+ |
name: String, |
|
507 |
+ |
}, |
| 404 |
508 |
|
/// List bookmarks with their heads and difference from origin |
| 405 |
509 |
|
List { |
|
510 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 406 |
511 |
|
#[arg(long)] |
| 407 |
512 |
|
json: bool, |
| 408 |
513 |
|
}, |
| 409 |
514 |
|
/// Make NAME this workspace's bookmark and check it out |
| 410 |
515 |
|
Switch { |
|
516 |
+ |
/// The bookmark to work on |
| 411 |
517 |
|
name: String, |
| 412 |
518 |
|
/// Throw away uncommitted working-copy changes |
| 413 |
519 |
|
#[arg(long)] |
|
|
… |
5 unchanged lines |
| 419 |
525 |
|
enum SubmoduleCmd { |
| 420 |
526 |
|
/// Clone URL (Oxygen or Git) into PATH and register it in .oxymodules |
| 421 |
527 |
|
Add { |
|
528 |
+ |
/// URL or path of the Oxygen or Git repository |
| 422 |
529 |
|
url: String, |
|
530 |
+ |
/// Where to put it, relative to the repository root |
| 423 |
531 |
|
path: String, |
| 424 |
532 |
|
/// Bookmark (Oxygen) or branch (Git) to check out and track |
| 425 |
533 |
|
#[arg(long)] |
|
|
… |
4 unchanged lines |
| 430 |
538 |
|
/// Clone nested repositories that are absent |
| 431 |
539 |
|
#[arg(long)] |
| 432 |
540 |
|
init: bool, |
|
541 |
+ |
/// Subrepos to update (default: all) |
| 433 |
542 |
|
paths: Vec<String>, |
| 434 |
543 |
|
}, |
| 435 |
544 |
|
/// List subrepos with their pins and on-disk state |
| 436 |
545 |
|
Status { |
|
546 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 437 |
547 |
|
#[arg(long)] |
| 438 |
548 |
|
json: bool, |
| 439 |
549 |
|
}, |
| 440 |
550 |
|
/// Unregister PATH; the directory stays, the node goes with the next commit |
| 441 |
|
- |
Remove { path: String }, |
|
551 |
+ |
Remove { |
|
552 |
+ |
/// The subrepo to unregister |
|
553 |
+ |
path: String, |
|
554 |
+ |
}, |
| 442 |
555 |
|
} |
| 443 |
556 |
|
|
| 444 |
557 |
|
#[derive(Subcommand)] |
| 445 |
558 |
|
enum TagCmd { |
| 446 |
559 |
|
/// Tag the state of CHANGE... (default: the bookmark heads) |
| 447 |
560 |
|
Create { |
|
561 |
+ |
/// Name of the tag; tags never move |
| 448 |
562 |
|
name: String, |
|
563 |
+ |
/// Changes whose state to tag (default: the bookmark heads) |
| 449 |
564 |
|
changes: Vec<String>, |
|
565 |
+ |
/// A message recorded with the tag |
| 450 |
566 |
|
#[arg(short, long, default_value = "")] |
| 451 |
567 |
|
message: String, |
| 452 |
568 |
|
}, |
|
569 |
+ |
/// List tags and the states they name |
| 453 |
570 |
|
List { |
|
571 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 454 |
572 |
|
#[arg(long)] |
| 455 |
573 |
|
json: bool, |
| 456 |
574 |
|
}, |
|
|
… |
1 unchanged lines |
| 458 |
576 |
|
|
| 459 |
577 |
|
#[derive(Subcommand)] |
| 460 |
578 |
|
enum RemoteCmd { |
|
579 |
+ |
/// Add a remote |
| 461 |
580 |
|
Add { |
|
581 |
+ |
/// Name to refer to the remote by, such as origin |
| 462 |
582 |
|
name: String, |
|
583 |
+ |
/// Local path or http(s) URL of the repository |
| 463 |
584 |
|
url: String, |
| 464 |
585 |
|
}, |
|
586 |
+ |
/// Remove a remote |
| 465 |
587 |
|
Remove { |
|
588 |
+ |
/// The remote to remove |
| 466 |
589 |
|
name: String, |
| 467 |
590 |
|
}, |
|
591 |
+ |
/// List remotes and their URLs |
| 468 |
592 |
|
List { |
|
593 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 469 |
594 |
|
#[arg(long)] |
| 470 |
595 |
|
json: bool, |
| 471 |
596 |
|
}, |
|
|
… |
3 unchanged lines |
| 475 |
600 |
|
enum ProposalCmd { |
| 476 |
601 |
|
/// Show a proposal by its hub URL |
| 477 |
602 |
|
Show { |
|
603 |
+ |
/// The proposal's URL on the hub |
| 478 |
604 |
|
url: String, |
|
605 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 479 |
606 |
|
#[arg(long)] |
| 480 |
607 |
|
json: bool, |
| 481 |
608 |
|
}, |
| 482 |
609 |
|
/// Fetch the proposal's changes, set the target bookmark to the projected state, and push with your key |
| 483 |
610 |
|
Land { |
|
611 |
+ |
/// The proposal's URL on the hub |
| 484 |
612 |
|
url: String, |
| 485 |
613 |
|
/// Land even when the hub reports blockers |
| 486 |
614 |
|
#[arg(long)] |
|
|
… |
5 unchanged lines |
| 492 |
620 |
|
enum IdentityCmd { |
| 493 |
621 |
|
/// Generate a composite Ed25519 + ML-DSA-65 key pair and make it your author identity |
| 494 |
622 |
|
Create { |
|
623 |
+ |
/// Your name (default: author.name) |
| 495 |
624 |
|
#[arg(long)] |
| 496 |
625 |
|
name: Option<String>, |
|
626 |
+ |
/// Your email (default: author.email) |
| 497 |
627 |
|
#[arg(long)] |
| 498 |
628 |
|
email: Option<String>, |
| 499 |
629 |
|
/// Record the author in this repository only, not in the user config |
|
|
… |
6 unchanged lines |
| 506 |
636 |
|
/// Show the configured identity |
| 507 |
637 |
|
Show, |
| 508 |
638 |
|
/// Write your identity object to a file, to hand to a policy holder |
| 509 |
|
- |
Export { path: Option<PathBuf> }, |
|
639 |
+ |
Export { |
|
640 |
+ |
/// File to write (default: stdout) |
|
641 |
+ |
path: Option<PathBuf>, |
|
642 |
+ |
}, |
| 510 |
643 |
|
/// Sign a host's challenge (hex, at least 16 bytes) to prove you hold your key; prints the signature as hex |
| 511 |
644 |
|
SignChallenge { |
|
645 |
+ |
/// The host's challenge, as hex |
| 512 |
646 |
|
challenge: String, |
|
647 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 513 |
648 |
|
#[arg(long)] |
| 514 |
649 |
|
json: bool, |
| 515 |
650 |
|
}, |
|
|
… |
1 unchanged lines |
| 517 |
652 |
|
Rotate, |
| 518 |
653 |
|
/// Re-issue your identity with a new expiry (in days from now; 0 = never) |
| 519 |
654 |
|
Renew { |
|
655 |
+ |
/// Days from now until it expires; 0 for never |
| 520 |
656 |
|
#[arg(long)] |
| 521 |
657 |
|
expires: u64, |
| 522 |
658 |
|
}, |
|
|
… |
1 unchanged lines |
| 524 |
660 |
|
Revoke { |
| 525 |
661 |
|
/// KeyId prefix (default: your own key) |
| 526 |
662 |
|
key: Option<String>, |
|
663 |
+ |
/// Why, recorded in the revocation |
| 527 |
664 |
|
#[arg(long, default_value = "")] |
| 528 |
665 |
|
reason: String, |
| 529 |
666 |
|
}, |
|
|
… |
7 unchanged lines |
| 537 |
674 |
|
#[derive(Subcommand)] |
| 538 |
675 |
|
enum DelegateCmd { |
| 539 |
676 |
|
/// Add a delegate from an exported identity file |
| 540 |
|
- |
Add { identity_file: PathBuf }, |
|
677 |
+ |
Add { |
|
678 |
+ |
/// The delegate's exported identity file |
|
679 |
+ |
identity_file: PathBuf, |
|
680 |
+ |
}, |
| 541 |
681 |
|
/// Remove a delegate by KeyId prefix |
| 542 |
|
- |
Remove { key: String }, |
|
682 |
+ |
Remove { |
|
683 |
+ |
/// KeyId prefix of the delegate |
|
684 |
+ |
key: String, |
|
685 |
+ |
}, |
| 543 |
686 |
|
/// List delegates |
| 544 |
687 |
|
List, |
| 545 |
688 |
|
} |
|
|
… |
2 unchanged lines |
| 548 |
691 |
|
enum GitCmd { |
| 549 |
692 |
|
/// Export the current bookmark to a Git repository (created bare if absent) |
| 550 |
693 |
|
Export { |
|
694 |
+ |
/// Git repository to write to (created bare if absent) |
| 551 |
695 |
|
git_dir: PathBuf, |
| 552 |
696 |
|
/// Git branch to write (default: the bookmark name) |
| 553 |
697 |
|
#[arg(long)] |
|
|
… |
1 unchanged lines |
| 555 |
699 |
|
}, |
| 556 |
700 |
|
/// Fetch a branch from a Git remote (path or URL) into the current bookmark |
| 557 |
701 |
|
Fetch { |
|
702 |
+ |
/// Git remote: a path or URL |
| 558 |
703 |
|
url: String, |
|
704 |
+ |
/// Branch to fetch |
| 559 |
705 |
|
#[arg(long, default_value = "main")] |
| 560 |
706 |
|
branch: String, |
| 561 |
707 |
|
}, |
| 562 |
708 |
|
/// Push the current bookmark to a branch of a Git remote (fetch first) |
| 563 |
709 |
|
Push { |
|
710 |
+ |
/// Git remote: a path or URL |
| 564 |
711 |
|
url: String, |
|
712 |
+ |
/// Branch to push to |
| 565 |
713 |
|
#[arg(long, default_value = "main")] |
| 566 |
714 |
|
branch: String, |
| 567 |
715 |
|
}, |
| 568 |
716 |
|
/// Import a Git branch into a new Oxygen repository at DEST |
| 569 |
717 |
|
Import { |
|
718 |
+ |
/// Git repository to read |
| 570 |
719 |
|
git_dir: PathBuf, |
|
720 |
+ |
/// Directory for the new Oxygen repository |
| 571 |
721 |
|
dest: PathBuf, |
|
722 |
+ |
/// Branch to import |
| 572 |
723 |
|
#[arg(long, default_value = "main")] |
| 573 |
724 |
|
branch: String, |
| 574 |
725 |
|
}, |
|
|
… |
5 unchanged lines |
| 580 |
731 |
|
Init, |
| 581 |
732 |
|
/// Show the current policy |
| 582 |
733 |
|
Show { |
|
734 |
+ |
/// Machine-readable output: one JSON document on stdout |
| 583 |
735 |
|
#[arg(long)] |
| 584 |
736 |
|
json: bool, |
| 585 |
737 |
|
}, |
| 586 |
738 |
|
/// Allow an identity (from `oxy identity export`) to update bookmarks |
| 587 |
739 |
|
Grant { |
|
740 |
+ |
/// The identity to grant, exported with `oxy identity export` |
| 588 |
741 |
|
identity_file: PathBuf, |
| 589 |
742 |
|
/// Reference pattern the grant applies to (default: every bookmark) |
| 590 |
743 |
|
#[arg(long)] |
|
|
… |
4 unchanged lines |
| 595 |
748 |
|
}, |
| 596 |
749 |
|
/// Revoke a key (KeyId prefix of an identity in the policy) |
| 597 |
750 |
|
Revoke { |
|
751 |
+ |
/// KeyId prefix of the identity |
| 598 |
752 |
|
key: String, |
| 599 |
753 |
|
/// Where to write the proposal when more root signatures are needed |
| 600 |
754 |
|
#[arg(long)] |
|
|
… |
6 unchanged lines |
| 607 |
761 |
|
}, |
| 608 |
762 |
|
/// Add your root signature to a proposal file; applies it once the threshold is met |
| 609 |
763 |
|
Sign { |
|
764 |
+ |
/// The proposal file to sign |
| 610 |
765 |
|
proposal: PathBuf, |
| 611 |
766 |
|
/// Where to write the proposal if it still needs signatures (default: in place) |
| 612 |
767 |
|
#[arg(long)] |
|
|
… |
5 unchanged lines |
| 618 |
773 |
|
enum RootCmd { |
| 619 |
774 |
|
/// Make an exported identity a root key |
| 620 |
775 |
|
Add { |
|
776 |
+ |
/// The identity to make a root, exported with `oxy identity export` |
| 621 |
777 |
|
identity_file: PathBuf, |
|
778 |
+ |
/// Where to write the proposal when more root signatures are needed |
| 622 |
779 |
|
#[arg(long)] |
| 623 |
780 |
|
out: Option<PathBuf>, |
| 624 |
781 |
|
}, |
| 625 |
782 |
|
/// Remove a root key by KeyId prefix |
| 626 |
783 |
|
Remove { |
|
784 |
+ |
/// KeyId prefix of the root key |
| 627 |
785 |
|
key: String, |
|
786 |
+ |
/// Where to write the proposal when more root signatures are needed |
| 628 |
787 |
|
#[arg(long)] |
| 629 |
788 |
|
out: Option<PathBuf>, |
| 630 |
789 |
|
}, |
| 631 |
790 |
|
/// Require this many root signatures on policy updates |
| 632 |
791 |
|
Threshold { |
|
792 |
+ |
/// Number of root signatures |
| 633 |
793 |
|
n: u32, |
|
794 |
+ |
/// Where to write the proposal when more root signatures are needed |
| 634 |
795 |
|
#[arg(long)] |
| 635 |
796 |
|
out: Option<PathBuf>, |
| 636 |
797 |
|
}, |
|
|
… |
4 unchanged lines |
| 641 |
802 |
|
Repo::open(&std::env::current_dir()?).context("opening repository") |
| 642 |
803 |
|
} |
| 643 |
804 |
|
|
|
805 |
+ |
/// `oxy man`: every page into `dir`, or the top-level page on stdout. |
|
806 |
+ |
fn man(dir: Option<PathBuf>) -> Result<i32> { |
|
807 |
+ |
use clap::CommandFactory; |
|
808 |
+ |
let cmd = Cli::command(); |
|
809 |
+ |
match dir { |
|
810 |
+ |
Some(dir) => { |
|
811 |
+ |
std::fs::create_dir_all(&dir)?; |
|
812 |
+ |
clap_mangen::generate_to(cmd, &dir)?; |
|
813 |
+ |
let pages = std::fs::read_dir(&dir)? |
|
814 |
+ |
.filter(|e| { |
|
815 |
+ |
e.as_ref() |
|
816 |
+ |
.is_ok_and(|e| e.path().extension().is_some_and(|x| x == "1")) |
|
817 |
+ |
}) |
|
818 |
+ |
.count(); |
|
819 |
+ |
println!( |
|
820 |
+ |
"{}", |
|
821 |
+ |
ui::done(format!("Wrote {pages} man pages to {}", dir.display())) |
|
822 |
+ |
); |
|
823 |
+ |
} |
|
824 |
+ |
None => clap_mangen::Man::new(cmd).render(&mut std::io::stdout())?, |
|
825 |
+ |
} |
|
826 |
+ |
Ok(0) |
|
827 |
+ |
} |
|
828 |
+ |
|
| 644 |
829 |
|
/// Parse arguments and run; returns the process exit code. |
| 645 |
830 |
|
pub fn run() -> Result<i32> { |
| 646 |
831 |
|
let cli = Cli::parse(); |
| 647 |
832 |
|
ui::init(cli.plain); |
| 648 |
833 |
|
match cli.cmd { |
|
834 |
+ |
Cmd::Completions { shell } => { |
|
835 |
+ |
use clap::CommandFactory; |
|
836 |
+ |
clap_complete::generate(shell, &mut Cli::command(), "oxy", &mut std::io::stdout()); |
|
837 |
+ |
Ok(0) |
|
838 |
+ |
} |
|
839 |
+ |
Cmd::Man { dir } => man(dir), |
| 649 |
840 |
|
Cmd::Init { path, bare } => workspace::init(path, bare), |
| 650 |
841 |
|
Cmd::Status { json } => workspace::status(json), |
| 651 |
842 |
|
Cmd::Prompt { |
|
|
… |
179 unchanged lines |
| 831 |
1022 |
|
Cmd::Config { key, value, user } => identity::config(&key, &value, user), |
| 832 |
1023 |
|
} |
| 833 |
1024 |
|
} |
|
1025 |
+ |
|
|
1026 |
+ |
#[cfg(test)] |
|
1027 |
+ |
mod help_tests { |
|
1028 |
+ |
use super::Cli; |
|
1029 |
+ |
use clap::CommandFactory; |
|
1030 |
+ |
|
|
1031 |
+ |
/// Every command and every argument explains itself, so `--help`, |
|
1032 |
+ |
/// completions and the man pages are complete. |
|
1033 |
+ |
#[test] |
|
1034 |
+ |
fn every_command_and_argument_has_help() { |
|
1035 |
+ |
fn walk(cmd: &clap::Command, path: &str, missing: &mut Vec<String>) { |
|
1036 |
+ |
let here = format!("{path} {}", cmd.get_name()).trim().to_string(); |
|
1037 |
+ |
if cmd.get_about().is_none() && path != "" { |
|
1038 |
+ |
missing.push(format!("{here} (command)")); |
|
1039 |
+ |
} |
|
1040 |
+ |
for a in cmd.get_arguments() { |
|
1041 |
+ |
let id = a.get_id().as_str(); |
|
1042 |
+ |
if id == "help" || id == "version" || a.is_global_set() && path != "" { |
|
1043 |
+ |
continue; |
|
1044 |
+ |
} |
|
1045 |
+ |
if a.get_help().is_none() && a.get_long_help().is_none() { |
|
1046 |
+ |
missing.push(format!("{here} {id}")); |
|
1047 |
+ |
} |
|
1048 |
+ |
} |
|
1049 |
+ |
for sub in cmd.get_subcommands() { |
|
1050 |
+ |
walk(sub, &here, missing); |
|
1051 |
+ |
} |
|
1052 |
+ |
} |
|
1053 |
+ |
let mut missing = Vec::new(); |
|
1054 |
+ |
walk(&Cli::command(), "", &mut missing); |
|
1055 |
+ |
assert!( |
|
1056 |
+ |
missing.is_empty(), |
|
1057 |
+ |
"{} without help:\n{}", |
|
1058 |
+ |
missing.len(), |
|
1059 |
+ |
missing.join("\n") |
|
1060 |
+ |
); |
|
1061 |
+ |
} |
|
1062 |
+ |
} |