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