1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! # `nk git` subcommands — Graph-Native Git Operations
//!
//! ## GRAPH MINDSET: NO FILES, NO SERIALIZATION TO DISK
//!
//! These commands operate on in-memory Arrow RecordBatches. Push/pull
//! transfers graph state as Parquet bytes over NATS — no files touched.
//! This works for ANY Arrow graph: being knowledge, code objects, research.
//!
//! Commands:
//! - `nk git push` — send local graph state to remote
//! - `nk git pull` — receive remote graph state
//! - `nk git clone` — first-time download of remote store
//! - `nk git log` — show commit history
//! - `nk git blame` — per-triple provenance
//! - `nk git rebase` — replay commits onto new base
use clap::Subcommand;
/// Git subcommands — graph-native versioning.
#[derive(Subcommand)]
pub enum GitCommands {
/// Push local graph state to the remote NATS server
Push {
/// Store path (default: .nusy-arrow)
#[arg(long, default_value = ".nusy-arrow")]
store: String,
},
/// Pull remote graph state from the NATS server
Pull {
/// Store path (default: .nusy-arrow)
#[arg(long, default_value = ".nusy-arrow")]
store: String,
},
/// Clone a remote graph store (first-time download)
Clone {
/// Store path to write to (default: .nusy-arrow)
#[arg(long, default_value = ".nusy-arrow")]
store: String,
},
/// Show commit history
Log {
/// Maximum number of commits to show (0 = all)
#[arg(long, default_value = "20")]
limit: usize,
/// Store path
#[arg(long, default_value = ".nusy-arrow")]
store: String,
},
/// Show per-triple provenance (who added what and when)
Blame {
/// Maximum commits to walk (0 = all)
#[arg(long, default_value = "0")]
limit: usize,
/// Store path
#[arg(long, default_value = ".nusy-arrow")]
store: String,
},
/// Rebase commits onto a new base
Rebase {
/// Old base commit ID (exclusive)
start: String,
/// Tip to rebase (inclusive)
end: String,
/// New base to replay onto
onto: String,
/// Store path
#[arg(long, default_value = ".nusy-arrow")]
store: String,
},
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Parser)]
struct TestCli {
#[command(subcommand)]
command: GitCommands,
}
#[test]
fn test_parse_push() {
let cli = TestCli::parse_from(["test", "push"]);
assert!(matches!(cli.command, GitCommands::Push { .. }));
}
#[test]
fn test_parse_pull() {
let cli = TestCli::parse_from(["test", "pull"]);
assert!(matches!(cli.command, GitCommands::Pull { .. }));
}
#[test]
fn test_parse_clone() {
let cli = TestCli::parse_from(["test", "clone"]);
assert!(matches!(cli.command, GitCommands::Clone { .. }));
}
#[test]
fn test_parse_log_with_limit() {
let cli = TestCli::parse_from(["test", "log", "--limit", "5"]);
match cli.command {
GitCommands::Log { limit, .. } => assert_eq!(limit, 5),
_ => panic!("expected Log"),
}
}
#[test]
fn test_parse_blame() {
let cli = TestCli::parse_from(["test", "blame"]);
assert!(matches!(cli.command, GitCommands::Blame { .. }));
}
#[test]
fn test_parse_rebase() {
let cli = TestCli::parse_from(["test", "rebase", "abc", "def", "ghi"]);
match cli.command {
GitCommands::Rebase {
start, end, onto, ..
} => {
assert_eq!(start, "abc");
assert_eq!(end, "def");
assert_eq!(onto, "ghi");
}
_ => panic!("expected Rebase"),
}
}
#[test]
fn test_default_store_path() {
let cli = TestCli::parse_from(["test", "push"]);
match cli.command {
GitCommands::Push { store } => assert_eq!(store, ".nusy-arrow"),
_ => panic!("expected Push"),
}
}
#[test]
fn test_custom_store_path() {
let cli = TestCli::parse_from(["test", "push", "--store", "/custom/path"]);
match cli.command {
GitCommands::Push { store } => assert_eq!(store, "/custom/path"),
_ => panic!("expected Push"),
}
}
}