The cleanup part of this change is about making the traits in libpijul::pristine smaller by making internal methods private. The simplification is about not considering the target of non-BLOCK edges as alive when applying a change, which makes missing context conflicts more intuitive.
I52XSRUH5RVHQBFWVMAQPTUSPAJ4KNVID2RMI3UGCVKFLYUO6WZAC PJ7T2VFLV5PYG3CV23GC2GIQETXKGC6CO74JBGREV3JC3LG5OXUAC YAJAXIV5VL263Z6FYLKFPROB3MQPRPH22P44GRGRVGEP56HOMBOAC AXVPNZ2NVKYRCTKYI77H72CALZBRTHY3OK4CMWGMTSU2NPMOS42QC YACC5QR6WTVC3IJCCVH6GUYFU4KAPITOED43V2MDP3TVGLTL2NEQC 7UPL3Y2A5QOBU6VIUHNWEFGSGQ5CMWGDGOVLRZ53UARXG3TDGLMAC 74HX2XZDHFRE7FIQ6LQZALIIKED2ABSGGBQ34ULYZ5ZBKK7UTHQQC YDKNUL6B4EFM5U2GG36SSEKXHS6XK4OLIWUVE4BUAJ5VYJFHAOIQC UNZXTNSJI4YRY3EQ3M4HMBKQDNYDTY6B7IZRBNYGDJXTA2UKYWRAC M5FK3ABTKBDG6HHW32G7UKRJEJQKD2U7BPXNZ3HVHBKULWVV6CTQC Q4SVMHAEZQQCBFGPJMWS5H4VXB2HFQREZ3AWGOHFWHLFARUQVPBAC ISQJRA3OJJRDMYVQX7XNYGJNMR6CPWIVAIFOS2NCIZH2ZAPDTC5QC UDHP4ZVBQZT2VBURB2MDCU2IZDNMCAFSIUKWRBDQ5BWMFKSN2LYQC GURIBVW66JDQK3SJZRGVJ2MQLMT7JD4KLI5QPQZGPAL7WH3T6T4AC FBXYP7QM7SG6P2JDJVQPPCRKJE3GVYXNQ5GVV4GRDUNG6Q4ZRDJQC VIHXB7SGRETFPHPYZFSRGOFRXEO4RZY57WDZSU6IAUEJRU3HPKQAC L4JXJHWXYNCL4QGJXNKKTOKKTAXKKXBJUUY7HFZGEUZ5A2V5H34QC SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC 76PCXGML77EZWTRI5E6KHLVRAFTJ2AB5YRN5EKOYNAPKTWY2KCGAC VQPAUKBQ2POZKL7CZFAZK5ZQKEBYL27XZYZWYUSH5AH25KK6DWKAC G734WNM64AR5BLAZMN5MDPKSFTYXTUQR6MAGB32NRBC5FXFRWSJAC Q7CAYX5N2GFOGMZL3VXVWORMAPWEOECXE22BLXK7Q4WEPS4CE2SAC for (v_, e) in txn.iter_graph(&channel.graph, v, None) {if v_.change < change {continue;} else if v_.change > change {break;}if e.flag.contains(libpijul::pristine::EdgeFlags::PARENT)&& !e.flag.contains(libpijul::pristine::EdgeFlags::DELETED){// Alive!debug!("sending alive");send_hash.send(*c).await?;debug!("sent");break;}
Record::FileMove { path, .. } =>format!("MV {}\n", path),Record::FileDel { path, .. } =>format!("D {}\n", path),Record::FileUndel { path, .. } =>format!("UD {}\n", path),Record::FileAdd { path, .. } =>format!("A {}", path),Record::SolveNameConflict { path, .. } =>format!("SC {}", path),Record::UnsolveNameConflict { path, .. } =>format!("UC {}", path),Record::Edit { local: Local { path, .. }, .. } =>format!("M {}", path),Record::Replacement { local: Local { path, .. }, .. } =>format!("R {}", path),Record::SolveOrderConflict { local: Local { path, .. }, .. } =>format!("SC {}", path),Record::UnsolveOrderConflict { local: Local { path, .. }, .. } =>format!("UC {}", path),Record::ResurrectZombies { local: Local { path, .. }, .. } =>format!("RZ {}", path),
Record::FileMove { path, .. } => format!("MV {}\n", path),Record::FileDel { path, .. } => format!("D {}\n", path),Record::FileUndel { path, .. } => format!("UD {}\n", path),Record::FileAdd { path, .. } => format!("A {}", path),Record::SolveNameConflict { path, .. } => format!("SC {}", path),Record::UnsolveNameConflict { path, .. } => format!("UC {}", path),Record::Edit {local: Local { path, .. },..} => format!("M {}", path),Record::Replacement {local: Local { path, .. },..} => format!("R {}", path),Record::SolveOrderConflict {local: Local { path, .. },..} => format!("SC {}", path),Record::UnsolveOrderConflict {local: Local { path, .. },..} => format!("UC {}", path),Record::ResurrectZombies {local: Local { path, .. },..} => format!("RZ {}", path),
let u = txn.find_block_end(channel, e.dest)?;txn.del_graph_with_rev(channel, e.flag - EdgeFlags::PARENT, u, v, e.introduced_by)?;if txn.iter_adjacent(channel,u,EdgeFlags::empty(),EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,).filter(|e| e.dest == v.start_pos()).next().is_none()
let u = find_block_end(txn, channel, e.dest)?;del_graph_with_rev(txn,channel,e.flag - EdgeFlags::PARENT,u,v,e.introduced_by,)?;if iter_adjacent(txn,channel,u,EdgeFlags::empty(),EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,).filter(|e| e.dest == v.start_pos()).next().is_none()
debug_to_file(&txn_alice, &channel_alice, "debug_alice1")?;crate::unrecord::unrecord(&mut txn_alice, &mut channel_alice, &changes, &bob_h2)?;crate::unrecord::unrecord(&mut txn_alice, &mut channel_alice, &changes, &bob_h)?;debug_to_file(&txn_alice, &channel_alice, "debug_alice1_unrec")?;apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, bob_h)?;apply::apply_change(&changes, &mut txn_alice, &mut channel_alice, bob_h2)?;
for parent in iter_adjacent(txn,channel,vertex,EdgeFlags::FOLDER | EdgeFlags::PARENT,EdgeFlags::all(),).filter(|e| e.flag.contains(EdgeFlags::PARENT)){assert!(parent.flag.contains(EdgeFlags::PARENT));assert!(parent.flag.contains(EdgeFlags::FOLDER));let parent_dest = find_block_end(txn, &channel, parent.dest).unwrap();for grandparent in iter_adjacent(txn,
.filter(|e| e.flag.contains(EdgeFlags::PARENT)){assert!(parent.flag.contains(EdgeFlags::PARENT));assert!(parent.flag.contains(EdgeFlags::FOLDER));let parent_dest = txn.find_block_end(&channel, parent.dest).unwrap();for grandparent in txn.iter_adjacent(channel,parent_dest,EdgeFlags::FOLDER | EdgeFlags::PARENT,EdgeFlags::all(),).filter(|e| {e.flag.contains(EdgeFlags::PARENT) && !e.flag.contains(EdgeFlags::PSEUDO)})
// Provided methods/// Iterate the graph between `(key, min_flag)` and `(key,/// max_flag)`, where both bounds are included.#[doc(hidden)]fn iter_adjacent<'db, 'txn: 'db>(&'txn self,channel: &'db Channel<Self>,key: Vertex<ChangeId>,min_flag: EdgeFlags,max_flag: EdgeFlags,) -> AdjacentIterator<'txn, Self> {let edge = Edge {flag: min_flag,dest: Position::ROOT,introduced_by: ChangeId::ROOT,};AdjacentIterator {it: self.iter_graph(&channel.graph, key, Some(edge)),key,min_flag,max_flag,}}/// Find the key where a position is.#[doc(hidden)]fn find_block<'db, 'txn: 'db>(&'txn self,channel: &'db Channel<Self>,p: Position<ChangeId>,) -> Result<Vertex<ChangeId>, crate::Error> {if p.change.is_root() {return Ok(Vertex::ROOT);}let key = Vertex {change: p.change,start: p.pos,end: p.pos,};debug!(target: "libpijul::find_block", "find_block {:?}", key);let mut cursor = self.cursor_graph(&channel.graph, Some((key, None)));let mut k = if let Some((k, _)) = cursor.next() {k} else {return Err(crate::Error::WrongBlock { block: p });};debug!("k = {:?}", k);// The only guarantee here is that k is either the first key// >= `key`, or the key just before that. We might need to// rewind by one step if key is strictly larger than the// result (i.e. if `p` is in the middle of the key).while k.change > p.change || (k.change == p.change && k.start > p.pos) {debug!(target: "libpijul::find_block", "find_block while {:?}", k);if let Some((k_, _)) = cursor.prev() {k = k_} else {break;}}loop {debug!(target: "libpijul::find_block", "find_block loop {:?}", k);if k.change == p.change && k.start <= p.pos {if k.end > p.pos || (k.start == k.end && k.end == p.pos) {return Ok(k);}} else if k.change > p.change {return Err(crate::Error::WrongBlock { block: p });}if let Some((k_, _)) = cursor.next() {k = k_} else {break;}}debug!(target: "libpijul::find_block", "find_block None, {:?}", k);Err(crate::Error::WrongBlock { block: p })}/// Find the key ending at a position.#[doc(hidden)]fn find_block_end<'db, 'txn: 'db>(&'txn self,channel: &'db Channel<Self>,p: Position<ChangeId>,) -> Result<Vertex<ChangeId>, crate::Error> {if p.change.is_root() {return Ok(Vertex::ROOT);}let key = Vertex {change: p.change,start: p.pos,end: p.pos,};debug!(target: "libpijul::find_block_end", "find_block_end {:?}, p.change.0 = {:?}", key, p.change.0);let mut cursor = self.cursor_graph(&channel.graph, Some((key, None)));let mut k = if let Some((k, _)) = cursor.next() {k} else {return Err(crate::Error::WrongBlock { block: p });};// The only guarantee here is that k is either the first key// before `key`, or the key just before that.loop {debug!(target: "libpijul::find_block_end", "find_block_end loop {:?} k.change.0 = {:?}", k, k.change.0);if k.change < p.change {break;} else if k.change == p.change {// Here we want to create an edge pointing between `p`// and its successor. If k.start == p.pos, the only// case where that's what we want is if k.start ==// k.end.if k.start == p.pos && k.end == p.pos {break;} else if k.start < p.pos {break;}}if let Some((k_, _)) = cursor.prev() {k = k_} else {break;}}// We also want k.end >= p.pos, so we just call next() until// we have that.debug!(target: "libpijul::find_block_end", "find_block_end k(0) = {:?} k.change.0 = {:?}", k, k.change.0);while k.change < p.change || (k.change == p.change && p.pos > k.end) {if let Some((k_, _)) = cursor.next() {k = k_} else {break;}}debug!(target: "libpijul::find_block_end", "find_block_end k(1) = {:?}, k.change.0 = {:?}", k, k.change.0);if k.change == p.change && k.start <= p.pos && p.pos <= k.end {Ok(k)} else {Err(crate::Error::WrongBlock { block: p })}}fn tree_path(&self, v: Position<ChangeId>) -> Option<String> {if let Some(mut inode) = self.get_revinodes(v, None) {let mut components = Vec::new();while !inode.is_root() {if let Some(next) = self.get_revtree(inode, None) {components.push(next.basename.as_str().to_string());inode = next.parent_inode;} else {assert!(components.is_empty());return None;}}if let Some(mut result) = components.pop() {while let Some(c) = components.pop() {result = result + "/" + c.as_str()}Some(result)} else {None}} else {None}}#[doc(hidden)]fn internal(&self, h: &Option<Hash>, p: ChangeId) -> Option<ChangeId> {match *h {Some(Hash::None) => Some(ChangeId::ROOT),Some(h) => self.get_internal(h),None => Some(p),}}#[doc(hidden)]fn internal_pos(&self,pos: &Position<Option<Hash>>,change_id: ChangeId,) -> Result<Position<ChangeId>, crate::Error> {Ok(Position {change: if let Some(p) = pos.change {if let Some(p) = self.get_internal(p) {p} else {return Err(crate::Error::InconsistentChange);}} else {change_id},pos: pos.pos,})}
#[doc(hidden)]fn iter_graph<'txn>(&'txn self,graph: &Self::Graph,k: Vertex<ChangeId>,v: Option<Edge>,) -> Cursor<Self, &'txn Self, Self::GraphCursor, Vertex<ChangeId>, Edge> {let curs = self.cursor_graph(graph, Some((k, v)));curs}#[doc(hidden)]fn iter_graph_ref<RT: std::ops::Deref<Target = Self>>(txn: RT,graph: &Self::Graph,k: Vertex<ChangeId>,v: Option<Edge>,) -> Cursor<Self, RT, Self::GraphCursor, Vertex<ChangeId>, Edge> {let curs = Self::cursor_graph_ref(txn, graph, Some((k, v)));curs}#[doc(hidden)]
#[doc(hidden)]fn changeid_log<'db, 'txn: 'db>(&'txn self,channel: &'db Channel<Self>,from: u64,) -> Cursor<Self, &'txn Self, Self::RevchangesetCursor, u64, (ChangeId, Merkle)> {self.cursor_revchangeset(&channel.revchanges, Some((from, None)))}fn current_state<'db, 'txn: 'db>(&'txn self, channel: &'db Channel<Self>) -> Option<Merkle> {self.rev_cursor_revchangeset(&channel.revchanges, None).next().map(|(_, (_, m))| m)}#[doc(hidden)]fn changeid_log_ref<RT: std::ops::Deref<Target = Self>>(txn: RT,channel: &Channel<Self>,from: u64,) -> Cursor<Self, RT, Self::RevchangesetCursor, u64, (ChangeId, Merkle)> {Self::cursor_revchangeset_ref(txn, &channel.revchanges, Some((from, None)))}#[doc(hidden)]fn changeid_rev_log<'db, 'txn: 'db>(&'txn self,channel: &'db Channel<Self>,from: Option<u64>,) -> RevCursor<Self, &'txn Self, Self::RevchangesetCursor, u64, (ChangeId, Merkle)> {self.rev_cursor_revchangeset(&channel.revchanges, from.map(|from| (from, None)))}#[doc(hidden)]fn log_for_path<'txn, 'channel>(&'txn self,channel: &'channel Channel<Self>,key: Position<ChangeId>,from_timestamp: u64,) -> PathChangeset<'channel, 'txn, Self> {PathChangeset {iter: self.cursor_revchangeset(&channel.revchanges, Some((from_timestamp, None))),txn: self,channel,key,}}#[doc(hidden)]fn rev_log_for_path<'txn, 'channel>(&'txn self,channel: &'channel Channel<Self>,key: Position<ChangeId>,from_timestamp: u64,) -> RevPathChangeset<'channel, 'txn, Self> {RevPathChangeset {iter: self.rev_cursor_revchangeset(&channel.revchanges, Some((from_timestamp, None))),txn: self,channel,key,}}/// Is there an alive/pseudo edge from `a` to `b`.#[doc(hidden)]fn test_edge(&self,channel: &Channel<Self>,a: Position<ChangeId>,b: Position<ChangeId>,min: EdgeFlags,max: EdgeFlags,) -> bool {debug!("is_connected {:?} {:?}", a, b);let key = Vertex {change: a.change,start: a.pos,end: a.pos,};let edge = Edge {flag: min,dest: b,introduced_by: ChangeId::ROOT,};let mut cursor = self.cursor_graph(&channel.graph, Some((key, Some(edge))));let (a_, b_) = cursor.next().unwrap();a_.change == a.change&& a_.start <= a.pos&& a_.end >= a.pos&& b_.flag >= min&& b_.flag <= max&& b_.dest == b}/// Is there an alive/pseudo edge to `a`.#[doc(hidden)]fn is_alive_or_pseudo(&self, channel: &Channel<Self>, a: Vertex<ChangeId>) -> bool {a.is_root()|| self.iter_adjacent(channel,a,EdgeFlags::PARENT,EdgeFlags::all() - EdgeFlags::DELETED,).next().is_some()}/// Is there an alive/pseudo edge to `a`.#[doc(hidden)]fn is_alive(&self, channel: &Channel<Self>, a: Vertex<ChangeId>) -> bool {a.is_root()|| self.iter_adjacent(channel,a,EdgeFlags::PARENT,EdgeFlags::all() - EdgeFlags::DELETED,).filter(|e| {e.flag.contains(EdgeFlags::FOLDER) || !e.flag.contains(EdgeFlags::PSEUDO)}).next().is_some()}// org id aY8inFkVguxv2TAAh/FWE+cCIjp5Jj3P0m2rXHfKTtg=#[doc(hidden)]fn make_changeid(&self, h: &Hash) -> ChangeId {if let Some(h) = self.get_internal(*h) {return h;}use byteorder::{ByteOrder, LittleEndian};use rand::Rng;let mut p = match h {Hash::None => return ChangeId::ROOT,Hash::Blake3(ref s) => ChangeId(LittleEndian::read_u64(&s[..])),};while self.get_external(p).is_some() {p = ChangeId(rand::thread_rng().gen());}p}// org id fPNhuKxfbhMSEoBk5AN8BJbDzTyyY8utYN4ZfwRiivg=#[doc(hidden)]fn make_random_changeid(&self) -> ChangeId {use rand::Rng;let mut p = ChangeId(rand::thread_rng().gen());while self.get_external(p).is_some() {p = ChangeId(rand::thread_rng().gen());}p}
// org id oGZWRYu9yo9VKkz4S9hipd5OpFx6kQiF5q/T9jSqp5A=/// Write the graph of a channel to file `f` in graphviz/// format. **Warning:** this can be really large on old channels.#[doc(hidden)]fn debug_to_file<P: AsRef<std::path::Path>>(&self,channel: &ChannelRef<Self>,f: P,) -> Result<bool, anyhow::Error> {info!("debug {:?}", f.as_ref());let mut f = std::fs::File::create(f)?;let channel = channel.r.borrow();let done = self.debug(&channel, &mut f)?;f.flush()?;info!("done debugging {:?}", done);Ok(done)
// #[cfg(debug_assertions)]cursor!(inodes, Inode, Position<ChangeId>);#[cfg(not(debug_assertions))]fn debug_inodes(&self) {}fn iter_inodes<'txn>(&'txn self,) -> Cursor<Self, &'txn Self, Self::InodesCursor, Inode, Position<ChangeId>>;}/// Iterate the graph between `(key, min_flag)` and `(key,/// max_flag)`, where both bounds are included.pub(crate) fn iter_adjacent<'db, 'txn: 'db, T: TxnT>(txn: &'txn T,channel: &'db Channel<T>,key: Vertex<ChangeId>,min_flag: EdgeFlags,max_flag: EdgeFlags,) -> AdjacentIterator<'txn, T> {let edge = Edge {flag: min_flag,dest: Position::ROOT,introduced_by: ChangeId::ROOT,};AdjacentIterator {it: iter_graph(txn, &channel.graph, key, Some(edge)),key,min_flag,max_flag,
#[doc(hidden)]fn debug_tree<P: AsRef<std::path::Path>>(&self, file: P) -> Result<(), anyhow::Error> {let root = OwnedPathId {parent_inode: Inode::ROOT,basename: SmallString::from_str(""),};let mut f = std::fs::File::create(file)?;for t in self.iter_tree(root, None) {writeln!(f, "{:?}", t)?
pub(crate) fn tree_path<T: TxnT>(txn: &T, v: Position<ChangeId>) -> Option<String> {if let Some(mut inode) = txn.get_revinodes(v, None) {let mut components = Vec::new();while !inode.is_root() {if let Some(next) = txn.get_revtree(inode, None) {components.push(next.basename.as_str().to_string());inode = next.parent_inode;} else {assert!(components.is_empty());return None;}}if let Some(mut result) = components.pop() {while let Some(c) = components.pop() {result = result + "/" + c.as_str()}Some(result)} else {None
#[doc(hidden)]fn debug_revtree<P: AsRef<std::path::Path>>(&self, file: P) -> Result<(), anyhow::Error> {let mut f = std::fs::File::create(file)?;for t in self.iter_revtree(Inode::ROOT, None) {writeln!(f, "{:?}", t)?}Ok(())
pub(crate) fn internal_pos<T: TxnT>(txn: &T,pos: &Position<Option<Hash>>,change_id: ChangeId,) -> Result<Position<ChangeId>, crate::Error> {Ok(Position {change: if let Some(p) = pos.change {if let Some(p) = txn.get_internal(p) {p} else {return Err(crate::Error::InconsistentChange);}} else {change_id},pos: pos.pos,})}pub(crate) fn iter_graph<'txn, T: TxnT>(txn: &'txn T,graph: &T::Graph,k: Vertex<ChangeId>,v: Option<Edge>,) -> Cursor<T, &'txn T, T::GraphCursor, Vertex<ChangeId>, Edge> {let curs = txn.cursor_graph(graph, Some((k, v)));curs}pub(crate) fn iter_graph_ref<T: TxnT, RT: std::ops::Deref<Target = T>>(txn: RT,graph: &T::Graph,k: Vertex<ChangeId>,v: Option<Edge>,) -> Cursor<T, RT, T::GraphCursor, Vertex<ChangeId>, Edge> {let curs = T::cursor_graph_ref(txn, graph, Some((k, v)));curs}pub(crate) fn changeid_log<'db, 'txn: 'db, T: TxnT>(txn: &'txn T,channel: &'db Channel<T>,from: u64,) -> Cursor<T, &'txn T, T::RevchangesetCursor, u64, (ChangeId, Merkle)> {txn.cursor_revchangeset(&channel.revchanges, Some((from, None)))}pub(crate) fn current_state<'db, 'txn: 'db, T: TxnT>(txn: &'txn T,channel: &'db Channel<T>,) -> Option<Merkle> {txn.rev_cursor_revchangeset(&channel.revchanges, None).next().map(|(_, (_, m))| m)}pub(crate) fn changeid_log_ref<T: TxnT, RT: std::ops::Deref<Target = T>>(txn: RT,channel: &Channel<T>,from: u64,) -> Cursor<T, RT, T::RevchangesetCursor, u64, (ChangeId, Merkle)> {T::cursor_revchangeset_ref(txn, &channel.revchanges, Some((from, None)))}pub(crate) fn changeid_rev_log<'db, 'txn: 'db, T: TxnT>(txn: &'txn T,channel: &'db Channel<T>,from: Option<u64>,) -> RevCursor<T, &'txn T, T::RevchangesetCursor, u64, (ChangeId, Merkle)> {txn.rev_cursor_revchangeset(&channel.revchanges, from.map(|from| (from, None)))}pub(crate) fn log_for_path<'txn, 'channel, T: TxnT>(txn: &'txn T,channel: &'channel Channel<T>,key: Position<ChangeId>,from_timestamp: u64,) -> PathChangeset<'channel, 'txn, T> {PathChangeset {iter: txn.cursor_revchangeset(&channel.revchanges, Some((from_timestamp, None))),txn,channel,key,
pub(crate) fn rev_log_for_path<'txn, 'channel, T: TxnT>(txn: &'txn T,channel: &'channel Channel<T>,key: Position<ChangeId>,from_timestamp: u64,) -> RevPathChangeset<'channel, 'txn, T> {RevPathChangeset {iter: txn.rev_cursor_revchangeset(&channel.revchanges, Some((from_timestamp, None))),txn,channel,key,
/// Is there an alive/pseudo edge from `a` to `b`.pub(crate) fn test_edge<T: TxnT>(txn: &T,channel: &Channel<T>,a: Position<ChangeId>,b: Position<ChangeId>,min: EdgeFlags,max: EdgeFlags,) -> bool {debug!("is_connected {:?} {:?}", a, b);let key = Vertex {change: a.change,start: a.pos,end: a.pos,};let edge = Edge {flag: min,dest: b,introduced_by: ChangeId::ROOT,};let mut cursor = txn.cursor_graph(&channel.graph, Some((key, Some(edge))));let (a_, b_) = cursor.next().unwrap();a_.change == a.change&& a_.start <= a.pos&& a_.end >= a.pos&& b_.flag >= min&& b_.flag <= max&& b_.dest == b}/// Is there an alive/pseudo edge to `a`.pub(crate) fn is_alive<T: TxnT>(txn: &T, channel: &Channel<T>, a: Vertex<ChangeId>) -> bool {a.is_root()|| iter_adjacent(txn,channel,a,EdgeFlags::PARENT,EdgeFlags::all() - EdgeFlags::DELETED,).filter(|e| e.flag.contains(EdgeFlags::BLOCK) || e.flag.contains(EdgeFlags::FOLDER)).next().is_some()}
#[doc(hidden)]// #[cfg(debug_assertions)]fn iter_inodes<'txn>(&'txn self,) -> Cursor<Self, &'txn Self, Self::InodesCursor, Inode, Position<ChangeId>>;#[cfg(debug_assertions)]#[doc(hidden)]fn debug_inodes(&self) {debug!("debug_inodes");for t in self.iter_inodes() {debug!("debug_inodes = {:?}", t)}debug!("/debug_inodes");
pub(crate) fn make_changeid<T: TxnT>(txn: &T, h: &Hash) -> ChangeId {if let Some(h) = txn.get_internal(*h) {return h;}use byteorder::{ByteOrder, LittleEndian};use rand::Rng;let mut p = match h {Hash::None => return ChangeId::ROOT,Hash::Blake3(ref s) => ChangeId(LittleEndian::read_u64(&s[..])),};while txn.get_external(p).is_some() {p = ChangeId(rand::thread_rng().gen());
#[cfg(debug_assertions)]pub fn debug_tree<P: AsRef<std::path::Path>, T: TxnT>(txn: &T,file: P,) -> Result<(), anyhow::Error> {let root = OwnedPathId {parent_inode: Inode::ROOT,basename: SmallString::from_str(""),};let mut f = std::fs::File::create(file)?;for t in txn.iter_tree(root, None) {writeln!(f, "{:?}", t)?}Ok(())}
/// Write the graph of a channel to write `W` in graphviz/// format. **Warning:** this can be really large on old channels.#[doc(hidden)]fn debug<W: Write>(&self, channel: &Channel<Self>, mut f: W) -> Result<bool, anyhow::Error> {let mut cursor = self.cursor_graph(&channel.graph, None);writeln!(f, "digraph {{")?;let mut keys = std::collections::HashSet::new();let mut at_least_one = false;while let Some((k, v)) = cursor.next() {at_least_one = true;debug!("debug {:?} {:?}", k, v);if keys.insert(k) {debug_vertex(&mut f, k)?}debug_edge(self, channel, &mut f, k, v)?}writeln!(f, "}}")?;Ok(at_least_one)
#[doc(hidden)]fn check_channel_log(&self, channel: &ChannelRef<Self>) {let channel = channel.r.borrow();for (t, (ch, _)) in self.cursor_revchangeset(&channel.revchanges, None) {if self.get_changeset(&channel.changes, ch, None) != Some(t) {panic!("ch = {:?}, {:?}, t = {:?}",ch,self.get_changeset(&channel.changes, ch, None),Some(t));}}for (ch, t) in self.cursor_changeset(&channel.changes, None) {if self.get_revchangeset(&channel.revchanges, t, None).unwrap().0!= ch{panic!("t = {:?}, {:?}, ch = {:?}",t,self.get_revchangeset(&channel.revchanges, t, None),Some(ch));}}
// org id oGZWRYu9yo9VKkz4S9hipd5OpFx6kQiF5q/T9jSqp5A=/// Write the graph of a channel to file `f` in graphviz/// format. **Warning:** this can be really large on old channels.#[cfg(debug_assertions)]pub fn debug_to_file<P: AsRef<std::path::Path>, T: TxnT>(txn: &T,channel: &ChannelRef<T>,f: P,) -> Result<bool, anyhow::Error> {info!("debug {:?}", f.as_ref());let mut f = std::fs::File::create(f)?;let channel = channel.r.borrow();let done = debug(txn, &channel, &mut f)?;f.flush()?;info!("done debugging {:?}", done);Ok(done)}#[cfg(debug_assertions)]pub fn debug_revtree<P: AsRef<std::path::Path>, T: TxnT>(txn: &T,file: P,) -> Result<(), anyhow::Error> {let mut f = std::fs::File::create(file)?;for t in txn.iter_revtree(Inode::ROOT, None) {writeln!(f, "{:?}", t)?
#[doc(hidden)]fn check_tree_revtree(&self) {let zero = OwnedPathId {parent_inode: Inode::ROOT,basename: SmallString::new(),};for (a, b) in self.iter_tree(zero, None) {if !a.basename.is_empty() {assert_eq!(self.get_revtree(b, Some(a.as_file_id())),Some(a.as_file_id()))}}let mut inodes = Vec::new();for (a, b) in self.iter_revtree(Inode::ROOT, None) {inodes.clear();for (c, d) in self.iter_tree(b.clone(), None) {if c > b {break;} else if c < b {continue;}if d == a {inodes.push(d)}}if inodes.len() > 1 {panic!("inodes for {:?} {:?} = {:?}", a, b, inodes);}}
// Find the reachable with a DFS.let mut reachable = HashSet::new();let mut stack = vec![Vertex::ROOT];while let Some(v) = stack.pop() {if !reachable.insert(v) {continue;}for e in self.iter_adjacent(&channel,v,EdgeFlags::empty(),EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,) {stack.push(self.find_block(&channel, e.dest).unwrap());}
/// Write the graph of a channel to write `W` in graphviz/// format. **Warning:** this can be really large on old channels.#[cfg(debug_assertions)]pub fn debug<W: Write, T: TxnT>(txn: &T,channel: &Channel<T>,mut f: W,) -> Result<bool, anyhow::Error> {let mut cursor = txn.cursor_graph(&channel.graph, None);writeln!(f, "digraph {{")?;let mut keys = std::collections::HashSet::new();let mut at_least_one = false;while let Some((k, v)) = cursor.next() {at_least_one = true;debug!("debug {:?} {:?}", k, v);if keys.insert(k) {debug_vertex(&mut f, k)?
// Find the alivelet mut alive_unreachable = HashMap::new();let mut cursor = self.cursor_graph(&channel.graph, None);let mut visited = HashSet::new();let mut k0 = Vertex::ROOT;let mut k0_has_pseudo_parents = false;let mut k0_has_regular_parents = false;let mut reachable_pseudo = Vec::new();while let Some((k, v)) = cursor.next() {debug!("check_alive, k = {:?}, v = {:?}", k, v);if k0 != k {if k0_has_pseudo_parents && !k0_has_regular_parents {reachable_pseudo.push((k0, self.find_file(&channel, k0, &mut stack, &mut visited)))}k0 = k;k0_has_pseudo_parents = false;k0_has_regular_parents = false;}if v.flag.contains(EdgeFlags::PARENT)&& !v.flag.contains(EdgeFlags::FOLDER)&& !v.flag.contains(EdgeFlags::DELETED){if v.flag.contains(EdgeFlags::PSEUDO) {k0_has_pseudo_parents = true} else {k0_has_regular_parents = true}}
#[doc(hidden)]fn find_file(&self,channel: &Channel<Self>,k: Vertex<ChangeId>,stack: &mut Vec<Vertex<ChangeId>>,visited: &mut HashSet<Vertex<ChangeId>>,) -> Option<Vertex<ChangeId>> {let mut file = None;stack.clear();stack.push(k);visited.clear();'outer: while let Some(kk) = stack.pop() {if !visited.insert(kk) {continue;
// Find the alivelet mut alive_unreachable = HashMap::new();let mut cursor = txn.cursor_graph(&channel.graph, None);let mut visited = HashSet::new();let mut k0 = Vertex::ROOT;let mut k0_has_pseudo_parents = false;let mut k0_has_regular_parents = false;let mut reachable_pseudo = Vec::new();while let Some((k, v)) = cursor.next() {debug!("check_alive, k = {:?}, v = {:?}", k, v);if k0 != k {if k0_has_pseudo_parents && !k0_has_regular_parents {reachable_pseudo.push((k0, find_file(txn, &channel, k0, &mut stack, &mut visited)))
k0 = k;k0_has_pseudo_parents = false;k0_has_regular_parents = false;}if v.flag.contains(EdgeFlags::PARENT)&& !v.flag.contains(EdgeFlags::FOLDER)&& !v.flag.contains(EdgeFlags::DELETED){if v.flag.contains(EdgeFlags::PSEUDO) {k0_has_pseudo_parents = true} else {k0_has_regular_parents = true
#[doc(hidden)]fn debug_root<W: Write>(&self,channel: &ChannelRef<Self>,root: Vertex<ChangeId>,mut f: W,) -> Result<(), anyhow::Error> {let channel = channel.r.borrow();writeln!(f, "digraph {{")?;// Find the reachable with a DFS.let mut visited = HashSet::new();let mut stack = vec![(root, false)];while let Some((v, is_going_up)) = stack.pop() {if !visited.insert(v) {continue;
debug_vertex(&mut f, v)?;for e in self.iter_adjacent(&channel, v, EdgeFlags::empty(), EdgeFlags::all()) {if e.flag.contains(EdgeFlags::PARENT) {debug_edge(self, &channel, &mut f, v, e)?;let v = self.find_block_end(&channel, e.dest).unwrap();stack.push((v, e.flag.contains(EdgeFlags::FOLDER)));} else if !is_going_up {debug_edge(self, &channel, &mut f, v, e)?;let v = self.find_block(&channel, e.dest).unwrap();stack.push((v, false));
}}if !k0.is_root() && k0_has_pseudo_parents && !k0_has_regular_parents {reachable_pseudo.push((k0, find_file(txn, &channel, k0, &mut stack, &mut visited)));}(alive_unreachable, reachable_pseudo)}#[cfg(test)]fn find_file<T: TxnT>(txn: &T,channel: &Channel<T>,k: Vertex<ChangeId>,stack: &mut Vec<Vertex<ChangeId>>,visited: &mut HashSet<Vertex<ChangeId>>,) -> Option<Vertex<ChangeId>> {let mut file = None;stack.clear();stack.push(k);visited.clear();'outer: while let Some(kk) = stack.pop() {if !visited.insert(kk) {continue;}for e in iter_adjacent(txn, &channel, kk, EdgeFlags::PARENT, EdgeFlags::all()) {if e.flag.contains(EdgeFlags::PARENT) {if e.flag.contains(EdgeFlags::FOLDER) {file = Some(kk);break 'outer;
#[doc(hidden)]fn debug_root_rev<W: Write>(&self,channel: &Channel<Self>,root: Vertex<ChangeId>,mut f: W,) -> Result<(), anyhow::Error> {writeln!(f, "digraph {{")?;let mut visited = HashSet::new();let mut stack = vec![root];while let Some(v) = stack.pop() {if !visited.insert(v) {continue;}debug_vertex(&mut f, v)?;for e in self.iter_adjacent(&channel, v, EdgeFlags::empty(), EdgeFlags::all()) {if e.flag.contains(EdgeFlags::PARENT) {debug_edge(self, &channel, &mut f, v, e)?;let v = self.find_block_end(&channel, e.dest).unwrap();stack.push(v);}
pub(crate) fn debug_root_rev<W: Write, T: TxnT>(txn: &T,channel: &Channel<T>,root: Vertex<ChangeId>,mut f: W,) -> Result<(), anyhow::Error> {writeln!(f, "digraph {{")?;let mut visited = HashSet::new();let mut stack = vec![root];while let Some(v) = stack.pop() {if !visited.insert(v) {continue;}debug_vertex(&mut f, v)?;for e in iter_adjacent(txn, &channel, v, EdgeFlags::empty(), EdgeFlags::all()) {if e.flag.contains(EdgeFlags::PARENT) {debug_edge(txn, &channel, &mut f, v, e)?;let v = find_block_end(txn, &channel, e.dest).unwrap();stack.push(v);
#[doc(hidden)]fn del_inodes_with_rev(&mut self,inode: Inode,position: Position<ChangeId>,) -> Result<(), anyhow::Error> {self.del_inodes(inode, None)?;self.del_revinodes(position, None)?;Ok(())}#[doc(hidden)]fn del_tree_with_rev(&mut self, inode: Inode) -> Result<(), anyhow::Error> {if let Some(parent) = self.get_revtree(inode, None) {let parent = parent.to_owned();assert!(self.del_tree(parent.as_file_id(), Some(inode))?);assert!(self.del_revtree(inode, Some(parent.as_file_id()))?);}Ok(())}
// Provided methods/// Split a key `[a, b[` at position `pos`, yielding two keys `[a,/// pos[` and `[pos, b[` linked by an edge.#[doc(hidden)]fn split_block<'db, 'txn: 'db>(&'txn mut self,channel: &'db mut Channel<Self>,key: Vertex<ChangeId>,pos: ChangePosition,) -> Result<(), anyhow::Error> {trace!("key = {:?}, pos = {:?}", key, pos);let adjacent: Vec<_> = self.cursor_graph(&channel.graph, Some((key, None))).take_while(|&(k, _)| k <= key).filter(|&(k, _)| k == key).map(|(_, e)| e).collect();debug!("adjacent {:?}", adjacent);for chi in adjacent {assert!(chi.introduced_by != ChangeId::ROOT || chi.flag.contains(EdgeFlags::PSEUDO));if chi.flag.contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {self.put_graph_with_rev(channel,chi.flag - EdgeFlags::PARENT,Vertex {change: key.change,start: key.start,end: pos,},Vertex {change: key.change,start: pos,end: key.end,},chi.introduced_by,)?;}self.del_graph(&mut channel.graph, key, Some(chi))?;self.put_graph(&mut channel.graph,if chi.flag.contains(EdgeFlags::PARENT) {Vertex {change: key.change,start: key.start,end: pos,}} else {Vertex {change: key.change,start: pos,end: key.end,}},chi,)?;}Ok(())}#[doc(hidden)]fn del_graph_with_rev(&mut self,channel: &mut Channel<Self>,mut flag: EdgeFlags,mut k0: Vertex<ChangeId>,mut k1: Vertex<ChangeId>,introduced_by: ChangeId,) -> Result<bool, anyhow::Error> {if flag.contains(EdgeFlags::PARENT) {std::mem::swap(&mut k0, &mut k1);flag -= EdgeFlags::PARENT}debug!("del_graph_with_rev {:?} {:?} {:?}", flag, k0, k1);let a = self.del_graph(&mut channel.graph,k0,Some(Edge {flag: flag,dest: Position {change: k1.change,pos: k1.start,},introduced_by,}),)?;let b = self.del_graph(&mut channel.graph,k1,Some(Edge {flag: flag | EdgeFlags::PARENT,dest: Position {change: k0.change,pos: k0.end,},introduced_by,}),)?;assert!((a && b) || (!a && !b));Ok(a && b)}#[doc(hidden)]fn put_graph_with_rev(&mut self,channel: &mut Channel<Self>,flag: EdgeFlags,k0: Vertex<ChangeId>,k1: Vertex<ChangeId>,introduced_by: ChangeId,) -> Result<bool, anyhow::Error> {debug_assert!(!flag.contains(EdgeFlags::PARENT));if k0.change == k1.change {assert_ne!(k0.start_pos(), k1.start_pos());}if introduced_by == ChangeId::ROOT {assert!(flag.contains(EdgeFlags::PSEUDO));}debug!("put_graph_with_rev {:?} {:?} {:?}", k0, k1, flag);let a = self.put_graph(&mut channel.graph,k0,Edge {flag: flag,dest: Position {change: k1.change,pos: k1.start,},introduced_by,},)?;let b = self.put_graph(&mut channel.graph,k1,Edge {flag: flag ^ EdgeFlags::PARENT,dest: Position {change: k0.change,pos: k0.end,},introduced_by,},)?;if flag.contains(EdgeFlags::FOLDER) {if !((k0.len() == 0 && k1.len() > 2) || (k0.len() > 2 && k1.len() == 0)) {let mut f = std::fs::File::create("folder_debug")?;self.debug(channel, &mut f)?;panic!("{:?} {:?}", k0, k1);}}assert!((a && b) || (!a && !b));Ok(a && b)}
#[doc(hidden)]fn register_change(&mut self,internal: ChangeId,hash: Hash,change: &Change,) -> Result<(), anyhow::Error> {self.put_external(internal, hash)?;self.put_internal(hash, internal)?;for dep in change.dependencies.iter() {debug!(target:"libpijul::register_change", "dep = {:?}", dep);let dep_internal = self.get_internal(*dep).unwrap();debug!(target:"libpijul::register_change", "{:?} depends on {:?}", internal, dep_internal);self.put_revdep(dep_internal, internal)?;self.put_dep(internal, dep_internal)?;}for hunk in change.changes.iter().flat_map(|r| r.iter()) {let inode = match *hunk {Atom::NewVertex(NewVertex { ref inode, .. }) => inode,Atom::EdgeMap(EdgeMap { ref inode, .. }) => inode,};let inode = Position {change: inode.change.and_then(|change| self.get_internal(change)).unwrap_or(internal),pos: inode.pos,};debug!(target:"libpijul::register_change", "touched: {:?} {:?}", inode, internal);self.put_touched_files(inode, internal)?;self.put_rev_touched_files(internal, inode)?;}Ok(())}#[doc(hidden)]
}pub(crate) fn put_inodes_with_rev<T: MutTxnT>(txn: &mut T,inode: Inode,position: Position<ChangeId>,) -> Result<(), anyhow::Error> {txn.put_inodes(inode, position)?;txn.put_revinodes(position, inode)?;Ok(())}/// Split a key `[a, b[` at position `pos`, yielding two keys `[a,/// pos[` and `[pos, b[` linked by an edge.pub(crate) fn split_block<T: MutTxnT>(txn: &mut T,channel: &mut Channel<T>,key: Vertex<ChangeId>,pos: ChangePosition,) -> Result<(), anyhow::Error> {trace!("key = {:?}, pos = {:?}", key, pos);let adjacent: Vec<_> = txn.cursor_graph(&channel.graph, Some((key, None))).take_while(|&(k, _)| k <= key).filter(|&(k, _)| k == key).map(|(_, e)| e).collect();debug!("adjacent {:?}", adjacent);for chi in adjacent {assert!(chi.introduced_by != ChangeId::ROOT || chi.flag.contains(EdgeFlags::PSEUDO));if chi.flag.contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {put_graph_with_rev(txn,channel,chi.flag - EdgeFlags::PARENT,Vertex {change: key.change,start: key.start,end: pos,},Vertex {change: key.change,start: pos,end: key.end,},chi.introduced_by,)?;}txn.del_graph(&mut channel.graph, key, Some(chi))?;txn.put_graph(&mut channel.graph,if chi.flag.contains(EdgeFlags::PARENT) {Vertex {change: key.change,start: key.start,end: pos,}} else {Vertex {change: key.change,start: pos,end: key.end,}},chi,)?;}Ok(())}pub(crate) fn del_graph_with_rev<T: MutTxnT>(txn: &mut T,channel: &mut Channel<T>,mut flag: EdgeFlags,mut k0: Vertex<ChangeId>,mut k1: Vertex<ChangeId>,introduced_by: ChangeId,) -> Result<bool, anyhow::Error> {if flag.contains(EdgeFlags::PARENT) {std::mem::swap(&mut k0, &mut k1);flag -= EdgeFlags::PARENT}debug!("del_graph_with_rev {:?} {:?} {:?}", flag, k0, k1);let a = txn.del_graph(&mut channel.graph,k0,Some(Edge {flag: flag,dest: Position {change: k1.change,pos: k1.start,},introduced_by,}),)?;let b = txn.del_graph(&mut channel.graph,k1,Some(Edge {flag: flag | EdgeFlags::PARENT,dest: Position {change: k0.change,pos: k0.end,},introduced_by,}),)?;assert!((a && b) || (!a && !b));Ok(a && b)}pub(crate) fn put_graph_with_rev<T: MutTxnT>(txn: &mut T,channel: &mut Channel<T>,flag: EdgeFlags,k0: Vertex<ChangeId>,k1: Vertex<ChangeId>,introduced_by: ChangeId,) -> Result<bool, anyhow::Error> {debug_assert!(!flag.contains(EdgeFlags::PARENT));if k0.change == k1.change {assert_ne!(k0.start_pos(), k1.start_pos());}if introduced_by == ChangeId::ROOT {assert!(flag.contains(EdgeFlags::PSEUDO));}debug!("put_graph_with_rev {:?} {:?} {:?}", k0, k1, flag);let a = txn.put_graph(&mut channel.graph,k0,Edge {flag: flag,dest: Position {change: k1.change,pos: k1.start,},introduced_by,},)?;let b = txn.put_graph(&mut channel.graph,k1,Edge {flag: flag ^ EdgeFlags::PARENT,dest: Position {change: k0.change,pos: k0.end,},introduced_by,},)?;assert!((a && b) || (!a && !b));Ok(a && b)
pub(crate) fn register_change<T: MutTxnT>(txn: &mut T,internal: ChangeId,hash: Hash,change: &Change,) -> Result<(), anyhow::Error> {txn.put_external(internal, hash)?;txn.put_internal(hash, internal)?;for dep in change.dependencies.iter() {debug!(target:"libpijul::register_change", "dep = {:?}", dep);let dep_internal = txn.get_internal(*dep).unwrap();debug!(target:"libpijul::register_change", "{:?} depends on {:?}", internal, dep_internal);txn.put_revdep(dep_internal, internal)?;txn.put_dep(internal, dep_internal)?;}for hunk in change.changes.iter().flat_map(|r| r.iter()) {let inode = match *hunk {Atom::NewVertex(NewVertex { ref inode, .. }) => inode,Atom::EdgeMap(EdgeMap { ref inode, .. }) => inode,};let inode = Position {change: inode.change.and_then(|change| txn.get_internal(change)).unwrap_or(internal),pos: inode.pos,};debug!(target:"libpijul::register_change", "touched: {:?} {:?}", inode, internal);txn.put_touched_files(inode, internal)?;txn.put_rev_touched_files(internal, inode)?;}Ok(())}
if !edge.flag.contains(EdgeFlags::FOLDER) {// If ~dest_vertex~ is a folder, there's no way to repair// its down context, so we can't disconnect here.let f = edge.flag - EdgeFlags::PARENT;txn.del_graph_with_rev(channel, f, p, dest_vertex, edge.introduced_by)?;}
}fn log_for_path<'channel, 'txn>(&'txn self,channel: &'channel pristine::Channel<Self>,pos: pristine::Position<pristine::ChangeId>,from: u64,) -> pristine::PathChangeset<'channel, 'txn, Self> {pristine::log_for_path(self, channel, pos, from)}fn rev_log_for_path<'channel, 'txn>(&'txn self,channel: &'channel pristine::Channel<Self>,pos: pristine::Position<pristine::ChangeId>,from: u64,) -> pristine::RevPathChangeset<'channel, 'txn, Self> {pristine::rev_log_for_path(self, channel, pos, from)
fn iter_adjacent<'db, 'txn: 'db>(&'txn self,channel: &'db pristine::Channel<Self>,key: Vertex<pristine::ChangeId>,min_flag: pristine::EdgeFlags,max_flag: pristine::EdgeFlags,) -> pristine::AdjacentIterator<'txn, Self> {pristine::iter_adjacent(self, channel, key, min_flag, max_flag)}
if !alive_parent && !n.flag.contains(EdgeFlags::FOLDER) {for (change, _) in ws.deleted_by.iter() {let flag = n.flag | EdgeFlags::BLOCK | EdgeFlags::DELETED;txn.put_graph_with_rev(channel, flag, vertex, down, *change)?;}} else if n.flag.contains(EdgeFlags::FOLDER) {txn.put_graph_with_rev(channel, n.flag | EdgeFlags::BLOCK, vertex, down, change)?;
let flag0 = EdgeFlags::PARENT | EdgeFlags::DELETED;let flag1 = flag0 | EdgeFlags::FOLDER | EdgeFlags::BLOCK;for parent in txn.iter_adjacent(&channel, up_vertex, flag0, flag1) {// This unwrap is ok: `parent` is in the channel.let introduced_by = txn.get_external(parent.introduced_by).unwrap();if !ch.knows(&introduced_by) {ws.deleted_by.insert((parent.introduced_by, parent.flag - EdgeFlags::PARENT));
let flag0 = EdgeFlags::PARENT | EdgeFlags::BLOCK;let flag1 = flag0 | EdgeFlags::DELETED | EdgeFlags::FOLDER;for parent in iter_adjacent(txn, &channel, up_vertex, flag0, flag1) {if parent.flag.contains(EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK){// This unwrap is ok: `parent` is in the channel.let introduced_by = txn.get_external(parent.introduced_by).unwrap();if !ch.knows(&introduced_by) {ws.deleted_by.insert((parent.introduced_by, parent.flag - EdgeFlags::PARENT));}} else if parent.flag.contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {// This vertex is alive, even if its parent is in conflict.debug!("up_context: alive {:?} {:?}", up_vertex, parent);break;
EdgeFlags::PARENT | EdgeFlags::BLOCK,EdgeFlags::PARENT | EdgeFlags::BLOCK | EdgeFlags::FOLDER,).next().is_none(){return None;}Some(AliveVertex {vertex,flags: if crate::pristine::iter_adjacent(txn,&channel,vertex,EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK,EdgeFlags::all(),).filter(|e| {e.flag.contains(EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK)}).next().is_some()
"base64 0.13.0" = rec {crateName = "base64";version = "0.13.0";edition = "2018";sha256 = "1z82g23mbzjgijkpcrilc7nljpxpvpf7zxf6iyiapkgka2ngwkch";authors = ["Alice Maz <alice@alicemaz.com>""Marshall Pierce <marshall@mpierce.org>"];features = {"default" = [ "std" ];};resolvedDefaultFeatures = [ "default" "std" ];};
}];};"console_error_panic_hook" = rec {crateName = "console_error_panic_hook";version = "0.1.6";edition = "2015";sha256 = "04d2narcrzk9bnddz17rr2l819l82pr0h6d98s2w9q236n87dndq";authors = ["Nick Fitzgerald <fitzgen@gmail.com>"];dependencies = [{name = "cfg-if";packageId = "cfg-if 0.1.10";
}{name = "lazy_static";packageId = "lazy_static";optional = true;}];buildDependencies = [{name = "autocfg";packageId = "autocfg";}];features = {"default" = [ "std" ];"std" = [ "lazy_static" ];};resolvedDefaultFeatures = [ "default" "lazy_static" "std" ];};"crossbeam-utils 0.8.0" = rec {crateName = "crossbeam-utils";version = "0.8.0";edition = "2018";sha256 = "199ywwmkg60kqavhw8rhy9wybsfjr9p5czinhq56jprmk06m94gc";authors = ["The Crossbeam Project Developers"];dependencies = [{name = "cfg-if";packageId = "cfg-if 1.0.0";}{name = "const_fn";packageId = "const_fn";
};"errno" = rec {crateName = "errno";version = "0.2.7";edition = "2015";sha256 = "1zj6rra8n7d7gagppvvs5pvrfblad6x4ln5knb4kg7dfkkxz4s7s";authors = ["Chris Wong <lambda.fairy@gmail.com>"];dependencies = [{name = "errno-dragonfly";packageId = "errno-dragonfly";target = { target, features }: (target."os" == "dragonfly");}{name = "libc";packageId = "libc";target = { target, features }: (target."os" == "hermit");}{name = "libc";packageId = "libc";target = { target, features }: (target."os" == "wasi");}{name = "libc";packageId = "libc";target = { target, features }: target."unix";}{name = "winapi";packageId = "winapi 0.3.9";target = { target, features }: target."windows";features = [ "errhandlingapi" "minwindef" "ntdef" "winbase" ];}];
"errno-dragonfly" = rec {crateName = "errno-dragonfly";version = "0.1.1";edition = "2015";sha256 = "0rshlc00nv45f14v2l1w0ma2nf1jg5j7q9pvw7hh018r6r73bjhl";authors = ["Michael Neumann <mneumann@ntecs.de>"];dependencies = [{name = "libc";packageId = "libc";}];buildDependencies = [{name = "gcc";packageId = "gcc";}];};
"pager" = rec {crateName = "pager";version = "0.16.0";edition = "2018";sha256 = "0s0r95q3jfbh2c3paab2bpl158lyaq35xnzy1x7mrdfhy26d1iq5";authors = ["Cyril Plisko <cyril.plisko@mountall.com>"];dependencies = [{name = "errno";packageId = "errno";}{name = "libc";packageId = "libc";}];features = {};};
"wasm-bindgen-test" = rec {crateName = "wasm-bindgen-test";version = "0.3.18";edition = "2018";sha256 = "0r3z37d48i91bg2akjzfcpj3a1jbkhcs2l1xfcj7ymcap74cvl9l";authors = ["The wasm-bindgen Developers"];dependencies = [{name = "console_error_panic_hook";packageId = "console_error_panic_hook";}{name = "js-sys";packageId = "js-sys";}{name = "scoped-tls";packageId = "scoped-tls";}{name = "wasm-bindgen";packageId = "wasm-bindgen";}{name = "wasm-bindgen-futures";packageId = "wasm-bindgen-futures";}{name = "wasm-bindgen-test-macro";packageId = "wasm-bindgen-test-macro";}];};"wasm-bindgen-test-macro" = rec {crateName = "wasm-bindgen-test-macro";version = "0.3.18";edition = "2018";sha256 = "11d5w6wdjd2if1n7jrjvhgi52pn094m51nxpn65fwfblprkrryz8";procMacro = true;authors = ["The wasm-bindgen Developers"];dependencies = [{name = "proc-macro2";packageId = "proc-macro2";}{name = "quote";packageId = "quote";}];};