pub fn record_single_thread<T, W: WorkingCopyRead + Clone, C: ChangeStore + Clone>(
&mut self,
txn: ArcTxn<T>,
diff_algorithm: diff::Algorithm,
stop_early: bool,
diff_separator: ®ex::bytes::Regex,
channel: ChannelRef<T>,
working_copy: &W,
changes: &C,
prefix: &str,
) -> Result<(), RecordError<C::Error, W::Error, T>>
where
T: ChannelMutTxnT + TreeTxnT,
<W as WorkingCopyRead>::Error: 'static,
{
info!("Starting to record");
let now = std::time::Instant::now();
let mut stack = vec![(RecordItem::root(), components(prefix))];
while let Some((mut item, mut components)) = stack.pop() {
debug!("stack.pop() = Some({:?})", item);
// Check for moves and file conflicts.
let vertex: Option<Position<Option<ChangeId>>> =
self.recorded_inodes.lock().get(&item.inode).cloned();
let mut root_vertices = Vec::new();
let vertex = if let Some(vertex) = vertex {
vertex
} else if item.inode == Inode::ROOT {
debug!("TAKING LOCK {}", line!());
let txn = txn.read();
debug!("TAKEN");
let channel = channel.r.read();
// Test for a "root" vertex below the null one.
let f0 = EdgeFlags::FOLDER | EdgeFlags::BLOCK;
let f1 = f0 | EdgeFlags::PSEUDO;
self.recorded_inodes
.lock()
.insert(Inode::ROOT, Position::ROOT.to_option());
let mut has_nonempty_root = false;
for e in iter_adjacent(&*txn, txn.graph(&*channel), Vertex::ROOT, f0, f1)? {
let e = e?;
let child = txn.find_block(txn.graph(&*channel), e.dest()).unwrap();
if child.start == child.end {
// This is the "new" format, with multiple
// roots, and `grandchild` is one of the
// roots.
let grandchild =
iter_adjacent(&*txn, txn.graph(&*channel), *child, f0, f1)?
.next()
.unwrap()?
.dest();
root_vertices.push(grandchild);
self.delete_obsolete_children(
&*txn,
txn.graph(&channel),
working_copy,
changes,
&item.full_path,
grandchild,
)?;
} else {
// Single-root repository, we need to follow
// the root's children.
has_nonempty_root = true
}
}
debug!("has_nonempty_root: {:?}", has_nonempty_root);
debug!("root_vertices: {:?}", root_vertices);
if has_nonempty_root && !root_vertices.is_empty() {
// This repository is mixed between "zero" roots,
// and new-style-roots.
root_vertices.push(Position::ROOT)
}
Position::ROOT.to_option()
} else if let Some(vertex) = get_inodes_::<_, C, W>(&txn, &channel, &item.inode)? {
{
let txn = txn.read();
let channel = channel.r.read();
let graph = txn.graph(&*channel);
self.delete_obsolete_children(
&*txn,
&graph,
working_copy,
changes,
&item.full_path,
vertex,
)?;
}
let rec = self.recorded();
let new_papa = {
let mut recorded = self.recorded_inodes.lock();
recorded.insert(item.inode, vertex.to_option());
recorded.get(&item.papa).cloned()
};
rec.lock().record_existing_file(
&txn,
diff_algorithm,
stop_early,
&diff_separator,
&channel,
working_copy.clone(),
changes,
&item,
new_papa,
vertex,
)?;
vertex.to_option()
} else {
let rec = self.recorded();
debug!("TAKING LOCK {}", line!());
let mut rec = rec.lock();
match rec.add_file(working_copy, item.clone()) {
Ok(Some(vertex)) => {
// Path addition (maybe just a single directory).
self.recorded_inodes.lock().insert(item.inode, vertex);
vertex
}
_ => continue,
}
};
if root_vertices.is_empty() {
// Move on to the next step.
debug!("TAKING LOCK {}", line!());
let txn = txn.read();
let channel = channel.r.read();
self.push_children::<_, _, C>(
&*txn,
&*channel,
working_copy,
&mut item,
&mut components,
vertex,
&mut stack,
prefix,
changes,
)?;
} else {
for vertex in root_vertices {
let txn = txn.read();
let channel = channel.r.read();
if !vertex.change.is_root() {
let mut r = self.new_root.lock();
let age = txn
.get_changeset(txn.changes(&*channel), &vertex.change)?
.unwrap();
if let Some((_, a)) = *r {
if a < (*age).into() {
*r = Some((vertex.to_option(), (*age).into()))
}
} else {
*r = Some((vertex.to_option(), (*age).into()))
}
}
item.v_papa = vertex.to_option();
self.push_children::<_, _, C>(
&*txn,
&*channel,
working_copy,
&mut item,
&mut components,
vertex.to_option(),
&mut stack,
prefix,
changes,
)?;
}
}
}
crate::TIMERS.lock().unwrap().record += now.elapsed();
info!("record done");
Ok(())
}