forked from mirrors/jj
op_walk: move "opset" query functions from jj_cli
This commit is contained in:
parent
e4460d5386
commit
26b5f38f45
3 changed files with 113 additions and 107 deletions
|
@ -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<Operation, OpsetEvaluationError> {
|
||||
// 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<StringPattern, StringPatternPar
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_op_for_load(
|
||||
op_store: &Arc<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
op_str: &str,
|
||||
) -> Result<Operation, OpsetEvaluationError> {
|
||||
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<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
get_current_op: impl FnOnce() -> Result<Operation, OpsetEvaluationError>,
|
||||
op_str: &str,
|
||||
) -> Result<Operation, OpsetEvaluationError> {
|
||||
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<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
) -> Result<Vec<Operation>, 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<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
op_str: &str,
|
||||
) -> Result<Operation, OpsetEvaluationError> {
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
op_str: &str,
|
||||
) -> Result<Operation, OpsetEvaluationError> {
|
||||
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<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
get_current_op: impl FnOnce() -> Result<Operation, OpsetEvaluationError>,
|
||||
op_str: &str,
|
||||
) -> Result<Operation, OpsetEvaluationError> {
|
||||
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<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
op_str: &str,
|
||||
) -> Result<Operation, OpsetEvaluationError> {
|
||||
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<dyn OpStore>,
|
||||
op_heads_store: &Arc<dyn OpHeadsStore>,
|
||||
) -> Result<Vec<Operation>, 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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue