operation: iterate ancestors incrementally with timestamp-based heuristic

Suppose the operation log is mostly linear, this means "jj op log" iterator
won't look ahead more than one entry.

Another idea is to either add a "generation" number to operation data, or
build index of operations. Since we'll eventually add GC command, I don't
think op index would be required. I think readdir() is good enough to resolve
hex prefix against ~10k entries.

For now, walk_ancestors() is a free function. If we add Repo-like abstraction
over OpStore + OpHeadsStore, this function will probably be migrated there.
This commit is contained in:
Yuya Nishihara 2023-06-06 20:30:51 +09:00
parent 5cecdb54ce
commit 7a6f832e14
2 changed files with 34 additions and 8 deletions

View file

@ -19,8 +19,8 @@ use std::hash::{Hash, Hasher};
use std::sync::Arc;
use crate::backend::CommitId;
use crate::op_store;
use crate::op_store::{OpStore, OperationId, ViewId};
use crate::{dag_walk, op_store};
#[derive(Clone)]
pub struct Operation {
@ -165,3 +165,34 @@ impl View {
&self.data.head_ids
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct OperationByEndTime(Operation);
impl Ord for OperationByEndTime {
fn cmp(&self, other: &Self) -> Ordering {
let self_end_time = &self.0.store_operation().metadata.end_time;
let other_end_time = &other.0.store_operation().metadata.end_time;
self_end_time
.cmp(other_end_time)
.then_with(|| self.0.cmp(&other.0)) // to comply with Eq
}
}
impl PartialOrd for OperationByEndTime {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
/// Walks `head_op` and its ancestors in reverse topological order.
pub fn walk_ancestors(head_op: &Operation) -> impl Iterator<Item = Operation> {
// Lazily load operations based on timestamp-based heuristic. This works so long
// as the operation history is mostly linear.
dag_walk::topo_order_reverse_lazy(
vec![OperationByEndTime(head_op.clone())],
|OperationByEndTime(op)| op.id().clone(),
|OperationByEndTime(op)| op.parents().into_iter().map(OperationByEndTime),
)
.map(|OperationByEndTime(op)| op)
}

View file

@ -1,6 +1,5 @@
use clap::Subcommand;
use jujutsu_lib::dag_walk::topo_order_reverse;
use jujutsu_lib::operation::Operation;
use jujutsu_lib::operation;
use crate::cli_util::{user_error, CommandError, CommandHelper, LogContentFormat};
use crate::graphlog::{get_graphlog, Edge};
@ -82,11 +81,7 @@ fn cmd_op_log(
let formatter = formatter.as_mut();
let mut graph = get_graphlog(command.settings(), formatter.raw());
let default_node_symbol = graph.default_node_symbol().to_owned();
for op in topo_order_reverse(
vec![head_op],
|op: &Operation| op.id().clone(),
|op: &Operation| op.parents(),
) {
for op in operation::walk_ancestors(&head_op) {
let mut edges = vec![];
for parent in op.parents() {
edges.push(Edge::direct(parent.id().clone()));