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