cob: Simplify the ChangeGraph implementation

Signed-off-by: Léon Zach <leon.zach@posteo.de>
This commit is contained in:
Léon Zach 2025-05-13 04:23:24 +02:00 committed by Fintan Halpenny
parent 4cd0782f2e
commit eceb7f29c8
1 changed files with 29 additions and 85 deletions

View File

@ -41,53 +41,48 @@ impl ChangeGraph {
{ {
log::debug!(target: "cob", "Loading object of type {typename} at {oid}"); log::debug!(target: "cob", "Loading object of type {typename} at {oid}");
let mut builder = GraphBuilder::default(); let mut graph: Dag<Oid, Entry> = Dag::new();
let mut edges_to_process: Vec<(Oid, Oid)> = Vec::new();
// Populate the initial set of edges_to_process from the refs we have // Populate the initial set of node ids from the refs we have
for reference in tip_refs { let mut child_ids = Vec::from_iter(tip_refs.map(|r| r.target.id));
log::trace!(target: "cob", "Loading object from reference '{}'", reference.name); let mut edges_to_add = Vec::new();
match storage.load(reference.target.id) { while let Some(child_id) = child_ids.pop() {
// Skip if we already processed this node.
if graph.contains(&child_id) {
continue;
}
match storage.load(child_id) {
Ok(change) => { Ok(change) => {
let new_edges = builder.add_change(reference.target.id, change); for parent_id in &change.parents {
edges_to_process.extend(new_edges); edges_to_add.push((child_id, *parent_id));
child_ids.push(*parent_id);
debug_assert_ne!(Some(*parent_id), change.resource);
}
graph.node(child_id, change);
} }
Err(e) => { Err(e) => {
log::warn!( log::warn!(
target: "cob", target: "cob",
"Unable to load change from reference {}->{}: {e}", "Unable to load change tree from commit {child_id}: {e}",
reference.name,
reference.target.id,
); );
} }
} }
} }
// Process edges until we have no more to process // The Dag::dependency() function implicitly assumes that both nodes already exist in the graph.
while let Some((parent_commit_id, child_commit_id)) = edges_to_process.pop() { // Therefore, we add the edges only after successfully processing all nodes.
log::trace!( for (child_id, parent_id) in edges_to_add {
target: "cob", graph.dependency(child_id, parent_id);
"Loading change parent='{}', child='{}'",
parent_commit_id,
child_commit_id
);
match storage.load(parent_commit_id) {
Ok(change) => {
let new_edges = builder.add_change(parent_commit_id, change);
edges_to_process.extend(new_edges);
builder.add_edge(child_commit_id, parent_commit_id);
}
Err(e) => {
log::warn!(
target: "cob",
"Unable to load change tree from commit {}: {e}",
parent_commit_id,
);
}
}
} }
builder.build(*oid)
graph.roots().next()?;
Some(ChangeGraph {
object_id: *oid,
graph,
})
} }
/// Given a graph evaluate it to produce a collaborative object. This will /// Given a graph evaluate it to produce a collaborative object. This will
@ -153,54 +148,3 @@ impl ChangeGraph {
x.1.timestamp.cmp(&y.1.timestamp).then(x.0.cmp(y.0)) x.1.timestamp.cmp(&y.1.timestamp).then(x.0.cmp(y.0))
} }
} }
struct GraphBuilder {
graph: Dag<Oid, Entry>,
}
impl Default for GraphBuilder {
fn default() -> Self {
GraphBuilder { graph: Dag::new() }
}
}
impl GraphBuilder {
/// Add a change to the graph which we are building up, returning any edges
/// corresponding to the parents of this node in the change graph
fn add_change(&mut self, commit_id: Oid, change: Entry) -> Vec<(Oid, Oid)> {
let resource = change.resource().copied();
let parents = change.parents.clone();
if !self.graph.contains(&commit_id) {
self.graph.node(commit_id, change);
}
parents
.into_iter()
.filter_map(move |parent| {
debug_assert_ne!(Some(parent), resource);
if !self.graph.has_dependency(&commit_id, &parent) {
Some((parent, commit_id))
} else {
None
}
})
.collect()
}
fn add_edge(&mut self, child: Oid, parent: Oid) {
self.graph.dependency(child, parent);
}
fn build(self, object_id: ObjectId) -> Option<ChangeGraph> {
if self.graph.roots().next().is_some() {
Some(ChangeGraph {
object_id,
graph: self.graph,
})
} else {
None
}
}
}