98286406 Andy McCall 1 month ago ↶
1
2
3
use super :: * ;
4
7aa2dc93 Andy McCall 1 month ago ↶
5
6
# [ derive ( Clone, Debug, Default ) ]
7
pub struct PushReport {
8
pub results : Vec< RefResult> ,
9
10
pub updated : Vec< RefUpdated> ,
11
12
pub imported : Vec< ChangeHash> ,
13
}
14
15
# [ derive ( Clone, Debug ) ]
16
pub struct RefUpdated {
17
pub name : String,
18
pub old_state : ObjectRef,
19
pub new_state : ObjectRef,
20
pub heads : Vec< ChangeHash> ,
21
}
22
83e0677b Andy McCall 1 month ago ↶
23
24
# [ derive ( Debug, Clone, PartialEq, Eq, Default ) ]
25
pub struct FetchReport {
26
27
pub refs : Vec< ( String, Vec< ChangeHash> , usize ) > ,
132b884b Andy McCall 1 month ago ↶
28
29
pub new_tags : usize ,
83e0677b Andy McCall 1 month ago ↶
30
31
32
pub unverified : bool ,
4b9d0677 Andy McCall 18 days ago ↶
33
34
35
36
pub log_notes : Vec< String> ,
d36ba2f7 Andy McCall 16 days ago ↶
37
38
39
40
41
pub skipped : Vec< ( String, String) > ,
83e0677b Andy McCall 1 month ago ↶
42
}
43
98286406 Andy McCall 1 month ago ↶
44
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
45
pub struct PullReport {
14edbaaf Andy McCall 1 month ago ↶
46
98286406 Andy McCall 1 month ago ↶
47
pub heads : Vec< ChangeHash> ,
14edbaaf Andy McCall 1 month ago ↶
48
98286406 Andy McCall 1 month ago ↶
49
pub changed : bool ,
50
pub checked_out : bool ,
14edbaaf Andy McCall 1 month ago ↶
51
52
53
pub others : Vec< String> ,
d36ba2f7 Andy McCall 16 days ago ↶
54
55
pub skipped : Vec< ( String, String) > ,
98286406 Andy McCall 1 month ago ↶
56
}
57
cb8b8082 Andy McCall 26 days ago ↶
58
59
60
pub const OBJECTS_RESULT : & str = " objects" ;
61
98286406 Andy McCall 1 month ago ↶
62
impl Repo {
63
64
65
fn ref_attest_path ( & self , bookmark : & str ) -> PathBuf {
54cd08a1 Andy McCall 1 month ago ↶
66
self .oxy.join ( " attest" ) .join ( name_file ( bookmark) )
98286406 Andy McCall 1 month ago ↶
67
}
68
69
70
pub fn latest_ref_attest ( & self , bookmark : & str ) -> Result< Option< ( ObjectRef, SignedAttest) > > {
71
match fs:: read_to_string( self .ref_attest_path ( bookmark) ) {
72
Ok ( h) => {
75167672 Andy McCall 1 month ago
73
let r = ObjectRef:: from_hex( h.trim ( ) )
74
.ok_or_else ( | | RepoError:: Msg( " corrupt attestation pointer" .into ( ) ) ) ? ;
75
Ok ( Some ( (
76
r,
77
SignedAttest {
78
attest: self .store.get_attest ( & r) ? ,
79
sigs: self .store.signatures ( & r) ? ,
80
} ,
81
) ) )
98286406 Andy McCall 1 month ago ↶
82
}
83
Err ( e) if e.kind ( ) = = std:: io:: ErrorKind:: NotFound => Ok ( None ) ,
84
Err ( e) => Err ( e.into ( ) ) ,
85
}
86
}
87
9f9e72b9 Andy McCall 1 month ago ↶
88
pub ( crate ) fn set_ref_attest ( & self , bookmark : & str , r : & ObjectRef) -> Result< ( ) > {
98286406 Andy McCall 1 month ago ↶
89
fs:: create_dir_all( self .oxy.join ( " attest" ) ) ? ;
90
write_atomic ( & self .ref_attest_path ( bookmark) , r.to_hex ( ) .as_bytes ( ) )
91
}
92
83e0677b Andy McCall 1 month ago ↶
93
pub fn remote_refs ( & self ) -> Result< RefsResponse> {
98286406 Andy McCall 1 month ago ↶
94
let view = self .view ( ) ? ;
95
let mut out = Vec :: new( ) ;
96
for ( name, state) in & view.bookmarks {
97
let latest = self .latest_ref_attest ( name) ? .map ( | ( _, a ) | a ) ;
98
let last_serial = latest.as_ref ( ) .and_then ( | a | match a.attest.body {
99
AttestBody:: Ref { serial, .. } => Some ( serial) ,
100
_ => None ,
101
} ) ;
102
let attestation = latest.filter ( | a | matches! ( & a.attest.body, AttestBody:: Ref { new_state, .. } if new_state = = state) ) ;
f53cbe7a Andy McCall 18 days ago ↶
103
let accepted = match & attestation {
104
Some ( a) => self .acceptance_of ( & a.attest.hash ( ) ) ? ,
105
None => None ,
106
} ;
75167672 Andy McCall 1 month ago
107
out.push ( RemoteRef {
108
name: name.clone ( ) ,
109
state: * state,
110
heads: self .store.get_state ( state) ? .heads,
111
attestation,
112
last_serial,
f53cbe7a Andy McCall 18 days ago ↶
113
accepted,
4b9d0677 Andy McCall 18 days ago ↶
114
logged: None ,
028545df Andy McCall 17 days ago ↶
115
integration: vec! [ ] ,
75167672 Andy McCall 1 month ago
116
} ) ;
98286406 Andy McCall 1 month ago ↶
117
}
28fc23d7 Andy McCall 1 month ago ↶
118
let policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp ) ;
75167672 Andy McCall 1 month ago
119
let history = self
120
.policy_history ( ) ?
121
.into_iter ( )
122
.filter ( | sp | {
123
policy
124
.as_ref ( )
125
.is_some_and ( | p | sp.policy.serial < p.policy.serial )
126
} )
127
.collect ( ) ;
128
Ok ( RefsResponse {
129
refs: out,
130
policy,
131
tags: self .tag_refs ( ) ? ,
132
history,
14edbaaf Andy McCall 1 month ago ↶
133
default_bookmark: Some ( self .config.bookmark.clone ( ) ) ,
4b9d0677 Andy McCall 18 days ago ↶
134
log: None ,
028545df Andy McCall 17 days ago ↶
135
witnesses: vec! [ ] ,
75167672 Andy McCall 1 month ago
136
} )
98286406 Andy McCall 1 month ago ↶
137
}
138
139
140
fn closure_in_store ( & self , heads : & [ChangeHash]) -> Result< BTreeSet< ChangeHash> > {
141
let mut seen = BTreeSet:: new( ) ;
142
let mut stack: Vec< ChangeHash> = heads.to_vec ( ) ;
143
while let Some ( h) = stack.pop ( ) {
144
if seen.contains ( & h) | | ! self .store.has ( & h) {
145
continue ;
146
}
147
seen.insert ( h) ;
69816d68 Andy McCall 1 month ago ↶
148
stack.extend ( self .store.get_change_meta ( & h) ? .deps) ;
98286406 Andy McCall 1 month ago ↶
149
}
150
Ok ( seen)
151
}
152
2863456a Andy McCall 1 month ago ↶
153
154
155
pub fn shallow_changes ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
156
let mut out = Vec :: new( ) ;
157
let mut seen: BTreeSet< ChangeHash> = BTreeSet:: new( ) ;
158
let mut layer: Vec< ChangeHash> = heads.to_vec ( ) ;
159
for _ in 0 .. depth.max ( 1 ) {
160
let mut next = Vec :: new( ) ;
161
for h in layer {
162
if ! seen.insert ( h) | | ! self .store.has ( & h) {
163
continue ;
164
}
165
let c = self .store.get_change ( & h) ? ;
166
next.extend ( c.deps.iter ( ) .copied ( ) ) ;
167
out.push ( h) ;
168
}
169
if next.is_empty ( ) {
170
break ;
171
}
172
layer = next;
173
}
174
out.reverse ( ) ;
175
Ok ( out)
176
}
177
178
fn shallow_path ( & self ) -> PathBuf {
179
self .oxy.join ( " shallow" )
180
}
181
182
183
pub fn boundary ( & self ) -> BTreeSet< ChangeHash> {
75167672 Andy McCall 1 month ago
184
fs:: read_to_string( self .shallow_path ( ) )
185
.map ( | t | t.lines ( ) .filter_map ( ObjectRef:: from_hex) .collect ( ) )
186
.unwrap_or_default ( )
2863456a Andy McCall 1 month ago ↶
187
}
188
189
pub fn is_shallow ( & self ) -> bool {
190
! self .boundary ( ) .is_empty ( )
191
}
192
193
fn write_boundary ( & self , boundary : & BTreeSet< ChangeHash> ) -> Result< ( ) > {
194
if boundary.is_empty ( ) {
195
let _ = fs:: remove_file( self .shallow_path ( ) ) ;
196
return Ok ( ( ) ) ;
197
}
75167672 Andy McCall 1 month ago
198
let text: String = boundary
199
.iter ( )
200
.map ( | h | format! ( " {} \n " , h.to_hex ( ) ) )
201
.collect ( ) ;
2863456a Andy McCall 1 month ago ↶
202
write_atomic ( & self .shallow_path ( ) , text.as_bytes ( ) )
203
}
204
205
206
207
fn refresh_boundary ( & self , imported : & [ChangeHash]) -> Result< ( ) > {
208
let mut boundary = self .boundary ( ) ;
209
boundary.retain ( | h | ! self .store.has ( h) ) ;
210
for h in imported {
69816d68 Andy McCall 1 month ago ↶
211
for d in self .store.get_change_meta ( h) ? .deps {
2863456a Andy McCall 1 month ago ↶
212
if ! self .store.has ( & d) {
213
boundary.insert ( d) ;
214
}
215
}
216
}
217
self .write_boundary ( & boundary)
218
}
219
220
221
pub fn deepen ( & self , remote_name : & str , depth : u32 ) -> Result< usize > {
222
let boundary = self .boundary ( ) ;
223
if boundary.is_empty ( ) {
224
return Ok ( 0 ) ;
225
}
226
let remote = self .open_named_remote ( remote_name) ? ;
227
let wanted = remote.shallow ( & boundary.iter ( ) .copied ( ) .collect:: < Vec< _ > > ( ) , depth) ? ;
228
let bundle = remote.fetch ( & wanted, self .config.max_blob) ? ;
229
let imported = self .import_bundle_shallow ( & bundle, false ) ? ;
230
if self .config.max_blob.is_some ( ) {
231
self .register_missing_blobs ( & imported, remote_name) ? ;
232
}
233
self .refresh_boundary ( & imported) ? ;
234
Ok ( imported.len ( ) )
235
}
236
98286406 Andy McCall 1 month ago ↶
237
238
pub fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
239
let have = self .closure_in_store ( have) ? ;
2863456a Andy McCall 1 month ago ↶
240
241
242
let boundary = self .boundary ( ) ;
98286406 Andy McCall 1 month ago ↶
243
let mut out = Vec :: new( ) ;
244
let mut done: BTreeSet< ChangeHash> = have.clone ( ) ;
2863456a Andy McCall 1 month ago ↶
245
done.extend ( boundary.iter ( ) .copied ( ) ) ;
98286406 Andy McCall 1 month ago ↶
246
247
let mut stack: Vec< ( ChangeHash, bool ) > = want.iter ( ) .rev ( ) .map ( | h | ( * h, false ) ) .collect ( ) ;
248
while let Some ( ( h, expanded) ) = stack.pop ( ) {
249
if done.contains ( & h) {
250
continue ;
251
}
252
if ! self .store.has ( & h) {
253
return fail ( format! ( " change {} is not in this repository" , h.short ( 12 ) ) ) ;
254
}
255
if expanded {
256
done.insert ( h) ;
257
out.push ( h) ;
258
continue ;
259
}
260
stack.push ( ( h, true ) ) ;
69816d68 Andy McCall 1 month ago ↶
261
for d in self .store.get_change_meta ( & h) ? .deps {
98286406 Andy McCall 1 month ago ↶
262
if ! done.contains ( & d) {
263
stack.push ( ( d, false ) ) ;
264
}
265
}
266
}
267
Ok ( out)
268
}
269
270
271
272
pub fn bundle_for ( & self , changes : & [ChangeHash]) -> Result< Bundle> {
d36ba2f7 Andy McCall 16 days ago ↶
273
self .bundle_of ( changes, None , false , false )
b3c5a59a Andy McCall 1 month ago ↶
274
}
275
276
277
pub fn objects_for ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
278
let mut out = Vec :: new( ) ;
279
for r in refs {
280
let ( ty, body) = match self .store.get ( r) {
281
Ok ( x) => x,
282
Err ( StoreError:: NotFound( _ ) ) => continue ,
283
Err ( e) => return Err ( e.into ( ) ) ,
284
} ;
285
if ty = = ObjectType:: Blob {
286
for c in Blob:: decode( & body) ? .chunks {
287
if let Ok ( ( cty, cb) ) = self .store.get ( & c) {
288
out.push ( ( cty, cb) ) ;
289
}
290
}
291
}
292
out.push ( ( ty, body) ) ;
293
}
294
Ok ( out)
295
}
296
297
298
d36ba2f7 Andy McCall 16 days ago ↶
299
300
301
302
75167672 Andy McCall 1 month ago
303
pub fn bundle_for_filtered (
304
& self ,
305
changes : & [ChangeHash],
306
max_blob : Option< u64 > ,
307
) -> Result< Bundle> {
d36ba2f7 Andy McCall 16 days ago ↶
308
self .bundle_of ( changes, max_blob, false , true )
2c75672f Andy McCall 1 month ago ↶
309
}
310
311
312
313
314
315
pub fn bundle_raw_for ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
d36ba2f7 Andy McCall 16 days ago ↶
316
self .bundle_of ( changes, max_blob, true , true )
2c75672f Andy McCall 1 month ago ↶
317
}
318
d36ba2f7 Andy McCall 16 days ago ↶
319
320
321
322
323
324
e10ba800 Andy McCall 16 days ago ↶
325
326
327
328
329
d36ba2f7 Andy McCall 16 days ago ↶
330
pub fn served_attest_signers ( & self ) -> Result< BTreeSet< KeyId> > {
e10ba800 Andy McCall 16 days ago ↶
331
type Signers = std:: collections:: HashMap< ( PathBuf, ObjectRef) , BTreeSet< KeyId> > ;
332
static SERVED : std:: sync:: Mutex< Option< Signers> > = std:: sync:: Mutex:: new( None ) ;
333
let key = ( self .oxy.clone ( ) , self .head_op ( ) ? .0 ) ;
334
if let Some ( keys) = SERVED
335
.lock ( )
336
.unwrap_or_else ( | p | p.into_inner ( ) )
337
.as_ref ( )
338
.and_then ( | m | m.get ( & key) )
339
{
340
return Ok ( keys.clone ( ) ) ;
341
}
342
let keys = self .attest_signers_now ( ) ? ;
343
let mut served = SERVED .lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) ;
344
let map = served.get_or_insert_with ( Default :: default) ;
345
346
347
map.retain ( | ( oxy , _) , _| * oxy ! = key.0 ) ;
348
if map.len ( ) > = 1024 {
349
map.clear ( ) ;
350
}
351
map.insert ( key, keys.clone ( ) ) ;
352
Ok ( keys)
353
}
354
355
fn attest_signers_now ( & self ) -> Result< BTreeSet< KeyId> > {
d36ba2f7 Andy McCall 16 days ago ↶
356
let view = self .view ( ) ? ;
357
let pointers = view
358
.bookmarks
359
.keys ( )
360
.map ( | b | self .ref_attest_path ( b) )
361
.chain ( view.tags.keys ( ) .map ( | t | self .tag_attest_path ( t) ) ) ;
362
let mut keys = BTreeSet:: new( ) ;
363
for p in pointers {
364
let Ok ( text) = fs:: read_to_string( & p) else {
365
continue ;
366
} ;
367
let Some ( r) = ObjectRef:: from_hex( text.trim ( ) ) else {
368
continue ;
369
} ;
370
keys.extend ( self .store.signatures ( & r) ? .into_iter ( ) .map ( | s | s.key ) ) ;
371
}
372
Ok ( keys)
373
}
374
375
376
2c75672f Andy McCall 1 month ago ↶
377
fn bundle_of (
378
& self ,
379
changes : & [ChangeHash],
380
max_blob : Option< u64 > ,
381
raw : bool ,
d36ba2f7 Andy McCall 16 days ago ↶
382
serving : bool ,
2c75672f Andy McCall 1 month ago ↶
383
) -> Result< Bundle> {
98286406 Andy McCall 1 month ago ↶
384
let mut bundle = Bundle:: default( ) ;
385
let mut objects: BTreeSet< ObjectRef> = BTreeSet:: new( ) ;
386
let mut seals: BTreeSet< ObjectRef> = BTreeSet:: new( ) ;
387
let add_object = | r : & ObjectRef, objects : & mut BTreeSet< ObjectRef> | {
388
if ! r.is_zero ( ) & & self .store.has ( r) {
389
objects.insert ( * r) ;
390
}
391
} ;
392
for h in changes {
2c75672f Andy McCall 1 month ago ↶
393
394
let ( c, encoded) = if raw {
395
let bytes = self .store.get_typed ( h, ObjectType:: Change) ? ;
396
let ( skel, texts) = Change:: decode_skeleton( & bytes, * h) ? ;
397
for t in texts {
0f93f49c Andy McCall 1 month ago ↶
398
if let crate :: objects:: TextRef:: Blob( r, _ , _ ) = t {
2c75672f Andy McCall 1 month ago ↶
399
let blob = Blob:: decode( & self .store.get_typed ( & r, ObjectType:: Blob) ? ) ? ;
400
objects.insert ( r) ;
401
objects.extend ( blob.chunks) ;
402
}
403
}
404
( skel, Some ( bytes) )
405
} else {
406
( self .store.get_change ( h) ? , None )
407
} ;
98286406 Andy McCall 1 month ago ↶
408
let sigs = self .store.signatures ( h) ? ;
409
add_object ( & c.author, & mut objects) ;
410
for s in & sigs {
411
add_object ( & s.key, & mut objects) ;
412
}
413
for e in & c.edits {
414
if let Edit:: Replace { old, new, .. } = e {
415
for b in [ old, new] .into_iter ( ) .flatten ( ) {
416
if self .store.has ( b) {
75167672 Andy McCall 1 month ago
417
if let Ok ( blob) =
418
Blob:: decode( & self .store.get_typed ( b, ObjectType:: Blob) ? )
419
{
b3c5a59a Andy McCall 1 month ago ↶
420
if max_blob.is_some_and ( | m | blob.length > m ) {
421
continue ;
422
}
423
objects.insert ( * b) ;
98286406 Andy McCall 1 month ago ↶
424
objects.extend ( blob.chunks.iter ( ) .copied ( ) ) ;
425
}
426
}
427
}
428
}
429
}
430
for s in self .store.seals_for ( h) ? {
431
seals.insert ( s) ;
2c75672f Andy McCall 1 month ago ↶
432
}
433
match encoded {
434
Some ( bytes) => bundle.raw_changes.push ( ( bytes, sigs) ) ,
435
None => {
436
bundle.changes.push ( ( c, sigs) ) ;
437
bundle.hashes.push ( * h) ;
438
}
98286406 Andy McCall 1 month ago ↶
439
}
440
}
441
for r in seals {
442
let attest = self .store.get_attest ( & r) ? ;
443
let sigs = self .store.signatures ( & r) ? ;
444
for s in & sigs {
445
add_object ( & s.key, & mut objects) ;
446
}
447
bundle.attests.push ( SignedAttest { attest, sigs } ) ;
448
}
d36ba2f7 Andy McCall 16 days ago ↶
449
let signers: Vec< KeyId> = if serving {
450
let signers = self .served_attest_signers ( ) ? ;
451
for k in & signers {
452
add_object ( k, & mut objects) ;
453
}
454
signers.into_iter ( ) .collect ( )
455
} else {
456
Vec :: new( )
457
} ;
98286406 Andy McCall 1 month ago ↶
458
for r in objects {
459
let ( ty, body) = self .store.get ( & r) ? ;
460
bundle.objects.push ( ( ty, body) ) ;
461
}
d36ba2f7 Andy McCall 16 days ago ↶
462
self .attach_certificates_for ( & mut bundle, & signers) ? ;
98286406 Andy McCall 1 month ago ↶
463
Ok ( bundle)
464
}
465
f0a517d2 Andy McCall 18 days ago ↶
466
467
468
469
470
pub ( crate ) fn attach_certificates_for (
471
& self ,
472
bundle : & mut Bundle,
473
also : & [KeyId],
474
) -> Result< ( ) > {
475
let mut keys: BTreeSet< KeyId> = also.iter ( ) .copied ( ) .collect ( ) ;
476
keys.extend (
477
bundle
478
.changes
479
.iter ( )
480
.flat_map ( | ( _, s ) | s.iter ( ) .map ( | s | s.key ) ) ,
481
) ;
482
keys.extend (
483
bundle
484
.raw_changes
485
.iter ( )
486
.flat_map ( | ( _, s ) | s.iter ( ) .map ( | s | s.key ) ) ,
487
) ;
488
keys.extend (
489
bundle
490
.attests
491
.iter ( )
492
.flat_map ( | a | a.sigs.iter ( ) .map ( | s | s.key ) ) ,
493
) ;
494
for k in keys {
495
let Some ( r) = self .store.certificate_of ( & k) ? else {
496
continue ;
497
} ;
498
let cert = self .store.get_certificate ( & r) ? ;
499
if bundle
500
.certificates
501
.iter ( )
502
.any ( | c | c.cert.hash ( ) = = cert.cert.hash ( ) )
503
{
504
continue ;
505
}
506
for id in [ & k, & cert.cert.issuer] {
507
if let Ok ( body) = self .store.get_typed ( id, ObjectType:: Identity) {
508
if ! bundle
509
.objects
510
.iter ( )
511
.any ( | ( t , b ) | * t = = ObjectType:: Identity & & * b = = body)
512
{
513
bundle.objects.push ( ( ObjectType:: Identity, body) ) ;
514
}
515
}
516
}
517
bundle.certificates.push ( cert) ;
518
}
519
Ok ( ( ) )
520
}
521
522
523
524
fn adopt_certificates ( & self , certs : & [crate:: cert:: SignedCertificate]) -> Result< ( ) > {
525
for c in certs {
526
let issuer = self .store.get_identity ( & c.cert.issuer) .map_err ( | _| {
527
RepoError:: Msg( format! (
528
" a certificate names issuer {} , whose identity did not come with it" ,
529
c.cert.issuer.short ( 12 )
530
) )
531
} ) ? ;
532
c.verify ( & issuer)
533
.map_err ( | e | RepoError:: Msg( format! ( " a certificate does not verify: {e} " ) ) ) ? ;
534
self .store.put_identity ( & c.cert.key) ? ;
535
self .store.put_certificate ( c) ? ;
536
}
537
Ok ( ( ) )
538
}
539
540
541
542
543
544
pub ( crate ) fn certified_authorized (
545
& self ,
546
p : & crate:: policy:: Policy,
547
signer : & KeyId,
548
reference : & str ,
549
scope : Scope,
550
at_ms : u64 ,
551
) -> bool {
552
let Ok ( Some ( r) ) = self .store.certificate_of ( signer) else {
553
return false ;
554
} ;
555
let Ok ( c) = self .store.get_certificate ( & r) else {
556
return false ;
557
} ;
558
let Some ( issuer) = p
559
.identity ( & c.cert.issuer)
560
.cloned ( )
561
.or_else ( | | self .store.get_identity ( & c.cert.issuer) .ok ( ) )
562
else {
563
return false ;
564
} ;
565
c.verify ( & issuer) .is_ok ( )
566
& & c.cert.key_id ( ) = = * signer
567
& & c.cert.covers ( scope, at_ms)
568
& & p.subject_grant ( reference, & c.cert.issuer, & c.cert.subject, scope)
569
}
570
98286406 Andy McCall 1 month ago ↶
571
572
573
pub fn import_bundle ( & self , bundle : & Bundle, strict : bool ) -> Result< Vec< ChangeHash> > {
2863456a Andy McCall 1 month ago ↶
574
self .import_bundle_inner ( bundle, strict, false )
575
}
576
577
578
579
pub fn import_bundle_shallow ( & self , bundle : & Bundle, strict : bool ) -> Result< Vec< ChangeHash> > {
580
self .import_bundle_inner ( bundle, strict, true )
581
}
582
75167672 Andy McCall 1 month ago
583
fn import_bundle_inner (
584
& self ,
585
bundle : & Bundle,
586
strict : bool ,
587
shallow : bool ,
588
) -> Result< Vec< ChangeHash> > {
8f10a07a Andy McCall 1 month ago ↶
589
let batch = self .store.begin_batch ( ) ? ;
590
let result = self .import_bundle_batched ( bundle, strict, shallow) ;
591
batch.finish ( ) ? ;
592
result
593
}
594
75167672 Andy McCall 1 month ago
595
fn import_bundle_batched (
596
& self ,
597
bundle : & Bundle,
598
strict : bool ,
599
shallow : bool ,
600
) -> Result< Vec< ChangeHash> > {
2c75672f Andy McCall 1 month ago ↶
601
for ( ty, _ ) in & bundle.objects {
602
if ! matches! (
603
ty,
75167672 Andy McCall 1 month ago
604
ObjectType:: Chunk
2c75672f Andy McCall 1 month ago ↶
605
| ObjectType:: Blob
606
| ObjectType:: Identity
607
| ObjectType:: State
608
| ObjectType:: Checkpoint
609
) {
610
return fail ( format! ( " bundle may not carry {} objects" , ty.name ( ) ) ) ;
98286406 Andy McCall 1 month ago ↶
611
}
612
}
2c75672f Andy McCall 1 month ago ↶
613
614
let objects: Vec< ( ObjectType, & [u8 ]) > = bundle
615
.objects
616
.iter ( )
617
.map ( | ( ty , body ) | ( * ty, body.as_slice ( ) ) )
618
.collect ( ) ;
619
self .store.put_many ( & objects) ? ;
f0a517d2 Andy McCall 18 days ago ↶
620
self .adopt_certificates ( & bundle.certificates) ? ;
98286406 Andy McCall 1 month ago ↶
621
for sa in & bundle.attests {
622
let r = self .store.put_attest ( & sa.attest) ? ;
623
for sig in & sa.sigs {
624
self .store.put_signature ( & r, sig) ? ;
625
}
626
if let AttestBody:: Seal { hashes } = & sa.attest.body {
627
for h in hashes {
628
self .store.index_seal ( h, & r) ? ;
629
}
630
}
631
}
598009a7 Andy McCall 1 month ago ↶
632
633
634
635
let hashes: Vec< ChangeHash> = if bundle.hashes.len ( ) = = bundle.changes.len ( ) {
636
bundle.hashes.clone ( )
637
} else {
638
bundle.changes.iter ( ) .map ( | ( c , _) | c.hash ( ) ) .collect ( )
639
} ;
2c75672f Andy McCall 1 month ago ↶
640
let raw_hashes: Vec< ChangeHash> = bundle
641
.raw_changes
642
.iter ( )
643
.map ( | ( b , _) | hash_object ( ObjectType:: Change, b) )
644
.collect ( ) ;
645
let incoming: BTreeSet< ChangeHash> = hashes.iter ( ) .chain ( & raw_hashes) .copied ( ) .collect ( ) ;
2863456a Andy McCall 1 month ago ↶
646
let boundary = self .boundary ( ) ;
98286406 Andy McCall 1 month ago ↶
647
let mut imported = Vec :: new( ) ;
2c75672f Andy McCall 1 month ago ↶
648
649
650
for ( ( bytes, sigs) , h) in bundle.raw_changes.iter ( ) .zip ( raw_hashes) {
651
let ( skel, texts) = Change:: decode_skeleton( bytes, h) ? ;
652
for d in & skel.deps {
653
if ! self .store.has ( d) & & ! incoming.contains ( d) & & ! shallow & & ! boundary.contains ( d)
654
{
655
return fail ( format! (
656
" change {} depends on {} , which is neither stored nor in the bundle" ,
657
h.short ( 12 ) ,
658
d.short ( 12 )
659
) ) ;
660
}
661
}
662
for t in texts {
0f93f49c Andy McCall 1 month ago ↶
663
if let crate :: objects:: TextRef:: Blob( r, _ , _ ) = t {
2c75672f Andy McCall 1 month ago ↶
664
if ! self .store.has ( & r) {
665
return fail ( format! (
666
" change {} came without its text blob {} " ,
667
h.short ( 12 ) ,
668
r.short ( 12 )
669
) ) ;
670
}
671
}
672
}
673
self .store.put ( ObjectType:: Change, bytes) ? ;
674
for sig in sigs {
675
self .store.put_signature ( & h, sig) ? ;
676
}
677
imported.push ( h) ;
678
}
598009a7 Andy McCall 1 month ago ↶
679
for ( ( c, sigs) , h) in bundle.changes.iter ( ) .zip ( hashes) {
98286406 Andy McCall 1 month ago ↶
680
for d in & c.deps {
75167672 Andy McCall 1 month ago
681
if ! self .store.has ( d) & & ! incoming.contains ( d) & & ! shallow & & ! boundary.contains ( d)
682
{
683
return fail ( format! (
684
" change {} depends on {} , which is neither stored nor in the bundle" ,
685
h.short ( 12 ) ,
686
d.short ( 12 )
687
) ) ;
98286406 Andy McCall 1 month ago ↶
688
}
689
}
598009a7 Andy McCall 1 month ago ↶
690
let stored = self .store.put_change ( c) ? ;
691
if stored ! = h {
692
return fail ( format! (
693
" change {} is not the change the bundle names" ,
694
h.short ( 12 )
695
) ) ;
696
}
98286406 Andy McCall 1 month ago ↶
697
for sig in sigs {
698
self .store.put_signature ( & h, sig) ? ;
699
}
700
imported.push ( h) ;
701
}
702
if strict {
703
for h in & imported {
704
if ! matches! ( self .authenticate_change ( h) ? , SignatureStatus:: Valid { .. } ) {
75167672 Andy McCall 1 month ago
705
return fail ( format! (
706
" change {} is not authenticated by its author" ,
707
h.short ( 12 )
708
) ) ;
98286406 Andy McCall 1 month ago ↶
709
}
710
}
711
}
712
Ok ( imported)
713
}
714
715
716
717
84b9e9ac Andy McCall 1 month ago ↶
718
719
7ba155f2 Andy McCall 1 month ago ↶
720
721
722
eae59556 Andy McCall 1 month ago ↶
723
pub ( crate ) fn delegate_authorized (
724
& self ,
725
p : & crate:: policy:: Policy,
726
signer : & KeyId,
727
reference : & str ,
7ba155f2 Andy McCall 1 month ago ↶
728
at_ms : u64 ,
eae59556 Andy McCall 1 month ago ↶
729
) -> bool {
84b9e9ac Andy McCall 1 month ago ↶
730
if p.is_revoked ( signer) {
731
return false ;
732
}
eae59556 Andy McCall 1 month ago ↶
733
let Some ( rule) = p.rule_for ( reference) else {
734
return false ;
735
} ;
84b9e9ac Andy McCall 1 month ago ↶
736
rule.keys.iter ( ) .any ( | k | {
737
! p.is_revoked ( k)
738
& & p.identity ( k)
739
.cloned ( )
740
.or_else ( | | self .store.get_identity ( k) .ok ( ) )
7ba155f2 Andy McCall 1 month ago ↶
741
.is_some_and ( | id | id.delegates_to ( signer, Scope:: REFS , at_ms) )
84b9e9ac Andy McCall 1 month ago ↶
742
} )
743
}
744
e5d9b051 Andy McCall 19 days ago ↶
745
746
747
748
749
750
751
pub ( crate ) fn predecessor_authorized (
752
& self ,
753
p : & crate:: policy:: Policy,
754
signer : & KeyId,
755
reference : & str ,
756
at_ms : u64 ,
757
) -> bool {
758
if p.is_revoked ( signer) {
759
return false ;
760
}
761
let Some ( rule) = p.rule_for ( reference) else {
762
return false ;
763
} ;
764
let identity = | k : & KeyId| {
765
p.identity ( k)
766
.cloned ( )
767
.or_else ( | | self .store.get_identity ( k) .ok ( ) )
768
} ;
769
rule.keys.iter ( ) .any ( | k | {
770
if p.is_revoked ( k) {
771
return false ;
772
}
773
let mut cur = identity ( k) ;
774
for _ in 0 .. 32 {
775
let Some ( id) = cur else {
776
return false ;
777
} ;
778
match id.previous {
779
Some ( prev) if prev = = * signer => return at_ms < id.created,
780
Some ( prev) if ! p.is_revoked ( & prev) => cur = identity ( & prev) ,
781
_ => return false ,
782
}
783
}
784
false
785
} )
786
}
787
75167672 Andy McCall 1 month ago
788
fn check_ref_attest (
d36ba2f7 Andy McCall 16 days ago ↶
789
& self ,
790
sa : & SignedAttest,
791
name : & str ,
792
old_state : & ObjectRef,
793
new_state : & ObjectRef,
794
last_serial : Option< u64 > ,
795
allow_equal : bool ,
796
) -> std:: result:: Result< u64 , String> {
797
let serial =
798
self .check_ref_attest_body ( sa, name, old_state, new_state, last_serial, allow_equal) ? ;
799
self .verify_attest_signature ( sa) ? ;
800
Ok ( serial)
801
}
802
803
804
805
806
fn check_ref_attest_body (
75167672 Andy McCall 1 month ago
807
& self ,
808
sa : & SignedAttest,
809
name : & str ,
810
old_state : & ObjectRef,
811
new_state : & ObjectRef,
812
last_serial : Option< u64 > ,
813
allow_equal : bool ,
814
) -> std:: result:: Result< u64 , String> {
815
let AttestBody:: Ref {
816
name: n,
817
old_state: o,
818
new_state: nw,
819
serial,
820
} = & sa.attest.body
821
else {
98286406 Andy McCall 1 month ago ↶
822
return Err ( " attestation is not a ref attestation" .into ( ) ) ;
823
} ;
824
if n ! = name | | o ! = old_state | | nw ! = new_state {
825
return Err ( " attestation does not describe this update" .into ( ) ) ;
826
}
827
if sa.attest.repo ! = self .id ( ) .map_err ( | e | e.to_string ( ) ) ? {
828
return Err ( " attestation is for a different repository" .into ( ) ) ;
829
}
830
if let Some ( last) = last_serial {
831
if * serial < last | | ( * serial = = last & & ! allow_equal) {
75167672 Andy McCall 1 month ago
832
return Err ( format! (
833
" attestation serial {serial} is not greater than the last seen {last} "
834
) ) ;
98286406 Andy McCall 1 month ago ↶
835
}
836
}
d36ba2f7 Andy McCall 16 days ago ↶
837
Ok ( * serial)
838
}
839
840
841
842
fn verify_attest_signature ( & self , sa : & SignedAttest) -> std:: result:: Result< ( ) , String> {
98286406 Andy McCall 1 month ago ↶
843
let r = sa.attest.hash ( ) ;
844
let sig = sa.sigs.first ( ) .ok_or ( " attestation carries no signature" ) ? ;
75167672 Andy McCall 1 month ago
845
let id = self
846
.store
847
.get_identity ( & sig.key)
848
.map_err ( | _| format! ( " unknown key {} " , sig.key.short ( 12 ) ) ) ? ;
d36ba2f7 Andy McCall 16 days ago ↶
849
verify_signature ( & id, sig, Domain:: Attest, & r) .map_err ( | e | e.to_string ( ) )
98286406 Andy McCall 1 month ago ↶
850
}
851
83e0677b Andy McCall 1 month ago ↶
852
98286406 Andy McCall 1 month ago ↶
853
83e0677b Andy McCall 1 month ago ↶
854
855
856
75167672 Andy McCall 1 month ago
857
pub fn receive_push (
858
& self ,
859
bundle : Bundle,
860
updates : Vec< RefUpdate> ,
861
policy : Option< SignedPolicy> ,
862
tags : Vec< crate:: protocol:: TagRef> ,
863
) -> Result< Vec< RefResult> > {
119bbf50 Andy McCall 1 month ago ↶
864
Ok ( self
865
.receive_push_report ( bundle, updates, policy, tags) ?
866
.results )
7aa2dc93 Andy McCall 1 month ago ↶
867
}
868
869
870
871
872
pub fn receive_push_report (
873
& self ,
874
bundle : Bundle,
875
updates : Vec< RefUpdate> ,
876
policy : Option< SignedPolicy> ,
877
tags : Vec< crate:: protocol:: TagRef> ,
878
) -> Result< PushReport> {
767ac6cd Andy McCall 17 days ago
879
880
881
882
self .refresh ( ) ? ;
7aa2dc93 Andy McCall 1 month ago ↶
883
let mut report = PushReport:: default( ) ;
884
let results = & mut report.results;
83e0677b Andy McCall 1 month ago ↶
885
if let Some ( sp) = policy {
28fc23d7 Andy McCall 1 month ago ↶
886
match self .adopt_policy_chain ( & bundle.policies, & sp, " push received" ) {
83e0677b Andy McCall 1 month ago ↶
887
Ok ( ( ) ) => {
75167672 Andy McCall 1 month ago
888
results.push ( RefResult {
889
name: " policy" .into ( ) ,
890
ok: true ,
891
reason: format! ( " serial {} " , sp.policy.serial) ,
892
} ) ;
83e0677b Andy McCall 1 month ago ↶
893
}
894
Err ( e) => {
75167672 Andy McCall 1 month ago
895
results.push ( RefResult {
896
name: " policy" .into ( ) ,
897
ok: false ,
898
reason: e,
899
} ) ;
83e0677b Andy McCall 1 month ago ↶
900
for u in updates {
75167672 Andy McCall 1 month ago
901
results.push ( RefResult {
902
name: u.name,
903
ok: false ,
904
reason: " policy update was rejected" .into ( ) ,
905
} ) ;
83e0677b Andy McCall 1 month ago ↶
906
}
7aa2dc93 Andy McCall 1 month ago ↶
907
return Ok ( report) ;
83e0677b Andy McCall 1 month ago ↶
908
}
909
}
910
}
911
let policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp.policy ) ;
cb8b8082 Andy McCall 26 days ago ↶
912
913
914
let objects_only = updates.is_empty ( ) & & tags.is_empty ( ) ;
7aa2dc93 Andy McCall 1 month ago ↶
915
match self .import_bundle ( & bundle, policy.is_some ( ) ) {
cb8b8082 Andy McCall 26 days ago ↶
916
Ok ( imported) => {
917
if objects_only {
918
results.push ( RefResult {
919
name: OBJECTS_RESULT .into ( ) ,
920
ok: true ,
921
reason: format! ( " {} changes stored" , imported.len ( ) ) ,
922
} ) ;
923
}
924
report.imported = imported;
925
}
7aa2dc93 Andy McCall 1 month ago ↶
926
Err ( e) => {
cb8b8082 Andy McCall 26 days ago ↶
927
if objects_only {
928
results.push ( RefResult {
929
name: OBJECTS_RESULT .into ( ) ,
930
ok: false ,
931
reason: e.to_string ( ) ,
932
} ) ;
933
}
7aa2dc93 Andy McCall 1 month ago ↶
934
for u in updates {
935
results.push ( RefResult {
936
name: u.name,
937
ok: false ,
938
reason: e.to_string ( ) ,
939
} ) ;
940
}
941
return Ok ( report) ;
83e0677b Andy McCall 1 month ago ↶
942
}
943
}
98286406 Andy McCall 1 month ago ↶
944
let ( op_ref, op) = self .head_op ( ) ? ;
945
let mut view = op.view.clone ( ) ;
946
let mut names = Vec :: new( ) ;
947
for u in updates {
83e0677b Andy McCall 1 month ago ↶
948
let outcome = self .apply_update ( & mut view, & u, policy.as_ref ( ) ) ;
98286406 Andy McCall 1 month ago ↶
949
let ( ok, reason) = match outcome {
950
Ok ( ( ) ) => ( true , String :: new( ) ) ,
951
Err ( e) => ( false , e) ,
952
} ;
953
if ok {
954
names.push ( u.name.clone ( ) ) ;
119bbf50 Andy McCall 1 month ago ↶
955
let heads = self
956
.store
957
.get_state ( & u.new_state)
958
.map ( | s | s.heads )
959
.unwrap_or_default ( ) ;
960
report.updated.push ( RefUpdated {
961
name: u.name.clone ( ) ,
962
old_state: u.old_state,
963
new_state: u.new_state,
964
heads,
965
} ) ;
98286406 Andy McCall 1 month ago ↶
966
}
75167672 Andy McCall 1 month ago
967
results.push ( RefResult {
968
name: u.name,
969
ok,
970
reason,
971
} ) ;
98286406 Andy McCall 1 month ago ↶
972
}
132b884b Andy McCall 1 month ago ↶
973
for t in & tags {
974
let name = format! ( " tags/{} " , t.name) ;
b2ef93bb Andy McCall 1 month ago ↶
975
let new = ! view.tags.contains_key ( & t.name) ;
132b884b Andy McCall 1 month ago ↶
976
match self .adopt_tag ( & mut view, t, policy.as_ref ( ) ) {
977
Ok ( ( ) ) => {
b2ef93bb Andy McCall 1 month ago ↶
978
979
980
981
if new {
982
report.updated.push ( RefUpdated {
983
name: name.clone ( ) ,
984
old_state: ObjectRef:: ZERO ,
985
new_state: t.state,
986
heads: t.heads.clone ( ) ,
987
} ) ;
988
}
132b884b Andy McCall 1 month ago ↶
989
names.push ( name.clone ( ) ) ;
75167672 Andy McCall 1 month ago
990
results.push ( RefResult {
991
name,
992
ok: true ,
993
reason: String :: new( ) ,
994
} ) ;
132b884b Andy McCall 1 month ago ↶
995
}
75167672 Andy McCall 1 month ago
996
Err ( e) => results.push ( RefResult {
997
name,
998
ok: false ,
999
reason: e,
1000
} ) ,
132b884b Andy McCall 1 month ago ↶
1001
}
1002
}
98286406 Andy McCall 1 month ago ↶
1003
if ! names.is_empty ( ) {
75167672 Andy McCall 1 month ago
1004
self .record_op (
1005
& [ op_ref] ,
1006
& format! ( " push received: {} " , names.join ( " " ) ) ,
1007
view,
1008
) ? ;
98286406 Andy McCall 1 month ago ↶
1009
}
7aa2dc93 Andy McCall 1 month ago ↶
1010
Ok ( report)
98286406 Andy McCall 1 month ago ↶
1011
}
1012
be451356 Andy McCall 1 month ago ↶
1013
1014
1015
1016
1017
fn record_update_attest (
1018
& self ,
1019
u : & RefUpdate,
1020
sa : & SignedAttest,
1021
) -> std:: result:: Result< ( ) , String> {
1022
let name = & u.name;
1023
let last = self
1024
.latest_ref_attest ( name)
1025
.map_err ( | e | format! ( " reading the last attestation of {name:?} : {e} " ) ) ?
1026
.and_then ( | ( _, a ) | match a.attest.body {
1027
AttestBody:: Ref { serial, .. } => Some ( serial) ,
1028
_ => None ,
1029
} ) ;
1030
self .check_ref_attest ( sa, name, & u.old_state, & u.new_state, last, false ) ? ;
1031
let store = | e : & dyn std:: fmt:: Display| format! ( " storing the attestation of {name:?} : {e} " ) ;
1032
let r = self .store.put_attest ( & sa.attest) .map_err ( | e | store ( & e) ) ? ;
1033
for sig in & sa.sigs {
1034
self .store.put_signature ( & r, sig) .map_err ( | e | store ( & e) ) ? ;
f53cbe7a Andy McCall 18 days ago ↶
1035
}
1036
self .set_ref_attest ( name, & r) .map_err ( | e | store ( & e) ) ? ;
1037
1038
let certified = sa
1039
.sigs
1040
.first ( )
1041
.is_some_and ( | s | self .store.certificate_of ( & s.key) .ok ( ) .flatten ( ) .is_some ( ) ) ;
1042
if certified {
1043
self .record_acceptance ( & r) .map_err ( | e | store ( & e) ) ? ;
be451356 Andy McCall 1 month ago ↶
1044
}
f53cbe7a Andy McCall 18 days ago ↶
1045
Ok ( ( ) )
be451356 Andy McCall 1 month ago ↶
1046
}
1047
f53cbe7a Andy McCall 18 days ago ↶
1048
1049
1050
pub ( crate ) fn record_acceptance ( & self , r : & ObjectRef) -> Result< ( ) > {
1051
let Some ( acceptor) = & self .acceptor else {
1052
return Ok ( ( ) ) ;
1053
} ;
1054
let ( id, sk) = ( & acceptor.0 , & acceptor.1 ) ;
1055
let attest = Attest {
1056
repo: self .id ( ) ? ,
1057
hlc: Hlc:: now( ) ,
1058
body: AttestBody:: Accepted { attest: * r } ,
1059
} ;
1060
let sig = sk.sign ( Domain:: Attest, & id.hash ( ) , & attest.hash ( ) ) ;
1061
self .store.put_identity ( id) ? ;
1062
let a = self .store.put_attest ( & attest) ? ;
1063
self .store.put_signature ( & a, & sig) ? ;
1064
self .store.index_add_pub ( " accepted" , r, & a) ? ;
1065
Ok ( ( ) )
1066
}
1067
1068
1069
pub fn acceptance_of ( & self , r : & ObjectRef) -> Result< Option< SignedAttest> > {
1070
let Some ( a) = self .store.index_get_pub ( " accepted" , r) ? .into_iter ( ) .next ( ) else {
1071
return Ok ( None ) ;
1072
} ;
1073
Ok ( Some ( SignedAttest {
1074
attest: self .store.get_attest ( & a) ? ,
1075
sigs: self .store.signatures ( & a) ? ,
1076
} ) )
1077
}
1078
1079
1080
1081
1082
1083
pub ( crate ) fn certified_time (
1084
& self ,
1085
signer : & KeyId,
1086
sa : & SignedAttest,
1087
accepted : Option< & SignedAttest> ,
1088
) -> u64 {
1089
let own = sa.attest.hlc.wall_ms;
1090
let Some ( acc) = accepted else {
1091
return own;
1092
} ;
1093
let Ok ( Some ( c) ) = self .store.certificate_of ( signer) else {
1094
return own;
1095
} ;
1096
let Ok ( cert) = self .store.get_certificate ( & c) else {
1097
return own;
1098
} ;
1099
let AttestBody:: Accepted { attest } = & acc.attest.body else {
1100
return own;
1101
} ;
1102
let Some ( sig) = acc.sigs.iter ( ) .find ( | s | s.key = = cert.cert.issuer ) else {
1103
return own;
1104
} ;
1105
let Ok ( issuer) = self .store.get_identity ( & cert.cert.issuer) else {
1106
return own;
1107
} ;
1108
if * attest = = sa.attest.hash ( )
1109
& & verify_signature ( & issuer, sig, Domain:: Attest, & acc.attest.hash ( ) ) .is_ok ( )
1110
{
1111
acc.attest.hlc.wall_ms
1112
} else {
1113
own
1114
}
1115
}
1116
028545df Andy McCall 17 days ago ↶
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
# [ allow ( clippy::too_many_arguments ) ]
1127
pub ( crate ) fn authorize_in_time (
1128
& self ,
1129
p : & crate:: policy:: Policy,
1130
remote_name : & str ,
1131
r : & crate:: protocol:: RemoteRef,
1132
sa : & SignedAttest,
1133
signer : & KeyId,
1134
log : & super:: transparency:: LogCheck,
1135
notes : & mut Vec< String> ,
1136
) -> Result< ( ) > {
1137
use super :: transparency:: { gap_note, judge, Timed} ;
1138
let own = sa.attest.hlc.wall_ms;
1139
let at = self .certified_time ( signer, sa, r.accepted.as_ref ( ) ) ;
1140
let time = log.time_of ( & r.name) ;
1141
let check = | t_delegate : u64 , t_cert : u64 | {
1142
self .delegate_authorized ( p, signer, & r.name, t_delegate)
1143
| | self .certified_authorized ( p, signer, & r.name, Scope:: REFS , t_cert)
1144
} ;
1145
let before = if at ! = own {
1146
" the host's acceptance"
1147
} else {
1148
" the time it claims"
1149
} ;
1150
let what = format! ( " {remote_name} /{} " , r.name) ;
1151
match judge ( time.as_ref ( ) .ok ( ) .map ( | i | i.ms ( ) ) , | t | check ( t, t) , | | check ( own, at) ) {
1152
Timed:: Refused => {
1153
return fail ( format! (
1154
" {what} : attested by {} , which the policy does not authorize for this bookmark; a policy owner can grant it with `oxy policy grant`" ,
1155
signer.short ( 12 )
1156
) )
1157
}
1158
Timed:: Integrated => { }
1159
Timed:: Disagrees => notes.push ( format! (
1160
" {what} : {} 's authority had lapsed when witness {} first saw its attestation, though not at {before} ; accepted while transparency logs are new" ,
1161
signer.short ( 12 ) ,
1162
time.as_ref ( ) .map ( | i | i.witness.as_str ( ) ) .unwrap_or ( " ?" )
1163
) ) ,
1164
Timed:: Fallback if log.kept => notes.push ( format! (
1165
" {what} : {} checked at {before} , not at a witnessed time: {} " ,
1166
signer.short ( 12 ) ,
1167
time.as_ref ( ) .err ( ) .map ( String :: as_str) .unwrap_or ( " none" )
1168
) ) ,
1169
Timed:: Fallback => { }
1170
}
1171
if let Some ( n) = time.ok ( ) .and_then ( | i | gap_note ( own, i) ) {
1172
notes.push ( format! ( " {what} 's attestation {n} " ) ) ;
1173
}
1174
Ok ( ( ) )
1175
}
1176
f8c69576 Andy McCall 1 month ago ↶
1177
8c30d814 Andy McCall 1 month ago ↶
1178
1179
1180
1181
f8c69576 Andy McCall 1 month ago ↶
1182
1183
1184
fn apply_deletion (
1185
& self ,
1186
view : & mut View,
1187
u : & RefUpdate,
1188
policy : Option< & crate:: policy:: Policy> ,
1189
) -> std:: result:: Result< ( ) , String> {
1190
let Some ( current) = view.bookmarks.get ( & u.name) .copied ( ) else {
1191
return Err ( format! ( " no bookmark {:?} to delete" , u.name) ) ;
1192
} ;
1193
if current ! = u.old_state {
1194
return Err ( " stale: the remote bookmark has moved; pull first" .into ( ) ) ;
1195
}
8c30d814 Andy McCall 1 month ago ↶
1196
if ! self .is_bare ( ) & & u.name = = self .config.bookmark {
f8c69576 Andy McCall 1 month ago ↶
1197
return Err ( format! (
8c30d814 Andy McCall 1 month ago ↶
1198
" {:?} is this repository's default bookmark (checked out there) and cannot be deleted" ,
f8c69576 Andy McCall 1 month ago ↶
1199
u.name
1200
) ) ;
1201
}
1202
if let Some ( p) = policy {
1203
let Some ( sa) = & u.attestation else {
1204
return Err ( format! (
1205
" deleting {:?} requires a signed reference attestation under this repository's policy" ,
1206
u.name
1207
) ) ;
1208
} ;
1209
let signer = sa
1210
.sigs
1211
.first ( )
1212
.map ( | s | s.key )
1213
.ok_or ( " attestation carries no signature" ) ? ;
f0a517d2 Andy McCall 18 days ago ↶
1214
1215
let now = Hlc:: now( ) .wall_ms;
7ba155f2 Andy McCall 1 month ago ↶
1216
if ! p.authorizes ( & signer, & u.name)
1217
& & ! self .delegate_authorized ( p, & signer, & u.name, sa.attest.hlc.wall_ms)
f0a517d2 Andy McCall 18 days ago ↶
1218
& & ! self .certified_authorized ( p, & signer, & u.name, Scope:: REFS , now)
7ba155f2 Andy McCall 1 month ago ↶
1219
{
f8c69576 Andy McCall 1 month ago ↶
1220
return Err ( format! (
1221
" key {} may not delete {:?} under this repository's policy" ,
1222
signer.short ( 12 ) ,
1223
u.name
1224
) ) ;
1225
}
1226
}
1227
if let Some ( sa) = & u.attestation {
be451356 Andy McCall 1 month ago ↶
1228
self .record_update_attest ( u, sa) ? ;
f8c69576 Andy McCall 1 month ago ↶
1229
}
1230
view.bookmarks.remove ( & u.name) ;
1231
Ok ( ( ) )
1232
}
1233
75167672 Andy McCall 1 month ago
1234
fn apply_update (
1235
& self ,
1236
view : & mut View,
1237
u : & RefUpdate,
1238
policy : Option< & crate:: policy:: Policy> ,
1239
) -> std:: result:: Result< ( ) , String> {
83e0677b Andy McCall 1 month ago ↶
1240
if u.name = = " policy" {
75167672 Andy McCall 1 month ago
1241
return Err (
1242
" the policy reference is updated by sending a policy, not a bookmark update" .into ( ) ,
1243
) ;
83e0677b Andy McCall 1 month ago ↶
1244
}
75167672 Andy McCall 1 month ago
1245
let empty = self
1246
.store
1247
.put_state ( & StateObject:: new( vec! [ ] ) )
1248
.map_err ( | e | e.to_string ( ) ) ? ;
f8c69576 Andy McCall 1 month ago ↶
1249
if u.new_state.is_zero ( ) {
1250
return self .apply_deletion ( view, u, policy) ;
1251
}
98286406 Andy McCall 1 month ago ↶
1252
let current = view.bookmarks.get ( & u.name) .copied ( ) .unwrap_or ( empty) ;
1253
if current ! = u.old_state {
1254
return Err ( " stale: the remote bookmark has moved; pull first" .into ( ) ) ;
1255
}
75167672 Andy McCall 1 month ago
1256
let new_state = self
1257
.store
1258
.get_state ( & u.new_state)
1259
.map_err ( | _| " new state object is missing" .to_string ( ) ) ? ;
98286406 Andy McCall 1 month ago ↶
1260
for h in & new_state.heads {
1261
if ! self .store.has ( h) {
1262
return Err ( format! ( " head {} is missing" , h.short ( 12 ) ) ) ;
1263
}
1264
}
1265
if ! u.force {
75167672 Andy McCall 1 month ago
1266
let old_heads = self
1267
.store
1268
.get_state ( & current)
1269
.map_err ( | e | e.to_string ( ) ) ?
1270
.heads;
1271
let closure = self
1272
.closure_in_store ( & new_state.heads)
1273
.map_err ( | e | e.to_string ( ) ) ? ;
98286406 Andy McCall 1 month ago ↶
1274
if let Some ( h) = old_heads.iter ( ) .find ( | h | ! closure.contains ( h) ) {
75167672 Andy McCall 1 month ago
1275
return Err ( format! (
1276
" not a fast-forward: current head {} would be dropped (use --force)" ,
1277
h.short ( 12 )
1278
) ) ;
98286406 Andy McCall 1 month ago ↶
1279
}
1280
}
83e0677b Andy McCall 1 month ago ↶
1281
if let Some ( p) = policy {
1282
let Some ( sa) = & u.attestation else {
75167672 Andy McCall 1 month ago
1283
return Err ( format! (
1284
" {:?} requires a signed reference attestation under this repository's policy" ,
1285
u.name
1286
) ) ;
83e0677b Andy McCall 1 month ago ↶
1287
} ;
75167672 Andy McCall 1 month ago
1288
let signer = sa
1289
.sigs
1290
.first ( )
1291
.map ( | s | s.key )
1292
.ok_or ( " attestation carries no signature" ) ? ;
f0a517d2 Andy McCall 18 days ago ↶
1293
1294
let now = Hlc:: now( ) .wall_ms;
7ba155f2 Andy McCall 1 month ago ↶
1295
if ! p.authorizes ( & signer, & u.name)
1296
& & ! self .delegate_authorized ( p, & signer, & u.name, sa.attest.hlc.wall_ms)
f0a517d2 Andy McCall 18 days ago ↶
1297
& & ! self .certified_authorized ( p, & signer, & u.name, Scope:: REFS , now)
7ba155f2 Andy McCall 1 month ago ↶
1298
{
75167672 Andy McCall 1 month ago
1299
return Err ( format! (
1300
" key {} may not update {:?} under this repository's policy" ,
1301
signer.short ( 12 ) ,
1302
u.name
1303
) ) ;
83e0677b Andy McCall 1 month ago ↶
1304
}
75167672 Andy McCall 1 month ago
1305
let old_heads = self
1306
.store
1307
.get_state ( & current)
1308
.map_err ( | e | e.to_string ( ) ) ?
1309
.heads;
83e0677b Andy McCall 1 month ago ↶
1310
self .state_allowed ( & old_heads, & new_state.heads, p, & u.name) ? ;
1311
}
98286406 Andy McCall 1 month ago ↶
1312
if let Some ( sa) = & u.attestation {
be451356 Andy McCall 1 month ago ↶
1313
self .record_update_attest ( u, sa) ? ;
98286406 Andy McCall 1 month ago ↶
1314
}
1315
view.bookmarks.insert ( u.name.clone ( ) , u.new_state) ;
1316
Ok ( ( ) )
1317
}
1318
1319
1320
1321
fn remote_url ( & self , name : & str ) -> Result< String> {
75167672 Andy McCall 1 month ago
1322
self .config.remotes.get ( name) .cloned ( ) .ok_or_else ( | | {
42ccf504 Andy McCall 27 days ago ↶
1323
let names: Vec< & str > = self .config.remotes.keys ( ) .map ( String :: as_str) .collect ( ) ;
1324
RepoError:: Msg( match names.as_slice ( ) {
1325
[ ] => format! ( " no remote named {name:?} ; add one with `oxy remote add`" ) ,
1326
_ if name = = " origin" & & names.len ( ) > 1 & & self .config.default_remote.is_none ( ) => format! (
1327
" this repository has no remote named origin, and several others ({} ): name one (`oxy pull {} `), or make one the default with `oxy config core.remote {} `" ,
1328
names.join ( " , " ) ,
1329
names[ 0 ] ,
1330
names[ 0 ]
1331
) ,
1332
_ => format! (
1333
" no remote named {name:?} ; this repository has {} " ,
1334
names.join ( " , " )
1335
) ,
1336
} )
75167672 Andy McCall 1 month ago
1337
} )
98286406 Andy McCall 1 month ago ↶
1338
}
1339
75167672 Andy McCall 1 month ago
1340
1341
1342
05e71ed6 Andy McCall 1 month ago ↶
1343
fn check_remote_id (
1344
& self ,
1345
remote : & dyn crate:: protocol:: Remote,
1346
remote_name : & str ,
1347
allow_unbound : bool ,
1348
) -> Result< ( ) > {
75167672 Andy McCall 1 month ago
1349
let theirs = remote.repo_id ( ) ? ;
1350
if theirs = = self .id ( ) ? {
1351
return Ok ( ( ) ) ;
1352
}
1353
if theirs = = crate :: protocol:: UNBOUND_ID {
1354
if allow_unbound {
1355
return Ok ( ( ) ) ;
1356
}
05e71ed6 Andy McCall 1 month ago ↶
1357
return fail ( format! (
1358
" remote {remote_name:?} is empty and unbound: push to it first"
1359
) ) ;
75167672 Andy McCall 1 month ago
1360
}
05e71ed6 Andy McCall 1 month ago ↶
1361
fail ( format! (
3e5772cc Andy McCall 1 month ago ↶
1362
" remote {remote_name:?} is a different repository (repository IDs differ); check its URL with `oxy remote list`"
05e71ed6 Andy McCall 1 month ago ↶
1363
) )
75167672 Andy McCall 1 month ago
1364
}
1365
ff1f62fe Andy McCall 1 month ago ↶
1366
1367
1368
3e5772cc Andy McCall 1 month ago ↶
1369
pub ( crate ) fn remote_options ( & self , name : & str ) -> RemoteOptions {
ff1f62fe Andy McCall 1 month ago ↶
1370
let url = self .config.remotes.get ( name) .cloned ( ) .unwrap_or_default ( ) ;
1371
let mut options = RemoteOptions {
75167672 Andy McCall 1 month ago
1372
repo_id: self .id ( ) .ok ( ) ,
ff1f62fe Andy McCall 1 month ago ↶
1373
token: self .config.remote_tokens.get ( name) .cloned ( ) ,
1374
ca: self .config.remote_cas.get ( name) .map ( PathBuf:: from) ,
9f9e72b9 Andy McCall 1 month ago ↶
1375
cache_dir: Some ( self .oxy.join ( " remotes" ) .join ( name) .join ( " strata" ) ) ,
68a92484 Andy McCall 22 days ago ↶
1376
.. RemoteOptions:: default( )
ff1f62fe Andy McCall 1 month ago ↶
1377
} ;
1378
self .fill_credentials ( & url, & mut options) ;
1379
options
1380
}
1381
1382
1383
1384
pub fn credentials ( & self ) -> crate:: credentials:: Credentials {
1385
crate :: credentials:: Credentials:: at(
1386
self .user_config
1387
.parent ( )
1388
.map ( Path:: to_path_buf)
1389
.unwrap_or_else ( default_home) ,
1390
)
1391
}
1392
1393
1394
1395
68a92484 Andy McCall 22 days ago ↶
1396
1397
1398
1399
ff1f62fe Andy McCall 1 month ago ↶
1400
fn fill_credentials ( & self , url : & str , options : & mut RemoteOptions) {
1401
if options.token.is_some ( ) & & options.ca.is_some ( ) {
1402
return ;
1403
}
68a92484 Andy McCall 22 days ago ↶
1404
let explicit =
1405
options.token.is_some ( ) | | std:: env:: var( " OXY_TOKEN" ) .is_ok_and ( | t | ! t.is_empty ( ) ) ;
ff1f62fe Andy McCall 1 month ago ↶
1406
let stored = self .credentials ( ) .options_for ( url) ;
1407
options.token = options.token.take ( ) .or ( stored.token) ;
1408
options.ca = options.ca.take ( ) .or ( stored.ca) ;
68a92484 Andy McCall 22 days ago ↶
1409
let enclosing = self .enclosing_credentials ( url) ;
1410
if options.token.is_none ( ) {
1411
options.token = enclosing.iter ( ) .find_map ( | ( t , _) | t.clone ( ) ) ;
1412
}
1413
if options.ca.is_none ( ) {
1414
options.ca = enclosing.iter ( ) .find_map ( | ( _, c ) | c.clone ( ) ) ;
1415
}
1416
if ! explicit {
1417
for t in enclosing.into_iter ( ) .filter_map ( | ( t , _) | t ) {
1418
if options.token.as_ref ( ) ! = Some ( & t) & & ! options.fallback_tokens.contains ( & t) {
1419
options.fallback_tokens.push ( t) ;
1420
}
1421
}
2b5ee371 Andy McCall 1 month ago ↶
1422
}
68a92484 Andy McCall 22 days ago ↶
1423
}
1424
1425
1426
1427
fn enclosing_credentials ( & self , url : & str ) -> Vec< ( Option< String> , Option< PathBuf> ) > {
1428
let mut out = Vec :: new( ) ;
ff1f62fe Andy McCall 1 month ago ↶
1429
let Some ( hub) = crate :: credentials:: hub_of( url) else {
68a92484 Andy McCall 22 days ago ↶
1430
return out;
ff1f62fe Andy McCall 1 month ago ↶
1431
} ;
1432
let mut at = self .root.parent ( ) ;
1433
while let Some ( dir) = at {
1434
let Ok ( cfg) = read_config_file ( & dir.join ( OXY_DIR ) .join ( " config" ) ) else {
1435
break ;
1436
} ;
1437
for ( key, remote_url) in & cfg {
1438
let Some ( name) = key
1439
.strip_prefix ( " remote." )
1440
.and_then ( | k | k.strip_suffix ( " .url" ) )
1441
else {
1442
continue ;
1443
} ;
1444
if crate :: credentials:: hub_of( remote_url) .as_deref ( ) ! = Some ( hub.as_str ( ) ) {
1445
continue ;
1446
}
68a92484 Andy McCall 22 days ago ↶
1447
out.push ( (
1448
cfg.get ( & format! ( " remote.{name} .token" ) ) .cloned ( ) ,
1449
cfg.get ( & format! ( " remote.{name} .ca" ) ) .map ( PathBuf:: from) ,
1450
) ) ;
ff1f62fe Andy McCall 1 month ago ↶
1451
}
1452
at = dir.parent ( ) ;
1453
}
68a92484 Andy McCall 22 days ago ↶
1454
out
98286406 Andy McCall 1 month ago ↶
1455
}
1456
d82e4409 Andy McCall 1 month ago ↶
1457
1458
1459
pub fn push_seals ( & self , remote_name : & str , seals : & [ObjectRef]) -> Result< ( ) > {
1460
let remote = self .open_named_remote ( remote_name) ? ;
1461
self .check_remote_id ( remote.as_ref ( ) , remote_name, false ) ? ;
1462
let mut bundle = Bundle:: default( ) ;
1463
for r in seals {
1464
let attest = self .store.get_attest ( r) ? ;
1465
let sigs = self .store.signatures ( r) ? ;
4213c41d Andy McCall 1 month ago ↶
1466
bundle
1467
.attests
1468
.push ( crate :: protocol:: SignedAttest { attest, sigs } ) ;
d82e4409 Andy McCall 1 month ago ↶
1469
}
1470
let results = remote.push ( bundle, vec! [ ] , None , vec! [ ] ) ? ;
1471
if let Some ( r) = results.iter ( ) .find ( | r | ! r.ok) {
1472
return fail ( format! ( " {} : {} " , r.name, r.reason) ) ;
1473
}
1474
Ok ( ( ) )
1475
}
1476
1477
1478
pub fn remote_for_url ( & self , base : & str ) -> Option< String> {
1479
let base = base.trim_end_matches ( ' /' ) ;
4213c41d Andy McCall 1 month ago ↶
1480
self .config
1481
.remotes
1482
.iter ( )
1483
.find ( | ( _, url ) | url.trim_end_matches ( ' /' ) = = base )
1484
.map ( | ( name , _) | name.clone ( ) )
d82e4409 Andy McCall 1 month ago ↶
1485
}
1486
1487
ff1f62fe Andy McCall 1 month ago ↶
1488
d82e4409 Andy McCall 1 month ago ↶
1489
pub fn open_remote_at ( & self , url : & str ) -> Result< ( String, Box< dyn crate:: protocol:: Remote> ) > {
1490
if let Some ( name) = self .remote_for_url ( url) {
1491
return Ok ( ( name.clone ( ) , self .open_named_remote ( & name) ? ) ) ;
1492
}
1493
let options = RemoteOptions {
1494
repo_id: self .id ( ) .ok ( ) ,
ff1f62fe Andy McCall 1 month ago ↶
1495
.. self .credentials_for ( url)
d82e4409 Andy McCall 1 month ago ↶
1496
} ;
1497
Ok ( ( url.to_string ( ) , open_remote ( url, & options) ? ) )
1498
}
1499
1500
1501
1502
pub fn options_for_url ( & self , url : & str ) -> RemoteOptions {
1503
match self .remote_for_url ( url) {
1504
Some ( name) => self .remote_options ( & name) ,
1505
None => RemoteOptions {
1506
repo_id: self .id ( ) .ok ( ) ,
ff1f62fe Andy McCall 1 month ago ↶
1507
.. self .credentials_for ( url)
d82e4409 Andy McCall 1 month ago ↶
1508
} ,
1509
}
1510
}
1511
98286406 Andy McCall 1 month ago ↶
1512
fn open_named_remote ( & self , name : & str ) -> Result< Box< dyn crate:: protocol:: Remote> > {
2b5ee371 Andy McCall 1 month ago ↶
1513
open_remote ( & self .remote_url ( name) ? , & self .remote_options ( name) )
98286406 Andy McCall 1 month ago ↶
1514
}
1515
1516
fn serial_path ( & self , remote : & str , bookmark : & str ) -> PathBuf {
75167672 Andy McCall 1 month ago
1517
self .oxy
1518
.join ( " remotes" )
1519
.join ( remote)
1520
.join ( format! ( " {bookmark} .serial" ) )
98286406 Andy McCall 1 month ago ↶
1521
}
1522
1523
fn last_seen_serial ( & self , remote : & str , bookmark : & str ) -> Option< u64 > {
75167672 Andy McCall 1 month ago
1524
fs:: read_to_string( self .serial_path ( remote, bookmark) )
1525
.ok ( )
1526
.and_then ( | s | s.trim ( ) .parse ( ) .ok ( ) )
98286406 Andy McCall 1 month ago ↶
1527
}
1528
1529
fn record_serial ( & self , remote : & str , bookmark : & str , serial : u64 ) -> Result< ( ) > {
1530
let p = self .serial_path ( remote, bookmark) ;
1531
fs:: create_dir_all( p.parent ( ) .unwrap ( ) ) ? ;
1532
write_atomic ( & p, serial.to_string ( ) .as_bytes ( ) )
1533
}
1534
83e0677b Andy McCall 1 month ago ↶
1535
fn policy_serial_path ( & self , remote : & str ) -> PathBuf {
1536
self .oxy.join ( " remotes" ) .join ( remote) .join ( " policy.serial" )
1537
}
1538
1539
1540
fn remote_policy_serial ( & self , remote : & str ) -> Option< u64 > {
75167672 Andy McCall 1 month ago
1541
fs:: read_to_string( self .policy_serial_path ( remote) )
1542
.ok ( )
1543
.and_then ( | s | s.trim ( ) .parse ( ) .ok ( ) )
83e0677b Andy McCall 1 month ago ↶
1544
}
1545
1546
fn record_policy_serial ( & self , remote : & str , serial : u64 ) -> Result< ( ) > {
1547
let p = self .policy_serial_path ( remote) ;
1548
fs:: create_dir_all( p.parent ( ) .unwrap ( ) ) ? ;
1549
write_atomic ( & p, serial.to_string ( ) .as_bytes ( ) )
1550
}
1551
b3c5a59a Andy McCall 1 month ago ↶
1552
fn lazy_dir ( & self ) -> PathBuf {
1553
self .oxy.join ( " lazy" )
1554
}
1555
1556
1557
fn register_lazy ( & self , blob : & ObjectRef, remote : & str ) -> Result< ( ) > {
1558
fs:: create_dir_all( self .lazy_dir ( ) ) ? ;
1559
write_atomic ( & self .lazy_dir ( ) .join ( blob.to_hex ( ) ) , remote.as_bytes ( ) )
1560
}
1561
1562
1563
pub fn lazy_remote ( & self , blob : & ObjectRef) -> Option< String> {
75167672 Andy McCall 1 month ago
1564
fs:: read_to_string( self .lazy_dir ( ) .join ( blob.to_hex ( ) ) )
1565
.ok ( )
1566
.map ( | s | s.trim ( ) .to_string ( ) )
b3c5a59a Andy McCall 1 month ago ↶
1567
}
1568
1569
1570
pub ( crate ) fn fetch_lazy_blob ( & self , blob : & ObjectRef) -> Result< ( ) > {
1571
let Some ( remote_name) = self .lazy_remote ( blob) else {
1572
return Err ( RepoError:: Store( StoreError:: NotFound( * blob) ) ) ;
1573
} ;
1574
let remote = self .open_named_remote ( & remote_name) ? ;
1575
let objects = remote.objects ( & [ * blob] ) ? ;
1576
for ( ty, body) in objects {
1577
if matches! ( ty, ObjectType:: Blob | ObjectType:: Chunk) {
1578
self .store.put ( ty, & body) ? ;
1579
}
1580
}
1581
if ! self .store.has ( blob) {
75167672 Andy McCall 1 month ago
1582
return fail ( format! (
1583
" {remote_name} did not send blob {} " ,
1584
blob.short ( 12 )
1585
) ) ;
b3c5a59a Andy McCall 1 month ago ↶
1586
}
1587
let _ = fs:: remove_file( self .lazy_dir ( ) .join ( blob.to_hex ( ) ) ) ;
1588
Ok ( ( ) )
1589
}
1590
1591
1592
1593
fn register_missing_blobs ( & self , changes : & [ChangeHash], remote : & str ) -> Result< usize > {
1594
let mut n = 0 ;
1595
for h in changes {
1596
let c = self .store.get_change ( h) ? ;
1597
for e in & c.edits {
1598
if let Edit:: Replace { old, new, .. } = e {
1599
for b in [ old, new] .into_iter ( ) .flatten ( ) {
1600
if ! self .store.has ( b) {
1601
self .register_lazy ( b, remote) ? ;
1602
n + = 1 ;
1603
}
1604
}
1605
}
1606
}
1607
}
1608
Ok ( n)
1609
}
1610
98286406 Andy McCall 1 month ago ↶
1611
d82e4409 Andy McCall 1 month ago ↶
1612
pub fn known_heads ( & self , view : & View) -> Result< Vec< ChangeHash> > {
98286406 Andy McCall 1 month ago ↶
1613
let mut have: BTreeSet< ChangeHash> = BTreeSet:: new( ) ;
75167672 Andy McCall 1 month ago
1614
for st in view
1615
.bookmarks
1616
.values ( )
1617
.chain ( view.remotes.values ( ) .flat_map ( | b | b.values ( ) ) )
1618
{
98286406 Andy McCall 1 month ago ↶
1619
if let Ok ( s) = self .store.get_state ( st) {
1620
have.extend ( s.heads) ;
1621
}
1622
}
1623
have.extend ( view.workspaces.values ( ) .copied ( ) ) ;
1624
Ok ( have.into_iter ( ) .collect ( ) )
1625
}
1626
1627
83e0677b Andy McCall 1 month ago ↶
1628
pub fn fetch ( & self , remote_name : & str ) -> Result< FetchReport> {
98286406 Andy McCall 1 month ago ↶
1629
let remote = self .open_named_remote ( remote_name) ? ;
75167672 Andy McCall 1 month ago
1630
self .check_remote_id ( remote.as_ref ( ) , remote_name, false ) ? ;
83e0677b Andy McCall 1 month ago ↶
1631
let resp = remote.refs ( ) ? ;
4b9d0677 Andy McCall 18 days ago ↶
1632
028545df Andy McCall 17 days ago ↶
1633
let log = self .check_log (
4b9d0677 Andy McCall 18 days ago ↶
1634
& self .remote_url ( remote_name) ? ,
1635
self .config.remote_cas.get ( remote_name) .map ( Path:: new) ,
1636
& resp,
1637
) ? ;
028545df Andy McCall 17 days ago ↶
1638
let mut log_notes = log.notes.clone ( ) ;
83e0677b Andy McCall 1 month ago ↶
1639
1640
1641
1642
1643
1644
let local_policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp.policy ) ;
1645
let seen = self .remote_policy_serial ( remote_name) ;
1646
let mut unverified = false ;
1647
match & resp.policy {
1648
Some ( sp) => {
1649
if seen.is_some_and ( | s | sp.policy.serial < s ) {
1650
return fail ( format! ( " {remote_name} presents policy serial {} but presented {} before: refusing the downgrade" , sp.policy.serial, seen.unwrap ( ) ) ) ;
1651
}
1652
match & local_policy {
1653
Some ( l) if sp.policy.serial < l.serial => { }
1654
Some ( l) if sp.policy.serial = = l.serial => {
1655
if sp.policy.hash ( ) ! = l.hash ( ) {
75167672 Andy McCall 1 month ago
1656
return fail ( format! (
1657
" {remote_name} presents a different policy with the same serial {} " ,
1658
l.serial
1659
) ) ;
83e0677b Andy McCall 1 month ago ↶
1660
}
1661
}
28fc23d7 Andy McCall 1 month ago ↶
1662
_ => {
75167672 Andy McCall 1 month ago
1663
self .adopt_policy_chain (
1664
& resp.history,
1665
sp,
1666
& format! ( " oxy fetch {remote_name} " ) ,
1667
)
1668
.map_err ( | e | RepoError:: Msg( format! ( " policy from {remote_name} : {e} " ) ) ) ? ;
83e0677b Andy McCall 1 month ago ↶
1669
}
1670
}
1671
self .record_policy_serial ( remote_name, sp.policy.serial) ? ;
1672
}
1673
None => {
1674
if seen.is_some ( ) {
1675
return fail ( format! ( " {remote_name} presented a policy before but has none now: refusing the downgrade" ) ) ;
1676
}
1677
unverified = true ;
1678
}
1679
}
75167672 Andy McCall 1 month ago
1680
let policy = if unverified {
1681
None
1682
} else {
1683
self .current_policy ( ) ? .map ( | ( _, sp ) | sp.policy )
1684
} ;
98286406 Andy McCall 1 month ago ↶
1685
let ( op_ref, op) = self .head_op ( ) ? ;
1686
let mut view = op.view.clone ( ) ;
1687
let have = self .known_heads ( & view) ? ;
1688
let mut report = Vec :: new( ) ;
d36ba2f7 Andy McCall 16 days ago ↶
1689
let mut skipped = Vec :: new( ) ;
b3c5a59a Andy McCall 1 month ago ↶
1690
let max_blob = self .config.max_blob;
83e0677b Andy McCall 1 month ago ↶
1691
for r in resp.refs {
091fef80 Andy McCall 1 month ago ↶
1692
let needed = {
1693
let _t = crate :: timing:: phase( " fetch: negotiate" ) ;
1694
remote.negotiate ( & have, & r.heads) ?
1695
} ;
1696
let bundle = {
1697
let _t = crate :: timing:: phase( " fetch: bundle" ) ;
1698
remote.fetch ( & needed, max_blob) ?
1699
} ;
83e0677b Andy McCall 1 month ago ↶
1700
1701
1702
091fef80 Andy McCall 1 month ago ↶
1703
let imported = {
1704
let _t = crate :: timing:: phase( " fetch: import" ) ;
1705
self .import_bundle ( & bundle, false ) ?
1706
} ;
b3c5a59a Andy McCall 1 month ago ↶
1707
if max_blob.is_some ( ) {
1708
self .register_missing_blobs ( & imported, remote_name) ? ;
1709
}
98286406 Andy McCall 1 month ago ↶
1710
for h in & r.heads {
1711
if ! self .store.has ( h) {
1712
return fail ( format! ( " remote did not send head {} " , h.short ( 12 ) ) ) ;
1713
}
1714
}
1715
let state = self .store.put_state ( & StateObject:: new( r.heads.clone ( ) ) ) ? ;
1716
if state ! = r.state {
75167672 Andy McCall 1 month ago
1717
return fail ( format! (
1718
" remote state {} does not match its heads" ,
1719
r.state.short ( 12 )
1720
) ) ;
98286406 Andy McCall 1 month ago ↶
1721
}
83e0677b Andy McCall 1 month ago ↶
1722
if let Some ( p) = & policy {
1723
let Some ( sa) = & r.attestation else {
75167672 Andy McCall 1 month ago
1724
return fail ( format! (
1725
" {remote_name} /{} has no attestation, which its policy requires" ,
1726
r.name
1727
) ) ;
83e0677b Andy McCall 1 month ago ↶
1728
} ;
75167672 Andy McCall 1 month ago
1729
let signer = sa
1730
.sigs
1731
.first ( )
1732
.map ( | s | s.key )
1733
.ok_or_else ( | | RepoError:: Msg( " attestation carries no signature" .into ( ) ) ) ? ;
028545df Andy McCall 17 days ago ↶
1734
if ! p.authorizes ( & signer, & r.name) {
1735
self .authorize_in_time ( p, remote_name, & r, sa, & signer, & log, & mut log_notes) ? ;
83e0677b Andy McCall 1 month ago ↶
1736
}
1737
}
98286406 Andy McCall 1 month ago ↶
1738
if let Some ( sa) = & r.attestation {
75167672 Andy McCall 1 month ago
1739
let AttestBody:: Ref {
1740
old_state,
1741
new_state,
1742
..
1743
} = & sa.attest.body
1744
else {
98286406 Andy McCall 1 month ago ↶
1745
return fail ( " remote sent a non-ref attestation for a bookmark" ) ;
1746
} ;
1747
let ( old_state, new_state) = ( * old_state, * new_state) ;
1748
let serial = self
d36ba2f7 Andy McCall 16 days ago ↶
1749
.check_ref_attest_body (
75167672 Andy McCall 1 month ago
1750
sa,
1751
& r.name,
1752
& old_state,
1753
& new_state,
1754
self .last_seen_serial ( remote_name, & r.name) ,
1755
true ,
1756
)
1757
.map_err ( | e | {
1758
RepoError:: Msg( format! ( " attestation for {} /{} : {e} " , remote_name, r.name) )
1759
} ) ? ;
98286406 Andy McCall 1 month ago ↶
1760
if new_state ! = r.state {
75167672 Andy McCall 1 month ago
1761
return fail ( format! (
1762
" attestation for {} /{} does not cover its current state" ,
1763
remote_name, r.name
1764
) ) ;
98286406 Andy McCall 1 month ago ↶
1765
}
d36ba2f7 Andy McCall 16 days ago ↶
1766
1767
1768
1769
1770
1771
1772
if let Err ( e) = self .verify_attest_signature ( sa) {
1773
let why = format! ( " attestation for {remote_name} /{} : {e} " , r.name) ;
1774
if r.name = = self .config.bookmark {
1775
return fail ( why) ;
1776
}
1777
skipped.push ( ( r.name, why) ) ;
1778
continue ;
1779
}
98286406 Andy McCall 1 month ago ↶
1780
let ar = self .store.put_attest ( & sa.attest) ? ;
1781
for sig in & sa.sigs {
1782
self .store.put_signature ( & ar, sig) ? ;
1783
}
1784
self .record_serial ( remote_name, & r.name, serial) ? ;
1785
}
75167672 Andy McCall 1 month ago
1786
view.remotes
1787
.entry ( remote_name.to_string ( ) )
1788
.or_default ( )
1789
.insert ( r.name.clone ( ) , state) ;
98286406 Andy McCall 1 month ago ↶
1790
report.push ( ( r.name, r.heads, imported.len ( ) ) ) ;
1791
}
132b884b Andy McCall 1 month ago ↶
1792
let mut new_tags = 0 ;
1793
for t in & resp.tags {
1794
if view.tags.contains_key ( & t.name) {
1795
continue ;
1796
}
1797
75167672 Andy McCall 1 month ago
1798
let missing: Vec< ChangeHash> = t
1799
.heads
1800
.iter ( )
1801
.filter ( | h | ! self .store.has ( h) )
1802
.copied ( )
1803
.collect ( ) ;
132b884b Andy McCall 1 month ago ↶
1804
if ! missing.is_empty ( ) {
1805
let needed = remote.negotiate ( & self .known_heads ( & view) ? , & missing) ? ;
1806
let bundle = remote.fetch ( & needed, max_blob) ? ;
1807
self .import_bundle ( & bundle, false ) ? ;
1808
}
1809
self .store.put_state ( & StateObject:: new( t.heads.clone ( ) ) ) ? ;
75167672 Andy McCall 1 month ago
1810
self .adopt_tag ( & mut view, t, policy.as_ref ( ) )
1811
.map_err ( | e | RepoError:: Msg( format! ( " {remote_name} {} : {e} " , t.name) ) ) ? ;
132b884b Andy McCall 1 month ago ↶
1812
new_tags + = 1 ;
1813
}
98286406 Andy McCall 1 month ago ↶
1814
if view ! = op.view {
1815
self .record_op ( & [ op_ref] , & format! ( " oxy fetch {remote_name} " ) , view) ? ;
1816
}
6d3eefbc Andy McCall 1 month ago ↶
1817
let _ = self .run_hook (
1818
" post-fetch" ,
1819
& format! (
1820
" {{ \" event\" :\" post-fetch\" ,\" remote\" :{} ,\" bookmarks\" :{} }} " ,
1821
json_str ( remote_name) ,
75167672 Andy McCall 1 month ago
1822
json_array ( report.iter ( ) .map ( | ( n , heads , _) | format! (
1823
" {{ \" name\" :{} ,\" heads\" :{} }} " ,
1824
json_str ( n) ,
1825
json_array ( heads.iter ( ) .map ( | h | json_str ( & h.to_hex ( ) ) ) )
1826
) ) )
6d3eefbc Andy McCall 1 month ago ↶
1827
) ,
1828
) ;
75167672 Andy McCall 1 month ago
1829
Ok ( FetchReport {
1830
refs: report,
1831
unverified,
1832
new_tags,
4b9d0677 Andy McCall 18 days ago ↶
1833
log_notes,
d36ba2f7 Andy McCall 16 days ago ↶
1834
skipped,
75167672 Andy McCall 1 month ago
1835
} )
98286406 Andy McCall 1 month ago ↶
1836
}
1837
2863456a Andy McCall 1 month ago ↶
1838
1839
1840
pub fn fetch_shallow ( & self , remote_name : & str , depth : u32 ) -> Result< usize > {
1841
let remote = self .open_named_remote ( remote_name) ? ;
75167672 Andy McCall 1 month ago
1842
self .check_remote_id ( remote.as_ref ( ) , remote_name, false ) ? ;
2863456a Andy McCall 1 month ago ↶
1843
let resp = remote.refs ( ) ? ;
1844
let bookmark = self .config.bookmark.clone ( ) ;
1845
if let Some ( sp) = & resp.policy {
75167672 Andy McCall 1 month ago
1846
self .adopt_policy_chain ( & resp.history, sp, " oxy clone --depth" )
1847
.map_err ( | e | RepoError:: Msg( format! ( " policy from {remote_name} : {e} " ) ) ) ? ;
2863456a Andy McCall 1 month ago ↶
1848
self .record_policy_serial ( remote_name, sp.policy.serial) ? ;
1849
}
1850
let Some ( r) = resp.refs.into_iter ( ) .find ( | r | r.name = = bookmark ) else {
1851
return fail ( format! ( " {remote_name} has no bookmark {bookmark:?} " ) ) ;
1852
} ;
1853
if r.heads.is_empty ( ) {
1854
return Ok ( 0 ) ;
1855
}
1856
1857
let cp_bundle = remote.checkpoint ( & r.heads) ? ;
1858
let mut cp_ref = None ;
1859
for ( ty, body) in & cp_bundle.objects {
1860
let put = self .store.put ( * ty, body) ? ;
1861
if * ty = = ObjectType:: Checkpoint {
1862
cp_ref = Some ( put) ;
1863
}
1864
}
75167672 Andy McCall 1 month ago
1865
let Some ( cp_ref) = cp_ref else {
1866
return fail ( format! ( " {remote_name} sent no checkpoint" ) ) ;
1867
} ;
2863456a Andy McCall 1 month ago ↶
1868
self .install_checkpoint ( & bookmark, & cp_ref) ? ;
1869
1870
let wanted = remote.shallow ( & r.heads, depth) ? ;
1871
let bundle = remote.fetch ( & wanted, self .config.max_blob) ? ;
1872
let imported = self .import_bundle_shallow ( & bundle, false ) ? ;
1873
if self .config.max_blob.is_some ( ) {
1874
self .register_missing_blobs ( & imported, remote_name) ? ;
1875
}
1876
self .refresh_boundary ( & imported) ? ;
1877
if let Some ( sa) = & r.attestation {
1878
let ar = self .store.put_attest ( & sa.attest) ? ;
1879
for sig in & sa.sigs {
1880
self .store.put_signature ( & ar, sig) ? ;
1881
}
1882
if let AttestBody:: Ref { serial, .. } = sa.attest.body {
1883
self .record_serial ( remote_name, & r.name, serial) ? ;
1884
}
1885
}
1886
let state = self .store.put_state ( & StateObject:: new( r.heads.clone ( ) ) ) ? ;
1887
let ( op_ref, op) = self .head_op ( ) ? ;
1888
let mut view = op.view.clone ( ) ;
75167672 Andy McCall 1 month ago
1889
view.remotes
1890
.entry ( remote_name.to_string ( ) )
1891
.or_default ( )
1892
.insert ( r.name.clone ( ) , state) ;
1893
self .record_op (
1894
& [ op_ref] ,
1895
& format! ( " oxy fetch {remote_name} --depth {depth} " ) ,
1896
view,
1897
) ? ;
2863456a Andy McCall 1 month ago ↶
1898
Ok ( imported.len ( ) )
1899
}
1900
98286406 Andy McCall 1 month ago ↶
1901
1902
1903
pub fn pull ( & self , remote_name : & str ) -> Result< PullReport> {
6caacfff Andy McCall 1 month ago ↶
1904
let _wc = self .lock_working_copy ( ) ? ;
d36ba2f7 Andy McCall 16 days ago ↶
1905
let skipped = self .fetch ( remote_name) ? .skipped;
98286406 Andy McCall 1 month ago ↶
1906
let ( op_ref, op) = self .head_op ( ) ? ;
14edbaaf Andy McCall 1 month ago ↶
1907
let current = self .config.bookmark.clone ( ) ;
1908
let mut local_current = self .heads_of ( & op.view, & current) ? ;
1909
local_current.sort ( ) ;
1910
let mut view = op.view.clone ( ) ;
1911
let mut current_heads = local_current.clone ( ) ;
1912
let mut changed = false ;
1913
let mut others = Vec :: new( ) ;
1914
let tracked = op
75167672 Andy McCall 1 month ago
1915
.view
1916
.remotes
1917
.get ( remote_name)
14edbaaf Andy McCall 1 month ago ↶
1918
.cloned ( )
1919
.unwrap_or_default ( ) ;
1920
for ( name, remote_state) in tracked {
1921
1922
1923
let local_state = op.view.bookmarks.get ( & name) .copied ( ) ;
1924
if local_state.is_none ( ) & & name ! = current {
1925
continue ;
1926
}
1927
if local_state = = Some ( remote_state) {
1928
continue ;
1929
}
1930
let local = self .heads_of ( & op.view, & name) ? ;
1931
let remote_heads = self .store.get_state ( & remote_state) ? .heads;
1932
if remote_heads.iter ( ) .all ( | h | local.contains ( h) ) {
1933
continue ;
1934
}
1935
let mut all = local.clone ( ) ;
1936
all.extend ( remote_heads) ;
1937
let st = self .load_state_history ( & all) ? ;
1938
let mut heads = st.maximal ( & all) ;
1939
heads.sort ( ) ;
1940
let mut sorted_local = local;
1941
sorted_local.sort ( ) ;
1942
if heads = = sorted_local {
1943
continue ;
1944
}
1945
view.bookmarks.insert (
1946
name.clone ( ) ,
1947
self .store.put_state ( & StateObject:: new( heads.clone ( ) ) ) ? ,
1948
) ;
1949
if name = = current {
1950
changed = true ;
1951
current_heads = heads;
1952
} else {
1953
others.push ( name) ;
1954
}
1955
}
f6dccc45 Andy McCall 27 days ago ↶
1956
if changed | | ! others.is_empty ( ) {
1957
self .record_op ( & [ op_ref] , & format! ( " oxy pull {remote_name} " ) , view) ? ;
98286406 Andy McCall 1 month ago ↶
1958
}
f6dccc45 Andy McCall 27 days ago ↶
1959
1960
1961
1962
let checked_out = self .follow_bookmark ( & current_heads, & current, " oxy pull" ) ? ;
75167672 Andy McCall 1 month ago
1963
Ok ( PullReport {
f6dccc45 Andy McCall 27 days ago ↶
1964
heads: if changed {
1965
current_heads
1966
} else {
1967
local_current
1968
} ,
14edbaaf Andy McCall 1 month ago ↶
1969
changed,
75167672 Andy McCall 1 month ago
1970
checked_out,
14edbaaf Andy McCall 1 month ago ↶
1971
others,
d36ba2f7 Andy McCall 16 days ago ↶
1972
skipped,
75167672 Andy McCall 1 month ago
1973
} )
f6dccc45 Andy McCall 27 days ago ↶
1974
}
1975
1976
1977
1978
1979
1980
1981
pub fn working_copy_behind ( & self ) -> Result< Option< usize > > {
1982
let ( _ , wc) = self .working_copy ( ) ? ;
1983
let mut base = wc.deps.clone ( ) ;
1984
base.sort ( ) ;
1985
let mut heads = self .bookmark_heads ( ) ? ;
1986
heads.sort ( ) ;
1987
if base = = heads | | heads.is_empty ( ) {
1988
return Ok ( None ) ;
1989
}
1990
let state = self .load_state_history ( & heads) ? ;
1991
if ! base.iter ( ) .all ( | h | state.contains ( h) ) {
1992
return Ok ( None ) ;
1993
}
1994
let had = if base.is_empty ( ) {
1995
0
1996
} else {
1997
self .load_state_history ( & base) ? .len ( )
1998
} ;
1999
Ok ( Some ( state.len ( ) .saturating_sub ( had) ) .filter ( | n | * n > 0 ) )
2000
}
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
pub fn follow_bookmark (
2012
& self ,
2013
heads : & [ChangeHash],
2014
bookmark : & str ,
2015
command : & str ,
2016
) -> Result< bool > {
2017
let snap = self .snapshot ( command) ? ;
2018
let clean = snap.wc.tree_ops.is_empty ( ) & & snap.wc.edits.is_empty ( ) ;
2019
let mut base = snap.base.maximal ( & snap.wc.deps) ;
2020
base.sort ( ) ;
2021
let mut target = heads.to_vec ( ) ;
2022
target.sort ( ) ;
2023
if base = = target {
2024
return Ok ( false ) ;
2025
}
2026
let state = self .load_state_history ( heads) ? ;
2027
if ! base.iter ( ) .all ( | h | state.contains ( h) ) {
2028
2029
return Ok ( false ) ;
2030
}
2031
if clean {
2032
self .checkout ( heads, false , & format! ( " {command} (checkout)" ) ) ? ;
2033
Ok ( true )
2034
} else {
2035
Ok ( self
2036
.checkout_carry ( heads, bookmark, & format! ( " {command} (checkout --carry)" ) )
2037
.is_ok ( ) )
2038
}
98286406 Andy McCall 1 month ago ↶
2039
}
2040
2041
2042
f8c69576 Andy McCall 1 month ago ↶
2043
2044
2045
2046
pub fn push_delete ( & self , remote_name : & str , bookmark : & str ) -> Result< Vec< RefResult> > {
2047
let remote = self .open_named_remote ( remote_name) ? ;
2048
self .check_remote_id ( remote.as_ref ( ) , remote_name, true ) ? ;
2049
let resp = remote.refs ( ) ? ;
2050
let Some ( r) = resp.refs.iter ( ) .find ( | r | r.name = = bookmark ) else {
2051
return fail ( format! ( " {remote_name} has no bookmark {bookmark} " ) ) ;
2052
} ;
2053
if resp.policy.is_some ( ) & & self .secret_key ( ) ? .is_none ( ) {
2054
return fail ( format! ( " {remote_name} enforces a trust policy; deleting needs an identity (`oxy identity create`)" ) ) ;
2055
}
2056
let attestation = match self .secret_key ( ) ? {
2057
Some ( _ ) => Some ( self .sign_attest ( & Attest {
2058
repo: self .id ( ) ? ,
2059
hlc: Hlc:: now( ) ,
2060
body: AttestBody:: Ref {
2061
name: bookmark.to_string ( ) ,
2062
old_state: r.state,
2063
new_state: ObjectRef:: ZERO ,
2064
serial: r.last_serial.unwrap_or ( 0 ) + 1 ,
2065
} ,
2066
} ) ? ) ,
2067
None => None ,
2068
} ;
2069
let results = remote.push (
2070
Bundle:: default( ) ,
2071
vec! [ RefUpdate {
2072
name: bookmark.to_string ( ) ,
2073
old_state: r.state,
2074
new_state: ObjectRef:: ZERO ,
2075
force: true ,
2076
attestation,
2077
} ] ,
2078
None ,
2079
vec! [ ] ,
2080
) ? ;
2081
if results.iter ( ) .any ( | x | x.ok & & x.name = = bookmark ) {
2082
let ( op_ref, op) = self .head_op ( ) ? ;
2083
let mut view = op.view.clone ( ) ;
2084
if let Some ( m) = view.remotes.get_mut ( remote_name) {
2085
m.remove ( bookmark) ;
2086
}
2087
self .record_op (
2088
& [ op_ref] ,
2089
& format! ( " oxy push {remote_name} --delete {bookmark} " ) ,
2090
view,
2091
) ? ;
2092
}
2093
Ok ( results)
2094
}
2095
34fad4a9 Andy McCall 25 days ago ↶
2096
2097
2098
2099
2100
pub fn remove_remote ( & mut self , name : & str ) -> Result< ( ) > {
2101
if ! self .config.remotes.contains_key ( name) {
2102
return fail ( format! ( " no remote {name} : `oxy remote list` shows them" ) ) ;
2103
}
2104
let prefix = format! ( " remote.{name} ." ) ;
2105
let keys: Vec< String> = self
2106
.config
2107
.repo_layer ( )
2108
.keys ( )
2109
.filter ( | k | k.starts_with ( & prefix) )
2110
.cloned ( )
2111
.collect ( ) ;
2112
for k in keys {
2113
self .config.set ( & k, " " ) ? ;
2114
}
2115
self .config.remotes.remove ( name) ;
2116
self .save_config ( ) ? ;
2117
let ( op_ref, op) = self .head_op ( ) ? ;
2118
if op.view.remotes.contains_key ( name) {
2119
let mut view = op.view.clone ( ) ;
2120
view.remotes.remove ( name) ;
2121
self .record_op ( & [ op_ref] , & format! ( " oxy remote remove {name} " ) , view) ? ;
2122
}
2123
let mirror = self .oxy.join ( " remotes" ) .join ( name) ;
2124
if mirror.is_dir ( ) {
2125
fs:: remove_dir_all( & mirror) ? ;
2126
}
2127
Ok ( ( ) )
2128
}
2129
cb8b8082 Andy McCall 26 days ago ↶
2130
2131
2132
2133
fn push_batches ( & self , changes : & [ChangeHash]) -> Result< Vec< Vec< ChangeHash> > > {
2134
let blob_len = | r : & ObjectRef| -> u64 {
2135
self .store
2136
.get_typed ( r, ObjectType:: Blob)
2137
.ok ( )
2138
.and_then ( | b | Blob:: decode( & b) .ok ( ) )
2139
.map_or ( 0 , | b | b.length )
2140
} ;
2141
let ( mut out, mut batch, mut bytes) = ( Vec :: new( ) , Vec :: new( ) , 0 u64 ) ;
2142
for h in changes {
2143
let raw = self .store.get_typed ( h, ObjectType:: Change) ? ;
2144
let ( c, texts) = Change:: decode_skeleton( & raw, * h) ? ;
2145
let mut weight = raw.len ( ) as u64 ;
2146
for t in & texts {
2147
if let crate :: objects:: TextRef:: Blob( r, _ , _ ) = t {
2148
weight + = blob_len ( r) ;
2149
}
2150
}
2151
for e in & c.edits {
2152
if let Edit:: Replace { new: Some ( b) , .. } = e {
2153
weight + = blob_len ( b) ;
2154
}
2155
}
2156
if ! batch.is_empty ( ) & & bytes + weight > self .config.push_batch_bytes {
2157
out.push ( std:: mem:: take( & mut batch) ) ;
2158
bytes = 0 ;
2159
}
2160
batch.push ( * h) ;
2161
bytes + = weight;
2162
}
2163
out.push ( batch) ;
2164
Ok ( out)
2165
}
2166
98286406 Andy McCall 1 month ago ↶
2167
pub fn push ( & self , remote_name : & str , force : bool ) -> Result< Vec< RefResult> > {
6d3eefbc Andy McCall 1 month ago ↶
2168
let bookmark = self .config.bookmark.clone ( ) ;
7b9809e6 Andy McCall 1 month ago ↶
2169
self .push_bookmark ( remote_name, & bookmark, force)
2170
}
2171
2172
75167672 Andy McCall 1 month ago
2173
pub fn push_bookmark (
cb8b8082 Andy McCall 26 days ago ↶
2174
& self ,
2175
remote_name : & str ,
2176
bookmark : & str ,
2177
force : bool ,
2178
) -> Result< Vec< RefResult> > {
2179
self .push_bookmark_with ( remote_name, bookmark, force, & mut | _ | { } )
2180
}
2181
2182
2183
2184
pub fn push_bookmark_with (
75167672 Andy McCall 1 month ago
2185
& self ,
2186
remote_name : & str ,
2187
bookmark : & str ,
2188
force : bool ,
cb8b8082 Andy McCall 26 days ago ↶
2189
progress : crate:: store:: Progress,
75167672 Andy McCall 1 month ago
2190
) -> Result< Vec< RefResult> > {
7b9809e6 Andy McCall 1 month ago ↶
2191
let ( op_ref, op) = self .head_op ( ) ? ;
2192
if ! op.view.bookmarks.contains_key ( bookmark) {
2193
return fail ( format! ( " no bookmark {bookmark} " ) ) ;
2194
}
2195
let bookmark = bookmark.to_string ( ) ;
6d3eefbc Andy McCall 1 month ago ↶
2196
let local_heads = self .heads_of ( & op.view, & bookmark) ? ;
2197
2198
self .run_hook (
2199
" pre-push" ,
75167672 Andy McCall 1 month ago
2200
& format! (
2201
" {{ \" event\" :\" pre-push\" ,\" remote\" :{} ,\" bookmark\" :{} ,\" heads\" :{} }} " ,
2202
json_str ( remote_name) ,
2203
json_str ( & bookmark) ,
2204
json_array ( local_heads.iter ( ) .map ( | h | json_str ( & h.to_hex ( ) ) ) )
2205
) ,
6d3eefbc Andy McCall 1 month ago ↶
2206
) ? ;
98286406 Andy McCall 1 month ago ↶
2207
let remote = self .open_named_remote ( remote_name) ? ;
75167672 Andy McCall 1 month ago
2208
self .check_remote_id ( remote.as_ref ( ) , remote_name, true ) ? ;
83e0677b Andy McCall 1 month ago ↶
2209
let resp = remote.refs ( ) ? ;
2210
let local_policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp ) ;
28fc23d7 Andy McCall 1 month ago ↶
2211
let resp_policy_serial = resp.policy.as_ref ( ) .map ( | p | p.policy.serial ) ;
83e0677b Andy McCall 1 month ago ↶
2212
let policy_to_send = match ( & local_policy, & resp.policy) {
2213
( Some ( l) , Some ( r) ) if l.policy.serial > r.policy.serial => Some ( l.clone ( ) ) ,
2214
( Some ( l) , None ) => Some ( l.clone ( ) ) ,
2215
( Some ( l) , Some ( r) ) if l.policy.serial < r.policy.serial => {
75167672 Andy McCall 1 month ago
2216
return fail ( format! (
2217
" {remote_name} has a newer policy (serial {} ); fetch first" ,
2218
r.policy.serial
2219
) ) ;
83e0677b Andy McCall 1 month ago ↶
2220
}
2221
_ => None ,
2222
} ;
2223
let remote_has_policy = resp.policy.is_some ( ) | | policy_to_send.is_some ( ) ;
2224
if remote_has_policy & & self .secret_key ( ) ? .is_none ( ) {
2225
return fail ( format! ( " {remote_name} enforces a trust policy; pushing needs an identity (`oxy identity create`)" ) ) ;
2226
}
132b884b Andy McCall 1 month ago ↶
2227
let remote_tags: BTreeSet< String> = resp.tags.iter ( ) .map ( | t | t.name.clone ( ) ) .collect ( ) ;
75167672 Andy McCall 1 month ago
2228
let tags_to_send: Vec< crate:: protocol:: TagRef> = self
2229
.tag_refs ( ) ?
2230
.into_iter ( )
2231
.filter ( | t | ! remote_tags.contains ( & t.name) )
2232
.collect ( ) ;
83e0677b Andy McCall 1 month ago ↶
2233
let refs = resp.refs;
98286406 Andy McCall 1 month ago ↶
2234
let empty = self .store.put_state ( & StateObject:: new( vec! [ ] ) ) ? ;
75167672 Andy McCall 1 month ago
2235
let ( remote_state, remote_heads, last_serial) =
2236
match refs.iter ( ) .find ( | r | r.name = = bookmark ) {
2237
Some ( r) => ( r.state, r.heads.clone ( ) , r.last_serial) ,
2238
None => ( empty, vec! [ ] , None ) ,
2239
} ;
2240
let new_state = self
2241
.store
2242
.put_state ( & StateObject:: new( local_heads.clone ( ) ) ) ? ;
83e0677b Andy McCall 1 month ago ↶
2243
2244
2245
2246
132b884b Andy McCall 1 month ago ↶
2247
if new_state = = remote_state & & policy_to_send.is_none ( ) & & tags_to_send.is_empty ( ) {
75167672 Andy McCall 1 month ago
2248
return Ok ( vec! [ RefResult {
2249
name: bookmark,
2250
ok: true ,
2251
reason: " already up to date" .into ( ) ,
2252
} ] ) ;
98286406 Andy McCall 1 month ago ↶
2253
}
2254
for h in & remote_heads {
2255
if ! self .store.has ( h) {
75167672 Andy McCall 1 month ago
2256
return fail ( format! (
3e5772cc Andy McCall 1 month ago ↶
2257
" the remote has change {} which you do not; pull first (`oxy pull {remote_name} `), then push again" ,
75167672 Andy McCall 1 month ago
2258
h.short ( 12 )
2259
) ) ;
98286406 Andy McCall 1 month ago ↶
2260
}
2261
}
2262
if ! force {
2263
let closure = self .closure_in_store ( & local_heads) ? ;
2264
if let Some ( h) = remote_heads.iter ( ) .find ( | h | ! closure.contains ( h) ) {
2265
return fail ( format! ( " not a fast-forward: remote head {} is not in your state; pull first or use --force" , h.short ( 12 ) ) ) ;
2266
}
2267
}
2268
let to_send = self .negotiate ( & remote_heads, & local_heads) ? ;
2269
let key = self .secret_key ( ) ? .map ( | ( k , _) | k ) ;
2270
if let Some ( key) = key {
05e71ed6 Andy McCall 1 month ago ↶
2271
cb8b8082 Andy McCall 26 days ago ↶
2272
2273
98286406 Andy McCall 1 month ago ↶
2274
let mut unsigned = Vec :: new( ) ;
2275
for h in & to_send {
cb8b8082 Andy McCall 26 days ago ↶
2276
let c = self .store.get_change_meta ( h) ? ;
a2387d54 Andy McCall 18 days ago ↶
2277
if self .seals_for_author ( & c.author, & key)
75167672 Andy McCall 1 month ago
2278
& & ! matches! ( self .authenticate_change ( h) ? , SignatureStatus:: Valid { .. } )
2279
{
98286406 Andy McCall 1 month ago ↶
2280
unsigned.push ( * h) ;
2281
}
2282
}
2283
for chunk in unsigned.chunks ( crate :: attest:: MAX_SEAL_HASHES ) {
cb8b8082 Andy McCall 26 days ago ↶
2284
self .sign_attest ( & Attest:: seal( self .id ( ) ? , chunk.to_vec ( ) ) ) ? ;
98286406 Andy McCall 1 month ago ↶
2285
}
cb8b8082 Andy McCall 26 days ago ↶
2286
}
2287
2288
2289
2290
2291
let batches = self .push_batches ( & to_send) ? ;
2292
let ( last, earlier) = batches.split_last ( ) .expect ( " at least one batch" ) ;
2293
for ( i, batch) in earlier.iter ( ) .enumerate ( ) {
2294
progress ( & format! (
2295
" part {} of {} : {} changes" ,
2296
i + 1 ,
2297
batches.len ( ) ,
2298
batch.len ( )
2299
) ) ;
2300
let results = remote.push ( self .bundle_for ( batch) ? , vec! [ ] , None , vec! [ ] ) ? ;
2301
match results.iter ( ) .find ( | r | r.name = = OBJECTS_RESULT ) {
2302
Some ( r) if r.ok => { }
2303
Some ( r) => {
2304
return fail ( format! (
2305
" {remote_name} refused part {} of the push: {} " ,
2306
i + 1 ,
2307
r.reason
2308
) )
2309
}
2310
None => {
2311
return fail ( format! (
2312
" {remote_name} does not take a push in parts, and this one is too long for one: update the server's oxy"
2313
) )
2314
}
2315
}
2316
}
2317
if batches.len ( ) > 1 {
2318
progress ( & format! (
2319
" part {} of {} : {} changes" ,
2320
batches.len ( ) ,
2321
batches.len ( ) ,
2322
last.len ( )
2323
) ) ;
98286406 Andy McCall 1 month ago ↶
2324
}
cb8b8082 Andy McCall 26 days ago ↶
2325
let mut bundle = self .bundle_for ( last) ? ;
75167672 Andy McCall 1 month ago
2326
bundle.objects.push ( (
2327
ObjectType:: State,
2328
StateObject:: new( local_heads.clone ( ) ) .encode ( ) ,
2329
) ) ;
98286406 Andy McCall 1 month ago ↶
2330
let attestation = match key {
2331
Some ( _ ) => Some ( self .sign_attest ( & Attest {
2332
repo: self .id ( ) ? ,
2333
hlc: Hlc:: now( ) ,
75167672 Andy McCall 1 month ago
2334
body: AttestBody:: Ref {
2335
name: bookmark.clone ( ) ,
2336
old_state: remote_state,
2337
new_state,
f8c69576 Andy McCall 1 month ago ↶
2338
serial: next_serial ( last_serial) ,
75167672 Andy McCall 1 month ago
2339
} ,
98286406 Andy McCall 1 month ago ↶
2340
} ) ? ) ,
2341
None => None ,
2342
} ;
68a92484 Andy McCall 22 days ago ↶
2343
2344
2345
2346
if attestation.is_some ( ) {
2347
for k in [ self .signing_key ( ) , self .config.author_key]
2348
.into_iter ( )
2349
.flatten ( )
2350
{
2351
if let Ok ( id) = self .identity ( & k) {
2352
let body = id.encode ( ) ;
2353
if ! bundle
2354
.objects
2355
.iter ( )
2356
.any ( | ( t , b ) | * t = = ObjectType:: Identity & & * b = = body)
2357
{
2358
bundle.objects.push ( ( ObjectType:: Identity, body) ) ;
2359
}
2360
}
2361
}
f0a517d2 Andy McCall 18 days ago ↶
2362
2363
let signers: Vec< KeyId> = self .signing_key ( ) .into_iter ( ) .collect ( ) ;
2364
self .attach_certificates_for ( & mut bundle, & signers) ? ;
68a92484 Andy McCall 22 days ago ↶
2365
}
98286406 Andy McCall 1 month ago ↶
2366
let serial = attestation.as_ref ( ) .and_then ( | a | match a.attest.body {
2367
AttestBody:: Ref { serial, .. } => Some ( serial) ,
2368
_ => None ,
2369
} ) ;
83e0677b Andy McCall 1 month ago ↶
2370
let sent_serial = policy_to_send.as_ref ( ) .map ( | p | p.policy.serial ) ;
132b884b Andy McCall 1 month ago ↶
2371
2372
for t in & tags_to_send {
2373
let extra = self .negotiate ( & remote_heads, & t.heads) ? ;
75167672 Andy McCall 1 month ago
2374
let extra: Vec< ChangeHash> =
2375
extra.into_iter ( ) .filter ( | h | ! to_send.contains ( h) ) .collect ( ) ;
132b884b Andy McCall 1 month ago ↶
2376
if ! extra.is_empty ( ) {
2377
let more = self .bundle_for ( & extra) ? ;
2378
bundle.changes.extend ( more.changes) ;
2c75672f Andy McCall 1 month ago ↶
2379
bundle.hashes.extend ( more.hashes) ;
132b884b Andy McCall 1 month ago ↶
2380
bundle.attests.extend ( more.attests) ;
2381
bundle.objects.extend ( more.objects) ;
2382
}
75167672 Andy McCall 1 month ago
2383
bundle.objects.push ( (
2384
ObjectType:: State,
2385
StateObject:: new( t.heads.clone ( ) ) .encode ( ) ,
2386
) ) ;
132b884b Andy McCall 1 month ago ↶
2387
}
28fc23d7 Andy McCall 1 month ago ↶
2388
if let Some ( l) = & policy_to_send {
2389
let remote_serial = resp_policy_serial;
75167672 Andy McCall 1 month ago
2390
bundle.policies = self
2391
.policy_history ( ) ?
2392
.into_iter ( )
2393
.filter ( | sp | {
2394
remote_serial.is_none_or ( | r | sp.policy.serial > r )
2395
& & sp.policy.serial < l.policy.serial
2396
} )
2397
.collect ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2398
}
75167672 Andy McCall 1 month ago
2399
let results = remote.push (
2400
bundle,
2401
vec! [ RefUpdate {
2402
name: bookmark.clone ( ) ,
2403
old_state: remote_state,
2404
new_state,
2405
force,
2406
attestation,
2407
} ] ,
2408
policy_to_send,
2409
tags_to_send,
2410
) ? ;
2411
if let ( Some ( serial) , true ) = (
2412
sent_serial,
2413
results.iter ( ) .any ( | r | r.ok & & r.name = = " policy" ) ,
2414
) {
83e0677b Andy McCall 1 month ago ↶
2415
self .record_policy_serial ( remote_name, serial) ? ;
2416
}
2417
if results.iter ( ) .any ( | r | r.ok & & r.name = = bookmark ) {
98286406 Andy McCall 1 month ago ↶
2418
let mut view = op.view.clone ( ) ;
75167672 Andy McCall 1 month ago
2419
view.remotes
2420
.entry ( remote_name.to_string ( ) )
2421
.or_default ( )
2422
.insert ( bookmark.clone ( ) , new_state) ;
98286406 Andy McCall 1 month ago ↶
2423
self .record_op ( & [ op_ref] , & format! ( " oxy push {remote_name} " ) , view) ? ;
2424
if let Some ( s) = serial {
2425
self .record_serial ( remote_name, & bookmark, s) ? ;
2426
}
2427
}
2428
Ok ( results)
2429
}
2430
2431
2432
2433
pub fn clone ( url : & str , dest : & Path) -> Result< Repo> {
7421309f Andy McCall 1 month ago ↶
2434
Self :: clone_with( url, dest, None , & [ ] )
b3c5a59a Andy McCall 1 month ago ↶
2435
}
2436
7421309f Andy McCall 1 month ago ↶
2437
2438
2439
75167672 Andy McCall 1 month ago
2440
pub fn clone_with (
2441
url : & str ,
2442
dest : & Path,
2443
max_blob : Option< u64 > ,
2444
sparse : & [String],
2445
) -> Result< Repo> {
2863456a Andy McCall 1 month ago ↶
2446
Self :: clone_full( url, dest, max_blob, sparse, None )
2447
}
2448
2449
2450
75167672 Andy McCall 1 month ago
2451
pub fn clone_shallow (
2452
url : & str ,
2453
dest : & Path,
2454
depth : u32 ,
2455
max_blob : Option< u64 > ,
2456
sparse : & [String],
2457
) -> Result< Repo> {
2863456a Andy McCall 1 month ago ↶
2458
Self :: clone_full( url, dest, max_blob, sparse, Some ( depth) )
2459
}
2460
75167672 Andy McCall 1 month ago
2461
fn clone_full (
2462
url : & str ,
2463
dest : & Path,
2464
max_blob : Option< u64 > ,
2465
sparse : & [String],
2466
depth : Option< u32 > ,
2467
) -> Result< Repo> {
d36ba2f7 Andy McCall 16 days ago ↶
2468
Ok ( Self :: clone_reporting( url, dest, max_blob, sparse, depth) ? .0 )
2469
}
2470
2471
2472
2473
2474
2475
2476
pub fn clone_reporting (
2477
url : & str ,
2478
dest : & Path,
2479
max_blob : Option< u64 > ,
2480
sparse : & [String],
2481
depth : Option< u32 > ,
2482
) -> Result< ( Repo, Vec< ( String, String) > ) > {
ff1f62fe Andy McCall 1 month ago ↶
2483
let options = crate :: credentials:: Credentials:: user( ) .options_for ( url) ;
2484
Self :: clone_using( url, dest, max_blob, sparse, depth, & options)
a07f60a3 Andy McCall 1 month ago ↶
2485
}
2486
ff1f62fe Andy McCall 1 month ago ↶
2487
2488
2489
a07f60a3 Andy McCall 1 month ago ↶
2490
pub ( crate ) fn clone_as_subrepo ( & self , url : & str , dest : & Path) -> Result< Repo> {
2491
let options = self .credentials_for ( url) ;
d36ba2f7 Andy McCall 16 days ago ↶
2492
Ok ( Self :: clone_using( url, dest, None , & [ ] , None , & options) ? .0 )
a07f60a3 Andy McCall 1 month ago ↶
2493
}
2494
2495
2496
2497
fn remote_on_same_hub ( & self , url : & str ) -> Option< String> {
2498
let origin = | u : & str | -> Option< String> {
2499
let ( scheme, rest) = u.split_once ( " ://" ) ? ;
9f9e72b9 Andy McCall 1 month ago ↶
2500
if ! matches! ( scheme, " http" | " https" ) {
2501
return None ;
2502
}
a07f60a3 Andy McCall 1 month ago ↶
2503
let host = rest.split ( ' /' ) .next ( ) ? ;
2504
Some ( format! (
2505
" {} ://{} " ,
2506
scheme.to_ascii_lowercase ( ) ,
2507
host.to_ascii_lowercase ( )
2508
) )
2509
} ;
2510
let want = origin ( url) ? ;
2511
self .config
2512
.remotes
2513
.iter ( )
2514
.find ( | ( _, u ) | origin ( u) .as_deref ( ) = = Some ( want.as_str ( ) ) )
2515
.map ( | ( name , _) | name.clone ( ) )
2516
}
2517
2518
2519
ff1f62fe Andy McCall 1 month ago ↶
2520
2521
a07f60a3 Andy McCall 1 month ago ↶
2522
pub ( crate ) fn credentials_for ( & self , url : & str ) -> RemoteOptions {
2523
match self .remote_on_same_hub ( url) {
2524
Some ( name) => RemoteOptions {
2525
repo_id: None ,
9f9e72b9 Andy McCall 1 month ago ↶
2526
cache_dir: None ,
a07f60a3 Andy McCall 1 month ago ↶
2527
.. self .remote_options ( & name)
2528
} ,
ff1f62fe Andy McCall 1 month ago ↶
2529
None => {
2530
let mut options = RemoteOptions:: default( ) ;
2531
self .fill_credentials ( url, & mut options) ;
2532
options
2533
}
a07f60a3 Andy McCall 1 month ago ↶
2534
}
2535
}
2536
2537
fn clone_using (
2538
url : & str ,
2539
dest : & Path,
2540
max_blob : Option< u64 > ,
2541
sparse : & [String],
2542
depth : Option< u32 > ,
2543
options : & RemoteOptions,
d36ba2f7 Andy McCall 16 days ago ↶
2544
) -> Result< ( Repo, Vec< ( String, String) > ) > {
a07f60a3 Andy McCall 1 month ago ↶
2545
let remote = open_remote ( url, options) ? ;
98286406 Andy McCall 1 month ago ↶
2546
let id = remote.repo_id ( ) ? ;
75167672 Andy McCall 1 month ago
2547
if id = = crate :: protocol:: UNBOUND_ID {
05e71ed6 Andy McCall 1 month ago ↶
2548
return fail ( format! (
2549
" {url} is empty and unbound: push a repository to it first"
2550
) ) ;
75167672 Andy McCall 1 month ago
2551
}
98286406 Andy McCall 1 month ago ↶
2552
let mut repo = Repo:: init_with_id( dest, id) ? ;
827281ee Andy McCall 1 month ago ↶
2553
repo.config
2554
.set ( " remote.origin.url" , & crate :: protocol:: stored_url( url) ) ? ;
b3c5a59a Andy McCall 1 month ago ↶
2555
if let Some ( m) = max_blob {
2556
repo.config.set ( " filter.max-blob" , & m.to_string ( ) ) ? ;
7421309f Andy McCall 1 month ago ↶
2557
} else if ! sparse.is_empty ( ) {
2558
repo.config.set ( " filter.max-blob" , " 0" ) ? ;
2559
}
2560
if ! sparse.is_empty ( ) {
2561
repo.config.set ( " filter.paths" , & sparse.join ( " ," ) ) ? ;
b3c5a59a Andy McCall 1 month ago ↶
2562
}
98286406 Andy McCall 1 month ago ↶
2563
repo.save_config ( ) ? ;
d36ba2f7 Andy McCall 16 days ago ↶
2564
let skipped = if let Some ( depth) = depth {
2863456a Andy McCall 1 month ago ↶
2565
repo.fetch_shallow ( " origin" , depth) ? ;
d36ba2f7 Andy McCall 16 days ago ↶
2566
Vec :: new( )
2863456a Andy McCall 1 month ago ↶
2567
} else {
d36ba2f7 Andy McCall 16 days ago ↶
2568
repo.fetch ( " origin" ) ? .skipped
2569
} ;
98286406 Andy McCall 1 month ago ↶
2570
let ( op_ref, op) = repo.head_op ( ) ? ;
d36ba2f7 Andy McCall 16 days ago ↶
2571
2572
2573
2574
let mut theirs = op.view.remotes.get ( " origin" ) .cloned ( ) .unwrap_or_default ( ) ;
2575
for ( name, _ ) in & skipped {
2576
theirs.insert ( name.clone ( ) , ObjectRef:: ZERO ) ;
2577
}
9f9e72b9 Andy McCall 1 month ago ↶
2578
2579
2580
let advertised = repo
2581
.open_named_remote ( " origin" )
2582
.ok ( )
2583
.and_then ( | r | r.refs ( ) .ok ( ) )
2584
.and_then ( | r | r.default_bookmark ) ;
14edbaaf Andy McCall 1 month ago ↶
2585
let bookmark = advertised
2586
.filter ( | b | theirs.contains_key ( b) )
2587
.or_else ( | | {
2588
theirs
2589
.contains_key ( & repo.config.bookmark)
2590
.then ( | | repo.config.bookmark.clone ( ) )
2591
} )
2592
.or_else ( | | {
2593
( theirs.len ( ) = = 1 )
2594
.then ( | | theirs.keys ( ) .next ( ) .cloned ( ) )
2595
.flatten ( )
2596
} )
2597
.unwrap_or_else ( | | repo.config.bookmark.clone ( ) ) ;
d36ba2f7 Andy McCall 16 days ago ↶
2598
if let Some ( ( _ , why) ) = skipped.iter ( ) .find ( | ( name , _) | * name = = bookmark) {
2599
return fail ( format! ( " cannot check out {bookmark} : {why} " ) ) ;
2600
}
14edbaaf Andy McCall 1 month ago ↶
2601
let initial = repo.config.bookmark.clone ( ) ;
2602
if bookmark ! = repo.config.bookmark {
2603
fs:: create_dir_all( repo.workspace_bookmark_path ( ) .parent ( ) .unwrap ( ) ) ? ;
2604
write_atomic ( & repo.workspace_bookmark_path ( ) , bookmark.as_bytes ( ) ) ? ;
2605
repo.config.bookmark = bookmark.clone ( ) ;
2606
}
2607
if let Some ( state) = theirs.get ( & bookmark) .copied ( ) {
98286406 Andy McCall 1 month ago ↶
2608
let heads = repo.store.get_state ( & state) ? .heads;
2609
let mut view = op.view.clone ( ) ;
14edbaaf Andy McCall 1 month ago ↶
2610
2611
if initial ! = bookmark & & repo.heads_of ( & view, & initial) ? .is_empty ( ) {
2612
view.bookmarks.remove ( & initial) ;
2613
}
98286406 Andy McCall 1 month ago ↶
2614
view.bookmarks.insert ( bookmark, state) ;
2615
repo.record_op ( & [ op_ref] , & format! ( " oxy clone {url} " ) , view) ? ;
2616
repo.checkout ( & heads, false , " oxy clone (checkout)" ) ? ;
2617
}
d36ba2f7 Andy McCall 16 days ago ↶
2618
Ok ( ( repo, skipped) )
f8c69576 Andy McCall 1 month ago ↶
2619
}
2620
}
2621
2622
2623
2624
2625
2626
fn next_serial ( last : Option< u64 > ) -> u64 {
2627
match last {
2628
Some ( l) => l + 1 ,
2629
None => Hlc:: now( ) .wall_ms.max ( 1 ) ,
98286406 Andy McCall 1 month ago ↶
2630
}
2631
}