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