From 26b5f38f4534a1e313f794a77aa41356063f5fdf Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 31 Dec 2023 10:39:24 +0900 Subject: [PATCH] op_walk: move "opset" query functions from jj_cli --- cli/src/cli_util.rs | 108 +++----------------------------------- cli/src/commands/debug.rs | 6 +-- lib/src/op_walk.rs | 106 +++++++++++++++++++++++++++++++++++-- 3 files changed, 113 insertions(+), 107 deletions(-) diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index bdaa195fd..d52c1b5b9 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{HashMap, HashSet, VecDeque}; +use std::collections::{HashMap, HashSet}; use std::env::{self, ArgsOs, VarError}; use std::ffi::{OsStr, OsString}; use std::fmt::Debug; @@ -42,9 +42,9 @@ use jj_lib::hex_util::to_reverse_hex; use jj_lib::id_prefix::IdPrefixContext; use jj_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher}; use jj_lib::merged_tree::MergedTree; -use jj_lib::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore}; -use jj_lib::op_store::{OpStore, OpStoreError, OperationId, WorkspaceId}; -use jj_lib::op_walk::{OpsetEvaluationError, OpsetResolutionError}; +use jj_lib::op_heads_store::{self, OpHeadResolutionError}; +use jj_lib::op_store::{OpStoreError, OperationId, WorkspaceId}; +use jj_lib::op_walk::OpsetEvaluationError; use jj_lib::operation::Operation; use jj_lib::repo::{ CheckOutCommitError, EditCommitError, MutableRepo, ReadonlyRepo, Repo, RepoLoader, @@ -70,7 +70,7 @@ use jj_lib::workspace::{ default_working_copy_factories, LockedWorkspace, WorkingCopyFactory, Workspace, WorkspaceInitError, WorkspaceLoadError, WorkspaceLoader, }; -use jj_lib::{dag_walk, file_util, git, revset}; +use jj_lib::{dag_walk, file_util, git, op_walk, revset}; use once_cell::unsync::OnceCell; use toml_edit; use tracing::instrument; @@ -659,7 +659,7 @@ impl CommandHelper { }, ) } else { - let operation = resolve_op_for_load( + let operation = op_walk::resolve_op_for_load( repo_loader.op_store(), repo_loader.op_heads_store(), &self.global_args.at_operation, @@ -977,7 +977,7 @@ impl WorkspaceCommandHelper { pub fn resolve_single_op(&self, op_str: &str) -> Result { // When resolving the "@" operation in a `ReadonlyRepo`, we resolve it to the // operation the repo was loaded at. - resolve_single_op( + op_walk::resolve_single_op( self.repo().op_store(), self.repo().op_heads_store(), || Ok(self.repo().operation().clone()), @@ -2019,45 +2019,6 @@ pub fn parse_string_pattern(src: &str) -> Result, - op_heads_store: &Arc, - op_str: &str, -) -> Result { - let get_current_op = || { - op_heads_store::resolve_op_heads(op_heads_store.as_ref(), op_store, |_| { - Err(OpsetResolutionError::MultipleOperations("@".to_owned()).into()) - }) - }; - resolve_single_op(op_store, op_heads_store, get_current_op, op_str) -} - -fn resolve_single_op( - op_store: &Arc, - op_heads_store: &Arc, - get_current_op: impl FnOnce() -> Result, - op_str: &str, -) -> Result { - let op_symbol = op_str.trim_end_matches('-'); - let op_postfix = &op_str[op_symbol.len()..]; - let mut operation = match op_symbol { - "@" => get_current_op(), - s => resolve_single_op_from_store(op_store, op_heads_store, s), - }?; - for _ in op_postfix.chars() { - let mut parent_ops = operation.parents(); - let Some(op) = parent_ops.next().transpose()? else { - return Err(OpsetResolutionError::EmptyOperations(op_str.to_owned()).into()); - }; - if parent_ops.next().is_some() { - return Err(OpsetResolutionError::MultipleOperations(op_str.to_owned()).into()); - } - drop(parent_ops); - operation = op; - } - Ok(operation) -} - /// Resolves revsets into revisions for use; useful for rebases or operations /// that take multiple parents. pub fn resolve_all_revs( @@ -2075,61 +2036,6 @@ pub fn resolve_all_revs( } } -fn find_all_operations( - op_store: &Arc, - op_heads_store: &Arc, -) -> Result, OpStoreError> { - let mut visited = HashSet::new(); - let mut work: VecDeque<_> = op_heads_store.get_op_heads().into_iter().collect(); - let mut operations = vec![]; - while let Some(op_id) = work.pop_front() { - if visited.insert(op_id.clone()) { - let store_operation = op_store.read_operation(&op_id)?; - work.extend(store_operation.parents.iter().cloned()); - let operation = Operation::new(op_store.clone(), op_id, store_operation); - operations.push(operation); - } - } - Ok(operations) -} - -fn resolve_single_op_from_store( - op_store: &Arc, - op_heads_store: &Arc, - op_str: &str, -) -> Result { - if op_str.is_empty() || !op_str.as_bytes().iter().all(|b| b.is_ascii_hexdigit()) { - return Err(OpsetResolutionError::InvalidIdPrefix(op_str.to_owned()).into()); - } - if let Ok(binary_op_id) = hex::decode(op_str) { - let op_id = OperationId::new(binary_op_id); - match op_store.read_operation(&op_id) { - Ok(operation) => { - return Ok(Operation::new(op_store.clone(), op_id, operation)); - } - Err(OpStoreError::ObjectNotFound { .. }) => { - // Fall through - } - Err(err) => { - return Err(OpsetEvaluationError::OpStore(err)); - } - } - } - let mut matches = vec![]; - for op in find_all_operations(op_store, op_heads_store)? { - if op.id().hex().starts_with(op_str) { - matches.push(op); - } - } - if matches.is_empty() { - Err(OpsetResolutionError::NoSuchOperation(op_str.to_owned()).into()) - } else if matches.len() == 1 { - Ok(matches.pop().unwrap()) - } else { - Err(OpsetResolutionError::AmbiguousIdPrefix(op_str.to_owned()).into()) - } -} - fn load_revset_aliases( ui: &Ui, layered_configs: &LayeredConfigs, diff --git a/cli/src/commands/debug.rs b/cli/src/commands/debug.rs index b86ed84eb..c2fa02b7f 100644 --- a/cli/src/commands/debug.rs +++ b/cli/src/commands/debug.rs @@ -21,10 +21,10 @@ use jj_lib::backend::ObjectId; use jj_lib::default_index::{AsCompositeIndex as _, DefaultIndexStore, DefaultReadonlyIndex}; use jj_lib::local_working_copy::LocalWorkingCopy; use jj_lib::repo::Repo; -use jj_lib::revset; use jj_lib::working_copy::WorkingCopy; +use jj_lib::{op_walk, revset}; -use crate::cli_util::{resolve_op_for_load, user_error, CommandError, CommandHelper, RevisionArg}; +use crate::cli_util::{user_error, CommandError, CommandHelper, RevisionArg}; use crate::template_parser; use crate::ui::Ui; @@ -267,7 +267,7 @@ fn cmd_debug_operation( // even if e.g. the view object is broken. let workspace = command.load_workspace()?; let repo_loader = workspace.repo_loader(); - let op = resolve_op_for_load( + let op = op_walk::resolve_op_for_load( repo_loader.op_store(), repo_loader.op_heads_store(), &args.operation, diff --git a/lib/src/op_walk.rs b/lib/src/op_walk.rs index 079165119..09a393ab6 100644 --- a/lib/src/op_walk.rs +++ b/lib/src/op_walk.rs @@ -15,14 +15,17 @@ //! Utility for operation id resolution and traversal. use std::cmp::Ordering; +use std::collections::{HashSet, VecDeque}; +use std::sync::Arc; use itertools::Itertools as _; use thiserror::Error; -use crate::dag_walk; -use crate::op_heads_store::OpHeadResolutionError; -use crate::op_store::{OpStoreError, OpStoreResult}; +use crate::backend::ObjectId as _; +use crate::op_heads_store::{OpHeadResolutionError, OpHeadsStore}; +use crate::op_store::{OpStore, OpStoreError, OpStoreResult, OperationId}; use crate::operation::Operation; +use crate::{dag_walk, op_heads_store}; /// Error that may occur during evaluation of operation set expression. #[derive(Debug, Error)] @@ -61,6 +64,103 @@ pub enum OpsetResolutionError { AmbiguousIdPrefix(String), } +/// Resolves operation set expression without loading a repo. +pub fn resolve_op_for_load( + op_store: &Arc, + op_heads_store: &Arc, + op_str: &str, +) -> Result { + let get_current_op = || { + op_heads_store::resolve_op_heads(op_heads_store.as_ref(), op_store, |_| { + Err(OpsetResolutionError::MultipleOperations("@".to_owned()).into()) + }) + }; + resolve_single_op(op_store, op_heads_store, get_current_op, op_str) +} + +/// Resolves operation set expression with the given "@" symbol resolution +/// callback. +pub fn resolve_single_op( + op_store: &Arc, + op_heads_store: &Arc, + get_current_op: impl FnOnce() -> Result, + op_str: &str, +) -> Result { + let op_symbol = op_str.trim_end_matches('-'); + let op_postfix = &op_str[op_symbol.len()..]; + let mut operation = match op_symbol { + "@" => get_current_op(), + s => resolve_single_op_from_store(op_store, op_heads_store, s), + }?; + for _ in op_postfix.chars() { + let mut parent_ops = operation.parents(); + let Some(op) = parent_ops.next().transpose()? else { + return Err(OpsetResolutionError::EmptyOperations(op_str.to_owned()).into()); + }; + if parent_ops.next().is_some() { + return Err(OpsetResolutionError::MultipleOperations(op_str.to_owned()).into()); + } + drop(parent_ops); + operation = op; + } + Ok(operation) +} + +fn resolve_single_op_from_store( + op_store: &Arc, + op_heads_store: &Arc, + op_str: &str, +) -> Result { + if op_str.is_empty() || !op_str.as_bytes().iter().all(|b| b.is_ascii_hexdigit()) { + return Err(OpsetResolutionError::InvalidIdPrefix(op_str.to_owned()).into()); + } + if let Ok(binary_op_id) = hex::decode(op_str) { + let op_id = OperationId::new(binary_op_id); + match op_store.read_operation(&op_id) { + Ok(operation) => { + return Ok(Operation::new(op_store.clone(), op_id, operation)); + } + Err(OpStoreError::ObjectNotFound { .. }) => { + // Fall through + } + Err(err) => { + return Err(OpsetEvaluationError::OpStore(err)); + } + } + } + let mut matches = vec![]; + for op in find_all_operations(op_store, op_heads_store)? { + if op.id().hex().starts_with(op_str) { + matches.push(op); + } + } + if matches.is_empty() { + Err(OpsetResolutionError::NoSuchOperation(op_str.to_owned()).into()) + } else if matches.len() == 1 { + Ok(matches.pop().unwrap()) + } else { + Err(OpsetResolutionError::AmbiguousIdPrefix(op_str.to_owned()).into()) + } +} + +fn find_all_operations( + op_store: &Arc, + op_heads_store: &Arc, +) -> Result, OpStoreError> { + let mut visited = HashSet::new(); + let mut work: VecDeque<_> = op_heads_store.get_op_heads().into_iter().collect(); + let mut operations = vec![]; + while let Some(op_id) = work.pop_front() { + if visited.insert(op_id.clone()) { + let store_operation = op_store.read_operation(&op_id)?; + work.extend(store_operation.parents.iter().cloned()); + let operation = Operation::new(op_store.clone(), op_id, store_operation); + operations.push(operation); + } + } + Ok(operations) +} + #[derive(Clone, Debug, Eq, Hash, PartialEq)] struct OperationByEndTime(Operation);