fce44ae9 Andy McCall 1 month ago ↶
1
2
94084087 Andy McCall 1 month ago ↶
3
use super :: format:: { author_name, deps_line, deps_of, plural, short_id, short_meta_id, signature_line, signature_mark, subject_of, when} ;
fce44ae9 Andy McCall 1 month ago ↶
4
use super :: open;
135212de Andy McCall 1 month ago ↶
5
use super :: ui;
fce44ae9 Andy McCall 1 month ago ↶
6
use anyhow:: Result;
94084087 Andy McCall 1 month ago ↶
7
use oxy_core:: objects:: ChangeKind;
fce44ae9 Andy McCall 1 month ago ↶
8
use oxy_core:: repo:: { render_change, ChangeStatus, Repo} ;
9
use std:: path:: PathBuf;
10
11
pub fn init ( path : Option< PathBuf> ) -> Result< i32 > {
12
let path = path.unwrap_or ( std:: env:: current_dir( ) ? ) ;
13
let repo = Repo:: init( & path) ? ;
94084087 Andy McCall 1 month ago ↶
14
println! ( " {} oxy repository in {} " , ui:: done( " Initialized" ) , ui:: path( repo.root ( ) .display ( ) ) ) ;
15
println! ( " {} " , ui:: kv( " bookmark" , ui:: bookmark( & repo.config.bookmark) ) ) ;
16
println! ( " {} " , ui:: kv( " author" , format! ( " {} <{} >" , ui:: author( & repo.config.author_name) , repo.config.author_email) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
17
Ok ( 0 )
18
}
19
20
fn status_tag ( s : ChangeStatus) -> & 'static str {
21
match s {
22
ChangeStatus:: Added => " A" ,
23
ChangeStatus:: Modified => " M" ,
24
ChangeStatus:: Removed => " D" ,
6e723b81 Andy McCall 1 month ago ↶
25
ChangeStatus:: Renamed => " R" ,
fce44ae9 Andy McCall 1 month ago ↶
26
ChangeStatus:: ModeChanged => " X" ,
27
ChangeStatus:: ConflictEdited => " C!" ,
6e723b81 Andy McCall 1 month ago ↶
28
ChangeStatus:: Resolved => " V" ,
fce44ae9 Andy McCall 1 month ago ↶
29
}
30
}
31
50a046e4 Andy McCall 1 month ago ↶
32
pub fn status ( json : bool ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
33
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
34
let sp = ui:: spinner( " Snapshotting the working copy…" ) ;
fce44ae9 Andy McCall 1 month ago ↶
35
let snap = repo.snapshot ( " oxy status" ) ? ;
36
let heads = repo.bookmark_heads ( ) ? ;
135212de Andy McCall 1 month ago ↶
37
sp.clear ( ) ;
50a046e4 Andy McCall 1 month ago ↶
38
if json {
39
println! ( " {} " , repo.status_json ( & snap) ? ) ;
40
return Ok ( if snap.base_snapshot.is_conflicted ( ) { 2 } else { 0 } ) ;
41
}
135212de Andy McCall 1 month ago ↶
42
println! (
94084087 Andy McCall 1 month ago ↶
43
" {} {} {} working copy {} {} {} " ,
44
ui:: dim( " On" ) ,
45
ui:: bookmark( & repo.config.bookmark) ,
46
ui:: sep( ) ,
135212de Andy McCall 1 month ago ↶
47
ui:: id( snap.wc.change_id.short ( 8 ) ) ,
94084087 Andy McCall 1 month ago ↶
48
ui:: sep( ) ,
49
ui:: dim( plural ( heads.len ( ) , " head" ) )
135212de Andy McCall 1 month ago ↶
50
) ;
fce44ae9 Andy McCall 1 month ago ↶
51
if snap.statuses.is_empty ( ) {
94084087 Andy McCall 1 month ago ↶
52
println! ( " {} {} " , ui:: ok( ui:: glyph( " ✔" , " *" ) ) , ui:: dim( " nothing to commit, working copy clean" ) ) ;
53
} else {
54
println! ( ) ;
55
for s in & snap.statuses {
56
println! ( " {} {} " , ui:: status_mark( status_tag ( s.status) ) , ui:: path( & s.path) ) ;
57
}
fce44ae9 Andy McCall 1 month ago ↶
58
}
59
if ! snap.base_snapshot.is_conflicted ( ) {
60
return Ok ( 0 ) ;
61
}
135212de Andy McCall 1 month ago ↶
62
println! ( ) ;
94084087 Andy McCall 1 month ago ↶
63
println! ( " {} {} " , ui:: section( ui:: err( " Conflicts" ) ) , ui:: dim( " edit the marked files, or the .oxy-conflict-N candidates, then commit" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
64
for c in & snap.base_snapshot.tree.conflicts {
94084087 Andy McCall 1 month ago ↶
65
println! ( " {} tree: {c:?} " , ui:: err( ui:: glyph( " ✱" , " !" ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
66
}
67
for ( p, f) in & snap.base_snapshot.files {
68
if f.is_conflicted ( ) {
69
let state = match snap.statuses.iter ( ) .find ( | s | s.path = = * p ) .map ( | s | s.status ) {
94084087 Andy McCall 1 month ago ↶
70
Some ( ChangeStatus:: Resolved) => ui:: ok( format! ( " {} resolved in working copy" , ui:: glyph( " ✔" , " *" ) ) ) ,
71
Some ( ChangeStatus:: ConflictEdited) => ui:: warn( format! ( " {} markers remain" , ui:: glyph( " ‼" , " !" ) ) ) ,
72
_ => ui:: err( format! ( " {} unresolved" , ui:: glyph( " ✱" , " !" ) ) ) ,
fce44ae9 Andy McCall 1 month ago ↶
73
} ;
135212de Andy McCall 1 month ago ↶
74
println! ( " {} {state} " , ui:: path( p) ) ;
fce44ae9 Andy McCall 1 month ago ↶
75
}
76
}
77
Ok ( 2 )
78
}
79
80
pub fn commit ( message : & str , depends_on : & [String]) -> Result< i32 > {
81
let repo = open ( ) ? ;
82
let mut deps = Vec :: new( ) ;
83
for d in depends_on {
84
deps.push ( repo.resolve_change ( d) ? ) ;
85
}
135212de Andy McCall 1 month ago ↶
86
let sp = ui:: spinner( " Committing…" ) ;
fce44ae9 Andy McCall 1 month ago ↶
87
let h = repo.commit ( message, & deps) ? ;
88
let c = repo.store ( ) .get_change ( & h) ? ;
94084087 Andy McCall 1 month ago ↶
89
sp.done ( & format! ( " {} {} {} {} " , ui:: done( " Committed" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , h.short ( 12 ) ) ) , ui:: subject( subject_of ( message) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
90
Ok ( 0 )
91
}
92
94084087 Andy McCall 1 month ago ↶
93
94
95
96
pub fn log ( limit : Option< usize > , verbose : bool ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
97
let repo = open ( ) ? ;
94084087 Andy McCall 1 month ago ↶
98
let snap = repo.snapshot ( " oxy log" ) ? ;
99
let conflicted = snap.base_snapshot.is_conflicted ( ) ;
100
let entries = repo.log ( ) ? ;
101
let total = entries.len ( ) ;
102
let shown = limit.unwrap_or ( total) .min ( total) ;
103
let rail = ui:: rail( ) ;
104
for ( i, e) in entries.iter ( ) .take ( shown) .enumerate ( ) {
fce44ae9 Andy McCall 1 month ago ↶
105
let c = & e.change;
94084087 Andy McCall 1 month ago ↶
106
let bullet = ui:: log_bullet( e.is_working_copy, e.is_head, e.is_working_copy & & conflicted) ;
107
let mut labels = Vec :: new( ) ;
fce44ae9 Andy McCall 1 month ago ↶
108
if e.is_working_copy {
94084087 Andy McCall 1 month ago ↶
109
labels.push ( ui:: warn( " working copy" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
110
}
94084087 Andy McCall 1 month ago ↶
111
if c.kind = = ChangeKind:: Resolution {
112
labels.push ( ui:: warn( " resolution" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
113
}
94084087 Andy McCall 1 month ago ↶
114
let labels = if labels.is_empty ( ) { String :: new( ) } else { format! ( " {} {} {} " , ui:: dim( " (" ) , labels.join ( ui:: dim( " , " ) .as_str ( ) ) , ui:: dim( " )" ) ) } ;
115
let subject = if e.is_working_copy & & c.message.is_empty ( ) {
116
ui:: dim( if snap.statuses.is_empty ( ) { " (clean)" } else { " (uncommitted changes)" } )
117
} else {
118
ui:: subject( subject_of ( & c.message) )
119
} ;
120
println! ( " {bullet} {} {labels} {subject} " , ui:: id( short_meta_id ( c) ) ) ;
121
let meta = format! (
122
" {} {} {} {} {} {} {} " ,
123
ui:: hash( format! ( " #{} " , e.hash.short ( 12 ) ) ) ,
124
ui:: sep( ) ,
125
ui:: author( author_name ( & repo, c) ) ,
126
ui:: sep( ) ,
127
ui:: dim( ui:: ago( c.hlc.wall_ms) ) ,
128
ui:: sep( ) ,
129
if e.is_working_copy { ui:: dim( " not yet committed" ) } else { signature_mark ( & repo, & e.hash) ? }
130
) ;
131
println! ( " {rail} {meta} " ) ;
132
if verbose {
133
if ! e.is_working_copy {
134
println! ( " {rail} {} " , ui:: dim( format! ( " {} {} " , when ( c.hlc.wall_ms) , signature_line ( & repo, & e.hash) ? ) ) ) ;
135
}
136
if ! c.deps.is_empty ( ) {
137
println! ( " {rail} {} {} " , ui:: dim( ui:: glyph( " ↳" , " ->" ) ) , ui:: dim( deps_of ( & c.deps) ) ) ;
138
}
139
let body: Vec< & str > = c.message.lines ( ) .skip ( 1 ) .skip_while ( | l | l.trim ( ) .is_empty ( ) ) .collect ( ) ;
140
if ! body.is_empty ( ) {
141
println! ( " {rail} " ) ;
142
for line in body {
143
println! ( " {rail} {line} " ) ;
144
}
145
}
fce44ae9 Andy McCall 1 month ago ↶
146
}
94084087 Andy McCall 1 month ago ↶
147
if i + 1 < shown {
148
println! ( " {rail} " ) ;
fce44ae9 Andy McCall 1 month ago ↶
149
}
150
}
94084087 Andy McCall 1 month ago ↶
151
if shown < total {
152
println! ( " {} " , ui:: dim( format! ( " {} {} more; pass -n to see them" , ui:: glyph( " …" , " ..." ) , total - shown) ) ) ;
153
}
fce44ae9 Andy McCall 1 month ago ↶
154
Ok ( 0 )
155
}
156
157
pub fn diff ( ) -> Result< i32 > {
158
let repo = open ( ) ? ;
159
let snap = repo.snapshot ( " oxy diff" ) ? ;
160
print! ( " {} " , render_change ( & snap.wc, & snap.base, & snap.base_snapshot) ) ;
161
Ok ( 0 )
162
}
163
164
pub fn show ( change : & str ) -> Result< i32 > {
165
let repo = open ( ) ? ;
166
repo.snapshot ( " oxy show" ) ? ;
167
let h = repo.resolve_change ( change) ? ;
168
let c = repo.store ( ) .get_change ( & h) ? ;
94084087 Andy McCall 1 month ago ↶
169
println! ( " {} {} {} " , ui:: section( " change" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{h} " ) ) ) ;
170
println! ( " {} " , ui:: kv( " kind" , format! ( " {:?} " , c.kind) .to_lowercase ( ) ) ) ;
171
println! ( " {} " , ui:: kv( " author" , ui:: author( author_name ( & repo, & oxy_core:: state:: ChangeMeta:: of( & c) ) ) ) ) ;
172
println! ( " {} " , ui:: kv( " date" , format! ( " {} {} " , when ( c.hlc.wall_ms) , ui:: dim( format! ( " ({} )" , ui:: ago( c.hlc.wall_ms) ) ) ) ) ) ;
173
println! ( " {} " , ui:: kv( " deps" , ui:: dim( deps_line ( & c) ) ) ) ;
174
println! ( " {} " , ui:: kv( " sig" , signature_line ( & repo, & h) ? ) ) ;
175
println! ( ) ;
176
for line in ( if c.message.is_empty ( ) { " (no description)" } else { & c.message } ) .lines ( ) {
177
println! ( " {line} " ) ;
178
}
179
println! ( ) ;
fce44ae9 Andy McCall 1 month ago ↶
180
print! ( " {} " , repo.describe_change ( & h) ? ) ;
181
Ok ( 0 )
182
}
183
184
pub fn checkout ( changes : & [String], discard : bool ) -> Result< i32 > {
185
let repo = open ( ) ? ;
186
let heads = if changes.is_empty ( ) {
187
repo.bookmark_heads ( ) ?
188
} else {
132b884b Andy McCall 1 month ago ↶
189
let mut heads = Vec :: new( ) ;
190
for c in changes {
191
heads.extend ( repo.resolve_heads ( c) ? ) ;
192
}
193
heads
fce44ae9 Andy McCall 1 month ago ↶
194
} ;
195
let spec = if changes.is_empty ( ) { repo.config.bookmark.clone ( ) } else { changes.join ( " " ) } ;
135212de Andy McCall 1 month ago ↶
196
let sp = ui:: spinner( & format! ( " Checking out {spec} …" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
197
let r = repo.checkout ( & heads, discard, & format! ( " oxy checkout {spec} " ) ) ? ;
94084087 Andy McCall 1 month ago ↶
198
sp.done ( & format! ( " {} on {} : {} written, {} removed" , ui:: done( " Working copy" ) , ui:: bookmark( & spec) , plural ( r.written, " file" ) , r.removed) ) ;
4dee277c Andy McCall 1 month ago ↶
199
for n in & r.notes {
200
println! ( " {} {n} " , ui:: note( ) ) ;
201
}
fce44ae9 Andy McCall 1 month ago ↶
202
if r.conflicted {
94084087 Andy McCall 1 month ago ↶
203
println! ( " {} {} " , ui:: caution( ) , ui:: warn( " this state has conflicts; see oxy status" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
204
}
205
Ok ( 0 )
206
}
207
208
pub fn resolve ( ) -> Result< i32 > {
209
let repo = open ( ) ? ;
210
let snap = repo.snapshot ( " oxy resolve" ) ? ;
211
if ! snap.base_snapshot.is_conflicted ( ) {
212
println! ( " The working copy's base has no conflicts." ) ;
213
return Ok ( 0 ) ;
214
}
215
let mut remaining = 0 ;
216
for ( p, f) in & snap.base_snapshot.files {
217
if ! f.is_conflicted ( ) {
218
continue ;
219
}
220
match snap.statuses.iter ( ) .find ( | s | s.path = = * p ) .map ( | s | s.status ) {
221
Some ( ChangeStatus:: Resolved) => println! ( " resolved {p} " ) ,
222
Some ( ChangeStatus:: ConflictEdited) => {
223
remaining + = 1 ;
224
println! ( " markers {p} " ) ;
225
}
226
_ => {
227
remaining + = 1 ;
228
println! ( " unresolved {p} " ) ;
229
}
230
}
231
}
232
for c in & snap.base_snapshot.tree.conflicts {
233
remaining + = 1 ;
234
println! ( " tree {c:?} " ) ;
235
}
236
if remaining = = 0 {
237
println! ( " All conflicts are resolved; `oxy commit` will record the resolution." ) ;
238
Ok ( 0 )
239
} else {
240
Ok ( 2 )
241
}
242
}
243
244
pub fn undo ( ) -> Result< i32 > {
245
let repo = open ( ) ? ;
246
let op = repo.undo ( ) ? ;
94084087 Andy McCall 1 month ago ↶
247
println! ( " {} now at: {} " , ui:: done( " Undone;" ) , ui:: dim( & op.command) ) ;
fce44ae9 Andy McCall 1 month ago ↶
248
Ok ( 0 )
249
}
250
251
pub fn op_log ( ) -> Result< i32 > {
252
let repo = open ( ) ? ;
253
for ( i, ( r, op) ) in repo.op_log ( ) ? .into_iter ( ) .enumerate ( ) {
94084087 Andy McCall 1 month ago ↶
254
let bullet = if i = = 0 { ui:: ok( ui:: glyph( " ●" , " @" ) ) } else { ui:: dim( ui:: glyph( " ○" , " o" ) ) } ;
255
let cmd = subject_of ( & op.command) .split ( " \\ n" ) .next ( ) .unwrap_or ( " " ) ;
256
println! ( " {bullet} {} {} {} {} " , ui:: hash( r.short ( 12 ) ) , ui:: sep( ) , ui:: dim( format! ( " {:<16}" , ui:: ago( op.hlc.wall_ms) ) ) , if i = = 0 { ui:: subject( cmd) } else { cmd.to_string ( ) } ) ;
fce44ae9 Andy McCall 1 month ago ↶
257
}
258
Ok ( 0 )
259
}
d02e545d Andy McCall 1 month ago ↶
260
ba160f60 Andy McCall 1 month ago ↶
261
pub fn gc ( now : bool ) -> Result< i32 > {
d02e545d Andy McCall 1 month ago ↶
262
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
263
let sp = ui:: spinner( " Packing and pruning…" ) ;
ba160f60 Andy McCall 1 month ago ↶
264
let r = repo.gc ( now) ? ;
135212de Andy McCall 1 month ago ↶
265
sp.clear ( ) ;
ba160f60 Andy McCall 1 month ago ↶
266
if r.packed.objects > 0 {
d02e545d Andy McCall 1 month ago ↶
267
println! (
ba160f60 Andy McCall 1 month ago ↶
268
" Packed {} : {} bytes -> {} bytes ({:.0} %)." ,
269
plural ( r.packed.objects, " loose object" ) ,
270
r.packed.raw_bytes,
271
r.packed.packed_bytes,
272
100. 0 * r.packed.packed_bytes as f64 / r.packed.raw_bytes.max ( 1 ) as f64
d02e545d Andy McCall 1 month ago ↶
273
) ;
274
}
135212de Andy McCall 1 month ago ↶
275
println! ( " {} reachable; pruned {} ({} bytes){} {} ; {} present." ,
ba160f60 Andy McCall 1 month ago ↶
276
plural ( r.reachable, " object" ) ,
277
plural ( r.pruned.objects, " unreachable object" ) ,
278
r.pruned.bytes,
279
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 ↶
280
if r.merged_packs > 0 { format! ( " ; merged {} into one" , plural ( r.merged_packs, " pack" ) ) } else { String :: new( ) } ,
ba160f60 Andy McCall 1 month ago ↶
281
plural ( repo.store ( ) .pack_count ( ) , " pack" )
282
) ;
d02e545d Andy McCall 1 month ago ↶
283
Ok ( 0 )
284
}
614541c3 Andy McCall 1 month ago ↶
285
286
287
fn current_head ( repo : & Repo) -> Result< oxy_core:: hash:: ChangeHash> {
288
let snap = repo.snapshot ( " oxy" ) ? ;
289
let base = snap.base.maximal ( & snap.wc.deps) ;
290
match base.as_slice ( ) {
291
[ h] => Ok ( * h) ,
292
_ => anyhow:: bail! ( " the working copy sits on {} heads; name the change explicitly" , base.len ( ) ) ,
293
}
294
}
295
296
pub fn describe ( message : & str , change : Option< & str > ) -> Result< i32 > {
297
let repo = open ( ) ? ;
298
let h = match change {
299
Some ( c) => repo.resolve_change ( c) ? ,
300
None => current_head ( & repo) ? ,
301
} ;
302
let new = repo.describe ( & h, message) ? ;
303
let c = repo.store ( ) .get_change ( & new) ? ;
94084087 Andy McCall 1 month ago ↶
304
println! ( " {} {} {} {} " , ui:: done( " Described" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , new.short ( 12 ) ) ) , ui:: subject( subject_of ( message) ) ) ;
614541c3 Andy McCall 1 month ago ↶
305
Ok ( 0 )
306
}
307
23b217d7 Andy McCall 1 month ago ↶
308
pub fn amend ( message : Option< & str > , into : Option< & str > ) -> Result< i32 > {
614541c3 Andy McCall 1 month ago ↶
309
let repo = open ( ) ? ;
23b217d7 Andy McCall 1 month ago ↶
310
let new = match into {
311
Some ( spec) => {
312
let target = repo.resolve_change ( spec) ? ;
313
repo.amend_into ( & target, message) ?
314
}
315
None => repo.amend ( message) ? ,
316
} ;
614541c3 Andy McCall 1 month ago ↶
317
let c = repo.store ( ) .get_change ( & new) ? ;
94084087 Andy McCall 1 month ago ↶
318
println! ( " {} {} {} {} " , ui:: done( " Amended" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , new.short ( 12 ) ) ) , ui:: subject( subject_of ( & c.message) ) ) ;
614541c3 Andy McCall 1 month ago ↶
319
Ok ( 0 )
320
}
321
322
pub fn abandon ( change : & str ) -> Result< i32 > {
323
let repo = open ( ) ? ;
324
let h = repo.resolve_change ( change) ? ;
325
let c = repo.store ( ) .get_change ( & h) ? ;
6706bdcf Andy McCall 1 month ago ↶
326
let notes = repo.abandon ( & h) ? ;
94084087 Andy McCall 1 month ago ↶
327
println! ( " {} {} {} {} " , ui:: done( " Abandoned" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , h.short ( 12 ) ) ) , ui:: dim( " its content remains on disk as uncommitted work if the working copy sat on it" ) ) ;
6706bdcf Andy McCall 1 month ago ↶
328
for n in notes {
94084087 Andy McCall 1 month ago ↶
329
println! ( " {} {n} " , ui:: note( ) ) ;
6706bdcf Andy McCall 1 month ago ↶
330
}
614541c3 Andy McCall 1 month ago ↶
331
Ok ( 0 )
332
}
252e8cb2 Andy McCall 1 month ago ↶
333
334
pub fn checkpoint ( ) -> Result< i32 > {
335
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
336
let sp = ui:: spinner( " Writing checkpoint…" ) ;
252e8cb2 Andy McCall 1 month ago ↶
337
let ( r, changes, bytes) = repo.checkpoint ( ) ? ;
94084087 Andy McCall 1 month ago ↶
338
sp.done ( & format! ( " {} {} of {} ({} bytes) for {} " , ui:: done( " Checkpoint" ) , ui:: hash( format! ( " #{} " , r.short ( 12 ) ) ) , plural ( changes, " change" ) , bytes, ui:: id( & repo.config.bookmark) ) ) ;
135212de Andy McCall 1 month ago ↶
339
Ok ( 0 )
340
}
341
342
343
pub fn prompt ( format : & str ) -> Result< i32 > {
344
let Ok ( repo) = Repo:: open( & std:: env:: current_dir( ) ? ) else { return Ok ( 1 ) } ;
345
let info = repo.quick_status ( ) ? ;
346
println! ( " {} " , info.format ( format) ) ;
252e8cb2 Andy McCall 1 month ago ↶
347
Ok ( 0 )
348
}
7421309f Andy McCall 1 month ago ↶
349
350
pub fn sparse ( paths : & [String]) -> Result< i32 > {
351
let mut repo = open ( ) ? ;
352
if paths.is_empty ( ) {
353
if repo.config.sparse_paths.is_empty ( ) {
354
println! ( " No sparse filter: the whole tree is materialized." ) ;
355
} else {
356
println! ( " {} " , ui:: heading( " Sparse filter" ) ) ;
357
for p in & repo.config.sparse_paths {
358
println! ( " {} " , ui:: path( p) ) ;
359
}
360
}
361
return Ok ( 0 ) ;
362
}
363
let snap = repo.snapshot ( " oxy sparse" ) ? ;
364
if ! snap.wc.tree_ops.is_empty ( ) | | ! snap.wc.edits.is_empty ( ) {
365
anyhow:: bail! ( " the working copy has changes; commit them before changing the sparse filter" ) ;
366
}
367
repo.config.set ( " filter.paths" , & paths.join ( " ," ) ) ? ;
368
repo.save_config ( ) ? ;
369
let heads = repo.bookmark_heads ( ) ? ;
370
let sp = ui:: spinner( " Re-checking out with the new filter…" ) ;
371
let r = repo.checkout ( & heads, true , " oxy sparse (checkout)" ) ? ;
372
sp.done ( & format! ( " {} {} : {} written, {} removed" , ui:: ok( " Sparse filter set to" ) , ui:: path( paths.join ( " ," ) ) , plural ( r.written, " file" ) , r.removed) ) ;
373
Ok ( 0 )
374
}
132b884b Andy McCall 1 month ago ↶
375
376
pub fn tag_create ( name : & str , changes : & [String], message : & str ) -> Result< i32 > {
377
let repo = open ( ) ? ;
378
repo.snapshot ( " oxy tag" ) ? ;
379
let heads = if changes.is_empty ( ) {
380
repo.bookmark_heads ( ) ?
381
} else {
382
let mut heads = Vec :: new( ) ;
383
for c in changes {
384
heads.extend ( repo.resolve_heads ( c) ? ) ;
385
}
386
heads
387
} ;
388
let t = repo.tag_create ( name, & heads, message) ? ;
389
println! (
94084087 Andy McCall 1 month ago ↶
390
" {} {} {} {} ({} ){} " ,
391
ui:: done( " Tagged" ) ,
392
ui:: bookmark( & t.name) ,
393
ui:: dim( ui:: glyph( " →" , " ->" ) ) ,
132b884b Andy McCall 1 month ago ↶
394
ui:: hash( format! ( " #{} " , t.state.short ( 12 ) ) ) ,
395
plural ( t.heads.len ( ) , " head" ) ,
396
t.signer.map ( | s | format! ( " , signed by {s} " ) ) .unwrap_or_else ( | | ui:: dim( " , unsigned" ) )
397
) ;
398
Ok ( 0 )
399
}
400
401
pub fn tag_list ( ) -> Result< i32 > {
402
let repo = open ( ) ? ;
403
let tags = repo.tags ( ) ? ;
404
if tags.is_empty ( ) {
405
println! ( " {} " , ui:: dim( " No tags." ) ) ;
406
}
407
for t in tags {
408
println! (
94084087 Andy McCall 1 month ago ↶
409
" {} {} {} {} {} {} " ,
410
ui:: ok( ui:: glyph( " ⚑" , " #" ) ) ,
411
ui:: bookmark( & t.name) ,
132b884b Andy McCall 1 month ago ↶
412
ui:: hash( format! ( " #{} " , t.state.short ( 12 ) ) ) ,
94084087 Andy McCall 1 month ago ↶
413
ui:: sep( ) + & ui:: dim( plural ( t.heads.len ( ) , " head" ) ) ,
414
if t.message.is_empty ( ) { String :: new( ) } else { format! ( " {} " , ui:: subject( subject_of ( & t.message) ) ) } ,
415
t.signer.map ( | s | ui:: sep( ) + & ui:: ok( format! ( " {} {s} " , ui:: glyph( " ✔" , " v" ) ) ) ) .unwrap_or_default ( )
132b884b Andy McCall 1 month ago ↶
416
) ;
417
}
418
Ok ( 0 )
419
}
4e88e774 Andy McCall 1 month ago ↶
420
421
pub fn workspace_add ( path : & std:: path:: Path, changes : & [String]) -> Result< i32 > {
422
let repo = open ( ) ? ;
423
let heads = if changes.is_empty ( ) {
424
None
425
} else {
426
let mut heads = Vec :: new( ) ;
427
for c in changes {
428
heads.extend ( repo.resolve_heads ( c) ? ) ;
429
}
430
Some ( heads)
431
} ;
432
let sp = ui:: spinner( & format! ( " Creating workspace at {} …" , path.display ( ) ) ) ;
433
let ws = repo.workspace_add ( path, heads.as_deref ( ) ) ? ;
94084087 Andy McCall 1 month ago ↶
434
sp.done ( & format! ( " {} {} (workspace {} )" , ui:: done( " Workspace created at" ) , ui:: path( ws.root ( ) .display ( ) ) , ui:: id( ws.workspace ( ) .short ( 8 ) ) ) ) ;
4e88e774 Andy McCall 1 month ago ↶
435
Ok ( 0 )
436
}
437
438
pub fn workspace_list ( ) -> Result< i32 > {
439
let repo = open ( ) ? ;
440
for w in repo.workspaces ( ) ? {
441
println! (
442
" {} {} {} {} " ,
94084087 Andy McCall 1 month ago ↶
443
if w.current { ui:: ok( ui:: glyph( " ●" , " *" ) ) } else { ui:: dim( ui:: glyph( " ○" , " o" ) ) } ,
4e88e774 Andy McCall 1 month ago ↶
444
ui:: id( w.id.short ( 8 ) ) ,
445
ui:: path( w.path.display ( ) ) ,
94084087 Andy McCall 1 month ago ↶
446
w.working_copy.map ( | h | ui:: sep( ) + & ui:: dim( format! ( " working copy #{} " , h.short ( 12 ) ) ) ) .unwrap_or_else ( | | ui:: sep( ) + & ui:: warn( " not in view" ) )
4e88e774 Andy McCall 1 month ago ↶
447
) ;
448
}
449
Ok ( 0 )
450
}
451
452
pub fn workspace_forget ( spec : & str ) -> Result< i32 > {
453
let repo = open ( ) ? ;
454
let all = repo.workspaces ( ) ? ;
455
let target = std:: fs:: canonicalize( spec) .ok ( ) ;
456
let matches: Vec< _ > = all.iter ( ) .filter ( | w | w.id.starts_with_hex ( spec) | | target.as_ref ( ) .is_some_and ( | t | * t = = w.path) ) .collect ( ) ;
457
let w = match matches.as_slice ( ) {
458
[ w] => * w,
459
[ ] => anyhow:: bail! ( " no workspace matches {spec:?}" ) ,
460
_ => anyhow:: bail! ( " {spec:?} is ambiguous" ) ,
461
} ;
462
repo.workspace_forget ( & w.id) ? ;
94084087 Andy McCall 1 month ago ↶
463
println! ( " {} workspace {} at {} ; its files were left in place." , ui:: done( " Forgot" ) , ui:: id( w.id.short ( 8 ) ) , ui:: path( w.path.display ( ) ) ) ;
4e88e774 Andy McCall 1 month ago ↶
464
Ok ( 0 )
465
}
cdb5beba Andy McCall 1 month ago ↶
466
467
pub fn lock ( path : & str ) -> Result< i32 > {
468
let repo = open ( ) ? ;
469
repo.lock_path ( path) ? ;
94084087 Andy McCall 1 month ago ↶
470
println! ( " {} {} {} " , ui:: done( " Locked" ) , ui:: path( path) , ui:: dim( " (advisory, local)" ) ) ;
cdb5beba Andy McCall 1 month ago ↶
471
Ok ( 0 )
472
}
473
474
pub fn unlock ( path : & str ) -> Result< i32 > {
475
let repo = open ( ) ? ;
476
repo.unlock_path ( path) ? ;
94084087 Andy McCall 1 month ago ↶
477
println! ( " {} {} " , ui:: done( " Unlocked" ) , ui:: path( path) ) ;
cdb5beba Andy McCall 1 month ago ↶
478
Ok ( 0 )
479
}
23b217d7 Andy McCall 1 month ago ↶
480
481
pub fn split ( change : & str , paths : & [String]) -> Result< i32 > {
482
let repo = open ( ) ? ;
483
let h = repo.resolve_change ( change) ? ;
484
let ( h1, h2) = repo.split ( & h, paths) ? ;
485
let c1 = repo.store ( ) .get_change ( & h1) ? ;
486
let c2 = repo.store ( ) .get_change ( & h2) ? ;
94084087 Andy McCall 1 month ago ↶
487
println! ( " {} into {} {} and {} {} " , ui:: done( " Split" ) , ui:: id( short_id ( & c1) ) , ui:: hash( format! ( " #{} " , h1.short ( 12 ) ) ) , ui:: id( short_id ( & c2) ) , ui:: hash( format! ( " #{} " , h2.short ( 12 ) ) ) ) ;
23b217d7 Andy McCall 1 month ago ↶
488
Ok ( 0 )
489
}
7b9809e6 Andy McCall 1 month ago ↶
490
491
fn heads_from ( repo : & Repo, changes : & [String]) -> Result< Vec< oxy_core:: hash:: ChangeHash> > {
492
let mut heads = Vec :: new( ) ;
493
for c in changes {
494
heads.extend ( repo.resolve_heads ( c) ? ) ;
495
}
496
Ok ( heads)
497
}
498
499
pub fn bookmark_create ( name : & str , changes : & [String]) -> Result< i32 > {
500
let repo = open ( ) ? ;
501
repo.snapshot ( " oxy bookmark create" ) ? ;
502
let heads = if changes.is_empty ( ) { None } else { Some ( heads_from ( & repo, changes) ? ) } ;
503
let heads = repo.bookmark_create ( name, heads.as_deref ( ) ) ? ;
504
println! ( " {} bookmark {} {} {} " , ui:: done( " Created" ) , ui:: bookmark( name) , ui:: dim( ui:: glyph( " →" , " ->" ) ) , ui:: dim( plural ( heads.len ( ) , " head" ) ) ) ;
505
println! ( " {} {} " , ui:: note( ) , ui:: dim( format! ( " switch to it with oxy bookmark switch {name} " ) ) ) ;
506
Ok ( 0 )
507
}
508
509
pub fn bookmark_set ( name : & str , changes : & [String]) -> Result< i32 > {
510
let repo = open ( ) ? ;
511
repo.snapshot ( " oxy bookmark set" ) ? ;
512
let heads = heads_from ( & repo, changes) ? ;
513
let heads = repo.bookmark_set ( name, & heads) ? ;
514
println! ( " {} bookmark {} {} {} " , ui:: done( " Moved" ) , ui:: bookmark( name) , ui:: dim( ui:: glyph( " →" , " ->" ) ) , ui:: dim( plural ( heads.len ( ) , " head" ) ) ) ;
515
Ok ( 0 )
516
}
517
518
pub fn bookmark_delete ( name : & str ) -> Result< i32 > {
519
let repo = open ( ) ? ;
520
repo.bookmark_delete ( name) ? ;
521
println! ( " {} bookmark {} {} " , ui:: done( " Deleted" ) , ui:: bookmark( name) , ui:: dim( " (its changes stay until unreachable)" ) ) ;
522
Ok ( 0 )
523
}
524
525
pub fn bookmark_list ( ) -> Result< i32 > {
526
let repo = open ( ) ? ;
527
for b in repo.bookmarks ( ) ? {
528
let mark = if b.current { ui:: ok( ui:: glyph( " ●" , " *" ) ) } else { ui:: dim( ui:: glyph( " ○" , " o" ) ) } ;
529
let mut tail = String :: new( ) ;
530
if let Some ( n) = b.ahead.filter ( | n | * n > 0 ) {
531
tail + = & ui:: ok( format! ( " {} {n} " , ui:: glyph( " ↑" , " +" ) ) ) ;
532
}
533
if let Some ( n) = b.behind.filter ( | n | * n > 0 ) {
534
tail + = & ui:: warn( format! ( " {} {n} " , ui:: glyph( " ↓" , " -" ) ) ) ;
535
}
536
let head = b.heads.first ( ) .map ( | h | repo.store ( ) .get_change ( h) .map ( | c | ui:: subject( subject_of ( & c.message) ) ) .unwrap_or_default ( ) ) .unwrap_or_else ( | | ui:: dim( " (empty)" ) ) ;
537
println! ( " {mark} {} {} {} {tail} {head} " , ui:: bookmark( & b.name) , ui:: sep( ) , ui:: dim( plural ( b.heads.len ( ) , " head" ) ) ) ;
538
}
539
Ok ( 0 )
540
}
541
542
pub fn bookmark_switch ( name : & str , discard : bool ) -> Result< i32 > {
543
let mut repo = open ( ) ? ;
544
let sp = ui:: spinner( & format! ( " Switching to {name} …" ) ) ;
545
let r = repo.bookmark_switch ( name, discard) ? ;
546
sp.done ( & format! ( " {} to {} : {} written, {} removed" , ui:: done( " Switched" ) , ui:: bookmark( name) , plural ( r.written, " file" ) , r.removed) ) ;
547
if r.conflicted {
548
println! ( " {} {} " , ui:: caution( ) , ui:: warn( " this state has conflicts; see oxy status" ) ) ;
549
}
550
Ok ( 0 )
551
}
552
553
pub fn merge ( bookmark : & str ) -> Result< i32 > {
554
let repo = open ( ) ? ;
555
let sp = ui:: spinner( & format! ( " Merging {bookmark} …" ) ) ;
556
let r = repo.merge ( bookmark) ? ;
557
if ! r.changed {
558
sp.done ( & format! ( " {} {} " , ui:: ok( ui:: glyph( " ✔" , " *" ) ) , ui:: dim( format! ( " {} already contains {bookmark} " , repo.config.bookmark) ) ) ) ;
559
return Ok ( 0 ) ;
560
}
561
sp.done ( & format! (
562
" {} {} into {} : now {} {} " ,
563
ui:: done( " Merged" ) ,
564
ui:: bookmark( bookmark) ,
565
ui:: bookmark( & repo.config.bookmark) ,
566
plural ( r.heads.len ( ) , " head" ) ,
567
ui:: dim( if r.checked_out { " ; working copy updated" } else { " ; run oxy checkout to update the working copy" } )
568
) ) ;
569
let snap = repo.snapshot ( " oxy merge" ) ? ;
570
if snap.base_snapshot.is_conflicted ( ) {
571
println! ( " {} {} " , ui:: caution( ) , ui:: warn( " the result has conflicts; see oxy status" ) ) ;
572
}
573
Ok ( 0 )
574
}
575