When unrecording a patch that introduced a zombie file by deleting a file, we must delete the paths made of pseudo-edges that represent the zombie conflict. The algorithm for finding that path was wrong, this patch replaces it with a proper one.
MQ6ERQ43MWASF2OSG65FHKWHCWT35BJVFJWKHAFPVCTLICCTJWUAC
UEWNF7X3S5V6N2LEJCFMZKFZZHQUCAF6FZF5DYO5XATNDEQ4G35QC
RSFUX6MLPHII3DRELHN62DOJCHFCDNKSRMWJDSFGTWN5S4RJVSSQC
YXAVFTPP2PQAYMKGQH7QNKFVGKDI2UHVWB7SIDA4QEYSBP75ZGUQC
ZJWCPRMHAYZYGCPYBTBIPBBFVCVDSFNGUL36HMC2DHCWZZKNA7PAC
BOJEBIOIRT73FEVDUZPKRIBQMP3XX2HCZQ6CIB2AGMWTGNN47JRQC
QY4E6CLE6MFO6GGT5XVCPMGJQK22W4FZNLWRZKVMYYPOMAAZUX4AC
BD5PC25AB5MKVIYDFSDGRZ4YGX4PKW4SMZ3YAYAPNA5HLDVJUR3QC
SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC
I24UEJQLCH2SOXA4UHIYWTRDCHSOPU7AFTRUOTX7HZIAV4AZKYEQC
3CFU4DQNHPPJC2B63RSVAVGHTQZT3RT5CCHHYC4ZM6DQ4GVQULSQC
IXC43DSHDSXCE2X6H6N47GGVKTYM2D2BUUWF34CFWMFT6Z45NOOAC
I52XSRUH5RVHQBFWVMAQPTUSPAJ4KNVID2RMI3UGCVKFLYUO6WZAC
YN63NUZO4LVJ7XPMURDULTXBVJKW5MVCTZ24R7Z52QMHO3HPDUVQC
6YMDOZIB5LVYLFIDGN2WNT5JTHEAMS4TFPVDEZ3OWXWOKJOC5QDAC
CCLLB7OIFNFYJZTG3UCI7536TOCWSCSXR67VELSB466R24WLJSDAC
HMMMKONLCRAXVT7SO2ITTFDOJIQKKVSRIZPXYVPDC34RCBHWMHVAC
3AMEP2Y5J6GA4AWQONF4JVA3XSR3ASLHHKMYG44R72SOUY3UQCDAC
BXD3IQYNMKMI5BTANCF6NZHZP4CKPWADGJMBT2R3WTMKVKONT5QAC
G55Y75FUL4LWA346HQCZ5EVA5G54EDTZAW7MPVTO44BP6D5UMURAC
RMDMAYRXYBU5OQXV5HSF6LFD4NBMKRNH5EPIVW3K5HAV6D56IG6QC
YRBOKAWJVS24QTCEMYIUWTEE6HE7BM5OBF5U5MPYFTYW4ZYV2THQC
AD6M434OFUCH6ISHP7GXSNNRBHEWP242ZDH7QCVCYLHMRASGE5MQC
ZDK3GNDBWXJ2OXFDYB72ZCEBGLBF4MKE5K3PVHDZATHJ7HJIDPRQC
let (pos, _) = txn
.follow_oldest_path(&repo.changes, &channel, &root)?;
let pos = if let Some(pos) = parse_pos(&root) {
pos
} else {
let inode = libpijul::fs::find_inode(&txn, &root)?;
debug!("inode {:?}", inode);
use libpijul::TreeTxnT;
if let Ok(Some(pos)) = txn.get_inodes(&inode, None) {
debug!("inode {:?}", pos);
*pos
} else {
debug!("no inode");
txn
.follow_oldest_path(&repo.changes, &channel, &root)?.0
}
};
}
}
fn parse_pos(s: &str) -> Option<libpijul::pristine::Position<libpijul::pristine::ChangeId>> {
let mut it = s.split('.');
if let (Some(a), Some(b)) = (it.next(),it.next()) {
use libpijul::pristine::{Position, ChangeId, ChangePosition, Base32};
let change = ChangeId::from_base32(a.as_bytes())?;
let pos: u64 = b.parse().ok()?;
Some(Position {
change,
pos: ChangePosition(pos.into()),
})
} else {
None
ws.stack.push(*to);
ws.zombies_stack.push((*to, false, false))
}
while let Some((v, alive, on_path)) = ws.zombies_stack.pop() {
if on_path {
// Already visited. If not alive, delete PSEUDO edges.
if !alive {
for e in iter_adj_all(txn, channel, v)? {
let e = e?;
if e.flag().contains(EdgeFlags::PSEUDO) {
ws.del_edges.push((v, *e))
}
}
if ws.zombies_stack.is_empty() {
debug_assert_eq!(v.start_pos(), to);
// Collect all the pseudo-paths up.
ws.parents.clear();
collect_zombies_up(txn, channel, to, ws)?
}
}
continue
}
// A vertex cannot be marked alive if it wasn't on the path.
assert!(!alive);
// If the vertex was already visited in another path, pass.
if !ws.parents.insert(v) {
continue;
}
// Else, iterate through all children. If any of them is
// alive, mark the entire path as alive. Else, just push onto
// the stack and continue the DFS.
let mut is_first = true;
for e in iter_alive_children(txn, channel, v)? {
let e = e?;
if e.flag().intersects(EdgeFlags::PARENT | EdgeFlags::DELETED) {
continue
}
let x = txn.find_block(channel, e.dest())?;
if is_alive(txn, channel, x)? {
// Mark all edges on the path as alive.
for (_, alive, on_path) in ws.zombies_stack.iter_mut() {
if *on_path {
*alive = true
}
}
} else {
if is_first {
is_first = false;
ws.zombies_stack.push((v, false, true))
}
ws.zombies_stack.push((*x, false, false))
}
}
}
ws.zombies_stack.clear();
ws.parents.clear();
Ok(())
}
fn collect_zombies_up<T: GraphTxnT>(
txn: &T,
channel: &T::Graph,
to: Position<ChangeId>,
ws: &mut Workspace,
) -> Result<(), BlockError<T::GraphError>> {
if let Ok(&to) = txn.find_block(channel, to) {
ws.stack.push(to);
{
let file = Vertex {
change: ChangeId(L64(5867590239053715538)),
start: ChangePosition(204370u64.into()),
end: ChangePosition(204370u64.into()),
};
let still_here: Vec<_> = iter_adjacent(
txn,
T::graph_mut(channel),
file,
EdgeFlags::empty(),
EdgeFlags::all(),
)?.collect();
debug!("still here! {:?}", still_here);
}
debug!("clean_obsolete_pseudo_edges {:?} {:?} {:?}", next_vertex, p, inode);
{
let still_here: Vec<_> = iter_adjacent(
txn,
channel,
next_vertex,
EdgeFlags::empty(),
EdgeFlags::all(),
)?.collect();
debug!("pseudo edge still here ? {:?} {:?}", next_vertex.change.0.0, still_here)
}