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