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