OXYGENHUB
# 252e8cb23fbc oxygen / src / cli / mod.rs
8.7 KiB · Rust · last changed Checkpoints: serialized states per bookmark, automatic every core.checkpoint-interval changes, oxy checkpoint, checkpoint-accelerated load_state; states keep change metadata only 1 month ago source blame history raw
fce44ae9 Andy McCall ↶ 1
//! Command-line definitions and dispatch.
2
3
mod format;
4
mod identity;
5
mod sync;
6
mod workspace;
7
8
use anyhow::{Context, Result};
9
use clap::{Parser, Subcommand};
10
use oxy_core::repo::Repo;
11
use std::path::PathBuf;
12
13
#[derive(Parser)]
14
#[command(name = "oxy", version, about = "Oxygen: breathing fresh air into version control")]
15
struct Cli {
16
    #[command(subcommand)]
17
    cmd: Cmd,
18
}
19
20
#[derive(Subcommand)]
21
enum Cmd {
22
    /// Create a repository in PATH (default: current directory)
23
    Init { path: Option<PathBuf> },
24
    /// Clone a repository from a local path or an http(s) URL
25
    Clone { url: String, dest: Option<PathBuf> },
26
    /// Show the working copy: changed paths and conflicts
27
    Status,
28
    /// Record the working copy as a change on the current bookmark
29
    Commit {
30
        #[arg(short, long)]
31
        message: String,
32
        /// Extra explicit dependencies (ChangeId prefix, #hash prefix)
33
        #[arg(long = "depends-on")]
34
        depends_on: Vec<String>,
35
    },
36
    /// List changes reachable from the bookmark and the working copy
37
    Log,
38
    /// Show what the working copy changes against its base
39
    Diff,
614541c3 Andy McCall ↶ 40
    /// Change the message of a head (default: the head the working copy sits on)
41
    Describe {
42
        #[arg(short, long)]
43
        message: String,
44
        change: Option<String>,
45
    },
46
    /// Fold the working copy into the head it sits on
47
    Amend {
48
        #[arg(short, long)]
49
        message: Option<String>,
50
    },
51
    /// Drop a head from the bookmark; its content stays on disk as uncommitted work
52
    Abandon { change: String },
fce44ae9 Andy McCall ↶ 53
    /// Show one change (ChangeId prefix, #hash prefix, or @)
54
    Show { change: String },
55
    /// Move the working copy to the state formed by CHANGE... (default: the bookmark heads)
56
    Checkout {
57
        changes: Vec<String>,
58
        /// Throw away uncommitted working-copy changes
59
        #[arg(long)]
60
        discard: bool,
61
    },
62
    /// Report which conflicts of the working copy's base are resolved by the current files
63
    Resolve,
64
    /// Undo the last operation
65
    Undo,
252e8cb2 Andy McCall ↶ 66
    /// Write a checkpoint of the current bookmark's state now
67
    Checkpoint,
ba160f60 Andy McCall ↶ 68
    /// Pack loose objects and prune unreachable ones older than the grace period
69
    Gc {
70
        /// Ignore the grace period (core.gc-grace) and prune immediately
71
        #[arg(long)]
72
        now: bool,
73
    },
fce44ae9 Andy McCall ↶ 74
    /// List operations
75
    #[command(name = "op")]
76
    Op {
77
        #[command(subcommand)]
78
        cmd: OpCmd,
79
    },
80
    /// Manage remotes
81
    Remote {
82
        #[command(subcommand)]
83
        cmd: RemoteCmd,
84
    },
85
    /// Fetch every bookmark of a remote into its tracking references
86
    Fetch { remote: Option<String> },
87
    /// Fetch, then union the remote bookmark into the local one (no merge change)
88
    Pull { remote: Option<String> },
89
    /// Push the current bookmark to a remote
90
    Push {
91
        remote: Option<String>,
92
        /// Allow dropping heads the remote has
93
        #[arg(long)]
94
        force: bool,
95
    },
2b5ee371 Andy McCall ↶ 96
    /// Serve repositories under ROOT over HTTPS (with --tls-cert/--tls-key) or plain HTTP
fce44ae9 Andy McCall ↶ 97
    Serve {
98
        /// Directory with one repository per subdirectory
99
        #[arg(long, default_value = ".")]
100
        root: PathBuf,
101
        #[arg(long, default_value = "127.0.0.1:8890")]
102
        bind: String,
103
        /// Bearer token allowed to push (repeatable); none means open
104
        #[arg(long = "token")]
105
        tokens: Vec<String>,
2b5ee371 Andy McCall ↶ 106
        /// PEM certificate chain; enables HTTPS together with --tls-key
107
        #[arg(long, requires = "tls_key")]
108
        tls_cert: Option<PathBuf>,
109
        /// PEM private key
110
        #[arg(long, requires = "tls_cert")]
111
        tls_key: Option<PathBuf>,
fce44ae9 Andy McCall ↶ 112
    },
113
    /// Manage the signing identity
114
    Identity {
115
        #[command(subcommand)]
116
        cmd: IdentityCmd,
117
    },
118
    /// Verify the signatures on a change (default: every change in the log)
119
    Verify { change: Option<String> },
79ab8528 Andy McCall ↶ 120
    /// Git interoperability
121
    Git {
122
        #[command(subcommand)]
123
        cmd: GitCmd,
124
    },
83e0677b Andy McCall ↶ 125
    /// Manage the trust policy (who may update which bookmarks)
126
    Policy {
127
        #[command(subcommand)]
128
        cmd: PolicyCmd,
129
    },
252e8cb2 Andy McCall ↶ 130
    /// Set a configuration key (author.*, core.sign, core.op-retention, core.gc-grace, core.checkpoint-interval, workspace.bookmark, remote.<name>.url|token|ca)
fce44ae9 Andy McCall ↶ 131
    Config {
132
        key: String,
133
        value: String,
134
        /// Write to the user file (~/.config/oxy/config.toml) instead of this repository
135
        #[arg(long)]
136
        user: bool,
137
    },
138
}
139
140
#[derive(Subcommand)]
141
enum OpCmd {
142
    Log,
143
}
144
145
#[derive(Subcommand)]
146
enum RemoteCmd {
147
    Add { name: String, url: String },
148
    Remove { name: String },
149
    List,
150
}
151
152
#[derive(Subcommand)]
153
enum IdentityCmd {
154
    /// Generate a composite Ed25519 + ML-DSA-65 key pair and make it your author identity
155
    Create {
156
        #[arg(long)]
157
        name: Option<String>,
158
        #[arg(long)]
159
        email: Option<String>,
160
        /// Record the author in this repository only, not in the user config
161
        #[arg(long)]
162
        local: bool,
163
    },
164
    /// Show the configured identity
165
    Show,
83e0677b Andy McCall ↶ 166
    /// Write your identity object to a file, to hand to a policy holder
167
    Export { path: Option<PathBuf> },
fce44ae9 Andy McCall ↶ 168
}
169
83e0677b Andy McCall ↶ 170
#[derive(Subcommand)]
79ab8528 Andy McCall ↶ 171
enum GitCmd {
172
    /// Export the current bookmark to a Git repository (created bare if absent)
173
    Export { git_dir: PathBuf },
c3069537 Andy McCall ↶ 174
    /// Import a Git branch into a new Oxygen repository at DEST
175
    Import {
176
        git_dir: PathBuf,
177
        dest: PathBuf,
178
        #[arg(long, default_value = "main")]
179
        branch: String,
180
    },
79ab8528 Andy McCall ↶ 181
}
182
183
#[derive(Subcommand)]
83e0677b Andy McCall ↶ 184
enum PolicyCmd {
185
    /// Create the initial policy with your identity as the only root
186
    Init,
187
    /// Show the current policy
188
    Show,
189
    /// Allow an identity (from `oxy identity export`) to update bookmarks
190
    Grant {
191
        identity_file: PathBuf,
192
        /// Reference pattern the grant applies to (default: every bookmark)
193
        #[arg(long)]
194
        pattern: Option<String>,
195
    },
196
    /// Revoke a key (KeyId prefix of an identity in the policy)
197
    Revoke { key: String },
198
}
199
fce44ae9 Andy McCall ↶ 200
/// Open the repository containing the current directory.
201
fn open() -> Result<Repo> {
202
    Repo::open(&std::env::current_dir()?).context("opening repository")
203
}
204
205
/// Parse arguments and run; returns the process exit code.
206
pub fn run() -> Result<i32> {
207
    let cli = Cli::parse();
208
    match cli.cmd {
209
        Cmd::Init { path } => workspace::init(path),
210
        Cmd::Status => workspace::status(),
211
        Cmd::Commit { message, depends_on } => workspace::commit(&message, &depends_on),
212
        Cmd::Log => workspace::log(),
213
        Cmd::Diff => workspace::diff(),
614541c3 Andy McCall ↶ 214
        Cmd::Describe { message, change } => workspace::describe(&message, change.as_deref()),
215
        Cmd::Amend { message } => workspace::amend(message.as_deref()),
216
        Cmd::Abandon { change } => workspace::abandon(&change),
fce44ae9 Andy McCall ↶ 217
        Cmd::Show { change } => workspace::show(&change),
218
        Cmd::Checkout { changes, discard } => workspace::checkout(&changes, discard),
219
        Cmd::Resolve => workspace::resolve(),
220
        Cmd::Undo => workspace::undo(),
ba160f60 Andy McCall ↶ 221
        Cmd::Gc { now } => workspace::gc(now),
252e8cb2 Andy McCall ↶ 222
        Cmd::Checkpoint => workspace::checkpoint(),
fce44ae9 Andy McCall ↶ 223
        Cmd::Op { cmd: OpCmd::Log } => workspace::op_log(),
224
        Cmd::Clone { url, dest } => sync::clone(&url, dest),
225
        Cmd::Remote { cmd } => match cmd {
226
            RemoteCmd::Add { name, url } => sync::remote_add(&name, &url),
227
            RemoteCmd::Remove { name } => sync::remote_remove(&name),
228
            RemoteCmd::List => sync::remote_list(),
229
        },
230
        Cmd::Fetch { remote } => sync::fetch(remote.as_deref()),
231
        Cmd::Pull { remote } => sync::pull(remote.as_deref()),
232
        Cmd::Push { remote, force } => sync::push(remote.as_deref(), force),
2b5ee371 Andy McCall ↶ 233
        Cmd::Serve { root, bind, tokens, tls_cert, tls_key } => sync::serve(root, &bind, tokens, tls_cert.zip(tls_key)),
fce44ae9 Andy McCall ↶ 234
        Cmd::Identity { cmd } => match cmd {
235
            IdentityCmd::Create { name, email, local } => identity::create(name, email, local),
236
            IdentityCmd::Show => identity::show(),
83e0677b Andy McCall ↶ 237
            IdentityCmd::Export { path } => identity::export(path),
238
        },
79ab8528 Andy McCall ↶ 239
        Cmd::Git { cmd: GitCmd::Export { git_dir } } => sync::git_export(&git_dir),
c3069537 Andy McCall ↶ 240
        Cmd::Git { cmd: GitCmd::Import { git_dir, dest, branch } } => sync::git_import(&git_dir, &dest, &branch),
83e0677b Andy McCall ↶ 241
        Cmd::Policy { cmd } => match cmd {
242
            PolicyCmd::Init => identity::policy_init(),
243
            PolicyCmd::Show => identity::policy_show(),
244
            PolicyCmd::Grant { identity_file, pattern } => identity::policy_grant(&identity_file, pattern.as_deref()),
245
            PolicyCmd::Revoke { key } => identity::policy_revoke(&key),
fce44ae9 Andy McCall ↶ 246
        },
247
        Cmd::Verify { change } => identity::verify(change.as_deref()),
248
        Cmd::Config { key, value, user } => identity::config(&key, &value, user),
249
    }
250
}