fce44ae9 Andy McCall 1 month ago ↶
1
2
3
use super :: format:: { deps_line, plural, short_id, signature_line, when} ;
4
use super :: open;
135212de Andy McCall 1 month ago ↶
5
use super :: ui;
fce44ae9 Andy McCall 1 month ago ↶
6
use anyhow:: Result;
7
use oxy_core:: repo:: { render_change, ChangeStatus, Repo} ;
8
use std:: path:: PathBuf;
9
10
pub fn init ( path : Option< PathBuf> ) -> Result< i32 > {
11
let path = path.unwrap_or ( std:: env:: current_dir( ) ? ) ;
12
let repo = Repo:: init( & path) ? ;
135212de Andy McCall 1 month ago ↶
13
println! ( " {} oxy repository in {} " , ui:: ok( " Initialized" ) , ui:: path( repo.root ( ) .display ( ) ) ) ;
14
println! ( " bookmark {} author {} <{} >" , ui:: id( & repo.config.bookmark) , repo.config.author_name, repo.config.author_email) ;
fce44ae9 Andy McCall 1 month ago ↶
15
Ok ( 0 )
16
}
17
18
fn status_tag ( s : ChangeStatus) -> & 'static str {
19
match s {
20
ChangeStatus:: Added => " A" ,
21
ChangeStatus:: Modified => " M" ,
22
ChangeStatus:: Removed => " D" ,
23
ChangeStatus:: ModeChanged => " X" ,
24
ChangeStatus:: ConflictEdited => " C!" ,
25
ChangeStatus:: Resolved => " R" ,
26
}
27
}
28
29
pub fn status ( ) -> Result< i32 > {
30
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
31
let sp = ui:: spinner( " Snapshotting the working copy…" ) ;
fce44ae9 Andy McCall 1 month ago ↶
32
let snap = repo.snapshot ( " oxy status" ) ? ;
33
let heads = repo.bookmark_heads ( ) ? ;
135212de Andy McCall 1 month ago ↶
34
sp.clear ( ) ;
35
println! (
36
" {} {} on {} {} " ,
37
ui:: heading( " Working copy" ) ,
38
ui:: id( snap.wc.change_id.short ( 8 ) ) ,
39
ui:: id( & repo.config.bookmark) ,
40
ui:: dim( format! ( " ({} )" , plural ( heads.len ( ) , " head" ) ) )
41
) ;
fce44ae9 Andy McCall 1 month ago ↶
42
if snap.statuses.is_empty ( ) {
135212de Andy McCall 1 month ago ↶
43
println! ( " {} " , ui:: dim( " clean" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
44
}
45
for s in & snap.statuses {
135212de Andy McCall 1 month ago ↶
46
println! ( " {:2} {} " , ui:: tag( status_tag ( s.status) ) , ui:: path( & s.path) ) ;
fce44ae9 Andy McCall 1 month ago ↶
47
}
48
if ! snap.base_snapshot.is_conflicted ( ) {
49
return Ok ( 0 ) ;
50
}
135212de Andy McCall 1 month ago ↶
51
println! ( ) ;
52
println! ( " {} {} " , ui:: err( " Conflicts" ) , ui:: dim( " edit the marked files, or the .oxy-conflict-N candidates, then commit" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
53
for c in & snap.base_snapshot.tree.conflicts {
54
println! ( " tree: {c:?} " ) ;
55
}
56
for ( p, f) in & snap.base_snapshot.files {
57
if f.is_conflicted ( ) {
58
let state = match snap.statuses.iter ( ) .find ( | s | s.path = = * p ) .map ( | s | s.status ) {
135212de Andy McCall 1 month ago ↶
59
Some ( ChangeStatus:: Resolved) => ui:: ok( " resolved in working copy" ) ,
60
Some ( ChangeStatus:: ConflictEdited) => ui:: warn( " markers remain" ) ,
61
_ => ui:: err( " unresolved" ) ,
fce44ae9 Andy McCall 1 month ago ↶
62
} ;
135212de Andy McCall 1 month ago ↶
63
println! ( " {} {state} " , ui:: path( p) ) ;
fce44ae9 Andy McCall 1 month ago ↶
64
}
65
}
66
Ok ( 2 )
67
}
68
69
pub fn commit ( message : & str , depends_on : & [String]) -> Result< i32 > {
70
let repo = open ( ) ? ;
71
let mut deps = Vec :: new( ) ;
72
for d in depends_on {
73
deps.push ( repo.resolve_change ( d) ? ) ;
74
}
135212de Andy McCall 1 month ago ↶
75
let sp = ui:: spinner( " Committing…" ) ;
fce44ae9 Andy McCall 1 month ago ↶
76
let h = repo.commit ( message, & deps) ? ;
77
let c = repo.store ( ) .get_change ( & h) ? ;
135212de Andy McCall 1 month ago ↶
78
sp.done ( & format! ( " {} {} {} {} " , ui:: ok( " Committed" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , h.short ( 12 ) ) ) , message.lines ( ) .next ( ) .unwrap_or ( " " ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
79
Ok ( 0 )
80
}
81
82
pub fn log ( ) -> Result< i32 > {
83
let repo = open ( ) ? ;
84
repo.snapshot ( " oxy log" ) ? ;
85
for e in repo.log ( ) ? {
86
let c = & e.change;
87
let mut flags = Vec :: new( ) ;
88
if e.is_working_copy {
89
flags.push ( " working copy" ) ;
90
}
91
if e.is_head {
92
flags.push ( " head" ) ;
93
}
135212de Andy McCall 1 month ago ↶
94
let flags = if flags.is_empty ( ) { String :: new( ) } else { ui:: warn( format! ( " ({} )" , flags.join ( " , " ) ) ) } ;
95
println! ( " {} {} {} " , ui:: id( short_id ( c) ) , ui:: hash( format! ( " #{} " , e.hash.short ( 12 ) ) ) , flags) ;
96
println! ( " {} " , ui:: dim( when ( c.hlc.wall_ms) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
97
if ! c.deps.is_empty ( ) {
135212de Andy McCall 1 month ago ↶
98
println! ( " {} " , ui:: dim( format! ( " deps: {} " , deps_line ( c) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
99
}
100
let msg = if c.message.is_empty ( ) { " (no description)" } else { & c.message } ;
101
for line in msg.lines ( ) {
102
println! ( " {line} " ) ;
103
}
104
println! ( ) ;
105
}
106
Ok ( 0 )
107
}
108
109
pub fn diff ( ) -> Result< i32 > {
110
let repo = open ( ) ? ;
111
let snap = repo.snapshot ( " oxy diff" ) ? ;
112
print! ( " {} " , render_change ( & snap.wc, & snap.base, & snap.base_snapshot) ) ;
113
Ok ( 0 )
114
}
115
116
pub fn show ( change : & str ) -> Result< i32 > {
117
let repo = open ( ) ? ;
118
repo.snapshot ( " oxy show" ) ? ;
119
let h = repo.resolve_change ( change) ? ;
120
let c = repo.store ( ) .get_change ( & h) ? ;
135212de Andy McCall 1 month ago ↶
121
println! ( " {} {} {} " , ui:: heading( " change" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{h} " ) ) ) ;
122
println! ( " kind {:?} " , c.kind) ;
123
println! ( " date {} " , when ( c.hlc.wall_ms) ) ;
124
println! ( " deps {} " , ui:: dim( deps_line ( & c) ) ) ;
125
println! ( " sig {} " , signature_line ( & repo, & h) ? ) ;
fce44ae9 Andy McCall 1 month ago ↶
126
println! ( " \n {} \n " , if c.message.is_empty ( ) { " (no description)" } else { & c.message } ) ;
127
print! ( " {} " , repo.describe_change ( & h) ? ) ;
128
Ok ( 0 )
129
}
130
131
pub fn checkout ( changes : & [String], discard : bool ) -> Result< i32 > {
132
let repo = open ( ) ? ;
133
let heads = if changes.is_empty ( ) {
134
repo.bookmark_heads ( ) ?
135
} else {
136
changes.iter ( ) .map ( | c | repo.resolve_change ( c) ) .collect:: < Result< Vec< _ > , _ > > ( ) ?
137
} ;
138
let spec = if changes.is_empty ( ) { repo.config.bookmark.clone ( ) } else { changes.join ( " " ) } ;
135212de Andy McCall 1 month ago ↶
139
let sp = ui:: spinner( & format! ( " Checking out {spec} …" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
140
let r = repo.checkout ( & heads, discard, & format! ( " oxy checkout {spec} " ) ) ? ;
135212de Andy McCall 1 month ago ↶
141
sp.done ( & format! ( " {} on {} : {} written, {} removed" , ui:: ok( " Working copy" ) , ui:: id( & spec) , plural ( r.written, " file" ) , r.removed) ) ;
fce44ae9 Andy McCall 1 month ago ↶
142
if r.conflicted {
135212de Andy McCall 1 month ago ↶
143
println! ( " {} " , ui:: warn( " this state has conflicts; see oxy status" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
144
}
145
Ok ( 0 )
146
}
147
148
pub fn resolve ( ) -> Result< i32 > {
149
let repo = open ( ) ? ;
150
let snap = repo.snapshot ( " oxy resolve" ) ? ;
151
if ! snap.base_snapshot.is_conflicted ( ) {
152
println! ( " The working copy's base has no conflicts." ) ;
153
return Ok ( 0 ) ;
154
}
155
let mut remaining = 0 ;
156
for ( p, f) in & snap.base_snapshot.files {
157
if ! f.is_conflicted ( ) {
158
continue ;
159
}
160
match snap.statuses.iter ( ) .find ( | s | s.path = = * p ) .map ( | s | s.status ) {
161
Some ( ChangeStatus:: Resolved) => println! ( " resolved {p} " ) ,
162
Some ( ChangeStatus:: ConflictEdited) => {
163
remaining + = 1 ;
164
println! ( " markers {p} " ) ;
165
}
166
_ => {
167
remaining + = 1 ;
168
println! ( " unresolved {p} " ) ;
169
}
170
}
171
}
172
for c in & snap.base_snapshot.tree.conflicts {
173
remaining + = 1 ;
174
println! ( " tree {c:?} " ) ;
175
}
176
if remaining = = 0 {
177
println! ( " All conflicts are resolved; `oxy commit` will record the resolution." ) ;
178
Ok ( 0 )
179
} else {
180
Ok ( 2 )
181
}
182
}
183
184
pub fn undo ( ) -> Result< i32 > {
185
let repo = open ( ) ? ;
186
let op = repo.undo ( ) ? ;
135212de Andy McCall 1 month ago ↶
187
println! ( " {} now at: {} " , ui:: ok( " Undone;" ) , ui:: dim( & op.command) ) ;
fce44ae9 Andy McCall 1 month ago ↶
188
Ok ( 0 )
189
}
190
191
pub fn op_log ( ) -> Result< i32 > {
192
let repo = open ( ) ? ;
193
for ( i, ( r, op) ) in repo.op_log ( ) ? .into_iter ( ) .enumerate ( ) {
135212de Andy McCall 1 month ago ↶
194
println! ( " {} {} {} {} " , if i = = 0 { ui:: ok( " @ " ) } else { " " .into ( ) } , ui:: hash( r.short ( 12 ) ) , ui:: dim( when ( op.hlc.wall_ms) ) , op.command) ;
fce44ae9 Andy McCall 1 month ago ↶
195
}
196
Ok ( 0 )
197
}
d02e545d Andy McCall 1 month ago ↶
198
ba160f60 Andy McCall 1 month ago ↶
199
pub fn gc ( now : bool ) -> Result< i32 > {
d02e545d Andy McCall 1 month ago ↶
200
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
201
let sp = ui:: spinner( " Packing and pruning…" ) ;
ba160f60 Andy McCall 1 month ago ↶
202
let r = repo.gc ( now) ? ;
135212de Andy McCall 1 month ago ↶
203
sp.clear ( ) ;
ba160f60 Andy McCall 1 month ago ↶
204
if r.packed.objects > 0 {
d02e545d Andy McCall 1 month ago ↶
205
println! (
ba160f60 Andy McCall 1 month ago ↶
206
" Packed {} : {} bytes -> {} bytes ({:.0} %)." ,
207
plural ( r.packed.objects, " loose object" ) ,
208
r.packed.raw_bytes,
209
r.packed.packed_bytes,
210
100. 0 * r.packed.packed_bytes as f64 / r.packed.raw_bytes.max ( 1 ) as f64
d02e545d Andy McCall 1 month ago ↶
211
) ;
212
}
135212de Andy McCall 1 month ago ↶
213
println! ( " {} reachable; pruned {} ({} bytes){} {} ; {} present." ,
ba160f60 Andy McCall 1 month ago ↶
214
plural ( r.reachable, " object" ) ,
215
plural ( r.pruned.objects, " unreachable object" ) ,
216
r.pruned.bytes,
217
if r.pruned.deferred > 0 { format! ( " ; {} deferred by the grace period (use --now)" , plural ( r.pruned.deferred, " object" ) ) } else { String :: new( ) } ,
135212de Andy McCall 1 month ago ↶
218
if r.merged_packs > 0 { format! ( " ; merged {} into one" , plural ( r.merged_packs, " pack" ) ) } else { String :: new( ) } ,
ba160f60 Andy McCall 1 month ago ↶
219
plural ( repo.store ( ) .pack_count ( ) , " pack" )
220
) ;
d02e545d Andy McCall 1 month ago ↶
221
Ok ( 0 )
222
}
614541c3 Andy McCall 1 month ago ↶
223
224
225
fn current_head ( repo : & Repo) -> Result< oxy_core:: hash:: ChangeHash> {
226
let snap = repo.snapshot ( " oxy" ) ? ;
227
let base = snap.base.maximal ( & snap.wc.deps) ;
228
match base.as_slice ( ) {
229
[ h] => Ok ( * h) ,
230
_ => anyhow:: bail! ( " the working copy sits on {} heads; name the change explicitly" , base.len ( ) ) ,
231
}
232
}
233
234
pub fn describe ( message : & str , change : Option< & str > ) -> Result< i32 > {
235
let repo = open ( ) ? ;
236
let h = match change {
237
Some ( c) => repo.resolve_change ( c) ? ,
238
None => current_head ( & repo) ? ,
239
} ;
240
let new = repo.describe ( & h, message) ? ;
241
let c = repo.store ( ) .get_change ( & new) ? ;
135212de Andy McCall 1 month ago ↶
242
println! ( " {} {} {} {} " , ui:: ok( " Described" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , new.short ( 12 ) ) ) , message.lines ( ) .next ( ) .unwrap_or ( " " ) ) ;
614541c3 Andy McCall 1 month ago ↶
243
Ok ( 0 )
244
}
245
246
pub fn amend ( message : Option< & str > ) -> Result< i32 > {
247
let repo = open ( ) ? ;
248
let new = repo.amend ( message) ? ;
249
let c = repo.store ( ) .get_change ( & new) ? ;
135212de Andy McCall 1 month ago ↶
250
println! ( " {} {} {} {} " , ui:: ok( " Amended" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , new.short ( 12 ) ) ) , c.message.lines ( ) .next ( ) .unwrap_or ( " " ) ) ;
614541c3 Andy McCall 1 month ago ↶
251
Ok ( 0 )
252
}
253
254
pub fn abandon ( change : & str ) -> Result< i32 > {
255
let repo = open ( ) ? ;
256
let h = repo.resolve_change ( change) ? ;
257
let c = repo.store ( ) .get_change ( & h) ? ;
258
repo.abandon ( & h) ? ;
259
println! ( " Abandoned {} #{} ; its content remains on disk as uncommitted work if the working copy sat on it." , short_id ( & c) , h.short ( 12 ) ) ;
260
Ok ( 0 )
261
}
252e8cb2 Andy McCall 1 month ago ↶
262
263
pub fn checkpoint ( ) -> Result< i32 > {
264
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
265
let sp = ui:: spinner( " Writing checkpoint…" ) ;
252e8cb2 Andy McCall 1 month ago ↶
266
let ( r, changes, bytes) = repo.checkpoint ( ) ? ;
135212de Andy McCall 1 month ago ↶
267
sp.done ( & format! ( " {} {} of {} ({} bytes) for {} " , ui:: ok( " Checkpoint" ) , ui:: hash( format! ( " #{} " , r.short ( 12 ) ) ) , plural ( changes, " change" ) , bytes, ui:: id( & repo.config.bookmark) ) ) ;
268
Ok ( 0 )
269
}
270
271
272
pub fn prompt ( format : & str ) -> Result< i32 > {
273
let Ok ( repo) = Repo:: open( & std:: env:: current_dir( ) ? ) else { return Ok ( 1 ) } ;
274
let info = repo.quick_status ( ) ? ;
275
println! ( " {} " , info.format ( format) ) ;
252e8cb2 Andy McCall 1 month ago ↶
276
Ok ( 0 )
277
}
7421309f Andy McCall 1 month ago ↶
278
279
pub fn sparse ( paths : & [String]) -> Result< i32 > {
280
let mut repo = open ( ) ? ;
281
if paths.is_empty ( ) {
282
if repo.config.sparse_paths.is_empty ( ) {
283
println! ( " No sparse filter: the whole tree is materialized." ) ;
284
} else {
285
println! ( " {} " , ui:: heading( " Sparse filter" ) ) ;
286
for p in & repo.config.sparse_paths {
287
println! ( " {} " , ui:: path( p) ) ;
288
}
289
}
290
return Ok ( 0 ) ;
291
}
292
let snap = repo.snapshot ( " oxy sparse" ) ? ;
293
if ! snap.wc.tree_ops.is_empty ( ) | | ! snap.wc.edits.is_empty ( ) {
294
anyhow:: bail! ( " the working copy has changes; commit them before changing the sparse filter" ) ;
295
}
296
repo.config.set ( " filter.paths" , & paths.join ( " ," ) ) ? ;
297
repo.save_config ( ) ? ;
298
let heads = repo.bookmark_heads ( ) ? ;
299
let sp = ui:: spinner( " Re-checking out with the new filter…" ) ;
300
let r = repo.checkout ( & heads, true , " oxy sparse (checkout)" ) ? ;
301
sp.done ( & format! ( " {} {} : {} written, {} removed" , ui:: ok( " Sparse filter set to" ) , ui:: path( paths.join ( " ," ) ) , plural ( r.written, " file" ) , r.removed) ) ;
302
Ok ( 0 )
303
}