Bug fixes when unrecording a patch that introduced zombie files

pmeunier
Oct 27, 2023, 4:03 PM
MQ6ERQ43MWASF2OSG65FHKWHCWT35BJVFJWKHAFPVCTLICCTJWUAC

Dependencies

  • [2] UEWNF7X3 Detecting "self-solving" zombie folder (or file) conflicts
  • [3] RSFUX6ML Correct find_alive cache system
  • [4] YXAVFTPP Allowing vertex buffer to use references to the transaction, by changing `output::output` to take an ArcTxn<T> instead of a simple T
  • [5] ZJWCPRMH Fixing known patches in deleted contexts
  • [6] BOJEBIOI Fixing "block error" in unrecord
  • [7] QY4E6CLE Replacing a panic with a proper error
  • [8] 3QXUJMZD More detailed display of conflicts
  • [9] SXEYMYF7 Fixing the bad changes in history (unfortunately, by rebooting).
  • [10] I24UEJQL Various post-fire fixes
  • [11] A3RM526Y Integrating identity malleability
  • [12] GHO6DWPI Refactoring iterators
  • [13] YRBOKAWJ Formatting (why wasn't this recorded before? I'm not sure)
  • [14] KQTD46KV Unrecord: restore files *after* having unapplied the *entire* change
  • [15] 3AMEP2Y5 More convenient interface for channels
  • [16] 3CFU4DQN Fixing a bug in unrecord, where a patch creating an undeletion conflict would not be properly unrecorded
  • [17] HMMMKONL Fixing alive vertices
  • [18] CCLLB7OI Upgrading to Sanakirja 0.15 + version bump
  • [19] I52XSRUH Massive cleanup, and simplification
  • [20] I3OVP3NH Archive: set the accurate and deterministic mtime
  • [21] 6YMDOZIB Refactoring apply
  • [22] BXD3IQYN Fixing --features git
  • [23] KDF6FJRV bigger clippy refactors
  • [24] AD6M434O find_alive performance (matters a lot for unrecord)
  • [25] VO5OQW4W Removing anyhow in libpijul
  • [26] RMDMAYRX Adding a root inode (aka supporting submodules)
  • [27] ZDK3GNDB Tag transactions (including a massive refactoring of errors)
  • [28] BD5PC25A Deleting conflict resolution vertices when the sides are deleted
  • [29] YN63NUZO Sanakirja 1.0
  • [30] G55Y75FU Avoiding deadlocks when using output.rs with a non-filesystem output
  • [31] 7ZFRYVVQ Cargo.nix and formatting
  • [32] MDADYULS Fix a panic when switching between channels that have different files
  • [*] IXC43DSH Fixing a bug in unrecord

Change contents

  • edit in pijul/src/commands/debug.rs at line 7
    [8.31]
    [8.177836]
    use log::*;
  • replacement in pijul/src/commands/debug.rs at line 44
    [8.21392][8.21392:21427](),[8.21427][7.0:74]()
    let (pos, _) = txn
    .follow_oldest_path(&repo.changes, &channel, &root)?;
    [8.21392]
    [8.21530]
    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
    }
    };
  • edit in pijul/src/commands/debug.rs at line 80
    [8.178663]
    [8.178663]
    }
    }
    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
  • replacement in libpijul/src/unrecord/mod.rs at line 251
    [8.939][8.939:968]()
    // edges are PSEUDO.
    [8.939]
    [8.968]
    // edges are PSEUDO|FOLDER.
  • edit in libpijul/src/unrecord/mod.rs at line 259
    [6.123]
    [6.123]
    debug!("line {}, del {:?} {:?} {:?}", line!(), u, v, e);
  • edit in libpijul/src/unrecord/mod.rs at line 264
    [6.312]
    [6.312]
    debug!("line {}, del {:?} {:?} {:?}", line!(), v, w, e);
  • edit in libpijul/src/unrecord/mod.rs at line 293
    [34.69]
    [8.235977]
    zombies_stack: Vec<(Vertex<ChangeId>, bool, bool)>,
  • edit in libpijul/src/unrecord/mod.rs at line 353
    [8.238407]
    [8.8010]
    debug!("line {}, del {:?} {:?} {:?}", line!(), a, b, e);
  • edit in libpijul/src/unrecord/mod.rs at line 591
    [8.18573]
    [8.18573]
    debug!("line {}, del {:?} {:?} {:?}", line!(), u, v, e);
  • edit in libpijul/src/unrecord/mod.rs at line 595
    [8.18721]
    [8.18721]
    debug!("line {}, del {:?} {:?} {:?}", line!(), v, w, e);
  • replacement in libpijul/src/unrecord/mod.rs at line 614
    [8.3151][8.3151:3198]()
    debug!("remove_zombies, v = {:?}", v);
    [8.3151]
    [8.3198]
    debug!("collect_zombies, v = {:?}", v);
  • edit in libpijul/src/unrecord/mod.rs at line 652
    [8.2201]
    [6.430]
    // First, collect the paths downwards.
  • replacement in libpijul/src/unrecord/mod.rs at line 655
    [6.480][6.480:508]()
    ws.stack.push(*to);
    [6.480]
    [6.508]
    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);
  • edit in libpijul/src/unrecord/mod.rs at line 727
    [6.514]
    [8.2251]
  • edit in libpijul/src/unrecord/mod.rs at line 738
    [8.2597][8.2597:2869]()
    if (e.flag() & (EdgeFlags::PSEUDO | EdgeFlags::DELETED)).is_empty() {
    // This isn't a pseudo edge, `v` isn't part
    ws.del_edges.truncate(del_len);
    ws.stack.truncate(stack_len);
    break
    }
  • edit in libpijul/src/unrecord/mod.rs at line 739
    [8.2923]
    [6.515]
    assert!(e.flag().contains(EdgeFlags::FOLDER));
    if !e.flag().intersects(EdgeFlags::PSEUDO | EdgeFlags::DELETED) {
    // Neither a pseudo edge nor a deleted edge.
    ws.del_edges.truncate(del_len);
    ws.stack.truncate(stack_len);
    break
    }
  • replacement in libpijul/src/unrecord/mod.rs at line 751
    [6.642][8.2994:3015](),[8.2994][8.2994:3015](),[8.3015][6.643:748]()
    } else {
    if let Ok(x) = txn.find_block(channel, e.dest()) {
    ws.stack.push(*x)
    [6.642]
    [6.748]
    if e.flag().contains(EdgeFlags::PSEUDO) {
    ws.del_edges.push((v, *e))
  • edit in libpijul/src/unrecord/mod.rs at line 755
    [8.3096][8.3096:3150](),[8.3150][8.19204:19247](),[8.19204][8.19204:19247](),[8.19247][8.3796:3810](),[8.3796][8.3796:3810]()
    if e.flag().contains(EdgeFlags::PSEUDO) {
    ws.del_edges.push((v, *e))
    }
  • replacement in libpijul/src/record.rs at line 1220
    [4.3954][4.3954:4017]()
    retrieve(&*txn, txn.graph(&*channel), vertex)?
    [4.3954]
    [4.4017]
    retrieve(&*txn, txn.graph(&*channel), vertex, false)?
  • replacement in libpijul/src/pristine/mod.rs at line 1339
    [8.67436][8.106045:106106]()
    if e.flag().contains(EdgeFlags::PARENT) ^ down {
    [8.67436]
    [8.106106]
    if e.flag().contains(EdgeFlags::PARENT) != down {
  • replacement in libpijul/src/pristine/mod.rs at line 2211
    [8.835][8.3822:3917]()
    let graph = crate::alive::retrieve::retrieve(txn, txn.graph(channel), file_).unwrap();
    [8.835]
    [8.3917]
    let graph = crate::alive::retrieve::retrieve(txn, txn.graph(channel), file_, true).unwrap();
  • replacement in libpijul/src/pristine/mod.rs at line 2215
    [8.906][8.3996:4072]()
    debug_root(txn, txn.graph(channel), file.unwrap(), &mut f, false)?;
    [8.906]
    [8.3117]
    debug_root(txn, txn.graph(channel), file.unwrap(), &mut f, true)?;
  • replacement in libpijul/src/output/output.rs at line 770
    [8.86][8.86:150]()
    retrieve(&*txn, txn.graph(&*channel), output_item.pos)?
    [8.86]
    [8.150]
    retrieve(&*txn, txn.graph(&*channel), output_item.pos, false)?
  • edit in libpijul/src/output/mod.rs at line 258
    [8.23999]
    [8.23999]
    return Ok(());
    // The following was supposed to be a panic, but actually it
    // doesn't need to be one, there just wasn't any file to
    // output.
    /*
  • edit in libpijul/src/output/mod.rs at line 281
    [8.24566]
    [8.24566]
    */
  • replacement in libpijul/src/output/mod.rs at line 339
    [4.4295][4.4295:4360]()
    crate::alive::retrieve(&*txn, txn.graph(&*channel), v0)?
    [4.4295]
    [4.4360]
    crate::alive::retrieve(&*txn, txn.graph(&*channel), v0, false)?
  • replacement in libpijul/src/output/archive.rs at line 268
    [4.5681][4.5681:5778]()
    crate::alive::retrieve(&*txn_, txn_.graph(&channel_), output_item.pos)?;
    [4.5681]
    [8.0]
    crate::alive::retrieve(&*txn_, txn_.graph(&channel_), output_item.pos, false)?;
  • replacement in libpijul/src/missing_context.rs at line 46
    [8.697496][8.88998:89078]()
    let mut graph = crate::alive::retrieve(txn, channel, pos)?;
    [8.697496]
    [8.697575]
    let mut graph = crate::alive::retrieve(txn, channel, pos, false)?;
  • edit in libpijul/src/missing_context.rs at line 367
    [2.3800]
    [2.3800]
    debug!("detect folder conflict resolution, deleting {:?} → {:?} {:?}", v, e, p);
  • edit in libpijul/src/find_alive.rs at line 166
    [8.734]
    [8.135056]
    if v.flag() & EdgeFlags::pseudof() == EdgeFlags::PSEUDO {
    continue;
    }
  • edit in libpijul/src/find_alive.rs at line 170
    [8.135095]
    [8.135095]
    debug!("is_file {:?} {:?}", is_file, !v.flag().is_folder());
  • edit in libpijul/src/find_alive.rs at line 172
    [8.15924][8.766374:766414](),[8.135145][8.766374:766414](),[8.766374][8.766374:766414](),[8.766414][8.135146:135216]()
    continue;
    }
    if v.flag() & EdgeFlags::pseudof() == EdgeFlags::PSEUDO {
  • replacement in libpijul/src/find_alive.rs at line 175
    [8.135257][3.6236:6275]()
    if vertex == vertex0 {
    [8.135257]
    [3.6275]
    if v.flag().is_block() && vertex == vertex0 {
  • edit in libpijul/src/find_alive.rs at line 212
    [8.135477]
    [3.7231]
    for e in it {
    let e = e?;
    is_file |= !e.flag().intersects(EdgeFlags::parent_folder())
    }
    debug!("{:?} is a file ? {:?}", vertex, is_file);
  • replacement in libpijul/src/apply.rs at line 337
    [8.949433][5.1565:1845]()
    Atom::NewVertex(ref n) => put_newvertex(
    txn,
    T::graph_mut(channel),
    changes,
    change,
    ws,
    change_id,
    n,
    )?,
    [8.949433]
    [8.949531]
    Atom::NewVertex(ref n) => {
    put_newvertex(
    txn,
    T::graph_mut(channel),
    changes,
    change,
    ws,
    change_id,
    n,
    )?
    },
  • edit in libpijul/src/apply.rs at line 384
    [8.951555]
    [8.951555]
    {
    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);
    }
  • edit in libpijul/src/apply.rs at line 586
    [8.971508]
    [8.146962]
    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)
    }
  • edit in libpijul/src/apply/edge.rs at line 270
    [8.44053]
    [8.103957]
    debug!("reconnect pseudo edges, parents = {:?}", ws.parents);
    debug!("reconnect pseudo edges, children = {:?}", ws.children);
  • edit in libpijul/src/alive/retrieve.rs at line 10
    [8.134379]
    [8.134379]
    include_deleted: bool,
  • replacement in libpijul/src/alive/retrieve.rs at line 42
    [8.985908][8.985908:985958]()
    EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
    [8.985908]
    [8.134424]
    if include_deleted {
    EdgeFlags::PSEUDO | EdgeFlags::BLOCK | EdgeFlags::DELETED
    } else {
    EdgeFlags::PSEUDO | EdgeFlags::BLOCK
    },
  • edit in libpijul/src/alive/retrieve.rs at line 49
    [8.134461]
    [8.148298]
    if e.flag().intersects(EdgeFlags::PARENT) {
    continue
    }
  • replacement in libpijul/src/alive/retrieve.rs at line 123
    [8.135400][8.135400:135450]()
    let mut graph = retrieve(txn, channel, pos)?;
    [8.135400]
    [8.987863]
    let mut graph = retrieve(txn, channel, pos, false)?;