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