// Copyright 2021 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. #![allow(missing_docs)] use std::collections::{HashMap, HashSet};use std::convert::Infallible;use std::ops::Range;use std::path::Path;use std::rc::Rc;use std::str::FromStr;use std::sync::Arc;use std::{error, fmt, iter}; use itertools::Itertools;use once_cell::sync::Lazy;use pest::iterators::{Pair, Pairs};use pest::pratt_parser::{Assoc, Op, PrattParser};use pest::Parser;use pest_derive::Parser;use thiserror::Error; use crate::backend::{BackendError, BackendResult, ChangeId, CommitId};use crate::commit::Commit;use crate::git;use crate::hex_util::to_forward_hex;use crate::object_id::{HexPrefix, PrefixResolution};use crate::op_store::WorkspaceId;use crate::repo::Repo;use crate::repo_path::{FsPathParseError, RepoPathBuf};use crate::revset_graph::RevsetGraphEdge;use crate::store::Store;use crate::str_util::StringPattern; /// Error occurred during symbol resolution.#[derive(Debug, Error)]pub enum RevsetResolutionError { #[error("Revision \"{name}\" doesn't exist")] NoSuchRevision { name: String, candidates: Vec<String>, }, #[error("Workspace \"{name}\" doesn't have a working copy")] WorkspaceMissingWorkingCopy { name: String }, #[error("An empty string is not a valid revision")] EmptyString, #[error("Commit ID prefix \"{0}\" is ambiguous")] AmbiguousCommitIdPrefix(String), #[error("Change ID prefix \"{0}\" is ambiguous")] AmbiguousChangeIdPrefix(String), #[error("Unexpected error from store")] StoreError(#[source] BackendError),} /// Error occurred during revset evaluation.#[derive(Debug, Error)]pub enum RevsetEvaluationError { #[error("Unexpected error from store")] StoreError(#[source] BackendError), #[error("{0}")] Other(String),} #[derive(Parser)]#[grammar = "revset.pest"]pub struct RevsetParser; impl Rule { /// Whether this is a placeholder rule for compatibility with the other /// systems. fn is_compat(&self) -> bool { matches!( self, Rule::compat_parents_op | Rule::compat_dag_range_op | Rule::compat_dag_range_pre_op | Rule::compat_dag_range_post_op | Rule::compat_add_op | Rule::compat_sub_op ) } fn to_symbol(self) -> Option<&'static str> { match self { Rule::EOI => None, Rule::identifier_part => None, Rule::identifier => None, Rule::symbol => None, Rule::literal_string => None, Rule::whitespace => None, Rule::at_op => Some("@"), Rule::pattern_kind_op => Some(":"), Rule::parents_op => Some("-"), Rule::children_op => Some("+"), Rule::compat_parents_op => Some("^"), Rule::dag_range_op | Rule::dag_range_pre_op | Rule::dag_range_post_op | Rule::dag_range_all_op => Some("::"), Rule::compat_dag_range_op | Rule::compat_dag_range_pre_op | Rule::compat_dag_range_post_op => Some(":"), Rule::range_op => Some(".."), Rule::range_pre_op | Rule::range_post_op | Rule::range_all_op => Some(".."), Rule::range_ops => None, Rule::range_pre_ops => None, Rule::range_post_ops => None, Rule::range_all_ops => None, Rule::negate_op => Some("~"), Rule::union_op => Some("|"), Rule::intersection_op => Some("&"), Rule::difference_op => Some("~"), Rule::compat_add_op => Some("+"), Rule::compat_sub_op => Some("-"), Rule::infix_op => None, Rule::function_name => None, Rule::keyword_argument => None, Rule::argument => None, Rule::function_arguments => None, Rule::formal_parameters => None, Rule::string_pattern => None, Rule::primary => None, Rule::neighbors_expression => None, Rule::range_expression => None, Rule::expression => None, Rule::program => None, Rule::alias_declaration_part => None, Rule::alias_declaration => None, } }} #[derive(Debug)]pub struct RevsetParseError { kind: RevsetParseErrorKind, pest_error: Option<Box<pest::error::Error<Rule>>>, origin: Option<Box<RevsetParseError>>,} #[derive(Debug, Error, PartialEq, Eq)]pub enum RevsetParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("'{op}' is not a prefix operator")] NotPrefixOperator { op: String, similar_op: String, description: String, }, #[error("'{op}' is not a postfix operator")] NotPostfixOperator { op: String, similar_op: String, description: String, }, #[error("'{op}' is not an infix operator")] NotInfixOperator { op: String, similar_op: String, description: String, }, #[error(r#"Revset function "{name}" doesn't exist"#)] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error("Invalid arguments to revset function \"{name}\": {message}")] InvalidFunctionArguments { name: String, message: String }, #[error("Invalid file pattern")] FsPathParseError(#[source] FsPathParseError), #[error("Cannot resolve file pattern without workspace")] FsPathWithoutWorkspace, #[error(r#"Cannot resolve "@" without workspace"#)] WorkingCopyWithoutWorkspace, #[error("Redefinition of function parameter")] RedefinedFunctionParameter, #[error(r#"Alias "{0}" cannot be expanded"#)] BadAliasExpansion(String), #[error(r#"Alias "{0}" expanded recursively"#)] RecursiveAlias(String),} impl RevsetParseError { fn new(kind: RevsetParseErrorKind) -> Self { RevsetParseError { kind, pest_error: None, origin: None, } } fn with_span(kind: RevsetParseErrorKind, span: pest::Span<'_>) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let err = pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, ); RevsetParseError { kind, pest_error: Some(Box::new(err)), origin: None, } } fn with_span_and_origin( kind: RevsetParseErrorKind, span: pest::Span<'_>, origin: Self, ) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let err = pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, ); RevsetParseError { kind, pest_error: Some(Box::new(err)), origin: Some(Box::new(origin)), } } pub fn kind(&self) -> &RevsetParseErrorKind { &self.kind } /// Original parsing error which typically occurred in an alias expression. pub fn origin(&self) -> Option<&Self> { self.origin.as_deref() }} impl From<pest::error::Error<Rule>> for RevsetParseError { fn from(err: pest::error::Error<Rule>) -> Self { RevsetParseError { kind: RevsetParseErrorKind::SyntaxError, pest_error: Some(Box::new(rename_rules_in_pest_error(err))), origin: None, } }} impl fmt::Display for RevsetParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(err) = &self.pest_error { err.fmt(f) } else { self.kind.fmt(f) } }} impl error::Error for RevsetParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { if let Some(e) = self.origin() { return Some(e as &dyn error::Error); } match &self.kind { // SyntaxError is a wrapper for pest::error::Error. RevsetParseErrorKind::SyntaxError => { self.pest_error.as_ref().map(|e| e as &dyn error::Error) } // Otherwise the kind represents this error. e => e.source(), } }} fn rename_rules_in_pest_error(mut err: pest::error::Error<Rule>) -> pest::error::Error<Rule> { let pest::error::ErrorVariant::ParsingError { positives, negatives, } = &mut err.variant else { return err; }; // Remove duplicated symbols. Compat symbols are also removed from the // (positive) suggestion. let mut known_syms = HashSet::new(); positives.retain(|rule| { !rule.is_compat() && rule.to_symbol().map_or(true, |sym| known_syms.insert(sym)) }); let mut known_syms = HashSet::new(); negatives.retain(|rule| rule.to_symbol().map_or(true, |sym| known_syms.insert(sym))); err.renamed_rules(|rule| { rule.to_symbol() .map(|sym| format!("`{sym}`")) .unwrap_or_else(|| format!("<{rule:?}>")) })} // assumes index has less than u64::MAX entries.pub const GENERATION_RANGE_FULL: Range<u64> = 0..u64::MAX;pub const GENERATION_RANGE_EMPTY: Range<u64> = 0..0; /// Symbol or function to be resolved to `CommitId`s.#[derive(Clone, Debug, Eq, PartialEq)]pub enum RevsetCommitRef { WorkingCopy(WorkspaceId), Symbol(String), RemoteSymbol { name: String, remote: String, }, VisibleHeads, Root, Branches(StringPattern), RemoteBranches { branch_pattern: StringPattern, remote_pattern: StringPattern, }, Tags, GitRefs, GitHead,} #[derive(Clone, Debug, Eq, PartialEq)]pub enum RevsetFilterPredicate { /// Commits with number of parents in the range. ParentCount(Range<u32>), /// Commits with description containing the needle. Description(StringPattern), /// Commits with author's name or email containing the needle. Author(StringPattern), /// Commits with committer's name or email containing the needle. Committer(StringPattern), /// Commits modifying the paths specified by the pattern. File(Option<Vec<RepoPathBuf>>), // TODO: embed matcher expression? /// Commits with conflicts HasConflict,} #[derive(Debug, PartialEq, Eq, Clone)]pub enum RevsetExpression { None, All, Commits(Vec<CommitId>), CommitRef(RevsetCommitRef), // TODO: This shouldn't be of RevsetExpression type. Maybe better to // introduce an intermediate AST tree where aliases will be substituted. StringPattern { kind: String, value: String, }, Ancestors { heads: Rc<RevsetExpression>, generation: Range<u64>, }, Descendants { roots: Rc<RevsetExpression>, generation: Range<u64>, }, // Commits that are ancestors of "heads" but not ancestors of "roots" Range { roots: Rc<RevsetExpression>, heads: Rc<RevsetExpression>, generation: Range<u64>, }, // Commits that are descendants of "roots" and ancestors of "heads" DagRange { roots: Rc<RevsetExpression>, heads: Rc<RevsetExpression>, // TODO: maybe add generation_from_roots/heads? }, Heads(Rc<RevsetExpression>), Roots(Rc<RevsetExpression>), Latest { candidates: Rc<RevsetExpression>, count: usize, }, Filter(RevsetFilterPredicate), /// Marker for subtree that should be intersected as filter. AsFilter(Rc<RevsetExpression>), Present(Rc<RevsetExpression>), NotIn(Rc<RevsetExpression>), Union(Rc<RevsetExpression>, Rc<RevsetExpression>), Intersection(Rc<RevsetExpression>, Rc<RevsetExpression>), Difference(Rc<RevsetExpression>, Rc<RevsetExpression>),} impl RevsetExpression { pub fn none() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::None) } pub fn all() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::All) } pub fn working_copy(workspace_id: WorkspaceId) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::WorkingCopy( workspace_id, ))) } pub fn symbol(value: String) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Symbol(value))) } pub fn remote_symbol(name: String, remote: String) -> Rc<RevsetExpression> { let commit_ref = RevsetCommitRef::RemoteSymbol { name, remote }; Rc::new(RevsetExpression::CommitRef(commit_ref)) } pub fn commit(commit_id: CommitId) -> Rc<RevsetExpression> { RevsetExpression::commits(vec![commit_id]) } pub fn commits(commit_ids: Vec<CommitId>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Commits(commit_ids)) } pub fn visible_heads() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::VisibleHeads)) } pub fn root() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Root)) } pub fn branches(pattern: StringPattern) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Branches( pattern, ))) } pub fn remote_branches( branch_pattern: StringPattern, remote_pattern: StringPattern, ) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef( RevsetCommitRef::RemoteBranches { branch_pattern, remote_pattern, }, )) } pub fn tags() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Tags)) } pub fn git_refs() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::GitRefs)) } pub fn git_head() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::GitHead)) } pub fn latest(self: &Rc<RevsetExpression>, count: usize) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Latest { candidates: self.clone(), count, }) } pub fn filter(predicate: RevsetFilterPredicate) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Filter(predicate)) } /// Commits in `self` that don't have descendants in `self`. pub fn heads(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Heads(self.clone())) } /// Commits in `self` that don't have ancestors in `self`. pub fn roots(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Roots(self.clone())) } /// Parents of `self`. pub fn parents(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Ancestors { heads: self.clone(), generation: 1..2, }) } /// Ancestors of `self`, including `self`. pub fn ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { self.ancestors_range(GENERATION_RANGE_FULL) } /// Ancestors of `self`, including `self` until `generation` back. pub fn ancestors_at(self: &Rc<RevsetExpression>, generation: u64) -> Rc<RevsetExpression> { self.ancestors_range(generation..(generation + 1)) } Lines 1–500 of 4711. Load more
// Copyright 2021 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![allow(missing_docs)] use std::collections::{HashMap, HashSet}; use std::convert::Infallible; use std::ops::Range; use std::path::Path; use std::rc::Rc; use std::str::FromStr; use std::sync::Arc; use std::{error, fmt, iter}; use itertools::Itertools; use once_cell::sync::Lazy; use pest::iterators::{Pair, Pairs}; use pest::pratt_parser::{Assoc, Op, PrattParser}; use pest::Parser; use pest_derive::Parser; use thiserror::Error; use crate::backend::{BackendError, BackendResult, ChangeId, CommitId}; use crate::commit::Commit; use crate::git; use crate::hex_util::to_forward_hex; use crate::object_id::{HexPrefix, PrefixResolution}; use crate::op_store::WorkspaceId; use crate::repo::Repo; use crate::repo_path::{FsPathParseError, RepoPathBuf}; use crate::revset_graph::RevsetGraphEdge; use crate::store::Store; use crate::str_util::StringPattern; /// Error occurred during symbol resolution. #[derive(Debug, Error)] pub enum RevsetResolutionError { #[error("Revision \"{name}\" doesn't exist")] NoSuchRevision { name: String, candidates: Vec<String>, }, #[error("Workspace \"{name}\" doesn't have a working copy")] WorkspaceMissingWorkingCopy { name: String }, #[error("An empty string is not a valid revision")] EmptyString, #[error("Commit ID prefix \"{0}\" is ambiguous")] AmbiguousCommitIdPrefix(String), #[error("Change ID prefix \"{0}\" is ambiguous")] AmbiguousChangeIdPrefix(String), #[error("Unexpected error from store")] StoreError(#[source] BackendError), } /// Error occurred during revset evaluation. #[derive(Debug, Error)] pub enum RevsetEvaluationError { #[error("Unexpected error from store")] StoreError(#[source] BackendError), #[error("{0}")] Other(String), } #[derive(Parser)] #[grammar = "revset.pest"] pub struct RevsetParser; impl Rule { /// Whether this is a placeholder rule for compatibility with the other /// systems. fn is_compat(&self) -> bool { matches!( self, Rule::compat_parents_op | Rule::compat_dag_range_op | Rule::compat_dag_range_pre_op | Rule::compat_dag_range_post_op | Rule::compat_add_op | Rule::compat_sub_op ) } fn to_symbol(self) -> Option<&'static str> { match self { Rule::EOI => None, Rule::identifier_part => None, Rule::identifier => None, Rule::symbol => None, Rule::literal_string => None, Rule::whitespace => None, Rule::at_op => Some("@"), Rule::pattern_kind_op => Some(":"), Rule::parents_op => Some("-"), Rule::children_op => Some("+"), Rule::compat_parents_op => Some("^"), Rule::dag_range_op | Rule::dag_range_pre_op | Rule::dag_range_post_op | Rule::dag_range_all_op => Some("::"), Rule::compat_dag_range_op | Rule::compat_dag_range_pre_op | Rule::compat_dag_range_post_op => Some(":"), Rule::range_op => Some(".."), Rule::range_pre_op | Rule::range_post_op | Rule::range_all_op => Some(".."), Rule::range_ops => None, Rule::range_pre_ops => None, Rule::range_post_ops => None, Rule::range_all_ops => None, Rule::negate_op => Some("~"), Rule::union_op => Some("|"), Rule::intersection_op => Some("&"), Rule::difference_op => Some("~"), Rule::compat_add_op => Some("+"), Rule::compat_sub_op => Some("-"), Rule::infix_op => None, Rule::function_name => None, Rule::keyword_argument => None, Rule::argument => None, Rule::function_arguments => None, Rule::formal_parameters => None, Rule::string_pattern => None, Rule::primary => None, Rule::neighbors_expression => None, Rule::range_expression => None, Rule::expression => None, Rule::program => None, Rule::alias_declaration_part => None, Rule::alias_declaration => None, } } } #[derive(Debug)] pub struct RevsetParseError { kind: RevsetParseErrorKind, pest_error: Option<Box<pest::error::Error<Rule>>>, origin: Option<Box<RevsetParseError>>, } #[derive(Debug, Error, PartialEq, Eq)] pub enum RevsetParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("'{op}' is not a prefix operator")] NotPrefixOperator { op: String, similar_op: String, description: String, }, #[error("'{op}' is not a postfix operator")] NotPostfixOperator { op: String, similar_op: String, description: String, }, #[error("'{op}' is not an infix operator")] NotInfixOperator { op: String, similar_op: String, description: String, }, #[error(r#"Revset function "{name}" doesn't exist"#)] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error("Invalid arguments to revset function \"{name}\": {message}")] InvalidFunctionArguments { name: String, message: String }, #[error("Invalid file pattern")] FsPathParseError(#[source] FsPathParseError), #[error("Cannot resolve file pattern without workspace")] FsPathWithoutWorkspace, #[error(r#"Cannot resolve "@" without workspace"#)] WorkingCopyWithoutWorkspace, #[error("Redefinition of function parameter")] RedefinedFunctionParameter, #[error(r#"Alias "{0}" cannot be expanded"#)] BadAliasExpansion(String), #[error(r#"Alias "{0}" expanded recursively"#)] RecursiveAlias(String), } impl RevsetParseError { fn new(kind: RevsetParseErrorKind) -> Self { RevsetParseError { kind, pest_error: None, origin: None, } } fn with_span(kind: RevsetParseErrorKind, span: pest::Span<'_>) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let err = pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, ); RevsetParseError { kind, pest_error: Some(Box::new(err)), origin: None, } } fn with_span_and_origin( kind: RevsetParseErrorKind, span: pest::Span<'_>, origin: Self, ) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let err = pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, ); RevsetParseError { kind, pest_error: Some(Box::new(err)), origin: Some(Box::new(origin)), } } pub fn kind(&self) -> &RevsetParseErrorKind { &self.kind } /// Original parsing error which typically occurred in an alias expression. pub fn origin(&self) -> Option<&Self> { self.origin.as_deref() } } impl From<pest::error::Error<Rule>> for RevsetParseError { fn from(err: pest::error::Error<Rule>) -> Self { RevsetParseError { kind: RevsetParseErrorKind::SyntaxError, pest_error: Some(Box::new(rename_rules_in_pest_error(err))), origin: None, } } } impl fmt::Display for RevsetParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(err) = &self.pest_error { err.fmt(f) } else { self.kind.fmt(f) } } } impl error::Error for RevsetParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { if let Some(e) = self.origin() { return Some(e as &dyn error::Error); } match &self.kind { // SyntaxError is a wrapper for pest::error::Error. RevsetParseErrorKind::SyntaxError => { self.pest_error.as_ref().map(|e| e as &dyn error::Error) } // Otherwise the kind represents this error. e => e.source(), } } } fn rename_rules_in_pest_error(mut err: pest::error::Error<Rule>) -> pest::error::Error<Rule> { let pest::error::ErrorVariant::ParsingError { positives, negatives, } = &mut err.variant else { return err; }; // Remove duplicated symbols. Compat symbols are also removed from the // (positive) suggestion. let mut known_syms = HashSet::new(); positives.retain(|rule| { !rule.is_compat() && rule.to_symbol().map_or(true, |sym| known_syms.insert(sym)) }); let mut known_syms = HashSet::new(); negatives.retain(|rule| rule.to_symbol().map_or(true, |sym| known_syms.insert(sym))); err.renamed_rules(|rule| { rule.to_symbol() .map(|sym| format!("`{sym}`")) .unwrap_or_else(|| format!("<{rule:?}>")) }) } // assumes index has less than u64::MAX entries. pub const GENERATION_RANGE_FULL: Range<u64> = 0..u64::MAX; pub const GENERATION_RANGE_EMPTY: Range<u64> = 0..0; /// Symbol or function to be resolved to `CommitId`s. #[derive(Clone, Debug, Eq, PartialEq)] pub enum RevsetCommitRef { WorkingCopy(WorkspaceId), Symbol(String), RemoteSymbol { name: String, remote: String, }, VisibleHeads, Root, Branches(StringPattern), RemoteBranches { branch_pattern: StringPattern, remote_pattern: StringPattern, }, Tags, GitRefs, GitHead, } #[derive(Clone, Debug, Eq, PartialEq)] pub enum RevsetFilterPredicate { /// Commits with number of parents in the range. ParentCount(Range<u32>), /// Commits with description containing the needle. Description(StringPattern), /// Commits with author's name or email containing the needle. Author(StringPattern), /// Commits with committer's name or email containing the needle. Committer(StringPattern), /// Commits modifying the paths specified by the pattern. File(Option<Vec<RepoPathBuf>>), // TODO: embed matcher expression? /// Commits with conflicts HasConflict, } #[derive(Debug, PartialEq, Eq, Clone)] pub enum RevsetExpression { None, All, Commits(Vec<CommitId>), CommitRef(RevsetCommitRef), // TODO: This shouldn't be of RevsetExpression type. Maybe better to // introduce an intermediate AST tree where aliases will be substituted. StringPattern { kind: String, value: String, }, Ancestors { heads: Rc<RevsetExpression>, generation: Range<u64>, }, Descendants { roots: Rc<RevsetExpression>, generation: Range<u64>, }, // Commits that are ancestors of "heads" but not ancestors of "roots" Range { roots: Rc<RevsetExpression>, heads: Rc<RevsetExpression>, generation: Range<u64>, }, // Commits that are descendants of "roots" and ancestors of "heads" DagRange { roots: Rc<RevsetExpression>, heads: Rc<RevsetExpression>, // TODO: maybe add generation_from_roots/heads? }, Heads(Rc<RevsetExpression>), Roots(Rc<RevsetExpression>), Latest { candidates: Rc<RevsetExpression>, count: usize, }, Filter(RevsetFilterPredicate), /// Marker for subtree that should be intersected as filter. AsFilter(Rc<RevsetExpression>), Present(Rc<RevsetExpression>), NotIn(Rc<RevsetExpression>), Union(Rc<RevsetExpression>, Rc<RevsetExpression>), Intersection(Rc<RevsetExpression>, Rc<RevsetExpression>), Difference(Rc<RevsetExpression>, Rc<RevsetExpression>), } impl RevsetExpression { pub fn none() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::None) } pub fn all() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::All) } pub fn working_copy(workspace_id: WorkspaceId) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::WorkingCopy( workspace_id, ))) } pub fn symbol(value: String) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Symbol(value))) } pub fn remote_symbol(name: String, remote: String) -> Rc<RevsetExpression> { let commit_ref = RevsetCommitRef::RemoteSymbol { name, remote }; Rc::new(RevsetExpression::CommitRef(commit_ref)) } pub fn commit(commit_id: CommitId) -> Rc<RevsetExpression> { RevsetExpression::commits(vec![commit_id]) } pub fn commits(commit_ids: Vec<CommitId>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Commits(commit_ids)) } pub fn visible_heads() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::VisibleHeads)) } pub fn root() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Root)) } pub fn branches(pattern: StringPattern) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Branches( pattern, ))) } pub fn remote_branches( branch_pattern: StringPattern, remote_pattern: StringPattern, ) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef( RevsetCommitRef::RemoteBranches { branch_pattern, remote_pattern, }, )) } pub fn tags() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Tags)) } pub fn git_refs() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::GitRefs)) } pub fn git_head() -> Rc<RevsetExpression> { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::GitHead)) } pub fn latest(self: &Rc<RevsetExpression>, count: usize) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Latest { candidates: self.clone(), count, }) } pub fn filter(predicate: RevsetFilterPredicate) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Filter(predicate)) } /// Commits in `self` that don't have descendants in `self`. pub fn heads(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Heads(self.clone())) } /// Commits in `self` that don't have ancestors in `self`. pub fn roots(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Roots(self.clone())) } /// Parents of `self`. pub fn parents(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { Rc::new(RevsetExpression::Ancestors { heads: self.clone(), generation: 1..2, }) } /// Ancestors of `self`, including `self`. pub fn ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> { self.ancestors_range(GENERATION_RANGE_FULL) } /// Ancestors of `self`, including `self` until `generation` back. pub fn ancestors_at(self: &Rc<RevsetExpression>, generation: u64) -> Rc<RevsetExpression> { self.ancestors_range(generation..(generation + 1)) }