ZCPGCKKYDBXUTIYGKGRA7C43CJ2WNWH3L6Q7ETSVLYB6AE4HAGTQC
ZDK3GNDBWXJ2OXFDYB72ZCEBGLBF4MKE5K3PVHDZATHJ7HJIDPRQC
YUJV2OHL2JRZBT2E25KLIPWO44IFCGJ47IQNQKHMZO5U2FYANQYQC
6YMDOZIB5LVYLFIDGN2WNT5JTHEAMS4TFPVDEZ3OWXWOKJOC5QDAC
IIV3EL2XYI2X7HZWKXEXQFAE3R3KC2Q7SGOT3Q332HSENMYVF32QC
SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC
YN63NUZO4LVJ7XPMURDULTXBVJKW5MVCTZ24R7Z52QMHO3HPDUVQC
CCLLB7OIFNFYJZTG3UCI7536TOCWSCSXR67VELSB466R24WLJSDAC
2BKYJ2JM5PTXWO6HTVBKFQANWWSCJ4UHJYKAXWGTVZB35AZJ76CQC
HMMMKONLCRAXVT7SO2ITTFDOJIQKKVSRIZPXYVPDC34RCBHWMHVAC
// If we're deleting a FOLDER edge, repair_context_deleted
// will not repair its potential descendants. Hence, we must
// also count as "alive" a FOLDER node with alive descendants.
if p.flag().is_folder() {
if folder_has_alive_descendants(txn, channel, &mut alive_folder, &mut folder_stack, b)?
{
continue;
}
}
if a.is_empty() && b_is_alive {
// In this case, `a` can be an inode, in which case we
// can't simply delete the edge, since b would become
// unreachable.
//
// We test this here:
let mut is_inode = false;
for e in iter_adjacent(
txn,
channel,
a,
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::all(),
)? {
let e = e?;
if e.flag().contains(EdgeFlags::FOLDER | EdgeFlags::PARENT) {
is_inode = true;
break;
}
}
if is_inode {
continue;
}
}
fn folder_has_alive_descendants<T: GraphMutTxnT + TreeTxnT>(
txn: &mut T,
channel: &mut T::Graph,
alive: &mut HashMap<Vertex<ChangeId>, bool>,
stack: &mut Vec<(Vertex<ChangeId>, bool)>,
b: Vertex<ChangeId>,
) -> Result<bool, LocalApplyError<T>> {
if let Some(r) = alive.get(&b) {
return Ok(*r);
}
debug!("alive descendants");
stack.clear();
stack.push((b, false));
while let Some((b, visited)) = stack.pop() {
debug!("visiting {:?} {:?}", b, visited);
if visited {
if !alive.contains_key(&b) {
alive.insert(b, false);
}
continue;
}
stack.push((b, true));
for e in iter_adjacent(
txn,
channel,
b,
EdgeFlags::empty(),
EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
)? {
let e = e?;
if e.flag().contains(EdgeFlags::FOLDER) {
let c = txn.find_block(channel, e.dest())?;
stack.push((*c, false));
} else {
// This is a non-deleted non-folder edge.
let c = txn.find_block(channel, e.dest())?;
if is_alive(txn, channel, &c)? {
// The entire path is alive.
for (x, on_path) in stack.iter() {
if *on_path {
alive.insert(*x, true);
}
}
}
}
}
}
Ok(*alive.get(&b).unwrap_or(&false))
}