Skip to content

Commit ba9ce95

Browse files
author
fossdd
committed
Update pijul
1 parent 166666a commit ba9ce95

File tree

14 files changed

+192
-115
lines changed

14 files changed

+192
-115
lines changed

libpijul/src/alive/output.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct ConflictStackElt {
1111
conflict: Vec<Path>,
1212
side: usize,
1313
idx: usize,
14+
id: usize,
1415
}
1516

1617
fn output_conflict<T: ChannelTxnT, B: VertexBuffer, P: ChangeStore>(
@@ -26,12 +27,12 @@ fn output_conflict<T: ChannelTxnT, B: VertexBuffer, P: ChangeStore>(
2627
conflict: vec![conflict],
2728
side: 0,
2829
idx: 0,
30+
id: 0,
2931
}];
3032
let mut is_zombie = None;
3133
let mut id = 0;
3234
while let Some(mut elt) = stack.pop() {
3335
let n_sides = elt.conflict.len();
34-
let id0 = id;
3536
if n_sides > 1 && elt.side == 0 && elt.idx == 0 {
3637
elt.conflict.sort_by(|a, b| {
3738
let a_ = a
@@ -58,7 +59,6 @@ fn output_conflict<T: ChannelTxnT, B: VertexBuffer, P: ChangeStore>(
5859
line_buf.begin_conflict(id, &[])?;
5960
}
6061
}
61-
id += 1;
6262
}
6363

6464
let mut next = None;
@@ -71,10 +71,10 @@ fn output_conflict<T: ChannelTxnT, B: VertexBuffer, P: ChangeStore>(
7171
PathElement::Scc { scc } => {
7272
let vid = sccs[scc][0];
7373
let ext = txn.get_external(&graph[vid].vertex.change)?.unwrap();
74-
line_buf.conflict_next(id0, &[&ext.into()])?;
74+
line_buf.conflict_next(elt.id, &[&ext.into()])?;
7575
}
7676
_ => {
77-
line_buf.conflict_next(id0, &[])?;
77+
line_buf.conflict_next(elt.id, &[])?;
7878
}
7979
}
8080
}
@@ -95,10 +95,12 @@ fn output_conflict<T: ChannelTxnT, B: VertexBuffer, P: ChangeStore>(
9595
PathElement::Conflict { ref mut sides } => {
9696
let sides = std::mem::replace(sides, Vec::new());
9797
elt.idx += 1;
98+
id += 1;
9899
next = Some(ConflictStackElt {
99100
side: 0,
100101
idx: 0,
101102
conflict: sides,
103+
id,
102104
});
103105
break 'outer;
104106
}
@@ -113,7 +115,7 @@ fn output_conflict<T: ChannelTxnT, B: VertexBuffer, P: ChangeStore>(
113115
if let Some(id) = is_zombie.take() {
114116
line_buf.end_zombie_conflict(id)?;
115117
}
116-
line_buf.end_conflict(id0)?;
118+
line_buf.end_conflict(elt.id)?;
117119
}
118120
} else {
119121
if let Some(id) = is_zombie.take() {

libpijul/src/apply/edge.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,22 @@ where
3636
let mut zombies = Vec::new();
3737

3838
loop {
39-
if !n.flag.contains(EdgeFlags::DELETED) {
39+
if (n.flag & EdgeFlags::df()).is_empty() {
40+
// Inside a file, nondeleted zombies occur when we are
41+
// re-introducing an alive edge (for example to solve a
42+
// zombie conflict), and another, unknown patch deleted
43+
// the context in parallel.
44+
//
45+
// For folder edges, children or parents are either on the
46+
// same level as this edge (i.e. to/from a basename
47+
// vertex), in which case the zombie nature of the file
48+
// will be detected at output and record time.
49+
//
50+
// Or the children or parents are at different levels,
51+
// which isn't a zombie conflict, since it is normal
52+
// commutation between folder operations.
53+
//
54+
// This justifies the EdgeFlags::df() exclusion above.
4055
collect_nondeleted_zombies::<_, _>(
4156
txn,
4257
graph,
@@ -68,6 +83,7 @@ where
6883
if apply_check(source, target) {
6984
put_graph_with_rev(txn, graph, n.flag, source, target, change)?;
7085
for intro in zombies.drain(..) {
86+
assert!(!n.flag.contains(EdgeFlags::FOLDER));
7187
put_graph_with_rev(txn, graph, EdgeFlags::DELETED, source, target, intro)?;
7288
}
7389
}
@@ -107,13 +123,17 @@ where
107123
T: GraphMutTxnT + TreeTxnT,
108124
K: FnMut(&Hash) -> bool,
109125
{
126+
// Iter through deleted parents of the edge's source. Any unknown
127+
// edge points towards a zombie.
110128
for v in iter_deleted_parents(txn, graph, source)? {
111129
let v = v?;
112130
let intro = v.introduced_by();
113131
if !known(&txn.get_external(&intro)?.unwrap().into()) {
114132
zombies.push(intro)
115133
}
116134
}
135+
// Iter through the children of the non-deleted target. Any deleted
136+
// parent of a child makes the child a zombie.
117137
for v in iter_adjacent(txn, graph, target, EdgeFlags::empty(), EdgeFlags::all())? {
118138
let v = v?;
119139
if v.flag().contains(EdgeFlags::PARENT) {

libpijul/src/change/text_changes.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,9 +1111,13 @@ pub fn get_deleted_names<C: ChangeStore>(
11111111
del: &Atom<Option<Hash>>,
11121112
) -> Result<Vec<String>, TextSerError<C::Error>> {
11131113
let mut res = Vec::new();
1114+
let mut h = HashSet::new();
11141115
if let Atom::EdgeMap(ref e) = del {
11151116
let mut tmp = Vec::new();
11161117
for d in e.edges.iter() {
1118+
if !h.insert(d.to) {
1119+
continue;
1120+
}
11171121
tmp.clear();
11181122
changes
11191123
.get_contents_ext(d.to, &mut tmp)

libpijul/src/fs.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ pub(crate) fn follow_oldest_path<T: ChannelTxnT, C: ChangeStore>(
862862
let flag1 = flag0 | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
863863
let mut name_buf = Vec::new();
864864
let mut ambiguous = false;
865+
let mut seen = HashSet::new();
865866
for c in crate::path::components(path) {
866867
'outer: loop {
867868
let mut next = None;
@@ -910,11 +911,14 @@ pub(crate) fn follow_oldest_path<T: ChannelTxnT, C: ChangeStore>(
910911
}
911912
}
912913
if let Some((next, _)) = next {
913-
current = iter_adjacent(txn, txn.graph(channel), *next, flag0, flag1)?
914+
let current_ = iter_adjacent(txn, txn.graph(channel), *next, flag0, flag1)?
914915
.next()
915916
.unwrap()?
916917
.dest();
917-
break;
918+
if seen.insert(current_) {
919+
current = current_;
920+
break;
921+
}
918922
} else {
919923
return Err(FsErrorC::NotFound(FsNotFound(path.to_string())));
920924
}
@@ -933,10 +937,12 @@ pub fn find_path<T: ChannelTxnT, C: ChangeStore>(
933937
debug!("oldest_path = {:?}", v);
934938
let mut path = Vec::new();
935939
let mut name_buf = Vec::new();
940+
let mut seen = HashSet::new();
936941
let flag0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
937942
let flag1 = EdgeFlags::all();
938943
let mut all_alive = true;
939944
'outer: while !v.change.is_root() {
945+
debug!("path = {:?}", path);
940946
let mut next_v = None;
941947
let mut alive = false;
942948
let inode_vertex = match txn.find_block_end(txn.graph(channel), v) {
@@ -964,6 +970,7 @@ pub fn find_path<T: ChannelTxnT, C: ChangeStore>(
964970
if !name.flag().contains(EdgeFlags::PARENT) {
965971
continue;
966972
}
973+
967974
debug!("oldest_path, name = {:?}", name);
968975
let age = txn
969976
.get_changeset(txn.changes(&channel), &name.dest().change)?
@@ -993,6 +1000,9 @@ pub fn find_path<T: ChannelTxnT, C: ChangeStore>(
9931000
continue;
9941001
}
9951002
}
1003+
if !seen.insert(next.dest()) {
1004+
continue;
1005+
}
9961006
next_v = Some((name_dest, age, next.dest()));
9971007
}
9981008
}

libpijul/src/pristine/edge.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ impl EdgeFlags {
2626
Self::DELETED | Self::BLOCK
2727
}
2828

29+
#[inline]
30+
pub(crate) fn df() -> Self {
31+
Self::DELETED | Self::FOLDER
32+
}
33+
2934
#[inline]
3035
pub(crate) fn bp() -> Self {
3136
Self::BLOCK | Self::PARENT

libpijul/src/record.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,7 +1407,9 @@ where
14071407
introduced_by: Some(parent.introduced_by()),
14081408
})
14091409
}
1410-
moved.need_new_name = false
1410+
moved.need_new_name = false;
1411+
// We've found an alive parent, delete the others.
1412+
is_first_parent = false;
14111413
} else {
14121414
// Clean up the extra deleted edges.
14131415
debug!("cleanup");
@@ -1428,7 +1430,12 @@ where
14281430
to: parent_dest.to_option(),
14291431
introduced_by: Some(grandparent.introduced_by()),
14301432
});
1431-
// The following is really important in missing context detection:
1433+
// The following extra edge is meant to allow
1434+
// detection of missing contexts in folders: indeed,
1435+
// if we didn't have it, we couldn't tell the
1436+
// difference between a convergent renaming or
1437+
// deletion and a conflict between a renaming and a
1438+
// deletion.
14321439
if !parent_was_resurrected && !parent.flag().contains(EdgeFlags::PSEUDO) {
14331440
moved.alive.push(NewEdge {
14341441
previous: parent.flag() - EdgeFlags::PARENT,
@@ -1444,9 +1451,8 @@ where
14441451
.entry((grandparent.dest(), *parent_dest))
14451452
.or_insert_with(Vec::new);
14461453
v.push(Some(grandparent.introduced_by()));
1447-
moved.need_new_name = false
1448-
}
1449-
if !grandparent.flag().contains(EdgeFlags::DELETED) {
1454+
moved.need_new_name = false;
1455+
// We've found an alive parent, delete the others.
14501456
is_first_parent = false;
14511457
}
14521458
}

0 commit comments

Comments
 (0)