/// If we're deleting a folder edge, there is a possibility that this
/// solves a "zombie conflict", by removing the last child of a folder
/// that was a zombie, in which case that parent folder can finally
/// rest in peace.
///
/// This function takes care of this for the entire change, by
/// removing all obsolete folder conflict edges.
pub(crate) fn detect_folder_conflict_resolutions<T: GraphMutTxnT>(
txn: &mut T,
channel: &mut T::Graph,
ws: &mut Workspace,
change_id: ChangeId,
change: &Change,
) -> Result<(), MissingError<T::GraphError>> {
for change_ in change.changes.iter() {
for change_ in change_.iter() {
if let Atom::EdgeMap(ref n) = *change_ {
for edge in n.edges.iter() {
if !edge.flag.contains(EdgeFlags::DELETED) {
continue;
}
detect_folder_conflict_resolution(txn, channel, ws, change_id, &n.inode, edge)?
}
}
}
}
Ok(())
}
fn detect_folder_conflict_resolution<T: GraphMutTxnT>(
txn: &mut T,
channel: &mut T::Graph,
ws: &mut Workspace,
change_id: ChangeId,
inode: &Position<Option<Hash>>,
e: &NewEdge<Option<Hash>>,
) -> Result<(), MissingError<T::GraphError>> {
let mut stack = vec![if e.flag.contains(EdgeFlags::FOLDER) {
if e.to.is_empty() {
internal_pos(txn, &e.to.start_pos(), change_id)?
} else {
internal_pos(txn, &e.from, change_id)?
}
} else {
internal_pos(txn, &inode, change_id)?
}];
let len = ws.pseudo.len();
while let Some(pos) = stack.pop() {
let dest_vertex = if let Ok(&dest_vertex) = txn.find_block_end(&channel, pos) {
if !dest_vertex.is_empty() {
continue;
}
dest_vertex
} else {
continue;
};
// Is `dest_vertex` alive? If so, stop this path.
let f0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
let f1 = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::BLOCK;
if let Some(e) = iter_adjacent(txn, channel, dest_vertex, f0, f1)?
.filter_map(|e| e.ok())
.filter(|e| !e.flag().contains(EdgeFlags::PSEUDO))
.next()
{
debug!("is_alive: {:?}", e);
continue;
}
// Does `dest_vertex` have alive or zombie descendants? If
// so, stop this path.
let f0 = EdgeFlags::empty();
let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
if let Some(e) = iter_adjacent(txn, channel, dest_vertex, f0, f1)?.next() {
debug!("child is_alive: {:?}", e);
continue;
}
// Else, `dest_vertex` is dead. We should remove its
// pseudo-parents.
let f = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO;
for e in iter_adjacent(txn, channel, dest_vertex, f, f)? {
let e = e?;
ws.pseudo.push((dest_vertex, *e));
let gr = *txn.find_block_end(&channel, e.dest()).unwrap();
for e in iter_adjacent(txn, channel, gr, f, f)? {
let e = e?;
ws.pseudo.push((gr, *e));
stack.push(e.dest())
}
}
}
// Finally, we were only squatting the `.pseudo` field of the
// workspace, since that field is normally used for missing
// children, and pseudo-edges from that field are treated in a
// special way (by `delete_pseudo_edges` in this module).
//
// Therefore, we need to delete our folder-pseudo-edges now.
for (v, e) in ws.pseudo.drain(len..) {
let p = *txn.find_block_end(channel, e.dest())?;
del_graph_with_rev(
txn,
channel,
e.flag() - EdgeFlags::PARENT,
p,
v,
e.introduced_by(),
)?;
}
Ok(())
}