ff0af1ee unknown 1 month ago
1
2
3
4
5
6
0bdc3f91 Andy McCall 1 month ago ↶
7
use crate :: fasthash:: { FastMap as HashMap, FastSet as HashSet} ;
ff0af1ee unknown 1 month ago
8
use crate :: hash:: ChangeHash;
01a47cd7 Andy McCall 1 month ago ↶
9
use crate :: lines:: { LineRef, Lines} ;
ff0af1ee unknown 1 month ago
10
use crate :: objects:: AtomRange;
0bdc3f91 Andy McCall 1 month ago ↶
11
use std:: collections:: { BTreeMap, BTreeSet} ;
ff0af1ee unknown 1 month ago
12
13
14
pub type AtomKey = ( ChangeHash, u32 ) ;
15
16
# [ derive ( Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug ) ]
17
pub enum Vertex {
18
Start,
19
Atom( AtomKey) ,
20
End,
21
}
22
23
24
pub trait DepOracle {
25
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool ;
26
}
27
28
29
pub struct NoDeps ;
30
impl DepOracle for NoDeps {
31
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool {
32
a = = b
33
}
34
}
35
36
# [ derive ( Default, Clone, Debug ) ]
37
struct AtomInfo {
01a47cd7 Andy McCall 1 month ago ↶
38
39
40
content : Option< LineRef> ,
ff0af1ee unknown 1 month ago
41
deleted_by : BTreeSet< ChangeHash> ,
42
}
43
44
# [ derive ( Clone, Debug ) ]
45
pub struct FileGraph {
46
atoms : HashMap< AtomKey, AtomInfo> ,
0bdc3f91 Andy McCall 1 month ago ↶
47
48
succ : HashMap< Vertex, Edges> ,
49
}
50
51
52
53
# [ derive ( Clone, Debug, Default, PartialEq, Eq ) ]
54
enum Edges {
55
# [ default ]
56
None ,
57
One( Vertex) ,
58
Many( Vec< Vertex> ) ,
ff0af1ee unknown 1 month ago
59
}
60
0bdc3f91 Andy McCall 1 month ago ↶
61
impl Edges {
62
fn insert ( & mut self , v : Vertex) {
63
match self {
64
Edges:: None => * self = Edges:: One( v) ,
65
Edges:: One( a) if * a = = v => { }
66
Edges:: One( a) => {
67
let a = * a;
68
* self = Edges:: Many( if a < v { vec! [ a, v] } else { vec! [ v, a] } ) ;
69
}
70
Edges:: Many( list) => {
71
if let Err ( i) = list.binary_search ( & v) {
72
list.insert ( i, v) ;
73
}
74
}
75
}
76
}
77
78
fn as_slice ( & self ) -> & [ Vertex] {
79
match self {
80
Edges:: None => & [ ] ,
81
Edges:: One( v) => std:: slice:: from_ref( v) ,
82
Edges:: Many( list) => list,
83
}
84
}
85
86
fn iter ( & self ) -> std:: slice:: Iter< '_ , Vertex> {
87
self .as_slice ( ) .iter ( )
88
}
89
}
90
ff0af1ee unknown 1 month ago
91
impl Default for FileGraph {
92
fn default ( ) -> Self {
93
Self :: new( )
94
}
95
}
96
97
98
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
99
pub enum Segment {
100
Lines( Vec< AtomKey> ) ,
101
102
Conflict( Vec< Vec< AtomKey> > ) ,
103
104
75167672 Andy McCall 1 month ago
105
Zombie {
106
deleted: Vec< AtomKey> ,
107
inserted: Vec< AtomKey> ,
108
} ,
ff0af1ee unknown 1 month ago
109
}
110
111
# [ derive ( Clone, Debug, PartialEq, Eq, Default ) ]
112
pub struct Materialized {
113
pub segments : Vec< Segment> ,
114
}
115
116
impl Materialized {
117
pub fn is_conflicted ( & self ) -> bool {
75167672 Andy McCall 1 month ago
118
self .segments
119
.iter ( )
120
.any ( | s | ! matches! ( s, Segment:: Lines( _ ) ) )
ff0af1ee unknown 1 month ago
121
}
122
pub fn conflict_count ( & self ) -> usize {
75167672 Andy McCall 1 month ago
123
self .segments
124
.iter ( )
125
.filter ( | s | ! matches! ( s, Segment:: Lines( _ ) ) )
126
.count ( )
ff0af1ee unknown 1 month ago
127
}
128
129
pub fn live_atoms ( & self ) -> Vec< AtomKey> {
130
let mut out = Vec :: new( ) ;
131
for s in & self .segments {
132
match s {
133
Segment:: Lines( a) => out.extend ( a) ,
134
Segment:: Conflict( sides) => sides.iter ( ) .for_each ( | x | out.extend ( x) ) ,
135
Segment:: Zombie { inserted, .. } => out.extend ( inserted) ,
136
}
137
}
138
out
139
}
140
}
141
142
impl FileGraph {
143
pub fn new ( ) -> Self {
75167672 Andy McCall 1 month ago
144
let mut g = FileGraph {
0bdc3f91 Andy McCall 1 month ago ↶
145
atoms: HashMap:: default( ) ,
146
succ: HashMap:: default( ) ,
75167672 Andy McCall 1 month ago
147
} ;
ff0af1ee unknown 1 month ago
148
g.edge ( Vertex:: Start, Vertex:: End) ;
149
g
150
}
151
152
fn edge ( & mut self , a : Vertex, b : Vertex) {
153
self .succ.entry ( a) .or_default ( ) .insert ( b) ;
154
self .succ.entry ( b) .or_default ( ) ;
155
}
156
157
75167672 Andy McCall 1 month ago
158
pub fn insert (
159
& mut self ,
160
change : ChangeHash,
161
first_index : u32 ,
162
after : Vertex,
163
before : Vertex,
01a47cd7 Andy McCall 1 month ago ↶
164
lines : & Lines,
0bdc3f91 Andy McCall 1 month ago ↶
165
) {
01a47cd7 Andy McCall 1 month ago ↶
166
self .insert_owned ( change, first_index, after, before, lines.clone ( ) ) ;
0bdc3f91 Andy McCall 1 month ago ↶
167
}
168
169
170
171
172
pub fn insert_owned (
173
& mut self ,
174
change : ChangeHash,
175
first_index : u32 ,
176
after : Vertex,
177
before : Vertex,
01a47cd7 Andy McCall 1 month ago ↶
178
lines : Lines,
75167672 Andy McCall 1 month ago
179
) {
0bdc3f91 Andy McCall 1 month ago ↶
180
self .atoms.reserve ( lines.len ( ) ) ;
181
self .succ.reserve ( lines.len ( ) + 1 ) ;
ff0af1ee unknown 1 month ago
182
let mut prev = after;
01a47cd7 Andy McCall 1 month ago ↶
183
for i in 0 .. lines.len ( ) {
ff0af1ee unknown 1 month ago
184
let key = ( change, first_index + i as u32 ) ;
01a47cd7 Andy McCall 1 month ago ↶
185
self .atoms.entry ( key) .or_default ( ) .content =
186
Some ( LineRef:: new( lines.clone ( ) , i as u32 ) ) ;
ff0af1ee unknown 1 month ago
187
let v = Vertex:: Atom( key) ;
0bdc3f91 Andy McCall 1 month ago ↶
188
self .succ.entry ( prev) .or_default ( ) .insert ( v) ;
ff0af1ee unknown 1 month ago
189
prev = v;
190
}
0bdc3f91 Andy McCall 1 month ago ↶
191
self .succ.entry ( prev) .or_default ( ) .insert ( before) ;
192
self .succ.entry ( before) .or_default ( ) ;
ff0af1ee unknown 1 month ago
193
}
194
195
196
pub fn delete ( & mut self , by : ChangeHash, range : & AtomRange) {
197
for i in range.from.. range.to {
75167672 Andy McCall 1 month ago
198
self .atoms
199
.entry ( ( range.change, i) )
200
.or_default ( )
201
.deleted_by
202
.insert ( by) ;
ff0af1ee unknown 1 month ago
203
}
204
}
205
206
pub fn is_live ( & self , k : & AtomKey) -> bool {
75167672 Andy McCall 1 month ago
207
self .atoms
208
.get ( k)
209
.is_some_and ( | a | a.content.is_some ( ) & & a.deleted_by.is_empty ( ) )
ff0af1ee unknown 1 month ago
210
}
211
212
pub fn content ( & self , k : & AtomKey) -> Option< & [ u8 ] > {
01a47cd7 Andy McCall 1 month ago ↶
213
self .atoms
214
.get ( k)
215
.and_then ( | a | a.content.as_ref ( ) .map ( LineRef:: bytes) )
ff0af1ee unknown 1 month ago
216
}
217
218
pub fn deleters ( & self , k : & AtomKey) -> impl Iterator< Item = & ChangeHash> {
75167672 Andy McCall 1 month ago
219
self .atoms
220
.get ( k)
221
.into_iter ( )
222
.flat_map ( | a | a.deleted_by.iter ( ) )
ff0af1ee unknown 1 month ago
223
}
224
225
226
pub fn atom_count ( & self ) -> usize {
227
self .atoms.len ( )
228
}
229
62794ecc Andy McCall 1 month ago ↶
230
231
232
83d61e80 Andy McCall 1 month ago ↶
233
234
235
236
237
pub fn between ( & self , first : & AtomKey, last : & AtomKey) -> Vec< AtomKey> {
238
let ( from, to) = ( Vertex:: Atom( * first) , Vertex:: Atom( * last) ) ;
239
let mut ahead: HashSet< Vertex> = HashSet:: default( ) ;
240
let mut stack = vec! [ from] ;
241
while let Some ( v) = stack.pop ( ) {
242
if ahead.insert ( v) {
243
stack.extend (
244
self .succ
245
.get ( & v)
246
.map ( | e | e.as_slice ( ) )
247
.unwrap_or ( & [ ] )
248
.iter ( )
249
.copied ( ) ,
250
) ;
251
}
252
}
253
if ! ahead.contains ( & to) {
254
return vec! [ ] ;
255
}
256
let mut pred: HashMap< Vertex, Vec< Vertex> > = HashMap:: default( ) ;
257
for ( u, es) in & self .succ {
258
if ahead.contains ( u) {
259
for w in es.iter ( ) {
260
pred.entry ( * w) .or_default ( ) .push ( * u) ;
261
}
262
}
263
}
264
let mut behind: HashSet< Vertex> = HashSet:: default( ) ;
265
let mut stack = vec! [ to] ;
266
while let Some ( v) = stack.pop ( ) {
267
if behind.insert ( v) {
268
stack.extend ( pred.get ( & v) .into_iter ( ) .flatten ( ) .copied ( ) ) ;
269
}
270
}
271
let mut out: Vec< AtomKey> = ahead
272
.intersection ( & behind)
273
.filter_map ( | v | match v {
274
Vertex:: Atom( k) if self .atoms.contains_key ( k) => Some ( * k) ,
275
_ => None ,
276
} )
277
.collect ( ) ;
278
out.sort ( ) ;
279
out
280
}
281
282
283
pub fn touching ( & self , atoms : & [AtomKey]) -> BTreeSet< ChangeHash> {
284
let mut out = BTreeSet:: new( ) ;
285
for k in atoms {
286
out.insert ( k.0 ) ;
287
if let Some ( info) = self .atoms.get ( k) {
288
out.extend ( info.deleted_by.iter ( ) .copied ( ) ) ;
289
}
290
}
291
out
292
}
293
62794ecc Andy McCall 1 month ago ↶
294
pub fn live_successor_changes ( & self , k : & AtomKey) -> Vec< ChangeHash> {
295
self .succ
296
.get ( & Vertex:: Atom( * k) )
297
.map ( | ss | {
298
ss.iter ( )
299
.filter_map ( | s | match s {
300
Vertex:: Atom( y) if self .is_live ( y) => Some ( y.0 ) ,
301
_ => None ,
302
} )
303
.collect ( )
304
} )
305
.unwrap_or_default ( )
306
}
307
ff0af1ee unknown 1 month ago
308
fn is_ghost ( & self , k : & AtomKey, info : & AtomInfo, deps : & dyn DepOracle) -> bool {
23b217d7 Andy McCall 1 month ago ↶
309
310
311
312
ff0af1ee unknown 1 month ago
313
self .succ.get ( & Vertex:: Atom( * k) ) .is_some_and ( | ss | {
314
ss.iter ( ) .any ( | s | match s {
75167672 Andy McCall 1 month ago
315
Vertex:: Atom( y) if self .is_live ( y) => ! info
316
.deleted_by
317
.iter ( )
318
.any ( | d | deps.depends_on ( d, & y.0 ) | | deps.depends_on ( & y.0 , d) ) ,
ff0af1ee unknown 1 month ago
319
_ => false ,
320
} )
321
} )
322
}
323
324
75167672 Andy McCall 1 month ago
325
fn reduced_succ (
326
& self ,
327
v : Vertex,
328
kept : & HashSet< Vertex> ,
329
memo : & mut HashMap< Vertex, BTreeSet< Vertex> > ,
330
) -> BTreeSet< Vertex> {
598009a7 Andy McCall 1 month ago ↶
331
let direct = self .succ.get ( & v) .map ( Edges:: as_slice) .unwrap_or_default ( ) ;
332
333
334
if direct.iter ( ) .all ( | s | kept.contains ( s) ) {
335
return direct.iter ( ) .copied ( ) .collect ( ) ;
336
}
ff0af1ee unknown 1 month ago
337
let mut out = BTreeSet:: new( ) ;
598009a7 Andy McCall 1 month ago ↶
338
let mut stack: Vec< Vertex> = direct.to_vec ( ) ;
0bdc3f91 Andy McCall 1 month ago ↶
339
let mut seen = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
340
while let Some ( s) = stack.pop ( ) {
341
if kept.contains ( & s) {
342
out.insert ( s) ;
343
} else if seen.insert ( s) {
344
if let Some ( m) = memo.get ( & s) {
345
out.extend ( m.iter ( ) .copied ( ) ) ;
346
} else if let Some ( next) = self .succ.get ( & s) {
347
stack.extend ( next.iter ( ) .copied ( ) ) ;
348
}
349
}
350
}
351
out
598009a7 Andy McCall 1 month ago ↶
352
}
353
354
355
356
357
358
359
fn as_chain ( & self ) -> Option< Vec< AtomKey> > {
360
if self
361
.atoms
362
.values ( )
363
.any ( | a | a.content.is_none ( ) | | ! a.deleted_by.is_empty ( ) )
364
{
365
return None ;
366
}
367
let mut order = Vec :: with_capacity( self .atoms.len ( ) ) ;
368
let mut v = Vertex:: Start;
369
loop {
370
let succ = self .succ.get ( & v) ? .as_slice ( ) ;
371
let mut atoms = succ.iter ( ) .filter ( | s | * * s ! = Vertex:: End) ;
372
match ( atoms.next ( ) , atoms.next ( ) ) {
373
( None , _ ) if succ.contains ( & Vertex:: End) => break ,
374
( Some ( Vertex:: Atom( k) ) , None ) if order.len ( ) < self .atoms.len ( ) => {
375
order.push ( * k) ;
376
v = Vertex:: Atom( * k) ;
377
}
378
_ => return None ,
379
}
380
}
381
( order.len ( ) = = self .atoms.len ( ) ) .then_some ( order)
ff0af1ee unknown 1 month ago
382
}
383
384
385
pub fn materialize ( & self , deps : & dyn DepOracle) -> Materialized {
598009a7 Andy McCall 1 month ago ↶
386
if let Some ( order) = self .as_chain ( ) {
387
return Materialized {
388
segments: if order.is_empty ( ) {
389
vec! [ ]
390
} else {
391
vec! [ Segment:: Lines( order) ]
392
} ,
393
} ;
394
}
395
self .materialize_general ( deps)
396
}
397
398
399
400
fn materialize_general ( & self , deps : & dyn DepOracle) -> Materialized {
ff0af1ee unknown 1 month ago
401
0bdc3f91 Andy McCall 1 month ago ↶
402
let mut kept: HashSet< Vertex> = HashSet:: default( ) ;
403
let mut ghosts: HashSet< AtomKey> = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
404
kept.insert ( Vertex:: Start) ;
405
kept.insert ( Vertex:: End) ;
406
for ( k, info) in & self .atoms {
407
if info.content.is_none ( ) {
408
continue ;
409
}
410
if info.deleted_by.is_empty ( ) {
411
kept.insert ( Vertex:: Atom( * k) ) ;
412
} else if self .is_ghost ( k, info, deps) {
413
kept.insert ( Vertex:: Atom( * k) ) ;
414
ghosts.insert ( * k) ;
415
}
416
}
417
418
0bdc3f91 Andy McCall 1 month ago ↶
419
let mut memo = HashMap:: default( ) ;
420
let mut rsucc: HashMap< Vertex, BTreeSet< Vertex> > = HashMap:: default( ) ;
421
let mut indeg: HashMap< Vertex, usize > = HashMap:: default( ) ;
ff0af1ee unknown 1 month ago
422
for & u in & kept {
423
indeg.entry ( u) .or_insert ( 0 ) ;
424
let rs = self .reduced_succ ( u, & kept, & mut memo) ;
425
for & w in & rs {
426
* indeg.entry ( w) .or_insert ( 0 ) + = 1 ;
427
}
428
rsucc.insert ( u, rs) ;
429
}
430
431
75167672 Andy McCall 1 month ago
432
let mut ready: BTreeSet< Vertex> = indeg
433
.iter ( )
434
.filter ( | ( _, &d ) | d = = 0 )
435
.map ( | ( v , _) | * v)
436
.collect ( ) ;
0bdc3f91 Andy McCall 1 month ago ↶
437
let mut side: HashMap< Vertex, u32 > = HashMap:: default( ) ;
438
let mut joins: HashSet< Vertex> = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
439
let mut next_side = 0 u32 ;
440
let mut plain: Vec< Vertex> = Vec :: new( ) ;
441
let mut conflict: Option< BTreeMap< u32 , Vec< Vertex> > > = None ;
442
let mut segments: Vec< Segment> = Vec :: new( ) ;
443
444
let flush_plain = | plain : & mut Vec< Vertex> , segments : & mut Vec< Segment> | {
75167672 Andy McCall 1 month ago
445
let atoms: Vec< AtomKey> = plain
446
.iter ( )
447
.filter_map ( | v | {
448
if let Vertex:: Atom( k) = v {
449
Some ( * k)
450
} else {
451
None
452
}
453
} )
454
.collect ( ) ;
ff0af1ee unknown 1 month ago
455
plain.clear ( ) ;
456
if ! atoms.is_empty ( ) {
457
segments.extend ( split_zombies ( atoms, & ghosts) ) ;
458
}
459
} ;
460
let finish_conflict = | c : BTreeMap< u32 , Vec< Vertex> > , segments : & mut Vec< Segment> | {
461
let sides: Vec< Vec< AtomKey> > = c
462
.into_values ( )
463
.map ( | vs | {
464
vs.into_iter ( )
465
.filter_map ( | v | match v {
466
Vertex:: Atom( k) if ! ghosts.contains ( & k) => Some ( k) ,
467
_ => None ,
468
} )
469
.collect:: < Vec< _ > > ( )
470
} )
471
.filter ( | s | ! s.is_empty ( ) )
472
.collect ( ) ;
473
match sides.len ( ) {
474
0 => { }
475
1 => segments.push ( Segment:: Lines( sides.into_iter ( ) .next ( ) .unwrap ( ) ) ) ,
476
_ => segments.push ( Segment:: Conflict( sides) ) ,
477
}
478
} ;
479
480
while ! ready.is_empty ( ) {
481
if ready.len ( ) > = 2 {
482
if conflict.is_none ( ) {
483
flush_plain ( & mut plain, & mut segments) ;
484
conflict = Some ( BTreeMap:: new( ) ) ;
485
}
486
for r in & ready {
487
if ! side.contains_key ( r) {
488
side.insert ( * r, next_side) ;
489
next_side + = 1 ;
490
}
491
}
492
}
493
let v = ready.pop_first ( ) .unwrap ( ) ;
494
let my_side = side.get ( & v) .copied ( ) ;
495
match ( & mut conflict, my_side) {
496
( Some ( c) , Some ( s) ) => c.entry ( s) .or_default ( ) .push ( v) ,
497
( Some ( _ ) , None ) => {
498
let c = conflict.take ( ) .unwrap ( ) ;
499
finish_conflict ( c, & mut segments) ;
500
plain.push ( v) ;
501
}
502
( None , _ ) => plain.push ( v) ,
503
}
504
for & w in & rsucc[ & v] {
505
let d = indeg.get_mut ( & w) .unwrap ( ) ;
506
* d - = 1 ;
507
if conflict.is_some ( ) & & ! joins.contains ( & w) {
508
match ( my_side, side.get ( & w) .copied ( ) ) {
509
( Some ( s) , None ) => {
510
side.insert ( w, s) ;
511
}
512
( Some ( s) , Some ( t) ) if s ! = t => {
513
side.remove ( & w) ;
514
joins.insert ( w) ;
515
}
516
( None , Some ( _ ) ) => {
517
side.remove ( & w) ;
518
joins.insert ( w) ;
519
}
520
_ => { }
521
}
522
}
523
if * d = = 0 {
524
ready.insert ( w) ;
525
}
526
}
527
}
528
if let Some ( c) = conflict.take ( ) {
529
finish_conflict ( c, & mut segments) ;
530
}
531
flush_plain ( & mut plain, & mut segments) ;
532
Materialized { segments }
533
}
534
535
536
537
pub fn render ( & self , m : & Materialized, label : & dyn Fn( &ChangeHash ) -> String) -> Vec< u8 > {
538
let total = m.conflict_count ( ) ;
539
let mut n = 0 ;
540
let mut out: Vec< u8 > = Vec :: new( ) ;
541
let push_atoms = | out : & mut Vec< u8 > , atoms : & [AtomKey]| {
542
for a in atoms {
543
if let Some ( c) = self .content ( a) {
544
out.extend_from_slice ( c) ;
545
}
546
}
547
} ;
548
let newline_if_needed = | out : & mut Vec< u8 > | {
549
if ! out.is_empty ( ) & & * out.last ( ) .unwrap ( ) ! = b ' \n ' {
550
out.push ( b ' \n ' ) ;
551
}
552
} ;
553
for seg in & m.segments {
554
match seg {
555
Segment:: Lines( a) => push_atoms ( & mut out, a) ,
556
Segment:: Conflict( sides) => {
557
n + = 1 ;
558
let k = sides.len ( ) ;
559
for ( i, s) in sides.iter ( ) .enumerate ( ) {
560
newline_if_needed ( & mut out) ;
561
let who = s.first ( ) .map ( | a | label ( & a.0 ) ) .unwrap_or_default ( ) ;
562
let marker = if i = = 0 { " <<<<<<<" } else { " =======" } ;
563
out.extend_from_slice (
564
format! ( " {marker} oxy conflict {n} of {total} , side {} of {k} : change {who} \n " , i + 1 ) .as_bytes ( ) ,
565
) ;
566
push_atoms ( & mut out, s) ;
567
}
568
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
569
out.extend_from_slice (
570
format! ( " >>>>>>> oxy conflict {n} of {total} end\n " ) .as_bytes ( ) ,
571
) ;
ff0af1ee unknown 1 month ago
572
}
573
Segment:: Zombie { deleted, inserted } => {
574
n + = 1 ;
575
newline_if_needed ( & mut out) ;
576
let deleter = deleted
577
.first ( )
578
.and_then ( | a | self .deleters ( a) .next ( ) .copied ( ) )
579
.map ( | d | label ( & d) )
580
.unwrap_or_default ( ) ;
581
let inserter = inserted.first ( ) .map ( | a | label ( & a.0 ) ) .unwrap_or_default ( ) ;
582
out.extend_from_slice (
583
format! ( " <<<<<<< oxy conflict {n} of {total} , side 1 of 2: deleted by change {deleter} \n -------\n " ) .as_bytes ( ) ,
584
) ;
585
push_atoms ( & mut out, deleted) ;
586
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
587
out.extend_from_slice (
588
format! (
589
" ======= oxy conflict {n} of {total} , side 2 of 2: change {inserter} \n "
590
)
591
.as_bytes ( ) ,
592
) ;
ff0af1ee unknown 1 month ago
593
push_atoms ( & mut out, inserted) ;
594
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
595
out.extend_from_slice (
596
format! ( " >>>>>>> oxy conflict {n} of {total} end\n " ) .as_bytes ( ) ,
597
) ;
ff0af1ee unknown 1 month ago
598
}
599
}
600
}
601
out
602
}
603
}
604
252e8cb2 Andy McCall 1 month ago ↶
605
fn vertex_value ( v : & Vertex) -> crate:: cbor:: Value {
606
use crate :: cbor:: Value;
607
match v {
608
Vertex:: Start => Value:: Uint( 0 ) ,
609
Vertex:: End => Value:: Uint( 1 ) ,
75167672 Andy McCall 1 month ago
610
Vertex:: Atom( ( c, i) ) => {
611
Value:: Array( vec! [ Value:: Bytes( c.0. to_vec ( ) ) , Value:: Uint( * i as u64 ) ] )
612
}
252e8cb2 Andy McCall 1 month ago ↶
613
}
614
}
615
616
fn vertex_from ( v : & crate:: cbor:: Value) -> Result< Vertex, crate:: objects:: ObjectError> {
617
use crate :: cbor:: Value;
618
use crate :: objects:: ObjectError;
619
match v {
620
Value:: Uint( 0 ) => Ok ( Vertex:: Start) ,
621
Value:: Uint( 1 ) => Ok ( Vertex:: End) ,
75167672 Andy McCall 1 month ago
622
Value:: Array( a) if a.len ( ) = = 2 => Ok ( Vertex:: Atom( (
623
crate :: objects:: oref( & a[ 0 ] , " vertex" ) ? ,
624
crate :: objects:: u32_( & a[ 1 ] , " vertex" ) ? ,
625
) ) ) ,
626
_ => Err ( ObjectError:: Schema(
627
" vertex: expected 0, 1, or [hash, index]" .into ( ) ,
628
) ) ,
252e8cb2 Andy McCall 1 month ago ↶
629
}
630
}
631
632
impl FileGraph {
633
634
pub fn to_value ( & self ) -> crate:: cbor:: Value {
635
use crate :: cbor:: Value;
636
let mut atoms: Vec< ( & AtomKey, & AtomInfo) > = self .atoms.iter ( ) .collect ( ) ;
637
atoms.sort_by_key ( | ( k , _) | * * k) ;
75167672 Andy McCall 1 month ago
638
let mut edges: Vec< ( Vertex, Vertex) > = self
639
.succ
640
.iter ( )
641
.flat_map ( | ( a , bs ) | bs.iter ( ) .map ( move | b| ( * a, * b) ) )
642
.collect ( ) ;
252e8cb2 Andy McCall 1 month ago ↶
643
edges.sort ( ) ;
644
Value:: map( [
645
(
646
0 ,
647
Value:: Array(
648
atoms
649
.into_iter ( )
650
.map ( | ( (c , i ) , info) | {
651
Value:: Array( vec! [
652
Value:: Bytes( c.0. to_vec ( ) ) ,
653
Value:: Uint( * i as u64 ) ,
75167672 Andy McCall 1 month ago
654
info.content
655
.as_ref ( )
01a47cd7 Andy McCall 1 month ago ↶
656
.map ( | b | Value:: Bytes( b.bytes ( ) .to_vec ( ) ) )
75167672 Andy McCall 1 month ago
657
.unwrap_or ( Value:: Null) ,
658
Value:: Array(
659
info.deleted_by
660
.iter ( )
661
.map ( | d | Value:: Bytes( d.0. to_vec ( ) ) )
662
.collect ( ) ,
663
) ,
252e8cb2 Andy McCall 1 month ago ↶
664
] )
665
} )
666
.collect ( ) ,
667
) ,
668
),
75167672 Andy McCall 1 month ago
669
(
670
1 ,
671
Value:: Array(
672
edges
673
.iter ( )
674
.map ( | ( a , b ) | Value:: Array( vec! [ vertex_value ( a) , vertex_value ( b) ] ) )
675
.collect ( ) ,
676
) ,
677
) ,
252e8cb2 Andy McCall 1 month ago ↶
678
] )
679
}
680
681
pub fn from_value ( v : & crate:: cbor:: Value) -> Result< FileGraph, crate:: objects:: ObjectError> {
682
use crate :: objects:: { array, bytes, field, map, oref, u32_, ObjectError} ;
683
let m = map ( v, " file graph" ) ? ;
75167672 Andy McCall 1 month ago
684
let mut g = FileGraph {
0bdc3f91 Andy McCall 1 month ago ↶
685
atoms: HashMap:: default( ) ,
686
succ: HashMap:: default( ) ,
75167672 Andy McCall 1 month ago
687
} ;
01a47cd7 Andy McCall 1 month ago ↶
688
let atoms = array ( field ( m, 0 , " atoms" ) ? , " atoms" ) ? ;
689
690
let mut texts: Vec< & [ u8 ] > = Vec :: new( ) ;
691
for a in atoms {
692
let e = array ( a, " atom" ) ? ;
693
if e.len ( ) = = 4 & & ! e[ 2 ] .is_null ( ) {
694
texts.push ( bytes ( & e[ 2 ] , " atom content" ) ? ) ;
695
}
696
}
697
let all: Lines = texts.into_iter ( ) .collect ( ) ;
698
let mut next = 0 u32 ;
699
for a in atoms {
252e8cb2 Andy McCall 1 month ago ↶
700
let e = array ( a, " atom" ) ? ;
701
if e.len ( ) ! = 4 {
702
return Err ( ObjectError:: Schema( " atom: expected 4 fields" .into ( ) ) ) ;
703
}
704
let key = ( oref ( & e[ 0 ] , " atom change" ) ? , u32_ ( & e[ 1 ] , " atom index" ) ? ) ;
75167672 Andy McCall 1 month ago
705
let content = if e[ 2 ] .is_null ( ) {
706
None
707
} else {
01a47cd7 Andy McCall 1 month ago ↶
708
next + = 1 ;
709
Some ( LineRef:: new( all.clone ( ) , next - 1 ) )
75167672 Andy McCall 1 month ago
710
} ;
711
let deleted_by = array ( & e[ 3 ] , " deleters" ) ?
712
.iter ( )
713
.map ( | d | oref ( d, " deleter" ) )
714
.collect:: < Result< BTreeSet< _ > , _ > > ( ) ? ;
715
g.atoms.insert (
716
key,
717
AtomInfo {
718
content,
719
deleted_by,
720
} ,
721
) ;
252e8cb2 Andy McCall 1 month ago ↶
722
}
723
for e in array ( field ( m, 1 , " edges" ) ? , " edges" ) ? {
724
let pair = array ( e, " edge" ) ? ;
725
if pair.len ( ) ! = 2 {
726
return Err ( ObjectError:: Schema( " edge: expected [from, to]" .into ( ) ) ) ;
727
}
728
g.edge ( vertex_from ( & pair[ 0 ] ) ? , vertex_from ( & pair[ 1 ] ) ? ) ;
729
}
730
Ok ( g)
731
}
732
}
733
ff0af1ee unknown 1 month ago
734
735
fn split_zombies ( atoms : Vec< AtomKey> , ghosts : & HashSet< AtomKey> ) -> Vec< Segment> {
736
let mut out = Vec :: new( ) ;
737
let mut lines: Vec< AtomKey> = Vec :: new( ) ;
738
let mut i = 0 ;
739
while i < atoms.len ( ) {
740
if ! ghosts.contains ( & atoms[ i] ) {
741
lines.push ( atoms[ i] ) ;
742
i + = 1 ;
743
continue ;
744
}
745
if ! lines.is_empty ( ) {
746
out.push ( Segment:: Lines( std:: mem:: take( & mut lines) ) ) ;
747
}
748
let mut deleted = Vec :: new( ) ;
749
while i < atoms.len ( ) & & ghosts.contains ( & atoms[ i] ) {
750
deleted.push ( atoms[ i] ) ;
751
i + = 1 ;
752
}
753
let mut inserted = Vec :: new( ) ;
754
if i < atoms.len ( ) {
755
let ch = atoms[ i] .0 ;
756
while i < atoms.len ( ) & & atoms[ i] .0 = = ch & & ! ghosts.contains ( & atoms[ i] ) {
757
inserted.push ( atoms[ i] ) ;
758
i + = 1 ;
759
}
760
}
761
out.push ( Segment:: Zombie { deleted, inserted } ) ;
762
}
763
if ! lines.is_empty ( ) {
764
out.push ( Segment:: Lines( lines) ) ;
765
}
766
out
767
}
768
769
# [ cfg ( test ) ]
770
mod tests {
771
use super :: * ;
772
use crate :: hash:: ObjectRef;
773
774
fn h ( n : u8 ) -> ChangeHash {
775
ObjectRef( [ n; 32 ] )
776
}
01a47cd7 Andy McCall 1 month ago ↶
777
fn lines ( v : & [& str ]) -> Lines {
778
v.iter ( ) .map ( | s | s.as_bytes ( ) ) .collect ( )
ff0af1ee unknown 1 month ago
779
}
780
fn text ( g : & FileGraph, m : & Materialized) -> String {
781
String :: from_utf8( g.render ( m, & | c| c.short ( 4 ) ) ) .unwrap ( )
782
}
783
784
struct Deps ( HashMap< ChangeHash, Vec< ChangeHash> > ) ;
785
impl DepOracle for Deps {
786
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool {
787
if a = = b {
788
return true ;
789
}
790
let mut stack = vec! [ * a] ;
0bdc3f91 Andy McCall 1 month ago ↶
791
let mut seen = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
792
while let Some ( x) = stack.pop ( ) {
793
if let Some ( ds) = self .0. get ( & x) {
794
for d in ds {
795
if d = = b {
796
return true ;
797
}
798
if seen.insert ( * d) {
799
stack.push ( * d) ;
800
}
801
}
802
}
803
}
804
false
805
}
598009a7 Andy McCall 1 month ago ↶
806
}
807
808
809
810
# [ test ]
811
fn the_chain_shortcut_agrees_with_the_general_order ( ) {
812
let mut g = FileGraph:: new( ) ;
813
g.insert (
814
h ( 1 ) ,
815
0 ,
816
Vertex:: Start,
817
Vertex:: End,
818
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
819
) ;
820
assert! ( g.as_chain ( ) .is_some ( ) ) ;
821
assert_eq! ( g.materialize ( & NoDeps) , g.materialize_general ( & NoDeps) ) ;
822
823
g.insert (
824
h ( 2 ) ,
825
0 ,
826
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
827
Vertex:: End,
828
& lines ( & [ " d\n " ] ) ,
829
) ;
830
assert! ( g.as_chain ( ) .is_some ( ) ) ;
831
assert_eq! ( g.materialize ( & NoDeps) , g.materialize_general ( & NoDeps) ) ;
832
833
g.insert (
834
h ( 4 ) ,
835
0 ,
836
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
837
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
838
& lines ( & [ " x\n " ] ) ,
839
) ;
840
assert! ( g.as_chain ( ) .is_none ( ) ) ;
841
assert_eq! ( text ( & g, & g.materialize ( & NoDeps) ) , " a\n x\n b\n c\n d\n " ) ;
842
843
let mut c = FileGraph:: new( ) ;
844
c.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " ] ) ) ;
845
c.insert ( h ( 2 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " b\n " ] ) ) ;
846
assert! ( c.as_chain ( ) .is_none ( ) ) ;
847
assert! ( c.materialize ( & NoDeps) .is_conflicted ( ) ) ;
848
849
let mut d = FileGraph:: new( ) ;
850
d.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
851
d.delete (
852
h ( 3 ) ,
853
& AtomRange {
854
change: h ( 1 ) ,
855
from: 0 ,
856
to: 1 ,
857
} ,
858
) ;
859
assert! ( d.as_chain ( ) .is_none ( ) ) ;
860
assert_eq! ( d.materialize ( & NoDeps) , d.materialize_general ( & NoDeps) ) ;
861
862
let e = FileGraph:: new( ) ;
863
assert_eq! ( e.materialize ( & NoDeps) , e.materialize_general ( & NoDeps) ) ;
ff0af1ee unknown 1 month ago
864
}
865
866
# [ test ]
867
fn sequential_inserts_and_deletes ( ) {
868
let mut g = FileGraph:: new( ) ;
75167672 Andy McCall 1 month ago
869
g.insert (
870
h ( 1 ) ,
871
0 ,
872
Vertex:: Start,
873
Vertex:: End,
874
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
875
) ;
ff0af1ee unknown 1 month ago
876
let m = g.materialize ( & NoDeps) ;
877
assert! ( ! m.is_conflicted ( ) ) ;
878
assert_eq! ( text ( & g, & m) , " a\n b\n c\n " ) ;
879
880
75167672 Andy McCall 1 month ago
881
g.insert (
882
h ( 2 ) ,
883
0 ,
884
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
885
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
886
& lines ( & [ " x\n " ] ) ,
887
) ;
888
g.delete (
889
h ( 2 ) ,
890
& AtomRange {
891
change: h ( 1 ) ,
892
from: 0 ,
893
to: 1 ,
894
} ,
895
) ;
ff0af1ee unknown 1 month ago
896
let deps = Deps( [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) ] .into_iter ( ) .collect ( ) ) ;
897
let m = g.materialize ( & deps) ;
898
assert! ( ! m.is_conflicted ( ) ) ;
899
assert_eq! ( text ( & g, & m) , " b\n x\n c\n " ) ;
900
assert_eq! ( m.live_atoms ( ) , vec! [ ( h ( 1 ) , 1 ) , ( h ( 2 ) , 0 ) , ( h ( 1 ) , 2 ) ] ) ;
901
}
902
903
# [ test ]
904
fn concurrent_inserts_at_same_anchor_conflict ( ) {
905
let mut g = FileGraph:: new( ) ;
906
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
907
g.insert (
908
h ( 2 ) ,
909
0 ,
910
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
911
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
912
& lines ( & [ " from-2\n " ] ) ,
913
) ;
914
g.insert (
915
h ( 3 ) ,
916
0 ,
917
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
918
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
919
& lines ( & [ " from-3\n " , " more-3\n " ] ) ,
920
) ;
921
let deps = Deps(
922
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
923
.into_iter ( )
924
.collect ( ) ,
925
) ;
ff0af1ee unknown 1 month ago
926
let m = g.materialize ( & deps) ;
927
assert! ( m.is_conflicted ( ) ) ;
928
assert_eq! ( m.conflict_count ( ) , 1 ) ;
929
assert_eq! (
930
m.segments,
931
vec! [
932
Segment:: Lines( vec! [ ( h ( 1 ) , 0 ) ] ) ,
933
Segment:: Conflict( vec! [ vec! [ ( h ( 2 ) , 0 ) ] , vec! [ ( h ( 3 ) , 0 ) , ( h ( 3 ) , 1 ) ] ] ) ,
934
Segment:: Lines( vec! [ ( h ( 1 ) , 1 ) ] ) ,
935
]
936
) ;
937
let t = text ( & g, & m) ;
938
assert! ( t.starts_with ( " a\n <<<<<<< oxy conflict 1 of 1, side 1 of 2: change 0202\n from-2\n ======= oxy conflict 1 of 1, side 2 of 2: change 0303\n from-3\n more-3\n >>>>>>> oxy conflict 1 of 1 end\n b\n " ) , " {t}" ) ;
939
}
940
941
# [ test ]
942
fn concurrent_inserts_at_different_anchors_do_not_conflict ( ) {
943
let mut g = FileGraph:: new( ) ;
75167672 Andy McCall 1 month ago
944
g.insert (
945
h ( 1 ) ,
946
0 ,
947
Vertex:: Start,
948
Vertex:: End,
949
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
950
) ;
951
g.insert (
952
h ( 2 ) ,
953
0 ,
954
Vertex:: Start,
955
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
956
& lines ( & [ " top\n " ] ) ,
957
) ;
958
g.insert (
959
h ( 3 ) ,
960
0 ,
961
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
962
Vertex:: End,
963
& lines ( & [ " bottom\n " ] ) ,
964
) ;
965
let deps = Deps(
966
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
967
.into_iter ( )
968
.collect ( ) ,
969
) ;
ff0af1ee unknown 1 month ago
970
let m = g.materialize ( & deps) ;
971
assert! ( ! m.is_conflicted ( ) ) ;
972
assert_eq! ( text ( & g, & m) , " top\n a\n b\n c\n bottom\n " ) ;
973
}
974
975
# [ test ]
976
fn concurrent_deletes_of_the_same_line_agree ( ) {
977
let mut g = FileGraph:: new( ) ;
978
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
979
g.delete (
980
h ( 2 ) ,
981
& AtomRange {
982
change: h ( 1 ) ,
983
from: 0 ,
984
to: 1 ,
985
} ,
986
) ;
987
g.delete (
988
h ( 3 ) ,
989
& AtomRange {
990
change: h ( 1 ) ,
991
from: 0 ,
992
to: 1 ,
993
} ,
994
) ;
995
let deps = Deps(
996
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
997
.into_iter ( )
998
.collect ( ) ,
999
) ;
ff0af1ee unknown 1 month ago
1000
let m = g.materialize ( & deps) ;
1001
assert! ( ! m.is_conflicted ( ) ) ;
1002
assert_eq! ( text ( & g, & m) , " b\n " ) ;
1003
}
1004
1005
# [ test ]
1006
fn delete_insert_conflict_is_a_zombie ( ) {
1007
let mut g = FileGraph:: new( ) ;
1008
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
1009
75167672 Andy McCall 1 month ago
1010
g.delete (
1011
h ( 2 ) ,
1012
& AtomRange {
1013
change: h ( 1 ) ,
1014
from: 0 ,
1015
to: 1 ,
1016
} ,
1017
) ;
1018
g.insert (
1019
h ( 3 ) ,
1020
0 ,
1021
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1022
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1023
& lines ( & [ " after-a\n " ] ) ,
1024
) ;
1025
let deps = Deps(
1026
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1027
.into_iter ( )
1028
.collect ( ) ,
1029
) ;
ff0af1ee unknown 1 month ago
1030
let m = g.materialize ( & deps) ;
1031
assert! ( m.is_conflicted ( ) ) ;
1032
assert_eq! (
1033
m.segments,
1034
vec! [
75167672 Andy McCall 1 month ago
1035
Segment:: Zombie {
1036
deleted: vec! [ ( h ( 1 ) , 0 ) ] ,
1037
inserted: vec! [ ( h ( 3 ) , 0 ) ]
1038
} ,
ff0af1ee unknown 1 month ago
1039
Segment:: Lines( vec! [ ( h ( 1 ) , 1 ) ] ) ,
1040
]
1041
) ;
1042
let t = text ( & g, & m) ;
1043
assert! ( t.contains ( " -------\n a\n =======" ) , " {t}" ) ;
1044
assert! ( t.contains ( " after-a\n >>>>>>>" ) , " {t}" ) ;
1045
1046
75167672 Andy McCall 1 month ago
1047
let deps = Deps(
1048
[ ( h ( 2 ) , vec! [ h ( 1 ) , h ( 3 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1049
.into_iter ( )
1050
.collect ( ) ,
1051
) ;
ff0af1ee unknown 1 month ago
1052
let m = g.materialize ( & deps) ;
1053
assert! ( ! m.is_conflicted ( ) ) ;
1054
assert_eq! ( text ( & g, & m) , " after-a\n b\n " ) ;
1055
}
1056
1057
# [ test ]
23b217d7 Andy McCall 1 month ago ↶
1058
fn insert_after_a_line_deleted_by_a_dependency_is_not_a_conflict ( ) {
1059
1060
1061
let mut g = FileGraph:: new( ) ;
1062
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1063
g.delete (
1064
h ( 2 ) ,
1065
& AtomRange {
1066
change: h ( 1 ) ,
1067
from: 1 ,
1068
to: 2 ,
1069
} ,
1070
) ;
1071
g.insert (
1072
h ( 3 ) ,
1073
0 ,
1074
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1075
Vertex:: End,
1076
& lines ( & [ " c\n " ] ) ,
1077
) ;
1078
let deps = Deps(
1079
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 2 ) ] ) ]
1080
.into_iter ( )
1081
.collect ( ) ,
1082
) ;
23b217d7 Andy McCall 1 month ago ↶
1083
let m = g.materialize ( & deps) ;
1084
assert! ( ! m.is_conflicted ( ) , " {:?}" , m.segments) ;
1085
assert_eq! ( text ( & g, & m) , " a\n c\n " ) ;
1086
1087
1088
let mut g = FileGraph:: new( ) ;
1089
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " x\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1090
g.delete (
1091
h ( 1 ) ,
1092
& AtomRange {
1093
change: h ( 1 ) ,
1094
from: 0 ,
1095
to: 1 ,
1096
} ,
1097
) ;
1098
g.insert (
1099
h ( 2 ) ,
1100
0 ,
1101
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1102
Vertex:: End,
1103
& lines ( & [ " y\n " ] ) ,
1104
) ;
23b217d7 Andy McCall 1 month ago ↶
1105
let deps = Deps( [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) ] .into_iter ( ) .collect ( ) ) ;
1106
let m = g.materialize ( & deps) ;
1107
assert! ( ! m.is_conflicted ( ) ) ;
1108
assert_eq! ( text ( & g, & m) , " y\n " ) ;
1109
}
1110
1111
# [ test ]
ff0af1ee unknown 1 month ago
1112
fn resolution_collapses_a_conflict ( ) {
1113
let mut g = FileGraph:: new( ) ;
1114
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1115
g.insert (
1116
h ( 2 ) ,
1117
0 ,
1118
Vertex:: Start,
1119
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1120
& lines ( & [ " x\n " ] ) ,
1121
) ;
1122
g.insert (
1123
h ( 3 ) ,
1124
0 ,
1125
Vertex:: Start,
1126
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1127
& lines ( & [ " y\n " ] ) ,
1128
) ;
1129
let mut deps: HashMap< _ , _ > = [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1130
.into_iter ( )
1131
.collect ( ) ;
ff0af1ee unknown 1 month ago
1132
assert! ( g.materialize ( & Deps( deps.clone ( ) ) ) .is_conflicted ( ) ) ;
1133
75167672 Andy McCall 1 month ago
1134
g.delete (
1135
h ( 4 ) ,
1136
& AtomRange {
1137
change: h ( 2 ) ,
1138
from: 0 ,
1139
to: 1 ,
1140
} ,
1141
) ;
ff0af1ee unknown 1 month ago
1142
deps.insert ( h ( 4 ) , vec! [ h ( 2 ) , h ( 3 ) ] ) ;
1143
let m = g.materialize ( & Deps( deps) ) ;
1144
assert! ( ! m.is_conflicted ( ) ) ;
1145
assert_eq! ( text ( & g, & m) , " y\n a\n " ) ;
1146
}
1147
1148
# [ test ]
1149
fn empty_and_unterminated_files ( ) {
1150
let g = FileGraph:: new( ) ;
1151
let m = g.materialize ( & NoDeps) ;
1152
assert_eq! ( m.segments, vec! [ ] ) ;
1153
assert_eq! ( text ( & g, & m) , " " ) ;
1154
let mut g = FileGraph:: new( ) ;
1155
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " no newline" ] ) ) ;
1156
assert_eq! ( text ( & g, & g.materialize ( & NoDeps) ) , " no newline" ) ;
1157
}
1158
252e8cb2 Andy McCall 1 month ago ↶
1159
# [ test ]
1160
fn serialization_round_trip ( ) {
1161
let mut g = FileGraph:: new( ) ;
1162
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1163
g.insert (
1164
h ( 2 ) ,
1165
0 ,
1166
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1167
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1168
& lines ( & [ " x\n " ] ) ,
1169
) ;
1170
g.delete (
1171
h ( 3 ) ,
1172
& AtomRange {
1173
change: h ( 1 ) ,
1174
from: 1 ,
1175
to: 2 ,
1176
} ,
1177
) ;
252e8cb2 Andy McCall 1 month ago ↶
1178
let v = g.to_value ( ) ;
1179
let bytes = crate :: cbor:: encode( & v) ;
1180
let back = FileGraph:: from_value( & crate :: cbor:: decode( & bytes) .unwrap ( ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1181
let deps = Deps(
1182
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1183
.into_iter ( )
1184
.collect ( ) ,
1185
) ;
252e8cb2 Andy McCall 1 month ago ↶
1186
assert_eq! ( back.materialize ( & deps) , g.materialize ( & deps) ) ;
1187
assert_eq! ( text ( & back, & back.materialize ( & deps) ) , " a\n x\n " ) ;
75167672 Andy McCall 1 month ago
1188
assert_eq! (
1189
crate :: cbor:: encode( & back.to_value ( ) ) ,
1190
bytes,
1191
" deterministic"
1192
) ;
252e8cb2 Andy McCall 1 month ago ↶
1193
}
1194
ff0af1ee unknown 1 month ago
1195
1196
# [ test ]
1197
fn application_order_is_irrelevant ( ) {
1198
use rand:: seq:: SliceRandom;
75167672 Andy McCall 1 month ago
1199
type Op = (
1200
ChangeHash,
01a47cd7 Andy McCall 1 month ago ↶
1201
Vec< ( u32 , Vertex, Vertex, Lines) > ,
75167672 Andy McCall 1 month ago
1202
Vec< AtomRange> ,
1203
) ;
ff0af1ee unknown 1 month ago
1204
let ops: Vec< Op> = vec! [
75167672 Andy McCall 1 month ago
1205
(
1206
h ( 1 ) ,
1207
vec! [ ( 0 , Vertex:: Start, Vertex:: End, lines ( & [ " a\n " , " b\n " , " c\n " ] ) ) ] ,
1208
vec! [ ] ,
1209
) ,
1210
(
1211
h ( 2 ) ,
1212
vec! [ (
1213
0 ,
1214
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1215
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1216
lines ( & [ " p\n " ] ) ,
1217
) ] ,
1218
vec! [ ] ,
1219
) ,
1220
(
1221
h ( 3 ) ,
1222
vec! [ (
1223
0 ,
1224
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1225
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1226
lines ( & [ " q\n " , " r\n " ] ) ,
1227
) ] ,
1228
vec! [ AtomRange {
1229
change: h ( 1 ) ,
1230
from: 2 ,
1231
to: 3 ,
1232
} ] ,
1233
) ,
1234
(
1235
h ( 4 ) ,
1236
vec! [ (
1237
0 ,
1238
Vertex:: Atom( ( h ( 3 ) , 1 ) ) ,
1239
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1240
lines ( & [ " s\n " ] ) ,
1241
) ] ,
1242
vec! [ AtomRange {
1243
change: h ( 1 ) ,
1244
from: 1 ,
1245
to: 2 ,
1246
} ] ,
1247
) ,
1248
(
1249
h ( 5 ) ,
1250
vec! [ ( 0 , Vertex:: Atom( ( h ( 1 ) , 2 ) ) , Vertex:: End, lines ( & [ " z\n " ] ) ) ] ,
1251
vec! [ ] ,
1252
) ,
ff0af1ee unknown 1 month ago
1253
] ;
1254
let deps = Deps(
75167672 Andy McCall 1 month ago
1255
[
1256
( h ( 2 ) , vec! [ h ( 1 ) ] ) ,
1257
( h ( 3 ) , vec! [ h ( 1 ) ] ) ,
1258
( h ( 4 ) , vec! [ h ( 3 ) , h ( 1 ) ] ) ,
1259
( h ( 5 ) , vec! [ h ( 1 ) ] ) ,
1260
]
1261
.into_iter ( )
1262
.collect ( ) ,
ff0af1ee unknown 1 month ago
1263
) ;
1264
let build = | order : & [usize ]| {
1265
let mut g = FileGraph:: new( ) ;
1266
for & i in order {
1267
let ( c, ins, dels) = & ops[ i] ;
1268
for ( idx, a, b, l) in ins {
1269
g.insert ( * c, * idx, * a, * b, l) ;
1270
}
1271
for d in dels {
1272
g.delete ( * c, d) ;
1273
}
1274
}
1275
let m = g.materialize ( & deps) ;
1276
( m.clone ( ) , text ( & g, & m) )
1277
} ;
1278
let reference = build ( & [ 0 , 1 , 2 , 3 , 4 ] ) ;
1279
assert! ( reference.0. is_conflicted ( ) ) ;
1280
let mut rng = rand:: thread_rng( ) ;
1281
let mut order: Vec< usize > = ( 0 .. ops.len ( ) ) .collect ( ) ;
1282
for _ in 0 .. 50 {
1283
order.shuffle ( & mut rng) ;
1284
assert_eq! ( build ( & order) , reference, " order {order:?}" ) ;
1285
}
1286
}
1287
}