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 alive
let 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 alive
let 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";
}
];
};