OXYGENHUB
# 387335ff7f4d oxygen / src / cli / mod.rs
26.0 KiB · Rust · last changed oxy fsck: check a repository's integrity 1 month ago source blame history raw
fce44ae9 Andy McCall ↶ 1
//! Command-line definitions and dispatch.
2
3
mod format;
75167672 Andy McCall 4
mod identity;
c6aa5bd9 Andy McCall ↶ 5
mod json;
fce44ae9 Andy McCall ↶ 6
mod sync;
135212de Andy McCall ↶ 7
pub mod ui;
fce44ae9 Andy McCall ↶ 8
mod workspace;
9
10
use anyhow::{Context, Result};
11
use clap::{Parser, Subcommand};
12
use oxy_core::repo::Repo;
13
use std::path::PathBuf;
14
15
#[derive(Parser)]
75167672 Andy McCall 16
#[command(
17
    name = "oxy",
18
    version,
19
    about = "Oxygen: breathing fresh air into version control"
20
)]
fce44ae9 Andy McCall ↶ 21
struct Cli {
135212de Andy McCall ↶ 22
    /// Plain output: no colour, no spinner (also implied by NO_COLOR or a non-terminal)
23
    #[arg(long, global = true)]
24
    plain: bool,
fce44ae9 Andy McCall ↶ 25
    #[command(subcommand)]
26
    cmd: Cmd,
27
}
28
29
#[derive(Subcommand)]
30
enum Cmd {
31
    /// Create a repository in PATH (default: current directory)
e02bf0cd Andy McCall ↶ 32
    Init {
33
        path: Option<PathBuf>,
34
        /// No working copy: an object store and references only, for hosting
35
        #[arg(long)]
36
        bare: bool,
37
    },
fce44ae9 Andy McCall ↶ 38
    /// Clone a repository from a local path or an http(s) URL
b3c5a59a Andy McCall ↶ 39
    Clone {
40
        url: String,
41
        dest: Option<PathBuf>,
42
        /// Leave blobs larger than this (e.g. 1M) to be fetched on first read
43
        #[arg(long)]
44
        max_blob: Option<String>,
7421309f Andy McCall ↶ 45
        /// Materialize only these paths (repeatable); implies lazy blobs
46
        #[arg(long = "sparse")]
47
        sparse: Vec<String>,
2863456a Andy McCall ↶ 48
        /// Shallow clone: fetch only this many levels of history below the heads
49
        #[arg(long)]
50
        depth: Option<u32>,
b3c5a59a Andy McCall ↶ 51
    },
2863456a Andy McCall ↶ 52
    /// Fetch more history below a shallow clone's boundary
53
    Deepen {
54
        #[arg(default_value_t = 10)]
55
        depth: u32,
56
        #[arg(long, default_value = "origin")]
57
        remote: String,
58
    },
7421309f Andy McCall ↶ 59
    /// Show or set the sparse filter (paths to materialize); re-checks out the bookmark
60
    Sparse { paths: Vec<String> },
fce44ae9 Andy McCall ↶ 61
    /// Show the working copy: changed paths and conflicts
50a046e4 Andy McCall ↶ 62
    Status {
63
        /// Machine-readable output: paths, and every conflict with its kind and contributing changes
64
        #[arg(long)]
65
        json: bool,
66
    },
135212de Andy McCall ↶ 67
    /// One-line summary for shell prompts; fast and records nothing
68
    Prompt {
694805cc Andy McCall ↶ 69
        /// Placeholders: %b bookmark, %c change id, %h heads, %d dirty (*), %x conflict (!), %A/%D ahead/behind (+n/-n), %a/%B raw counts, %s state glyph, %U/%O ahead/behind arrows, %i icon
135212de Andy McCall ↶ 70
        #[arg(long, default_value = oxy_core::repo::DEFAULT_PROMPT_FORMAT)]
71
        format: String,
694805cc Andy McCall ↶ 72
        /// Glyph format (%i %b %c %s%U%O) instead of the plain default
73
        #[arg(long)]
74
        glyphs: bool,
75
        /// Colour by state (cyan clean, yellow dirty, red conflicted): raw escapes, or guarded for a bash or zsh PS1
76
        #[arg(long, value_parser = ["raw", "bash", "zsh"])]
77
        color: Option<String>,
135212de Andy McCall ↶ 78
    },
fce44ae9 Andy McCall ↶ 79
    /// Record the working copy as a change on the current bookmark
80
    Commit {
81
        #[arg(short, long)]
82
        message: String,
83
        /// Extra explicit dependencies (ChangeId prefix, #hash prefix)
84
        #[arg(long = "depends-on")]
85
        depends_on: Vec<String>,
957fee30 Andy McCall ↶ 86
        /// Commit only these paths; the rest stays as uncommitted work
87
        paths: Vec<String>,
88
        /// Choose hunks one by one
89
        #[arg(short, long)]
90
        interactive: bool,
c6aa5bd9 Andy McCall ↶ 91
        /// Commit these hunks (as `oxy hunks` numbers them), comma separated
92
        #[arg(long, value_delimiter = ',')]
93
        hunks: Vec<usize>,
fce44ae9 Andy McCall ↶ 94
    },
728da0f9 Andy McCall ↶ 95
    /// Fold CHANGE into one of its dependencies (keeps that change's id)
96
    Squash {
97
        change: String,
98
        #[arg(long)]
99
        into: String,
100
    },
101
    /// Re-express CHANGE against another base state
102
    Rebase {
103
        change: String,
104
        /// The changes, bookmark or tag whose state becomes the new base
105
        #[arg(long, required = true, num_args = 1..)]
106
        onto: Vec<String>,
107
    },
957fee30 Andy McCall ↶ 108
    /// Show which change introduced each line of a file
109
    Blame {
110
        path: String,
111
        #[arg(long)]
112
        json: bool,
113
    },
114
    /// Find the change that introduced a regression
115
    Bisect {
116
        #[command(subcommand)]
117
        cmd: BisectCmd,
118
    },
fce44ae9 Andy McCall ↶ 119
    /// List changes reachable from the bookmark and the working copy
94084087 Andy McCall ↶ 120
    Log {
121
        /// Show at most this many changes
122
        #[arg(short = 'n', long)]
123
        limit: Option<usize>,
124
        /// Show full messages, dependencies, and signatures
125
        #[arg(short, long)]
126
        verbose: bool,
c6aa5bd9 Andy McCall ↶ 127
        /// Only changes touching this path (renames followed)
128
        #[arg(long)]
129
        path: Option<String>,
130
        #[arg(long)]
131
        json: bool,
94084087 Andy McCall ↶ 132
    },
fce44ae9 Andy McCall ↶ 133
    /// Show what the working copy changes against its base
c6aa5bd9 Andy McCall ↶ 134
    Diff {
135
        /// A change instead of the working copy
136
        change: Option<String>,
137
        #[arg(long)]
138
        json: bool,
139
    },
140
    /// Print a file as it is in a state (default: the working copy's base)
141
    Cat {
142
        path: String,
143
        /// A change, bookmark, or tag
144
        #[arg(short = 'r', long)]
145
        change: Option<String>,
146
    },
147
    /// Print the repository root, workspace, and bookmark
148
    Root {
149
        #[arg(long)]
150
        json: bool,
151
    },
152
    /// Print the version of oxy, its repository format, and protocol
153
    Version {
154
        #[arg(long)]
155
        json: bool,
156
    },
157
    /// Restore paths to the working copy's base, or undo chosen hunks
158
    Discard {
159
        paths: Vec<String>,
160
        /// Hunk indices as `oxy hunks` numbers them, comma separated
161
        #[arg(long, value_delimiter = ',')]
162
        hunks: Vec<usize>,
163
    },
164
    /// List the working copy's hunks, numbered as commit --hunks expects
165
    Hunks {
166
        #[arg(long)]
167
        json: bool,
168
    },
614541c3 Andy McCall ↶ 169
    /// Change the message of a head (default: the head the working copy sits on)
170
    Describe {
171
        #[arg(short, long)]
172
        message: String,
173
        change: Option<String>,
174
    },
23b217d7 Andy McCall ↶ 175
    /// Fold the working copy into the head it sits on, or into CHANGE with --into
614541c3 Andy McCall ↶ 176
    Amend {
177
        #[arg(short, long)]
178
        message: Option<String>,
23b217d7 Andy McCall ↶ 179
        /// Amend an older change; the edits must touch only lines it already knows
180
        #[arg(long)]
181
        into: Option<String>,
614541c3 Andy McCall ↶ 182
    },
23b217d7 Andy McCall ↶ 183
    /// Split a head: the named paths become a separate first change
75167672 Andy McCall 184
    Split { change: String, paths: Vec<String> },
614541c3 Andy McCall ↶ 185
    /// Drop a head from the bookmark; its content stays on disk as uncommitted work
186
    Abandon { change: String },
fce44ae9 Andy McCall ↶ 187
    /// Show one change (ChangeId prefix, #hash prefix, or @)
c6aa5bd9 Andy McCall ↶ 188
    Show {
189
        change: String,
190
        #[arg(long)]
191
        json: bool,
192
    },
fce44ae9 Andy McCall ↶ 193
    /// Move the working copy to the state formed by CHANGE... (default: the bookmark heads)
194
    Checkout {
195
        changes: Vec<String>,
196
        /// Throw away uncommitted working-copy changes
197
        #[arg(long)]
198
        discard: bool,
199
    },
200
    /// Report which conflicts of the working copy's base are resolved by the current files
c6aa5bd9 Andy McCall ↶ 201
    Resolve {
202
        #[arg(long)]
203
        json: bool,
204
    },
fce44ae9 Andy McCall ↶ 205
    /// Undo the last operation
206
    Undo,
252e8cb2 Andy McCall ↶ 207
    /// Write a checkpoint of the current bookmark's state now
208
    Checkpoint,
387335ff Andy McCall ↶ 209
    /// Check the repository's integrity: objects, operation log, references, signatures, checkpoints
210
    Fsck {
211
        #[arg(long)]
212
        json: bool,
213
    },
ba160f60 Andy McCall ↶ 214
    /// Pack loose objects and prune unreachable ones older than the grace period
215
    Gc {
216
        /// Ignore the grace period (core.gc-grace) and prune immediately
217
        #[arg(long)]
218
        now: bool,
219
    },
fce44ae9 Andy McCall ↶ 220
    /// List operations
221
    #[command(name = "op")]
222
    Op {
223
        #[command(subcommand)]
224
        cmd: OpCmd,
225
    },
cdb5beba Andy McCall ↶ 226
    /// Take the advisory local lock on a lockable file, making it writable
227
    Lock { path: String },
228
    /// Release the local lock and make the file read-only again
229
    Unlock { path: String },
4e88e774 Andy McCall ↶ 230
    /// Workspaces: extra checkout directories sharing this repository
231
    Workspace {
232
        #[command(subcommand)]
233
        cmd: WorkspaceCmd,
234
    },
7b9809e6 Andy McCall ↶ 235
    /// Bookmarks: named states; each workspace works on one
236
    Bookmark {
237
        #[command(subcommand)]
238
        cmd: BookmarkCmd,
239
    },
240
    /// Union another bookmark's heads into the current one (no merge change)
241
    Merge { bookmark: String },
4dee277c Andy McCall ↶ 242
    /// Subrepositories: nested repositories pinned to a state
243
    Submodule {
244
        #[command(subcommand)]
245
        cmd: SubmoduleCmd,
246
    },
132b884b Andy McCall ↶ 247
    /// Tags: immutable named states
248
    Tag {
249
        #[command(subcommand)]
250
        cmd: TagCmd,
251
    },
fce44ae9 Andy McCall ↶ 252
    /// Manage remotes
253
    Remote {
254
        #[command(subcommand)]
255
        cmd: RemoteCmd,
256
    },
257
    /// Fetch every bookmark of a remote into its tracking references
c6aa5bd9 Andy McCall ↶ 258
    Fetch {
259
        remote: Option<String>,
260
        #[arg(long)]
261
        json: bool,
262
    },
fce44ae9 Andy McCall ↶ 263
    /// Fetch, then union the remote bookmark into the local one (no merge change)
c6aa5bd9 Andy McCall ↶ 264
    Pull {
265
        remote: Option<String>,
266
        #[arg(long)]
267
        json: bool,
268
    },
fce44ae9 Andy McCall ↶ 269
    /// Push the current bookmark to a remote
270
    Push {
271
        remote: Option<String>,
272
        /// Allow dropping heads the remote has
273
        #[arg(long)]
274
        force: bool,
7b9809e6 Andy McCall ↶ 275
        /// Push this bookmark instead of the workspace's current one
276
        #[arg(short = 'b', long)]
277
        bookmark: Option<String>,
c6aa5bd9 Andy McCall ↶ 278
        #[arg(long)]
279
        json: bool,
fce44ae9 Andy McCall ↶ 280
    },
2b5ee371 Andy McCall ↶ 281
    /// Serve repositories under ROOT over HTTPS (with --tls-cert/--tls-key) or plain HTTP
fce44ae9 Andy McCall ↶ 282
    Serve {
283
        /// Directory with one repository per subdirectory
284
        #[arg(long, default_value = ".")]
285
        root: PathBuf,
286
        #[arg(long, default_value = "127.0.0.1:8890")]
287
        bind: String,
288
        /// Bearer token allowed to push (repeatable); none means open
289
        #[arg(long = "token")]
290
        tokens: Vec<String>,
2b5ee371 Andy McCall ↶ 291
        /// PEM certificate chain; enables HTTPS together with --tls-key
292
        #[arg(long, requires = "tls_key")]
293
        tls_cert: Option<PathBuf>,
294
        /// PEM private key
295
        #[arg(long, requires = "tls_cert")]
296
        tls_key: Option<PathBuf>,
fce44ae9 Andy McCall ↶ 297
    },
298
    /// Manage the signing identity
299
    Identity {
300
        #[command(subcommand)]
301
        cmd: IdentityCmd,
302
    },
05e71ed6 Andy McCall ↶ 303
    /// Seal changes with your key: named ones, or every change on the current bookmark you can authenticate but have not
304
    Seal {
305
        changes: Vec<String>,
306
        #[arg(long)]
307
        json: bool,
308
    },
fce44ae9 Andy McCall ↶ 309
    /// Verify the signatures on a change (default: every change in the log)
c6aa5bd9 Andy McCall ↶ 310
    Verify {
311
        change: Option<String>,
312
        #[arg(long)]
313
        json: bool,
314
    },
79ab8528 Andy McCall ↶ 315
    /// Git interoperability
316
    Git {
317
        #[command(subcommand)]
318
        cmd: GitCmd,
319
    },
83e0677b Andy McCall ↶ 320
    /// Manage the trust policy (who may update which bookmarks)
321
    Policy {
322
        #[command(subcommand)]
323
        cmd: PolicyCmd,
324
    },
7421309f Andy McCall ↶ 325
    /// 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)
fce44ae9 Andy McCall ↶ 326
    Config {
327
        key: String,
328
        value: String,
329
        /// Write to the user file (~/.config/oxy/config.toml) instead of this repository
330
        #[arg(long)]
331
        user: bool,
332
    },
333
}
334
335
#[derive(Subcommand)]
336
enum OpCmd {
c6aa5bd9 Andy McCall ↶ 337
    Log {
338
        #[arg(long)]
339
        json: bool,
340
    },
fce44ae9 Andy McCall ↶ 341
}
342
343
#[derive(Subcommand)]
4e88e774 Andy McCall ↶ 344
enum WorkspaceCmd {
345
    /// Create a workspace at PATH checked out on CHANGE... (default: the bookmark heads)
346
    Add { path: PathBuf, changes: Vec<String> },
1196b29b Andy McCall ↶ 347
    List {
348
        #[arg(long)]
349
        json: bool,
350
    },
4e88e774 Andy McCall ↶ 351
    /// Stop tracking a workspace (by id prefix or path); its files stay
352
    Forget { workspace: String },
353
}
354
355
#[derive(Subcommand)]
957fee30 Andy McCall ↶ 356
enum BisectCmd {
357
    /// Begin; remembers where the working copy was
358
    Start,
359
    /// Mark CHANGE... (or the state under test) as good
360
    Good { changes: Vec<String> },
361
    /// Mark CHANGE... (or the state under test) as bad
362
    Bad { changes: Vec<String> },
363
    /// The state under test cannot be tested; try another
364
    Skip,
365
    /// Run a command for each step: exit 0 good, 125 skip, else bad
366
    Run {
367
        #[arg(required = true, trailing_var_arg = true)]
368
        command: Vec<String>,
369
    },
370
    /// Where the search stands
c6aa5bd9 Andy McCall ↶ 371
    Status {
372
        #[arg(long)]
373
        json: bool,
374
    },
957fee30 Andy McCall ↶ 375
    /// Restore the working copy and forget the search
376
    Reset,
377
}
378
379
#[derive(Subcommand)]
7b9809e6 Andy McCall ↶ 380
enum BookmarkCmd {
381
    /// Create a bookmark at CHANGE... (default: the working copy's base)
382
    Create { name: String, changes: Vec<String> },
383
    /// Point an existing bookmark at CHANGE...
384
    Set { name: String, changes: Vec<String> },
385
    /// Remove a bookmark; its changes stay
386
    Delete { name: String },
387
    /// List bookmarks with their heads and difference from origin
c6aa5bd9 Andy McCall ↶ 388
    List {
389
        #[arg(long)]
390
        json: bool,
391
    },
7b9809e6 Andy McCall ↶ 392
    /// Make NAME this workspace's bookmark and check it out
393
    Switch {
394
        name: String,
395
        /// Throw away uncommitted working-copy changes
396
        #[arg(long)]
397
        discard: bool,
398
    },
399
}
400
401
#[derive(Subcommand)]
4dee277c Andy McCall ↶ 402
enum SubmoduleCmd {
403
    /// Clone URL (Oxygen or Git) into PATH and register it in .oxymodules
404
    Add {
405
        url: String,
406
        path: String,
407
        /// Bookmark (Oxygen) or branch (Git) to check out and track
408
        #[arg(long)]
409
        bookmark: Option<String>,
410
    },
411
    /// Bring nested repositories to their pins
412
    Update {
413
        /// Clone nested repositories that are absent
414
        #[arg(long)]
415
        init: bool,
416
        paths: Vec<String>,
417
    },
418
    /// List subrepos with their pins and on-disk state
c6aa5bd9 Andy McCall ↶ 419
    Status {
420
        #[arg(long)]
421
        json: bool,
422
    },
4dee277c Andy McCall ↶ 423
    /// Unregister PATH; the directory stays, the node goes with the next commit
424
    Remove { path: String },
425
}
426
427
#[derive(Subcommand)]
132b884b Andy McCall ↶ 428
enum TagCmd {
429
    /// Tag the state of CHANGE... (default: the bookmark heads)
430
    Create {
431
        name: String,
432
        changes: Vec<String>,
433
        #[arg(short, long, default_value = "")]
434
        message: String,
435
    },
1196b29b Andy McCall ↶ 436
    List {
437
        #[arg(long)]
438
        json: bool,
439
    },
132b884b Andy McCall ↶ 440
}
441
442
#[derive(Subcommand)]
fce44ae9 Andy McCall ↶ 443
enum RemoteCmd {
75167672 Andy McCall 444
    Add {
445
        name: String,
446
        url: String,
447
    },
448
    Remove {
449
        name: String,
450
    },
c6aa5bd9 Andy McCall ↶ 451
    List {
452
        #[arg(long)]
453
        json: bool,
454
    },
fce44ae9 Andy McCall ↶ 455
}
456
457
#[derive(Subcommand)]
458
enum IdentityCmd {
459
    /// Generate a composite Ed25519 + ML-DSA-65 key pair and make it your author identity
460
    Create {
461
        #[arg(long)]
462
        name: Option<String>,
463
        #[arg(long)]
464
        email: Option<String>,
465
        /// Record the author in this repository only, not in the user config
466
        #[arg(long)]
467
        local: bool,
28fc23d7 Andy McCall ↶ 468
        /// Expire the identity this many days from now
469
        #[arg(long)]
470
        expires: Option<u64>,
fce44ae9 Andy McCall ↶ 471
    },
472
    /// Show the configured identity
473
    Show,
83e0677b Andy McCall ↶ 474
    /// Write your identity object to a file, to hand to a policy holder
475
    Export { path: Option<PathBuf> },
7aa2dc93 Andy McCall ↶ 476
    /// Sign a host's challenge (hex, at least 16 bytes) to prove you hold your key; prints the signature as hex
477
    SignChallenge {
478
        challenge: String,
479
        #[arg(long)]
480
        json: bool,
481
    },
28fc23d7 Andy McCall ↶ 482
    /// Move to a fresh key pair; the policy follows if it names your key
483
    Rotate,
484
    /// Re-issue your identity with a new expiry (in days from now; 0 = never)
485
    Renew {
486
        #[arg(long)]
487
        expires: u64,
488
    },
489
    /// Revoke a key: yours by default, or any key if you hold a policy root
490
    Revoke {
491
        /// KeyId prefix (default: your own key)
492
        key: Option<String>,
493
        #[arg(long, default_value = "")]
494
        reason: String,
495
    },
496
    /// Keys allowed to sign or seal on your behalf
497
    Delegate {
498
        #[command(subcommand)]
499
        cmd: DelegateCmd,
500
    },
501
}
502
503
#[derive(Subcommand)]
504
enum DelegateCmd {
505
    /// Add a delegate from an exported identity file
506
    Add { identity_file: PathBuf },
507
    /// Remove a delegate by KeyId prefix
508
    Remove { key: String },
509
    /// List delegates
510
    List,
fce44ae9 Andy McCall ↶ 511
}
512
83e0677b Andy McCall ↶ 513
#[derive(Subcommand)]
79ab8528 Andy McCall ↶ 514
enum GitCmd {
515
    /// Export the current bookmark to a Git repository (created bare if absent)
30ea9c1e Andy McCall ↶ 516
    Export {
517
        git_dir: PathBuf,
518
        /// Git branch to write (default: the bookmark name)
519
        #[arg(long)]
520
        branch: Option<String>,
521
    },
522
    /// Fetch a branch from a Git remote (path or URL) into the current bookmark
523
    Fetch {
524
        url: String,
525
        #[arg(long, default_value = "main")]
526
        branch: String,
527
    },
528
    /// Push the current bookmark to a branch of a Git remote (fetch first)
529
    Push {
530
        url: String,
531
        #[arg(long, default_value = "main")]
532
        branch: String,
533
    },
c3069537 Andy McCall ↶ 534
    /// Import a Git branch into a new Oxygen repository at DEST
535
    Import {
536
        git_dir: PathBuf,
537
        dest: PathBuf,
538
        #[arg(long, default_value = "main")]
539
        branch: String,
540
    },
79ab8528 Andy McCall ↶ 541
}
542
543
#[derive(Subcommand)]
83e0677b Andy McCall ↶ 544
enum PolicyCmd {
545
    /// Create the initial policy with your identity as the only root
546
    Init,
547
    /// Show the current policy
c6aa5bd9 Andy McCall ↶ 548
    Show {
549
        #[arg(long)]
550
        json: bool,
551
    },
83e0677b Andy McCall ↶ 552
    /// Allow an identity (from `oxy identity export`) to update bookmarks
553
    Grant {
554
        identity_file: PathBuf,
555
        /// Reference pattern the grant applies to (default: every bookmark)
556
        #[arg(long)]
557
        pattern: Option<String>,
28fc23d7 Andy McCall ↶ 558
        /// Where to write the proposal when more root signatures are needed
559
        #[arg(long)]
560
        out: Option<PathBuf>,
83e0677b Andy McCall ↶ 561
    },
562
    /// Revoke a key (KeyId prefix of an identity in the policy)
28fc23d7 Andy McCall ↶ 563
    Revoke {
564
        key: String,
565
        /// Where to write the proposal when more root signatures are needed
566
        #[arg(long)]
567
        out: Option<PathBuf>,
568
    },
569
    /// Manage root keys and the signature threshold for policy updates
570
    Root {
571
        #[command(subcommand)]
572
        cmd: RootCmd,
573
    },
574
    /// Add your root signature to a proposal file; applies it once the threshold is met
575
    Sign {
576
        proposal: PathBuf,
577
        /// Where to write the proposal if it still needs signatures (default: in place)
578
        #[arg(long)]
579
        out: Option<PathBuf>,
580
    },
581
}
582
583
#[derive(Subcommand)]
584
enum RootCmd {
585
    /// Make an exported identity a root key
586
    Add {
587
        identity_file: PathBuf,
588
        #[arg(long)]
589
        out: Option<PathBuf>,
590
    },
591
    /// Remove a root key by KeyId prefix
592
    Remove {
593
        key: String,
594
        #[arg(long)]
595
        out: Option<PathBuf>,
596
    },
597
    /// Require this many root signatures on policy updates
598
    Threshold {
599
        n: u32,
600
        #[arg(long)]
601
        out: Option<PathBuf>,
602
    },
83e0677b Andy McCall ↶ 603
}
604
fce44ae9 Andy McCall ↶ 605
/// Open the repository containing the current directory.
606
fn open() -> Result<Repo> {
607
    Repo::open(&std::env::current_dir()?).context("opening repository")
608
}
609
610
/// Parse arguments and run; returns the process exit code.
611
pub fn run() -> Result<i32> {
612
    let cli = Cli::parse();
135212de Andy McCall ↶ 613
    ui::init(cli.plain);
fce44ae9 Andy McCall ↶ 614
    match cli.cmd {
e02bf0cd Andy McCall ↶ 615
        Cmd::Init { path, bare } => workspace::init(path, bare),
50a046e4 Andy McCall ↶ 616
        Cmd::Status { json } => workspace::status(json),
75167672 Andy McCall 617
        Cmd::Prompt {
618
            format,
619
            glyphs,
620
            color,
621
        } => workspace::prompt(&format, glyphs, color.as_deref()),
622
        Cmd::Commit {
623
            message,
624
            depends_on,
625
            paths,
626
            interactive,
627
            hunks,
628
        } => workspace::commit(&message, &depends_on, &paths, interactive, &hunks),
c6aa5bd9 Andy McCall ↶ 629
        Cmd::Cat { path, change } => workspace::cat(&path, change.as_deref()),
630
        Cmd::Root { json } => workspace::root(json),
631
        Cmd::Version { json } => workspace::version(json),
632
        Cmd::Discard { paths, hunks } => workspace::discard(&paths, &hunks),
633
        Cmd::Hunks { json } => workspace::hunks(json),
728da0f9 Andy McCall ↶ 634
        Cmd::Squash { change, into } => workspace::squash(&change, &into),
635
        Cmd::Rebase { change, onto } => workspace::rebase(&change, &onto),
957fee30 Andy McCall ↶ 636
        Cmd::Blame { path, json } => workspace::blame(&path, json),
637
        Cmd::Bisect { cmd } => match cmd {
638
            BisectCmd::Start => workspace::bisect_start(),
639
            BisectCmd::Good { changes } => workspace::bisect_mark(Some(true), &changes),
640
            BisectCmd::Bad { changes } => workspace::bisect_mark(Some(false), &changes),
641
            BisectCmd::Skip => workspace::bisect_mark(None, &[]),
642
            BisectCmd::Run { command } => workspace::bisect_run(&command),
c6aa5bd9 Andy McCall ↶ 643
            BisectCmd::Status { json } => workspace::bisect_status(json),
957fee30 Andy McCall ↶ 644
            BisectCmd::Reset => workspace::bisect_reset(),
645
        },
75167672 Andy McCall 646
        Cmd::Log {
647
            limit,
648
            verbose,
649
            path,
650
            json,
651
        } => workspace::log(limit, verbose, path.as_deref(), json),
c6aa5bd9 Andy McCall ↶ 652
        Cmd::Diff { change, json } => workspace::diff(change.as_deref(), json),
614541c3 Andy McCall ↶ 653
        Cmd::Describe { message, change } => workspace::describe(&message, change.as_deref()),
23b217d7 Andy McCall ↶ 654
        Cmd::Amend { message, into } => workspace::amend(message.as_deref(), into.as_deref()),
655
        Cmd::Split { change, paths } => workspace::split(&change, &paths),
614541c3 Andy McCall ↶ 656
        Cmd::Abandon { change } => workspace::abandon(&change),
c6aa5bd9 Andy McCall ↶ 657
        Cmd::Show { change, json } => workspace::show(&change, json),
fce44ae9 Andy McCall ↶ 658
        Cmd::Checkout { changes, discard } => workspace::checkout(&changes, discard),
c6aa5bd9 Andy McCall ↶ 659
        Cmd::Resolve { json } => workspace::resolve(json),
fce44ae9 Andy McCall ↶ 660
        Cmd::Undo => workspace::undo(),
ba160f60 Andy McCall ↶ 661
        Cmd::Gc { now } => workspace::gc(now),
387335ff Andy McCall ↶ 662
        Cmd::Fsck { json } => workspace::fsck(json),
252e8cb2 Andy McCall ↶ 663
        Cmd::Checkpoint => workspace::checkpoint(),
75167672 Andy McCall 664
        Cmd::Op {
665
            cmd: OpCmd::Log { json },
666
        } => workspace::op_log(json),
667
        Cmd::Clone {
668
            url,
669
            dest,
670
            max_blob,
671
            sparse,
672
            depth,
673
        } => sync::clone(&url, dest, max_blob.as_deref(), &sparse, depth),
2863456a Andy McCall ↶ 674
        Cmd::Deepen { depth, remote } => sync::deepen(&remote, depth),
7421309f Andy McCall ↶ 675
        Cmd::Sparse { paths } => workspace::sparse(&paths),
cdb5beba Andy McCall ↶ 676
        Cmd::Lock { path } => workspace::lock(&path),
677
        Cmd::Unlock { path } => workspace::unlock(&path),
4e88e774 Andy McCall ↶ 678
        Cmd::Workspace { cmd } => match cmd {
679
            WorkspaceCmd::Add { path, changes } => workspace::workspace_add(&path, &changes),
1196b29b Andy McCall ↶ 680
            WorkspaceCmd::List { json } => workspace::workspace_list(json),
4e88e774 Andy McCall ↶ 681
            WorkspaceCmd::Forget { workspace } => workspace::workspace_forget(&workspace),
682
        },
7b9809e6 Andy McCall ↶ 683
        Cmd::Bookmark { cmd } => match cmd {
684
            BookmarkCmd::Create { name, changes } => workspace::bookmark_create(&name, &changes),
685
            BookmarkCmd::Set { name, changes } => workspace::bookmark_set(&name, &changes),
686
            BookmarkCmd::Delete { name } => workspace::bookmark_delete(&name),
c6aa5bd9 Andy McCall ↶ 687
            BookmarkCmd::List { json } => workspace::bookmark_list(json),
7b9809e6 Andy McCall ↶ 688
            BookmarkCmd::Switch { name, discard } => workspace::bookmark_switch(&name, discard),
689
        },
690
        Cmd::Merge { bookmark } => workspace::merge(&bookmark),
4dee277c Andy McCall ↶ 691
        Cmd::Submodule { cmd } => match cmd {
75167672 Andy McCall 692
            SubmoduleCmd::Add {
693
                url,
694
                path,
695
                bookmark,
696
            } => sync::submodule_add(&url, &path, bookmark.as_deref()),
4dee277c Andy McCall ↶ 697
            SubmoduleCmd::Update { init, paths } => sync::submodule_update(init, &paths),
c6aa5bd9 Andy McCall ↶ 698
            SubmoduleCmd::Status { json } => sync::submodule_status(json),
4dee277c Andy McCall ↶ 699
            SubmoduleCmd::Remove { path } => sync::submodule_remove(&path),
700
        },
132b884b Andy McCall ↶ 701
        Cmd::Tag { cmd } => match cmd {
75167672 Andy McCall 702
            TagCmd::Create {
703
                name,
704
                changes,
705
                message,
706
            } => workspace::tag_create(&name, &changes, &message),
1196b29b Andy McCall ↶ 707
            TagCmd::List { json } => workspace::tag_list(json),
132b884b Andy McCall ↶ 708
        },
fce44ae9 Andy McCall ↶ 709
        Cmd::Remote { cmd } => match cmd {
710
            RemoteCmd::Add { name, url } => sync::remote_add(&name, &url),
711
            RemoteCmd::Remove { name } => sync::remote_remove(&name),
c6aa5bd9 Andy McCall ↶ 712
            RemoteCmd::List { json } => sync::remote_list(json),
fce44ae9 Andy McCall ↶ 713
        },
c6aa5bd9 Andy McCall ↶ 714
        Cmd::Fetch { remote, json } => sync::fetch(remote.as_deref(), json),
715
        Cmd::Pull { remote, json } => sync::pull(remote.as_deref(), json),
75167672 Andy McCall 716
        Cmd::Push {
717
            remote,
718
            force,
719
            bookmark,
720
            json,
721
        } => sync::push(remote.as_deref(), force, bookmark.as_deref(), json),
722
        Cmd::Serve {
723
            root,
724
            bind,
725
            tokens,
726
            tls_cert,
727
            tls_key,
728
        } => sync::serve(root, &bind, tokens, tls_cert.zip(tls_key)),
fce44ae9 Andy McCall ↶ 729
        Cmd::Identity { cmd } => match cmd {
75167672 Andy McCall 730
            IdentityCmd::Create {
731
                name,
732
                email,
733
                local,
734
                expires,
735
            } => identity::create(name, email, local, expires),
fce44ae9 Andy McCall ↶ 736
            IdentityCmd::Show => identity::show(),
83e0677b Andy McCall ↶ 737
            IdentityCmd::Export { path } => identity::export(path),
7aa2dc93 Andy McCall ↶ 738
            IdentityCmd::SignChallenge { challenge, json } => identity::sign_challenge(&challenge, json),
28fc23d7 Andy McCall ↶ 739
            IdentityCmd::Rotate => identity::rotate(),
740
            IdentityCmd::Renew { expires } => identity::renew(expires),
741
            IdentityCmd::Revoke { key, reason } => identity::revoke(key.as_deref(), &reason),
742
            IdentityCmd::Delegate { cmd } => match cmd {
743
                DelegateCmd::Add { identity_file } => identity::delegate_add(&identity_file),
744
                DelegateCmd::Remove { key } => identity::delegate_remove(&key),
745
                DelegateCmd::List => identity::delegate_list(),
746
            },
83e0677b Andy McCall ↶ 747
        },
75167672 Andy McCall 748
        Cmd::Git {
749
            cmd: GitCmd::Export { git_dir, branch },
750
        } => sync::git_export(&git_dir, branch.as_deref()),
751
        Cmd::Git {
752
            cmd: GitCmd::Fetch { url, branch },
753
        } => sync::git_fetch(&url, &branch),
754
        Cmd::Git {
755
            cmd: GitCmd::Push { url, branch },
756
        } => sync::git_push(&url, &branch),
757
        Cmd::Git {
758
            cmd:
759
                GitCmd::Import {
760
                    git_dir,
761
                    dest,
762
                    branch,
763
                },
764
        } => sync::git_import(&git_dir, &dest, &branch),
83e0677b Andy McCall ↶ 765
        Cmd::Policy { cmd } => match cmd {
766
            PolicyCmd::Init => identity::policy_init(),
c6aa5bd9 Andy McCall ↶ 767
            PolicyCmd::Show { json } => identity::policy_show(json),
75167672 Andy McCall 768
            PolicyCmd::Grant {
769
                identity_file,
770
                pattern,
771
                out,
772
            } => identity::policy_grant(&identity_file, pattern.as_deref(), out),
28fc23d7 Andy McCall ↶ 773
            PolicyCmd::Revoke { key, out } => identity::policy_revoke(&key, out),
774
            PolicyCmd::Root { cmd } => match cmd {
75167672 Andy McCall 775
                RootCmd::Add { identity_file, out } => {
776
                    identity::policy_root_add(&identity_file, out)
777
                }
28fc23d7 Andy McCall ↶ 778
                RootCmd::Remove { key, out } => identity::policy_root_remove(&key, out),
779
                RootCmd::Threshold { n, out } => identity::policy_threshold(n, out),
780
            },
781
            PolicyCmd::Sign { proposal, out } => identity::policy_sign(&proposal, out),
fce44ae9 Andy McCall ↶ 782
        },
05e71ed6 Andy McCall ↶ 783
        Cmd::Seal { changes, json } => identity::seal(&changes, json),
c6aa5bd9 Andy McCall ↶ 784
        Cmd::Verify { change, json } => identity::verify(change.as_deref(), json),
fce44ae9 Andy McCall ↶ 785
        Cmd::Config { key, value, user } => identity::config(&key, &value, user),
786
    }
787
}