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
32
pub fn status ( ) -> Result< i32 > {
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 ( ) ;
38
println! (
94084087 Andy McCall 1 month ago ↶
39
" {} {} {} working copy {} {} {} " ,
40
ui:: dim( " On" ) ,
41
ui:: bookmark( & repo.config.bookmark) ,
42
ui:: sep( ) ,
135212de Andy McCall 1 month ago ↶
43
ui:: id( snap.wc.change_id.short ( 8 ) ) ,
94084087 Andy McCall 1 month ago ↶
44
ui:: sep( ) ,
45
ui:: dim( plural ( heads.len ( ) , " head" ) )
135212de Andy McCall 1 month ago ↶
46
) ;
fce44ae9 Andy McCall 1 month ago ↶
47
if snap.statuses.is_empty ( ) {
94084087 Andy McCall 1 month ago ↶
48
println! ( " {} {} " , ui:: ok( ui:: glyph( " ✔" , " *" ) ) , ui:: dim( " nothing to commit, working copy clean" ) ) ;
49
} else {
50
println! ( ) ;
51
for s in & snap.statuses {
52
println! ( " {} {} " , ui:: status_mark( status_tag ( s.status) ) , ui:: path( & s.path) ) ;
53
}
fce44ae9 Andy McCall 1 month ago ↶
54
}
55
if ! snap.base_snapshot.is_conflicted ( ) {
56
return Ok ( 0 ) ;
57
}
135212de Andy McCall 1 month ago ↶
58
println! ( ) ;
94084087 Andy McCall 1 month ago ↶
59
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 ↶
60
for c in & snap.base_snapshot.tree.conflicts {
94084087 Andy McCall 1 month ago ↶
61
println! ( " {} tree: {c:?} " , ui:: err( ui:: glyph( " ✱" , " !" ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
62
}
63
for ( p, f) in & snap.base_snapshot.files {
64
if f.is_conflicted ( ) {
65
let state = match snap.statuses.iter ( ) .find ( | s | s.path = = * p ) .map ( | s | s.status ) {
94084087 Andy McCall 1 month ago ↶
66
Some ( ChangeStatus:: Resolved) => ui:: ok( format! ( " {} resolved in working copy" , ui:: glyph( " ✔" , " *" ) ) ) ,
67
Some ( ChangeStatus:: ConflictEdited) => ui:: warn( format! ( " {} markers remain" , ui:: glyph( " ‼" , " !" ) ) ) ,
68
_ => ui:: err( format! ( " {} unresolved" , ui:: glyph( " ✱" , " !" ) ) ) ,
fce44ae9 Andy McCall 1 month ago ↶
69
} ;
135212de Andy McCall 1 month ago ↶
70
println! ( " {} {state} " , ui:: path( p) ) ;
fce44ae9 Andy McCall 1 month ago ↶
71
}
72
}
73
Ok ( 2 )
74
}
75
76
pub fn commit ( message : & str , depends_on : & [String]) -> Result< i32 > {
77
let repo = open ( ) ? ;
78
let mut deps = Vec :: new( ) ;
79
for d in depends_on {
80
deps.push ( repo.resolve_change ( d) ? ) ;
81
}
135212de Andy McCall 1 month ago ↶
82
let sp = ui:: spinner( " Committing…" ) ;
fce44ae9 Andy McCall 1 month ago ↶
83
let h = repo.commit ( message, & deps) ? ;
84
let c = repo.store ( ) .get_change ( & h) ? ;
94084087 Andy McCall 1 month ago ↶
85
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 ↶
86
Ok ( 0 )
87
}
88
94084087 Andy McCall 1 month ago ↶
89
90
91
92
pub fn log ( limit : Option< usize > , verbose : bool ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
93
let repo = open ( ) ? ;
94084087 Andy McCall 1 month ago ↶
94
let snap = repo.snapshot ( " oxy log" ) ? ;
95
let conflicted = snap.base_snapshot.is_conflicted ( ) ;
96
let entries = repo.log ( ) ? ;
97
let total = entries.len ( ) ;
98
let shown = limit.unwrap_or ( total) .min ( total) ;
99
let rail = ui:: rail( ) ;
100
for ( i, e) in entries.iter ( ) .take ( shown) .enumerate ( ) {
fce44ae9 Andy McCall 1 month ago ↶
101
let c = & e.change;
94084087 Andy McCall 1 month ago ↶
102
let bullet = ui:: log_bullet( e.is_working_copy, e.is_head, e.is_working_copy & & conflicted) ;
103
let mut labels = Vec :: new( ) ;
fce44ae9 Andy McCall 1 month ago ↶
104
if e.is_working_copy {
94084087 Andy McCall 1 month ago ↶
105
labels.push ( ui:: warn( " working copy" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
106
}
94084087 Andy McCall 1 month ago ↶
107
if c.kind = = ChangeKind:: Resolution {
108
labels.push ( ui:: warn( " resolution" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
109
}
94084087 Andy McCall 1 month ago ↶
110
let labels = if labels.is_empty ( ) { String :: new( ) } else { format! ( " {} {} {} " , ui:: dim( " (" ) , labels.join ( ui:: dim( " , " ) .as_str ( ) ) , ui:: dim( " )" ) ) } ;
111
let subject = if e.is_working_copy & & c.message.is_empty ( ) {
112
ui:: dim( if snap.statuses.is_empty ( ) { " (clean)" } else { " (uncommitted changes)" } )
113
} else {
114
ui:: subject( subject_of ( & c.message) )
115
} ;
116
println! ( " {bullet} {} {labels} {subject} " , ui:: id( short_meta_id ( c) ) ) ;
117
let meta = format! (
118
" {} {} {} {} {} {} {} " ,
119
ui:: hash( format! ( " #{} " , e.hash.short ( 12 ) ) ) ,
120
ui:: sep( ) ,
121
ui:: author( author_name ( & repo, c) ) ,
122
ui:: sep( ) ,
123
ui:: dim( ui:: ago( c.hlc.wall_ms) ) ,
124
ui:: sep( ) ,
125
if e.is_working_copy { ui:: dim( " not yet committed" ) } else { signature_mark ( & repo, & e.hash) ? }
126
) ;
127
println! ( " {rail} {meta} " ) ;
128
if verbose {
129
if ! e.is_working_copy {
130
println! ( " {rail} {} " , ui:: dim( format! ( " {} {} " , when ( c.hlc.wall_ms) , signature_line ( & repo, & e.hash) ? ) ) ) ;
131
}
132
if ! c.deps.is_empty ( ) {
133
println! ( " {rail} {} {} " , ui:: dim( ui:: glyph( " ↳" , " ->" ) ) , ui:: dim( deps_of ( & c.deps) ) ) ;
134
}
135
let body: Vec< & str > = c.message.lines ( ) .skip ( 1 ) .skip_while ( | l | l.trim ( ) .is_empty ( ) ) .collect ( ) ;
136
if ! body.is_empty ( ) {
137
println! ( " {rail} " ) ;
138
for line in body {
139
println! ( " {rail} {line} " ) ;
140
}
141
}
fce44ae9 Andy McCall 1 month ago ↶
142
}
94084087 Andy McCall 1 month ago ↶
143
if i + 1 < shown {
144
println! ( " {rail} " ) ;
fce44ae9 Andy McCall 1 month ago ↶
145
}
146
}
94084087 Andy McCall 1 month ago ↶
147
if shown < total {
148
println! ( " {} " , ui:: dim( format! ( " {} {} more; pass -n to see them" , ui:: glyph( " …" , " ..." ) , total - shown) ) ) ;
149
}
fce44ae9 Andy McCall 1 month ago ↶
150
Ok ( 0 )
151
}
152
153
pub fn diff ( ) -> Result< i32 > {
154
let repo = open ( ) ? ;
155
let snap = repo.snapshot ( " oxy diff" ) ? ;
156
print! ( " {} " , render_change ( & snap.wc, & snap.base, & snap.base_snapshot) ) ;
157
Ok ( 0 )
158
}
159
160
pub fn show ( change : & str ) -> Result< i32 > {
161
let repo = open ( ) ? ;
162
repo.snapshot ( " oxy show" ) ? ;
163
let h = repo.resolve_change ( change) ? ;
164
let c = repo.store ( ) .get_change ( & h) ? ;
94084087 Andy McCall 1 month ago ↶
165
println! ( " {} {} {} " , ui:: section( " change" ) , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{h} " ) ) ) ;
166
println! ( " {} " , ui:: kv( " kind" , format! ( " {:?} " , c.kind) .to_lowercase ( ) ) ) ;
167
println! ( " {} " , ui:: kv( " author" , ui:: author( author_name ( & repo, & oxy_core:: state:: ChangeMeta:: of( & c) ) ) ) ) ;
168
println! ( " {} " , ui:: kv( " date" , format! ( " {} {} " , when ( c.hlc.wall_ms) , ui:: dim( format! ( " ({} )" , ui:: ago( c.hlc.wall_ms) ) ) ) ) ) ;
169
println! ( " {} " , ui:: kv( " deps" , ui:: dim( deps_line ( & c) ) ) ) ;
170
println! ( " {} " , ui:: kv( " sig" , signature_line ( & repo, & h) ? ) ) ;
171
println! ( ) ;
172
for line in ( if c.message.is_empty ( ) { " (no description)" } else { & c.message } ) .lines ( ) {
173
println! ( " {line} " ) ;
174
}
175
println! ( ) ;
fce44ae9 Andy McCall 1 month ago ↶
176
print! ( " {} " , repo.describe_change ( & h) ? ) ;
177
Ok ( 0 )
178
}
179
180
pub fn checkout ( changes : & [String], discard : bool ) -> Result< i32 > {
181
let repo = open ( ) ? ;
182
let heads = if changes.is_empty ( ) {
183
repo.bookmark_heads ( ) ?
184
} else {
132b884b Andy McCall 1 month ago ↶
185
let mut heads = Vec :: new( ) ;
186
for c in changes {
187
heads.extend ( repo.resolve_heads ( c) ? ) ;
188
}
189
heads
fce44ae9 Andy McCall 1 month ago ↶
190
} ;
191
let spec = if changes.is_empty ( ) { repo.config.bookmark.clone ( ) } else { changes.join ( " " ) } ;
135212de Andy McCall 1 month ago ↶
192
let sp = ui:: spinner( & format! ( " Checking out {spec} …" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
193
let r = repo.checkout ( & heads, discard, & format! ( " oxy checkout {spec} " ) ) ? ;
94084087 Andy McCall 1 month ago ↶
194
sp.done ( & format! ( " {} on {} : {} written, {} removed" , ui:: done( " Working copy" ) , ui:: bookmark( & spec) , plural ( r.written, " file" ) , r.removed) ) ;
fce44ae9 Andy McCall 1 month ago ↶
195
if r.conflicted {
94084087 Andy McCall 1 month ago ↶
196
println! ( " {} {} " , ui:: caution( ) , ui:: warn( " this state has conflicts; see oxy status" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
197
}
198
Ok ( 0 )
199
}
200
201
pub fn resolve ( ) -> Result< i32 > {
202
let repo = open ( ) ? ;
203
let snap = repo.snapshot ( " oxy resolve" ) ? ;
204
if ! snap.base_snapshot.is_conflicted ( ) {
205
println! ( " The working copy's base has no conflicts." ) ;
206
return Ok ( 0 ) ;
207
}
208
let mut remaining = 0 ;
209
for ( p, f) in & snap.base_snapshot.files {
210
if ! f.is_conflicted ( ) {
211
continue ;
212
}
213
match snap.statuses.iter ( ) .find ( | s | s.path = = * p ) .map ( | s | s.status ) {
214
Some ( ChangeStatus:: Resolved) => println! ( " resolved {p} " ) ,
215
Some ( ChangeStatus:: ConflictEdited) => {
216
remaining + = 1 ;
217
println! ( " markers {p} " ) ;
218
}
219
_ => {
220
remaining + = 1 ;
221
println! ( " unresolved {p} " ) ;
222
}
223
}
224
}
225
for c in & snap.base_snapshot.tree.conflicts {
226
remaining + = 1 ;
227
println! ( " tree {c:?} " ) ;
228
}
229
if remaining = = 0 {
230
println! ( " All conflicts are resolved; `oxy commit` will record the resolution." ) ;
231
Ok ( 0 )
232
} else {
233
Ok ( 2 )
234
}
235
}
236
237
pub fn undo ( ) -> Result< i32 > {
238
let repo = open ( ) ? ;
239
let op = repo.undo ( ) ? ;
94084087 Andy McCall 1 month ago ↶
240
println! ( " {} now at: {} " , ui:: done( " Undone;" ) , ui:: dim( & op.command) ) ;
fce44ae9 Andy McCall 1 month ago ↶
241
Ok ( 0 )
242
}
243
244
pub fn op_log ( ) -> Result< i32 > {
245
let repo = open ( ) ? ;
246
for ( i, ( r, op) ) in repo.op_log ( ) ? .into_iter ( ) .enumerate ( ) {
94084087 Andy McCall 1 month ago ↶
247
let bullet = if i = = 0 { ui:: ok( ui:: glyph( " ●" , " @" ) ) } else { ui:: dim( ui:: glyph( " ○" , " o" ) ) } ;
248
let cmd = subject_of ( & op.command) .split ( " \\ n" ) .next ( ) .unwrap_or ( " " ) ;
249
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 ↶
250
}
251
Ok ( 0 )
252
}
d02e545d Andy McCall 1 month ago ↶
253
ba160f60 Andy McCall 1 month ago ↶
254
pub fn gc ( now : bool ) -> Result< i32 > {
d02e545d Andy McCall 1 month ago ↶
255
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
256
let sp = ui:: spinner( " Packing and pruning…" ) ;
ba160f60 Andy McCall 1 month ago ↶
257
let r = repo.gc ( now) ? ;
135212de Andy McCall 1 month ago ↶
258
sp.clear ( ) ;
ba160f60 Andy McCall 1 month ago ↶
259
if r.packed.objects > 0 {
d02e545d Andy McCall 1 month ago ↶
260
println! (
ba160f60 Andy McCall 1 month ago ↶
261
" Packed {} : {} bytes -> {} bytes ({:.0} %)." ,
262
plural ( r.packed.objects, " loose object" ) ,
263
r.packed.raw_bytes,
264
r.packed.packed_bytes,
265
100. 0 * r.packed.packed_bytes as f64 / r.packed.raw_bytes.max ( 1 ) as f64
d02e545d Andy McCall 1 month ago ↶
266
) ;
267
}
135212de Andy McCall 1 month ago ↶
268
println! ( " {} reachable; pruned {} ({} bytes){} {} ; {} present." ,
ba160f60 Andy McCall 1 month ago ↶
269
plural ( r.reachable, " object" ) ,
270
plural ( r.pruned.objects, " unreachable object" ) ,
271
r.pruned.bytes,
272
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 ↶
273
if r.merged_packs > 0 { format! ( " ; merged {} into one" , plural ( r.merged_packs, " pack" ) ) } else { String :: new( ) } ,
ba160f60 Andy McCall 1 month ago ↶
274
plural ( repo.store ( ) .pack_count ( ) , " pack" )
275
) ;
d02e545d Andy McCall 1 month ago ↶
276
Ok ( 0 )
277
}
614541c3 Andy McCall 1 month ago ↶
278
279
280
fn current_head ( repo : & Repo) -> Result< oxy_core:: hash:: ChangeHash> {
281
let snap = repo.snapshot ( " oxy" ) ? ;
282
let base = snap.base.maximal ( & snap.wc.deps) ;
283
match base.as_slice ( ) {
284
[ h] => Ok ( * h) ,
285
_ => anyhow:: bail! ( " the working copy sits on {} heads; name the change explicitly" , base.len ( ) ) ,
286
}
287
}
288
289
pub fn describe ( message : & str , change : Option< & str > ) -> Result< i32 > {
290
let repo = open ( ) ? ;
291
let h = match change {
292
Some ( c) => repo.resolve_change ( c) ? ,
293
None => current_head ( & repo) ? ,
294
} ;
295
let new = repo.describe ( & h, message) ? ;
296
let c = repo.store ( ) .get_change ( & new) ? ;
94084087 Andy McCall 1 month ago ↶
297
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 ↶
298
Ok ( 0 )
299
}
300
23b217d7 Andy McCall 1 month ago ↶
301
pub fn amend ( message : Option< & str > , into : Option< & str > ) -> Result< i32 > {
614541c3 Andy McCall 1 month ago ↶
302
let repo = open ( ) ? ;
23b217d7 Andy McCall 1 month ago ↶
303
let new = match into {
304
Some ( spec) => {
305
let target = repo.resolve_change ( spec) ? ;
306
repo.amend_into ( & target, message) ?
307
}
308
None => repo.amend ( message) ? ,
309
} ;
614541c3 Andy McCall 1 month ago ↶
310
let c = repo.store ( ) .get_change ( & new) ? ;
94084087 Andy McCall 1 month ago ↶
311
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 ↶
312
Ok ( 0 )
313
}
314
315
pub fn abandon ( change : & str ) -> Result< i32 > {
316
let repo = open ( ) ? ;
317
let h = repo.resolve_change ( change) ? ;
318
let c = repo.store ( ) .get_change ( & h) ? ;
6706bdcf Andy McCall 1 month ago ↶
319
let notes = repo.abandon ( & h) ? ;
94084087 Andy McCall 1 month ago ↶
320
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 ↶
321
for n in notes {
94084087 Andy McCall 1 month ago ↶
322
println! ( " {} {n} " , ui:: note( ) ) ;
6706bdcf Andy McCall 1 month ago ↶
323
}
614541c3 Andy McCall 1 month ago ↶
324
Ok ( 0 )
325
}
252e8cb2 Andy McCall 1 month ago ↶
326
327
pub fn checkpoint ( ) -> Result< i32 > {
328
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
329
let sp = ui:: spinner( " Writing checkpoint…" ) ;
252e8cb2 Andy McCall 1 month ago ↶
330
let ( r, changes, bytes) = repo.checkpoint ( ) ? ;
94084087 Andy McCall 1 month ago ↶
331
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 ↶
332
Ok ( 0 )
333
}
334
335
336
pub fn prompt ( format : & str ) -> Result< i32 > {
337
let Ok ( repo) = Repo:: open( & std:: env:: current_dir( ) ? ) else { return Ok ( 1 ) } ;
338
let info = repo.quick_status ( ) ? ;
339
println! ( " {} " , info.format ( format) ) ;
252e8cb2 Andy McCall 1 month ago ↶
340
Ok ( 0 )
341
}
7421309f Andy McCall 1 month ago ↶
342
343
pub fn sparse ( paths : & [String]) -> Result< i32 > {
344
let mut repo = open ( ) ? ;
345
if paths.is_empty ( ) {
346
if repo.config.sparse_paths.is_empty ( ) {
347
println! ( " No sparse filter: the whole tree is materialized." ) ;
348
} else {
349
println! ( " {} " , ui:: heading( " Sparse filter" ) ) ;
350
for p in & repo.config.sparse_paths {
351
println! ( " {} " , ui:: path( p) ) ;
352
}
353
}
354
return Ok ( 0 ) ;
355
}
356
let snap = repo.snapshot ( " oxy sparse" ) ? ;
357
if ! snap.wc.tree_ops.is_empty ( ) | | ! snap.wc.edits.is_empty ( ) {
358
anyhow:: bail! ( " the working copy has changes; commit them before changing the sparse filter" ) ;
359
}
360
repo.config.set ( " filter.paths" , & paths.join ( " ," ) ) ? ;
361
repo.save_config ( ) ? ;
362
let heads = repo.bookmark_heads ( ) ? ;
363
let sp = ui:: spinner( " Re-checking out with the new filter…" ) ;
364
let r = repo.checkout ( & heads, true , " oxy sparse (checkout)" ) ? ;
365
sp.done ( & format! ( " {} {} : {} written, {} removed" , ui:: ok( " Sparse filter set to" ) , ui:: path( paths.join ( " ," ) ) , plural ( r.written, " file" ) , r.removed) ) ;
366
Ok ( 0 )
367
}
132b884b Andy McCall 1 month ago ↶
368
369
pub fn tag_create ( name : & str , changes : & [String], message : & str ) -> Result< i32 > {
370
let repo = open ( ) ? ;
371
repo.snapshot ( " oxy tag" ) ? ;
372
let heads = if changes.is_empty ( ) {
373
repo.bookmark_heads ( ) ?
374
} else {
375
let mut heads = Vec :: new( ) ;
376
for c in changes {
377
heads.extend ( repo.resolve_heads ( c) ? ) ;
378
}
379
heads
380
} ;
381
let t = repo.tag_create ( name, & heads, message) ? ;
382
println! (
94084087 Andy McCall 1 month ago ↶
383
" {} {} {} {} ({} ){} " ,
384
ui:: done( " Tagged" ) ,
385
ui:: bookmark( & t.name) ,
386
ui:: dim( ui:: glyph( " →" , " ->" ) ) ,
132b884b Andy McCall 1 month ago ↶
387
ui:: hash( format! ( " #{} " , t.state.short ( 12 ) ) ) ,
388
plural ( t.heads.len ( ) , " head" ) ,
389
t.signer.map ( | s | format! ( " , signed by {s} " ) ) .unwrap_or_else ( | | ui:: dim( " , unsigned" ) )
390
) ;
391
Ok ( 0 )
392
}
393
394
pub fn tag_list ( ) -> Result< i32 > {
395
let repo = open ( ) ? ;
396
let tags = repo.tags ( ) ? ;
397
if tags.is_empty ( ) {
398
println! ( " {} " , ui:: dim( " No tags." ) ) ;
399
}
400
for t in tags {
401
println! (
94084087 Andy McCall 1 month ago ↶
402
" {} {} {} {} {} {} " ,
403
ui:: ok( ui:: glyph( " ⚑" , " #" ) ) ,
404
ui:: bookmark( & t.name) ,
132b884b Andy McCall 1 month ago ↶
405
ui:: hash( format! ( " #{} " , t.state.short ( 12 ) ) ) ,
94084087 Andy McCall 1 month ago ↶
406
ui:: sep( ) + & ui:: dim( plural ( t.heads.len ( ) , " head" ) ) ,
407
if t.message.is_empty ( ) { String :: new( ) } else { format! ( " {} " , ui:: subject( subject_of ( & t.message) ) ) } ,
408
t.signer.map ( | s | ui:: sep( ) + & ui:: ok( format! ( " {} {s} " , ui:: glyph( " ✔" , " v" ) ) ) ) .unwrap_or_default ( )
132b884b Andy McCall 1 month ago ↶
409
) ;
410
}
411
Ok ( 0 )
412
}
4e88e774 Andy McCall 1 month ago ↶
413
414
pub fn workspace_add ( path : & std:: path:: Path, changes : & [String]) -> Result< i32 > {
415
let repo = open ( ) ? ;
416
let heads = if changes.is_empty ( ) {
417
None
418
} else {
419
let mut heads = Vec :: new( ) ;
420
for c in changes {
421
heads.extend ( repo.resolve_heads ( c) ? ) ;
422
}
423
Some ( heads)
424
} ;
425
let sp = ui:: spinner( & format! ( " Creating workspace at {} …" , path.display ( ) ) ) ;
426
let ws = repo.workspace_add ( path, heads.as_deref ( ) ) ? ;
94084087 Andy McCall 1 month ago ↶
427
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 ↶
428
Ok ( 0 )
429
}
430
431
pub fn workspace_list ( ) -> Result< i32 > {
432
let repo = open ( ) ? ;
433
for w in repo.workspaces ( ) ? {
434
println! (
435
" {} {} {} {} " ,
94084087 Andy McCall 1 month ago ↶
436
if w.current { ui:: ok( ui:: glyph( " ●" , " *" ) ) } else { ui:: dim( ui:: glyph( " ○" , " o" ) ) } ,
4e88e774 Andy McCall 1 month ago ↶
437
ui:: id( w.id.short ( 8 ) ) ,
438
ui:: path( w.path.display ( ) ) ,
94084087 Andy McCall 1 month ago ↶
439
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 ↶
440
) ;
441
}
442
Ok ( 0 )
443
}
444
445
pub fn workspace_forget ( spec : & str ) -> Result< i32 > {
446
let repo = open ( ) ? ;
447
let all = repo.workspaces ( ) ? ;
448
let target = std:: fs:: canonicalize( spec) .ok ( ) ;
449
let matches: Vec< _ > = all.iter ( ) .filter ( | w | w.id.starts_with_hex ( spec) | | target.as_ref ( ) .is_some_and ( | t | * t = = w.path) ) .collect ( ) ;
450
let w = match matches.as_slice ( ) {
451
[ w] => * w,
452
[ ] => anyhow:: bail! ( " no workspace matches {spec:?}" ) ,
453
_ => anyhow:: bail! ( " {spec:?} is ambiguous" ) ,
454
} ;
455
repo.workspace_forget ( & w.id) ? ;
94084087 Andy McCall 1 month ago ↶
456
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 ↶
457
Ok ( 0 )
458
}
cdb5beba Andy McCall 1 month ago ↶
459
460
pub fn lock ( path : & str ) -> Result< i32 > {
461
let repo = open ( ) ? ;
462
repo.lock_path ( path) ? ;
94084087 Andy McCall 1 month ago ↶
463
println! ( " {} {} {} " , ui:: done( " Locked" ) , ui:: path( path) , ui:: dim( " (advisory, local)" ) ) ;
cdb5beba Andy McCall 1 month ago ↶
464
Ok ( 0 )
465
}
466
467
pub fn unlock ( path : & str ) -> Result< i32 > {
468
let repo = open ( ) ? ;
469
repo.unlock_path ( path) ? ;
94084087 Andy McCall 1 month ago ↶
470
println! ( " {} {} " , ui:: done( " Unlocked" ) , ui:: path( path) ) ;
cdb5beba Andy McCall 1 month ago ↶
471
Ok ( 0 )
472
}
23b217d7 Andy McCall 1 month ago ↶
473
474
pub fn split ( change : & str , paths : & [String]) -> Result< i32 > {
475
let repo = open ( ) ? ;
476
let h = repo.resolve_change ( change) ? ;
477
let ( h1, h2) = repo.split ( & h, paths) ? ;
478
let c1 = repo.store ( ) .get_change ( & h1) ? ;
479
let c2 = repo.store ( ) .get_change ( & h2) ? ;
94084087 Andy McCall 1 month ago ↶
480
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 ↶
481
Ok ( 0 )
482
}