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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::env::{self, ArgsOs, VarError};
|
use std::env::{self, ArgsOs, VarError};
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::fmt::Debug;
|
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::id_prefix::IdPrefixContext;
|
||||||
use jj_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher};
|
use jj_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher};
|
||||||
use jj_lib::merged_tree::MergedTree;
|
use jj_lib::merged_tree::MergedTree;
|
||||||
use jj_lib::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore};
|
use jj_lib::op_heads_store::{self, OpHeadResolutionError};
|
||||||
use jj_lib::op_store::{OpStore, OpStoreError, OperationId, WorkspaceId};
|
use jj_lib::op_store::{OpStoreError, OperationId, WorkspaceId};
|
||||||
use jj_lib::op_walk::{OpsetEvaluationError, OpsetResolutionError};
|
use jj_lib::op_walk::OpsetEvaluationError;
|
||||||
use jj_lib::operation::Operation;
|
use jj_lib::operation::Operation;
|
||||||
use jj_lib::repo::{
|
use jj_lib::repo::{
|
||||||
CheckOutCommitError, EditCommitError, MutableRepo, ReadonlyRepo, Repo, RepoLoader,
|
CheckOutCommitError, EditCommitError, MutableRepo, ReadonlyRepo, Repo, RepoLoader,
|
||||||
|
@ -70,7 +70,7 @@ use jj_lib::workspace::{
|
||||||
default_working_copy_factories, LockedWorkspace, WorkingCopyFactory, Workspace,
|
default_working_copy_factories, LockedWorkspace, WorkingCopyFactory, Workspace,
|
||||||
WorkspaceInitError, WorkspaceLoadError, WorkspaceLoader,
|
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 once_cell::unsync::OnceCell;
|
||||||
use toml_edit;
|
use toml_edit;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
@ -659,7 +659,7 @@ impl CommandHelper {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let operation = resolve_op_for_load(
|
let operation = op_walk::resolve_op_for_load(
|
||||||
repo_loader.op_store(),
|
repo_loader.op_store(),
|
||||||
repo_loader.op_heads_store(),
|
repo_loader.op_heads_store(),
|
||||||
&self.global_args.at_operation,
|
&self.global_args.at_operation,
|
||||||
|
@ -977,7 +977,7 @@ impl WorkspaceCommandHelper {
|
||||||
pub fn resolve_single_op(&self, op_str: &str) -> Result<Operation, OpsetEvaluationError> {
|
pub fn resolve_single_op(&self, op_str: &str) -> Result<Operation, OpsetEvaluationError> {
|
||||||
// When resolving the "@" operation in a `ReadonlyRepo`, we resolve it to the
|
// When resolving the "@" operation in a `ReadonlyRepo`, we resolve it to the
|
||||||
// operation the repo was loaded at.
|
// operation the repo was loaded at.
|
||||||
resolve_single_op(
|
op_walk::resolve_single_op(
|
||||||
self.repo().op_store(),
|
self.repo().op_store(),
|
||||||
self.repo().op_heads_store(),
|
self.repo().op_heads_store(),
|
||||||
|| Ok(self.repo().operation().clone()),
|
|| 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
|
/// Resolves revsets into revisions for use; useful for rebases or operations
|
||||||
/// that take multiple parents.
|
/// that take multiple parents.
|
||||||
pub fn resolve_all_revs(
|
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(
|
fn load_revset_aliases(
|
||||||
ui: &Ui,
|
ui: &Ui,
|
||||||
layered_configs: &LayeredConfigs,
|
layered_configs: &LayeredConfigs,
|
||||||
|
|
|
@ -21,10 +21,10 @@ use jj_lib::backend::ObjectId;
|
||||||
use jj_lib::default_index::{AsCompositeIndex as _, DefaultIndexStore, DefaultReadonlyIndex};
|
use jj_lib::default_index::{AsCompositeIndex as _, DefaultIndexStore, DefaultReadonlyIndex};
|
||||||
use jj_lib::local_working_copy::LocalWorkingCopy;
|
use jj_lib::local_working_copy::LocalWorkingCopy;
|
||||||
use jj_lib::repo::Repo;
|
use jj_lib::repo::Repo;
|
||||||
use jj_lib::revset;
|
|
||||||
use jj_lib::working_copy::WorkingCopy;
|
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::template_parser;
|
||||||
use crate::ui::Ui;
|
use crate::ui::Ui;
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ fn cmd_debug_operation(
|
||||||
// even if e.g. the view object is broken.
|
// even if e.g. the view object is broken.
|
||||||
let workspace = command.load_workspace()?;
|
let workspace = command.load_workspace()?;
|
||||||
let repo_loader = workspace.repo_loader();
|
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_store(),
|
||||||
repo_loader.op_heads_store(),
|
repo_loader.op_heads_store(),
|
||||||
&args.operation,
|
&args.operation,
|
||||||
|
|
|
@ -15,14 +15,17 @@
|
||||||
//! Utility for operation id resolution and traversal.
|
//! Utility for operation id resolution and traversal.
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use itertools::Itertools as _;
|
use itertools::Itertools as _;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::dag_walk;
|
use crate::backend::ObjectId as _;
|
||||||
use crate::op_heads_store::OpHeadResolutionError;
|
use crate::op_heads_store::{OpHeadResolutionError, OpHeadsStore};
|
||||||
use crate::op_store::{OpStoreError, OpStoreResult};
|
use crate::op_store::{OpStore, OpStoreError, OpStoreResult, OperationId};
|
||||||
use crate::operation::Operation;
|
use crate::operation::Operation;
|
||||||
|
use crate::{dag_walk, op_heads_store};
|
||||||
|
|
||||||
/// Error that may occur during evaluation of operation set expression.
|
/// Error that may occur during evaluation of operation set expression.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
@ -61,6 +64,103 @@ pub enum OpsetResolutionError {
|
||||||
AmbiguousIdPrefix(String),
|
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)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
struct OperationByEndTime(Operation);
|
struct OperationByEndTime(Operation);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue