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