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