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