1#![expect(missing_docs)]
16
17use std::any::Any;
18use std::collections::HashMap;
19use std::collections::HashSet;
20use std::collections::hash_map;
21use std::convert::Infallible;
22use std::fmt;
23use std::ops::ControlFlow;
24use std::ops::Range;
25use std::sync::Arc;
26use std::sync::LazyLock;
27
28use futures::Stream;
29use futures::StreamExt as _;
30use futures::future::LocalBoxFuture;
31use futures::stream::LocalBoxStream;
32use itertools::Itertools as _;
33use once_cell::unsync::OnceCell;
34use pollster::FutureExt as _;
35use thiserror::Error;
36
37use crate::backend::BackendError;
38use crate::backend::ChangeId;
39use crate::backend::CommitId;
40use crate::commit::Commit;
41use crate::dsl_util;
42use crate::dsl_util::collect_similar;
43use crate::fileset;
44use crate::fileset::FilesetAliasesMap;
45use crate::fileset::FilesetDiagnostics;
46use crate::fileset::FilesetExpression;
47use crate::fileset::FilesetParseContext;
48use crate::graph::GraphNode;
49use crate::id_prefix::IdPrefixContext;
50use crate::id_prefix::IdPrefixIndex;
51use crate::index::MutableIndex;
52use crate::index::ResolvedChangeTargets;
53use crate::object_id::HexPrefix;
54use crate::object_id::PrefixResolution;
55use crate::op_store::LocalRemoteRefTarget;
56use crate::op_store::OperationId;
57use crate::op_store::RefTarget;
58use crate::op_store::RemoteRefState;
59use crate::op_walk;
60use crate::ref_name::RefName;
61use crate::ref_name::RemoteName;
62use crate::ref_name::RemoteRefSymbol;
63use crate::ref_name::RemoteRefSymbolBuf;
64use crate::ref_name::WorkspaceName;
65use crate::ref_name::WorkspaceNameBuf;
66use crate::repo::ReadonlyRepo;
67use crate::repo::Repo;
68use crate::repo::RepoLoaderError;
69use crate::revset_parser;
70pub use crate::revset_parser::BinaryOp;
71pub use crate::revset_parser::ExpressionKind;
72pub use crate::revset_parser::ExpressionNode;
73pub use crate::revset_parser::FunctionCallNode;
74pub use crate::revset_parser::RevsetAliasesMap;
75pub use crate::revset_parser::RevsetDiagnostics;
76pub use crate::revset_parser::RevsetParseError;
77pub use crate::revset_parser::RevsetParseErrorKind;
78pub use crate::revset_parser::UnaryOp;
79pub use crate::revset_parser::expect_literal;
80pub use crate::revset_parser::format_remote_symbol;
81pub use crate::revset_parser::format_symbol;
82pub use crate::revset_parser::new_string_node;
83pub use crate::revset_parser::parse_program;
84pub use crate::revset_parser::parse_symbol;
85use crate::store::Store;
86use crate::str_util::StringExpression;
87use crate::str_util::StringPattern;
88use crate::time_util::DatePattern;
89use crate::time_util::DatePatternContext;
90use crate::ui_path::RepoPathUiConverter;
91
92#[derive(Debug, Error)]
94pub enum RevsetResolutionError {
95 #[error("Revision `{name}` doesn't exist")]
96 NoSuchRevision {
97 name: String,
98 candidates: Vec<String>,
99 },
100 #[error("Workspace `{}` doesn't have a working-copy commit", name.as_symbol())]
101 WorkspaceMissingWorkingCopy { name: WorkspaceNameBuf },
102 #[error("An empty string is not a valid revision")]
103 EmptyString,
104 #[error("Commit ID prefix `{0}` is ambiguous")]
105 AmbiguousCommitIdPrefix(String),
106 #[error("Change ID prefix `{0}` is ambiguous")]
107 AmbiguousChangeIdPrefix(String),
108 #[error("Change ID `{symbol}` is divergent")]
109 DivergentChangeId {
110 symbol: String,
111 visible_targets: Vec<(usize, CommitId)>,
112 },
113 #[error("Name `{symbol}` is conflicted")]
114 ConflictedRef {
115 kind: &'static str,
116 symbol: String,
117 targets: Vec<CommitId>,
118 },
119 #[error("Unexpected error from commit backend")]
120 Backend(#[source] BackendError),
121 #[error(transparent)]
122 Other(#[from] Box<dyn std::error::Error + Send + Sync>),
123}
124
125#[derive(Debug, Error)]
127pub enum RevsetEvaluationError {
128 #[error("Unexpected error from commit backend")]
129 Backend(#[from] BackendError),
130 #[error(transparent)]
131 Other(Box<dyn std::error::Error + Send + Sync>),
132}
133
134impl RevsetEvaluationError {
135 pub fn into_backend_error(self) -> BackendError {
138 match self {
139 Self::Backend(err) => err,
140 Self::Other(err) => BackendError::Other(err),
141 }
142 }
143}
144
145pub const GENERATION_RANGE_FULL: Range<u64> = 0..u64::MAX;
147pub const GENERATION_RANGE_EMPTY: Range<u64> = 0..0;
148
149pub const PARENTS_RANGE_FULL: Range<u32> = 0..u32::MAX;
150
151#[derive(Clone, Debug)]
153pub enum RevsetCommitRef {
154 WorkingCopy(WorkspaceNameBuf),
155 WorkingCopies,
156 Symbol(String),
157 RemoteSymbol(RemoteRefSymbolBuf),
158 ChangeId(HexPrefix),
159 CommitId(HexPrefix),
160 Bookmarks(StringExpression),
161 RemoteBookmarks {
162 symbol: RemoteRefSymbolExpression,
163 remote_ref_state: Option<RemoteRefState>,
164 },
165 Tags(StringExpression),
166 RemoteTags {
167 symbol: RemoteRefSymbolExpression,
168 remote_ref_state: Option<RemoteRefState>,
169 },
170}
171
172#[derive(Clone, Debug)]
174pub struct RemoteRefSymbolExpression {
175 pub name: StringExpression,
177 pub remote: StringExpression,
179}
180
181pub trait RevsetFilterExtension: std::fmt::Debug + Any + Send + Sync {
183 fn matches_commit(&self, commit: &Commit) -> bool;
185}
186
187impl dyn RevsetFilterExtension {
188 pub fn downcast_ref<T: RevsetFilterExtension>(&self) -> Option<&T> {
190 (self as &dyn Any).downcast_ref()
191 }
192}
193
194#[derive(Eq, Copy, Clone, Debug, PartialEq)]
195pub enum DiffMatchSide {
196 Either,
197 Left,
198 Right,
199}
200
201#[derive(Clone, Debug)]
202pub enum RevsetFilterPredicate {
203 ParentCount(Range<u32>),
205 Description(StringExpression),
207 Subject(StringExpression),
209 AuthorName(StringExpression),
211 AuthorEmail(StringExpression),
213 AuthorDate(DatePattern),
215 CommitterName(StringExpression),
217 CommitterEmail(StringExpression),
219 CommitterDate(DatePattern),
221 File(FilesetExpression),
223 DiffLines {
225 text: StringExpression,
226 files: FilesetExpression,
227 side: DiffMatchSide,
228 },
229 HasConflict,
231 Signed,
233 Extension(Arc<dyn RevsetFilterExtension>),
235}
236
237mod private {
238 pub trait ExpressionState {
240 type CommitRef: Clone;
241 type Operation: Clone;
242 }
243
244 #[derive(Debug)]
246 pub enum UserExpressionState {}
247 #[derive(Debug)]
248 pub enum ResolvedExpressionState {}
249}
250
251use private::ExpressionState;
252use private::ResolvedExpressionState;
253use private::UserExpressionState;
254
255impl ExpressionState for UserExpressionState {
256 type CommitRef = RevsetCommitRef;
257 type Operation = String;
258}
259
260impl ExpressionState for ResolvedExpressionState {
261 type CommitRef = Infallible;
262 type Operation = Infallible;
263}
264
265pub type UserRevsetExpression = RevsetExpression<UserExpressionState>;
267
268pub type ResolvedRevsetExpression = RevsetExpression<ResolvedExpressionState>;
270
271#[derive(Clone, Debug)]
276pub enum RevsetExpression<St: ExpressionState> {
277 None,
278 All,
279 VisibleHeads,
280 VisibleHeadsOrReferenced,
283 Root,
284 Commits(Vec<CommitId>),
285 CommitRef(St::CommitRef),
286 Ancestors {
287 heads: Arc<Self>,
288 generation: Range<u64>,
289 parents_range: Range<u32>,
290 },
291 Descendants {
292 roots: Arc<Self>,
293 generation: Range<u64>,
294 },
295 Range {
297 roots: Arc<Self>,
298 heads: Arc<Self>,
299 generation: Range<u64>,
300 parents_range: Range<u32>,
302 },
303 DagRange {
305 roots: Arc<Self>,
306 heads: Arc<Self>,
307 },
309 Reachable {
311 sources: Arc<Self>,
312 domain: Arc<Self>,
313 },
314 Heads(Arc<Self>),
315 HeadsRange {
318 roots: Arc<Self>,
319 heads: Arc<Self>,
320 parents_range: Range<u32>,
321 filter: Arc<Self>,
322 },
323 Roots(Arc<Self>),
324 Forks,
325 ForkPoint(Arc<Self>),
326 MergePoint(Arc<Self>),
327 Bisect(Arc<Self>),
328 HasSize {
329 candidates: Arc<Self>,
330 count: usize,
331 },
332 Latest {
333 candidates: Arc<Self>,
334 count: usize,
335 },
336 Filter(RevsetFilterPredicate),
337 AsFilter(Arc<Self>),
339 Divergent,
340 AtOperation {
342 operation: St::Operation,
343 candidates: Arc<Self>,
344 },
345 WithinReference {
348 candidates: Arc<Self>,
349 commits: Vec<CommitId>,
351 },
352 WithinVisibility {
356 candidates: Arc<Self>,
357 visible_heads: Vec<CommitId>,
359 },
360 Coalesce(Arc<Self>, Arc<Self>),
361 Present(Arc<Self>),
362 NotIn(Arc<Self>),
363 Union(Arc<Self>, Arc<Self>),
364 Intersection(Arc<Self>, Arc<Self>),
365 Difference(Arc<Self>, Arc<Self>),
366}
367
368impl<St: ExpressionState> RevsetExpression<St> {
371 pub fn none() -> Arc<Self> {
372 Arc::new(Self::None)
373 }
374
375 pub fn all() -> Arc<Self> {
378 Arc::new(Self::All)
379 }
380
381 pub fn visible_heads() -> Arc<Self> {
382 Arc::new(Self::VisibleHeads)
383 }
384
385 fn visible_heads_or_referenced() -> Arc<Self> {
386 Arc::new(Self::VisibleHeadsOrReferenced)
387 }
388
389 pub fn root() -> Arc<Self> {
390 Arc::new(Self::Root)
391 }
392
393 pub fn forks() -> Arc<Self> {
394 Arc::new(Self::Forks)
395 }
396
397 pub fn commit(commit_id: CommitId) -> Arc<Self> {
398 Self::commits(vec![commit_id])
399 }
400
401 pub fn commits(commit_ids: Vec<CommitId>) -> Arc<Self> {
402 Arc::new(Self::Commits(commit_ids))
403 }
404
405 pub fn filter(predicate: RevsetFilterPredicate) -> Arc<Self> {
406 Arc::new(Self::Filter(predicate))
407 }
408
409 pub fn divergent() -> Arc<Self> {
410 Arc::new(Self::AsFilter(Arc::new(Self::Divergent)))
411 }
412
413 pub fn is_empty() -> Arc<Self> {
415 Self::filter(RevsetFilterPredicate::File(FilesetExpression::all())).negated()
416 }
417}
418
419impl<St: ExpressionState<CommitRef = RevsetCommitRef>> RevsetExpression<St> {
421 pub fn working_copy(name: WorkspaceNameBuf) -> Arc<Self> {
422 Arc::new(Self::CommitRef(RevsetCommitRef::WorkingCopy(name)))
423 }
424
425 pub fn working_copies() -> Arc<Self> {
426 Arc::new(Self::CommitRef(RevsetCommitRef::WorkingCopies))
427 }
428
429 pub fn symbol(value: String) -> Arc<Self> {
430 Arc::new(Self::CommitRef(RevsetCommitRef::Symbol(value)))
431 }
432
433 pub fn remote_symbol(value: RemoteRefSymbolBuf) -> Arc<Self> {
434 let commit_ref = RevsetCommitRef::RemoteSymbol(value);
435 Arc::new(Self::CommitRef(commit_ref))
436 }
437
438 pub fn change_id_prefix(prefix: HexPrefix) -> Arc<Self> {
439 let commit_ref = RevsetCommitRef::ChangeId(prefix);
440 Arc::new(Self::CommitRef(commit_ref))
441 }
442
443 pub fn commit_id_prefix(prefix: HexPrefix) -> Arc<Self> {
444 let commit_ref = RevsetCommitRef::CommitId(prefix);
445 Arc::new(Self::CommitRef(commit_ref))
446 }
447
448 pub fn bookmarks(expression: StringExpression) -> Arc<Self> {
449 Arc::new(Self::CommitRef(RevsetCommitRef::Bookmarks(expression)))
450 }
451
452 pub fn remote_bookmarks(
453 symbol: RemoteRefSymbolExpression,
454 remote_ref_state: Option<RemoteRefState>,
455 ) -> Arc<Self> {
456 Arc::new(Self::CommitRef(RevsetCommitRef::RemoteBookmarks {
457 symbol,
458 remote_ref_state,
459 }))
460 }
461
462 pub fn tags(expression: StringExpression) -> Arc<Self> {
463 Arc::new(Self::CommitRef(RevsetCommitRef::Tags(expression)))
464 }
465
466 pub fn remote_tags(
467 symbol: RemoteRefSymbolExpression,
468 remote_ref_state: Option<RemoteRefState>,
469 ) -> Arc<Self> {
470 Arc::new(Self::CommitRef(RevsetCommitRef::RemoteTags {
471 symbol,
472 remote_ref_state,
473 }))
474 }
475}
476
477impl<St: ExpressionState> RevsetExpression<St> {
479 pub fn latest(self: &Arc<Self>, count: usize) -> Arc<Self> {
480 Arc::new(Self::Latest {
481 candidates: self.clone(),
482 count,
483 })
484 }
485
486 pub fn heads(self: &Arc<Self>) -> Arc<Self> {
488 Arc::new(Self::Heads(self.clone()))
489 }
490
491 pub fn roots(self: &Arc<Self>) -> Arc<Self> {
493 Arc::new(Self::Roots(self.clone()))
494 }
495
496 pub fn parents(self: &Arc<Self>) -> Arc<Self> {
498 self.ancestors_at(1)
499 }
500
501 pub fn ancestors(self: &Arc<Self>) -> Arc<Self> {
503 self.ancestors_range(GENERATION_RANGE_FULL)
504 }
505
506 pub fn ancestors_at(self: &Arc<Self>, generation: u64) -> Arc<Self> {
509 self.ancestors_range(generation..generation.saturating_add(1))
510 }
511
512 pub fn ancestors_range(self: &Arc<Self>, generation_range: Range<u64>) -> Arc<Self> {
514 Arc::new(Self::Ancestors {
515 heads: self.clone(),
516 generation: generation_range,
517 parents_range: PARENTS_RANGE_FULL,
518 })
519 }
520
521 pub fn first_ancestors(self: &Arc<Self>) -> Arc<Self> {
523 self.first_ancestors_range(GENERATION_RANGE_FULL)
524 }
525
526 pub fn first_ancestors_at(self: &Arc<Self>, generation: u64) -> Arc<Self> {
529 self.first_ancestors_range(generation..generation.saturating_add(1))
530 }
531
532 pub fn first_ancestors_range(self: &Arc<Self>, generation_range: Range<u64>) -> Arc<Self> {
534 Arc::new(Self::Ancestors {
535 heads: self.clone(),
536 generation: generation_range,
537 parents_range: 0..1,
538 })
539 }
540
541 pub fn children(self: &Arc<Self>) -> Arc<Self> {
543 self.descendants_at(1)
544 }
545
546 pub fn descendants(self: &Arc<Self>) -> Arc<Self> {
548 self.descendants_range(GENERATION_RANGE_FULL)
549 }
550
551 pub fn descendants_at(self: &Arc<Self>, generation: u64) -> Arc<Self> {
554 self.descendants_range(generation..generation.saturating_add(1))
555 }
556
557 pub fn descendants_range(self: &Arc<Self>, generation_range: Range<u64>) -> Arc<Self> {
559 Arc::new(Self::Descendants {
560 roots: self.clone(),
561 generation: generation_range,
562 })
563 }
564
565 pub fn fork_point(self: &Arc<Self>) -> Arc<Self> {
567 Arc::new(Self::ForkPoint(self.clone()))
568 }
569
570 pub fn merge_point(self: &Arc<Self>) -> Arc<Self> {
572 Arc::new(Self::MergePoint(self.clone()))
573 }
574
575 pub fn bisect(self: &Arc<Self>) -> Arc<Self> {
577 Arc::new(Self::Bisect(self.clone()))
578 }
579
580 pub fn has_size(self: &Arc<Self>, count: usize) -> Arc<Self> {
582 Arc::new(Self::HasSize {
583 candidates: self.clone(),
584 count,
585 })
586 }
587
588 pub fn filtered(self: &Arc<Self>, predicate: RevsetFilterPredicate) -> Arc<Self> {
590 self.intersection(&Self::filter(predicate))
591 }
592
593 pub fn dag_range_to(self: &Arc<Self>, heads: &Arc<Self>) -> Arc<Self> {
596 Arc::new(Self::DagRange {
597 roots: self.clone(),
598 heads: heads.clone(),
599 })
600 }
601
602 pub fn connected(self: &Arc<Self>) -> Arc<Self> {
605 self.dag_range_to(self)
606 }
607
608 pub fn reachable(self: &Arc<Self>, domain: &Arc<Self>) -> Arc<Self> {
611 Arc::new(Self::Reachable {
612 sources: self.clone(),
613 domain: domain.clone(),
614 })
615 }
616
617 pub fn range(self: &Arc<Self>, heads: &Arc<Self>) -> Arc<Self> {
619 Arc::new(Self::Range {
620 roots: self.clone(),
621 heads: heads.clone(),
622 generation: GENERATION_RANGE_FULL,
623 parents_range: PARENTS_RANGE_FULL,
624 })
625 }
626
627 pub fn within_visibility(self: &Arc<Self>, repo: &dyn Repo) -> Arc<Self> {
633 Arc::new(Self::WithinVisibility {
634 candidates: self.clone(),
635 visible_heads: repo.view().heads().iter().cloned().collect(),
636 })
637 }
638
639 pub fn present(self: &Arc<Self>) -> Arc<Self> {
641 Arc::new(Self::Present(self.clone()))
642 }
643
644 pub fn negated(self: &Arc<Self>) -> Arc<Self> {
646 Arc::new(Self::NotIn(self.clone()))
647 }
648
649 pub fn union(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
651 Arc::new(Self::Union(self.clone(), other.clone()))
652 }
653
654 pub fn union_all(expressions: &[Arc<Self>]) -> Arc<Self> {
656 to_binary_expression(expressions, &Self::none, &Self::union)
657 }
658
659 pub fn intersection(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
661 Arc::new(Self::Intersection(self.clone(), other.clone()))
662 }
663
664 pub fn minus(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
666 Arc::new(Self::Difference(self.clone(), other.clone()))
667 }
668
669 pub fn coalesce(expressions: &[Arc<Self>]) -> Arc<Self> {
672 to_binary_expression(expressions, &Self::none, &Self::coalesce2)
673 }
674
675 fn coalesce2(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
676 Arc::new(Self::Coalesce(self.clone(), other.clone()))
677 }
678}
679
680impl<St: ExpressionState<CommitRef = RevsetCommitRef>> RevsetExpression<St> {
681 pub fn as_symbol(&self) -> Option<&str> {
683 match self {
684 Self::CommitRef(RevsetCommitRef::Symbol(name)) => Some(name),
685 _ => None,
686 }
687 }
688}
689
690impl UserRevsetExpression {
691 pub fn resolve_user_expression<'a>(
694 &self,
695 repo: &'a dyn Repo,
696 symbol_resolver: &SymbolResolver,
697 ) -> Result<ResolvedRevset<'a>, RevsetResolutionError> {
698 let mut resolver = ExpressionSymbolResolver::new(repo, symbol_resolver);
699 let expression = resolver.fold_expression(self)?;
700 Ok(ResolvedRevset {
701 repo,
702 expression,
703 other_repos: resolver.other_repos,
704 merged_index: OnceCell::new(),
705 })
706 }
707}
708
709pub struct ResolvedRevset<'a> {
718 repo: &'a dyn Repo,
720 expression: Arc<ResolvedRevsetExpression>,
722 other_repos: Vec<Arc<ReadonlyRepo>>,
724 merged_index: OnceCell<Box<dyn MutableIndex>>,
727}
728
729impl fmt::Debug for ResolvedRevset<'_> {
730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
731 f.debug_struct("ResolvedRevset")
732 .field("expression", &self.expression)
733 .field("other_repos", &self.other_repos)
734 .finish_non_exhaustive()
735 }
736}
737
738impl ResolvedRevset<'_> {
739 pub fn evaluate(&self) -> Result<Box<dyn Revset>, RevsetEvaluationError> {
741 let expr = optimize(self.expression.clone()).to_backend_expression(self.repo);
742 let merged_index = if self.other_repos.is_empty() {
743 self.repo.index()
744 } else {
745 let merged = self
746 .merged_index
747 .get_or_try_init(|| Self::merge_indexes(self.repo, &self.other_repos))?;
748 merged.as_index()
749 };
750 merged_index.evaluate_revset(&expr, self.repo.store())
751 }
752
753 pub fn into_inner(self) -> (Arc<ResolvedRevsetExpression>, Vec<Arc<ReadonlyRepo>>) {
762 (self.expression, self.other_repos)
763 }
764
765 fn merge_indexes(
768 repo: &dyn Repo,
769 other_repos: &[Arc<ReadonlyRepo>],
770 ) -> Result<Box<dyn MutableIndex>, RevsetEvaluationError> {
771 let base_readonly_repo = repo.base_repo();
774 let mut merged_index = base_readonly_repo.readonly_index().start_modification();
775 for other_repo in other_repos {
776 merged_index
777 .merge_in(other_repo.readonly_index())
778 .map_err(|err| RevsetEvaluationError::Other(err.into()))?;
779 }
780 Ok(merged_index)
781 }
782}
783
784impl ResolvedRevsetExpression {
785 pub fn evaluate(
787 self: Arc<Self>,
788 repo: &dyn Repo,
789 ) -> Result<Box<dyn Revset>, RevsetEvaluationError> {
790 let expr = optimize(self).to_backend_expression(repo);
791 repo.index().evaluate_revset(&expr, repo.store())
792 }
793
794 pub fn evaluate_unoptimized(
799 self: &Arc<Self>,
800 repo: &dyn Repo,
801 other_repos: &[Arc<ReadonlyRepo>],
802 ) -> Result<Box<dyn Revset>, RevsetEvaluationError> {
803 let expr = resolve_referenced_commits(self)
806 .as_ref()
807 .unwrap_or(self)
808 .to_backend_expression(repo);
809 let merged_index = ResolvedRevset::merge_indexes(repo, other_repos)?;
810 merged_index.as_index().evaluate_revset(&expr, repo.store())
811 }
812
813 pub fn to_backend_expression(&self, repo: &dyn Repo) -> ResolvedExpression {
816 resolve_visibility(repo, self)
817 }
818}
819
820#[derive(Clone, Debug)]
821pub enum ResolvedPredicateExpression {
822 Filter(RevsetFilterPredicate),
824 Divergent {
825 visible_heads: Vec<CommitId>,
826 },
827 Set(Box<ResolvedExpression>),
830 NotIn(Box<Self>),
831 Union(Box<Self>, Box<Self>),
832 Intersection(Box<Self>, Box<Self>),
833}
834
835#[derive(Clone, Debug)]
843pub enum ResolvedExpression {
844 Commits(Vec<CommitId>),
845 Ancestors {
846 heads: Box<Self>,
847 generation: Range<u64>,
848 parents_range: Range<u32>,
849 },
850 Range {
852 roots: Box<Self>,
853 heads: Box<Self>,
854 generation: Range<u64>,
855 parents_range: Range<u32>,
857 },
858 DagRange {
860 roots: Box<Self>,
861 heads: Box<Self>,
862 generation_from_roots: Range<u64>,
863 },
864 Reachable {
866 sources: Box<Self>,
867 domain: Box<Self>,
868 },
869 Heads(Box<Self>),
870 HeadsRange {
873 roots: Box<Self>,
874 heads: Box<Self>,
875 parents_range: Range<u32>,
876 filter: Option<ResolvedPredicateExpression>,
877 },
878 Roots(Box<Self>),
879 Forks {
880 heads: Box<Self>,
881 },
882 ForkPoint(Box<Self>),
883 MergePoint {
884 roots: Box<Self>,
885 visible_heads: Box<Self>,
886 },
887 Bisect(Box<Self>),
888 HasSize {
889 candidates: Box<Self>,
890 count: usize,
891 },
892 Latest {
893 candidates: Box<Self>,
894 count: usize,
895 },
896 Coalesce(Box<Self>, Box<Self>),
897 Union(Box<Self>, Box<Self>),
898 FilterWithin {
900 candidates: Box<Self>,
901 predicate: ResolvedPredicateExpression,
902 },
903 Intersection(Box<Self>, Box<Self>),
905 Difference(Box<Self>, Box<Self>),
906}
907
908pub type RevsetFunction = fn(
909 &mut RevsetDiagnostics,
910 &FunctionCallNode,
911 &LoweringContext,
912) -> Result<Arc<UserRevsetExpression>, RevsetParseError>;
913
914static BUILTIN_FUNCTION_MAP: LazyLock<HashMap<&str, RevsetFunction>> = LazyLock::new(|| {
915 let mut map: HashMap<&str, RevsetFunction> = HashMap::new();
918 map.insert("parents", |diagnostics, function, context| {
919 let ([arg], [depth_opt_arg]) = function.expect_arguments()?;
920 let expression = lower_expression(diagnostics, arg, context)?;
921 if let Some(depth_arg) = depth_opt_arg {
922 let depth = expect_literal("integer", depth_arg)?;
923 Ok(expression.ancestors_at(depth))
924 } else {
925 Ok(expression.parents())
926 }
927 });
928 map.insert("children", |diagnostics, function, context| {
929 let ([arg], [depth_opt_arg]) = function.expect_arguments()?;
930 let expression = lower_expression(diagnostics, arg, context)?;
931 if let Some(depth_arg) = depth_opt_arg {
932 let depth = expect_literal("integer", depth_arg)?;
933 Ok(expression.descendants_at(depth))
934 } else {
935 Ok(expression.children())
936 }
937 });
938 map.insert("ancestors", |diagnostics, function, context| {
939 let ([heads_arg], [depth_opt_arg]) = function.expect_arguments()?;
940 let heads = lower_expression(diagnostics, heads_arg, context)?;
941 let generation = if let Some(depth_arg) = depth_opt_arg {
942 let depth = expect_literal("integer", depth_arg)?;
943 0..depth
944 } else {
945 GENERATION_RANGE_FULL
946 };
947 Ok(heads.ancestors_range(generation))
948 });
949 map.insert("descendants", |diagnostics, function, context| {
950 let ([roots_arg], [depth_opt_arg]) = function.expect_arguments()?;
951 let roots = lower_expression(diagnostics, roots_arg, context)?;
952 let generation = if let Some(depth_arg) = depth_opt_arg {
953 let depth = expect_literal("integer", depth_arg)?;
954 0..depth
955 } else {
956 GENERATION_RANGE_FULL
957 };
958 Ok(roots.descendants_range(generation))
959 });
960 map.insert("first_parent", |diagnostics, function, context| {
961 let ([arg], [depth_opt_arg]) = function.expect_arguments()?;
962 let expression = lower_expression(diagnostics, arg, context)?;
963 let depth = if let Some(depth_arg) = depth_opt_arg {
964 expect_literal("integer", depth_arg)?
965 } else {
966 1
967 };
968 Ok(expression.first_ancestors_at(depth))
969 });
970 map.insert("first_ancestors", |diagnostics, function, context| {
971 let ([heads_arg], [depth_opt_arg]) = function.expect_arguments()?;
972 let heads = lower_expression(diagnostics, heads_arg, context)?;
973 let generation = if let Some(depth_arg) = depth_opt_arg {
974 let depth = expect_literal("integer", depth_arg)?;
975 0..depth
976 } else {
977 GENERATION_RANGE_FULL
978 };
979 Ok(heads.first_ancestors_range(generation))
980 });
981 map.insert("connected", |diagnostics, function, context| {
982 let [arg] = function.expect_exact_arguments()?;
983 let candidates = lower_expression(diagnostics, arg, context)?;
984 Ok(candidates.connected())
985 });
986 map.insert("reachable", |diagnostics, function, context| {
987 let [source_arg, domain_arg] = function.expect_exact_arguments()?;
988 let sources = lower_expression(diagnostics, source_arg, context)?;
989 let domain = lower_expression(diagnostics, domain_arg, context)?;
990 Ok(sources.reachable(&domain))
991 });
992 map.insert("none", |_diagnostics, function, _context| {
993 function.expect_no_arguments()?;
994 Ok(RevsetExpression::none())
995 });
996 map.insert("all", |_diagnostics, function, _context| {
997 function.expect_no_arguments()?;
998 Ok(RevsetExpression::all())
999 });
1000 map.insert("working_copies", |_diagnostics, function, _context| {
1001 function.expect_no_arguments()?;
1002 Ok(RevsetExpression::working_copies())
1003 });
1004 map.insert("heads", |diagnostics, function, context| {
1005 let [arg] = function.expect_exact_arguments()?;
1006 let candidates = lower_expression(diagnostics, arg, context)?;
1007 Ok(candidates.heads())
1008 });
1009 map.insert("roots", |diagnostics, function, context| {
1010 let [arg] = function.expect_exact_arguments()?;
1011 let candidates = lower_expression(diagnostics, arg, context)?;
1012 Ok(candidates.roots())
1013 });
1014 map.insert("visible_heads", |_diagnostics, function, _context| {
1015 function.expect_no_arguments()?;
1016 Ok(RevsetExpression::visible_heads())
1017 });
1018 map.insert("root", |_diagnostics, function, _context| {
1019 function.expect_no_arguments()?;
1020 Ok(RevsetExpression::root())
1021 });
1022 map.insert("change_id", |diagnostics, function, _context| {
1023 let [arg] = function.expect_exact_arguments()?;
1024 let prefix = revset_parser::catch_aliases(diagnostics, arg, |_diagnostics, arg| {
1025 let value = revset_parser::expect_string_literal("change ID prefix", arg)?;
1026 HexPrefix::try_from_reverse_hex(value)
1027 .ok_or_else(|| RevsetParseError::expression("Invalid change ID prefix", arg.span))
1028 })?;
1029 Ok(RevsetExpression::change_id_prefix(prefix))
1030 });
1031 map.insert("commit_id", |diagnostics, function, _context| {
1032 let [arg] = function.expect_exact_arguments()?;
1033 let prefix = revset_parser::catch_aliases(diagnostics, arg, |_diagnostics, arg| {
1034 let value = revset_parser::expect_string_literal("commit ID prefix", arg)?;
1035 HexPrefix::try_from_hex(value)
1036 .ok_or_else(|| RevsetParseError::expression("Invalid commit ID prefix", arg.span))
1037 })?;
1038 Ok(RevsetExpression::commit_id_prefix(prefix))
1039 });
1040 map.insert("bookmarks", |diagnostics, function, _context| {
1041 let ([], [opt_arg]) = function.expect_arguments()?;
1042 let expr = if let Some(arg) = opt_arg {
1043 expect_string_expression(diagnostics, arg)?
1044 } else {
1045 StringExpression::all()
1046 };
1047 Ok(RevsetExpression::bookmarks(expr))
1048 });
1049 map.insert("remote_bookmarks", |diagnostics, function, context| {
1050 let symbol = parse_remote_refs_arguments(diagnostics, function, context)?;
1051 let state = None;
1052 Ok(RevsetExpression::remote_bookmarks(symbol, state))
1053 });
1054 map.insert(
1055 "tracked_remote_bookmarks",
1056 |diagnostics, function, context| {
1057 let symbol = parse_remote_refs_arguments(diagnostics, function, context)?;
1058 let state = Some(RemoteRefState::Tracked);
1059 Ok(RevsetExpression::remote_bookmarks(symbol, state))
1060 },
1061 );
1062 map.insert(
1063 "untracked_remote_bookmarks",
1064 |diagnostics, function, context| {
1065 let symbol = parse_remote_refs_arguments(diagnostics, function, context)?;
1066 let state = Some(RemoteRefState::New);
1067 Ok(RevsetExpression::remote_bookmarks(symbol, state))
1068 },
1069 );
1070 map.insert("tags", |diagnostics, function, _context| {
1071 let ([], [opt_arg]) = function.expect_arguments()?;
1072 let expr = if let Some(arg) = opt_arg {
1073 expect_string_expression(diagnostics, arg)?
1074 } else {
1075 StringExpression::all()
1076 };
1077 Ok(RevsetExpression::tags(expr))
1078 });
1079 map.insert("remote_tags", |diagnostics, function, context| {
1080 let symbol = parse_remote_refs_arguments(diagnostics, function, context)?;
1081 let state = None;
1082 Ok(RevsetExpression::remote_tags(symbol, state))
1083 });
1084 map.insert("tracked_remote_tags", |diagnostics, function, context| {
1085 let symbol = parse_remote_refs_arguments(diagnostics, function, context)?;
1086 let state = Some(RemoteRefState::Tracked);
1087 Ok(RevsetExpression::remote_tags(symbol, state))
1088 });
1089 map.insert("untracked_remote_tags", |diagnostics, function, context| {
1090 let symbol = parse_remote_refs_arguments(diagnostics, function, context)?;
1091 let state = Some(RemoteRefState::New);
1092 Ok(RevsetExpression::remote_tags(symbol, state))
1093 });
1094 map.insert("latest", |diagnostics, function, context| {
1095 let ([candidates_arg], [count_opt_arg]) = function.expect_arguments()?;
1096 let candidates = lower_expression(diagnostics, candidates_arg, context)?;
1097 let count = if let Some(count_arg) = count_opt_arg {
1098 expect_literal("integer", count_arg)?
1099 } else {
1100 1
1101 };
1102 Ok(candidates.latest(count))
1103 });
1104 map.insert("fork_point", |diagnostics, function, context| {
1105 let [expression_arg] = function.expect_exact_arguments()?;
1106 let expression = lower_expression(diagnostics, expression_arg, context)?;
1107 Ok(RevsetExpression::fork_point(&expression))
1108 });
1109 map.insert("merge_point", |diagnostics, function, context| {
1110 let [expression_arg] = function.expect_exact_arguments()?;
1111 let expression = lower_expression(diagnostics, expression_arg, context)?;
1112 Ok(RevsetExpression::merge_point(&expression))
1113 });
1114 map.insert("bisect", |diagnostics, function, context| {
1115 let [expression_arg] = function.expect_exact_arguments()?;
1116 let expression = lower_expression(diagnostics, expression_arg, context)?;
1117 Ok(RevsetExpression::bisect(&expression))
1118 });
1119 map.insert("exactly", |diagnostics, function, context| {
1120 let ([candidates_arg, count_arg], []) = function.expect_arguments()?;
1121 let candidates = lower_expression(diagnostics, candidates_arg, context)?;
1122 let count = expect_literal("integer", count_arg)?;
1123 Ok(candidates.has_size(count))
1124 });
1125 map.insert("merges", |_diagnostics, function, _context| {
1126 function.expect_no_arguments()?;
1127 Ok(RevsetExpression::filter(
1128 RevsetFilterPredicate::ParentCount(2..u32::MAX),
1129 ))
1130 });
1131 map.insert("forks", |_diagnostics, function, _context| {
1132 function.expect_no_arguments()?;
1133 Ok(RevsetExpression::forks())
1134 });
1135 map.insert("description", |diagnostics, function, _context| {
1136 let [arg] = function.expect_exact_arguments()?;
1137 let expr = expect_string_expression(diagnostics, arg)?;
1138 let predicate = RevsetFilterPredicate::Description(expr);
1139 Ok(RevsetExpression::filter(predicate))
1140 });
1141 map.insert("subject", |diagnostics, function, _context| {
1142 let [arg] = function.expect_exact_arguments()?;
1143 let expr = expect_string_expression(diagnostics, arg)?;
1144 let predicate = RevsetFilterPredicate::Subject(expr);
1145 Ok(RevsetExpression::filter(predicate))
1146 });
1147 map.insert("author", |diagnostics, function, _context| {
1148 let [arg] = function.expect_exact_arguments()?;
1149 let expr = expect_string_expression(diagnostics, arg)?;
1150 let name_predicate = RevsetFilterPredicate::AuthorName(expr.clone());
1151 let email_predicate = RevsetFilterPredicate::AuthorEmail(expr);
1152 Ok(RevsetExpression::filter(name_predicate)
1153 .union(&RevsetExpression::filter(email_predicate)))
1154 });
1155 map.insert("author_name", |diagnostics, function, _context| {
1156 let [arg] = function.expect_exact_arguments()?;
1157 let expr = expect_string_expression(diagnostics, arg)?;
1158 let predicate = RevsetFilterPredicate::AuthorName(expr);
1159 Ok(RevsetExpression::filter(predicate))
1160 });
1161 map.insert("author_email", |diagnostics, function, _context| {
1162 let [arg] = function.expect_exact_arguments()?;
1163 let expr = expect_string_expression(diagnostics, arg)?;
1164 let predicate = RevsetFilterPredicate::AuthorEmail(expr);
1165 Ok(RevsetExpression::filter(predicate))
1166 });
1167 map.insert("author_date", |diagnostics, function, context| {
1168 let [arg] = function.expect_exact_arguments()?;
1169 let pattern = expect_date_pattern(diagnostics, arg, context.date_pattern_context())?;
1170 Ok(RevsetExpression::filter(RevsetFilterPredicate::AuthorDate(
1171 pattern,
1172 )))
1173 });
1174 map.insert("signed", |_diagnostics, function, _context| {
1175 function.expect_no_arguments()?;
1176 let predicate = RevsetFilterPredicate::Signed;
1177 Ok(RevsetExpression::filter(predicate))
1178 });
1179 map.insert("mine", |_diagnostics, function, context| {
1180 function.expect_no_arguments()?;
1181 let pattern = StringPattern::exact_i(context.user_email);
1185 let predicate = RevsetFilterPredicate::AuthorEmail(StringExpression::pattern(pattern));
1186 Ok(RevsetExpression::filter(predicate))
1187 });
1188 map.insert("committer", |diagnostics, function, _context| {
1189 let [arg] = function.expect_exact_arguments()?;
1190 let expr = expect_string_expression(diagnostics, arg)?;
1191 let name_predicate = RevsetFilterPredicate::CommitterName(expr.clone());
1192 let email_predicate = RevsetFilterPredicate::CommitterEmail(expr);
1193 Ok(RevsetExpression::filter(name_predicate)
1194 .union(&RevsetExpression::filter(email_predicate)))
1195 });
1196 map.insert("committer_name", |diagnostics, function, _context| {
1197 let [arg] = function.expect_exact_arguments()?;
1198 let expr = expect_string_expression(diagnostics, arg)?;
1199 let predicate = RevsetFilterPredicate::CommitterName(expr);
1200 Ok(RevsetExpression::filter(predicate))
1201 });
1202 map.insert("committer_email", |diagnostics, function, _context| {
1203 let [arg] = function.expect_exact_arguments()?;
1204 let expr = expect_string_expression(diagnostics, arg)?;
1205 let predicate = RevsetFilterPredicate::CommitterEmail(expr);
1206 Ok(RevsetExpression::filter(predicate))
1207 });
1208 map.insert("committer_date", |diagnostics, function, context| {
1209 let [arg] = function.expect_exact_arguments()?;
1210 let pattern = expect_date_pattern(diagnostics, arg, context.date_pattern_context())?;
1211 Ok(RevsetExpression::filter(
1212 RevsetFilterPredicate::CommitterDate(pattern),
1213 ))
1214 });
1215 map.insert("empty", |_diagnostics, function, _context| {
1216 function.expect_no_arguments()?;
1217 Ok(RevsetExpression::is_empty())
1218 });
1219 map.insert("files", |diagnostics, function, context| {
1220 let fileset_context = context.fileset_parse_context().ok_or_else(|| {
1221 RevsetParseError::with_span(
1222 RevsetParseErrorKind::FsPathWithoutWorkspace,
1223 function.args_span, )
1225 })?;
1226 let [arg] = function.expect_exact_arguments()?;
1227 let expr = expect_fileset_expression(diagnostics, arg, &fileset_context)?;
1228 Ok(RevsetExpression::filter(RevsetFilterPredicate::File(expr)))
1229 });
1230 map.insert("diff_lines", |diagnostics, function, context| {
1231 if function.name != "diff_lines" {
1232 diagnostics.add_warning(RevsetParseError::expression(
1234 "diff_contains() is deprecated; use diff_lines() instead",
1235 function.name_span,
1236 ));
1237 }
1238 let ([text_arg], [files_opt_arg]) = function.expect_arguments()?;
1239 let text = expect_string_expression(diagnostics, text_arg)?;
1240 let files = expand_optional_files_arg(files_opt_arg, diagnostics, context)?;
1241 let predicate = RevsetFilterPredicate::DiffLines {
1242 text,
1243 files,
1244 side: DiffMatchSide::Either,
1245 };
1246 Ok(RevsetExpression::filter(predicate))
1247 });
1248 map.insert("diff_lines_added", |diagnostics, function, context| {
1249 let ([text_arg], [files_opt_arg]) = function.expect_arguments()?;
1250 let text = expect_string_expression(diagnostics, text_arg)?;
1251 let files = expand_optional_files_arg(files_opt_arg, diagnostics, context)?;
1252 let predicate = RevsetFilterPredicate::DiffLines {
1253 text,
1254 files,
1255 side: DiffMatchSide::Right,
1256 };
1257 Ok(RevsetExpression::filter(predicate))
1258 });
1259 map.insert("diff_lines_removed", |diagnostics, function, context| {
1260 let ([text_arg], [files_opt_arg]) = function.expect_arguments()?;
1261 let text = expect_string_expression(diagnostics, text_arg)?;
1262 let files = expand_optional_files_arg(files_opt_arg, diagnostics, context)?;
1263 let predicate = RevsetFilterPredicate::DiffLines {
1264 text,
1265 files,
1266 side: DiffMatchSide::Left,
1267 };
1268 Ok(RevsetExpression::filter(predicate))
1269 });
1270 map.insert("diff_contains", map["diff_lines"]);
1272 map.insert("conflicts", |_diagnostics, function, _context| {
1273 function.expect_no_arguments()?;
1274 Ok(RevsetExpression::filter(RevsetFilterPredicate::HasConflict))
1275 });
1276 map.insert("divergent", |_diagnostics, function, _context| {
1277 function.expect_no_arguments()?;
1278 Ok(RevsetExpression::divergent())
1279 });
1280 map.insert("present", |diagnostics, function, context| {
1281 let [arg] = function.expect_exact_arguments()?;
1282 let expression = lower_expression(diagnostics, arg, context)?;
1283 Ok(expression.present())
1284 });
1285 map.insert("at_operation", |diagnostics, function, context| {
1286 let [op_arg, cand_arg] = function.expect_exact_arguments()?;
1287 let operation = revset_parser::catch_aliases(diagnostics, op_arg, |_diagnostics, node| {
1289 Ok(node.span.as_str().to_owned())
1290 })?;
1291 let candidates = lower_expression(diagnostics, cand_arg, context)?;
1292 Ok(Arc::new(RevsetExpression::AtOperation {
1293 operation,
1294 candidates,
1295 }))
1296 });
1297 map.insert("coalesce", |diagnostics, function, context| {
1298 let ([], args) = function.expect_some_arguments()?;
1299 let expressions: Vec<_> = args
1300 .iter()
1301 .map(|arg| lower_expression(diagnostics, arg, context))
1302 .try_collect()?;
1303 Ok(RevsetExpression::coalesce(&expressions))
1304 });
1305 map
1306});
1307
1308fn expand_optional_files_arg(
1309 files_opt_arg: Option<&ExpressionNode>,
1310 diagnostics: &mut RevsetDiagnostics,
1311 context: &LoweringContext,
1312) -> Result<FilesetExpression, RevsetParseError> {
1313 if let Some(files_arg) = files_opt_arg {
1314 let fileset_context = context.fileset_parse_context().ok_or_else(|| {
1315 RevsetParseError::with_span(
1316 RevsetParseErrorKind::FsPathWithoutWorkspace,
1317 files_arg.span,
1318 )
1319 })?;
1320 expect_fileset_expression(diagnostics, files_arg, &fileset_context)
1321 } else {
1322 Ok(FilesetExpression::all())
1325 }
1326}
1327
1328pub fn expect_fileset_expression(
1330 diagnostics: &mut RevsetDiagnostics,
1331 node: &ExpressionNode,
1332 context: &FilesetParseContext,
1333) -> Result<FilesetExpression, RevsetParseError> {
1334 revset_parser::catch_aliases(diagnostics, node, |diagnostics, node| {
1338 let mut inner_diagnostics = FilesetDiagnostics::new();
1339 let expression = fileset::parse(&mut inner_diagnostics, node.span.as_str(), context)
1340 .map_err(|err| {
1341 RevsetParseError::expression("In fileset expression", node.span).with_source(err)
1342 })?;
1343 diagnostics.extend_with(inner_diagnostics, |diag| {
1344 RevsetParseError::expression("In fileset expression", node.span).with_source(diag)
1345 });
1346 Ok(expression)
1347 })
1348}
1349
1350pub fn expect_string_expression(
1352 diagnostics: &mut RevsetDiagnostics,
1353 node: &ExpressionNode,
1354) -> Result<StringExpression, RevsetParseError> {
1355 revset_parser::catch_aliases(diagnostics, node, |diagnostics, node| {
1356 let expr_error = || RevsetParseError::expression("Invalid string expression", node.span);
1357 let pattern_error = || RevsetParseError::expression("Invalid string pattern", node.span);
1358 let default_pattern = |value: &str| {
1359 let pattern =
1360 StringPattern::glob(value).map_err(|err| pattern_error().with_source(err))?;
1361 Ok(StringExpression::pattern(pattern))
1362 };
1363 match &node.kind {
1364 ExpressionKind::Identifier(value) => default_pattern(value),
1365 ExpressionKind::String(value) => default_pattern(value),
1366 ExpressionKind::Pattern(pattern) => {
1367 let value = revset_parser::expect_string_literal("string", &pattern.value)?;
1368 let pattern = StringPattern::from_str_kind(value, pattern.name)
1369 .map_err(|err| pattern_error().with_source(err))?;
1370 Ok(StringExpression::pattern(pattern))
1371 }
1372 ExpressionKind::RemoteSymbol(_)
1373 | ExpressionKind::AtWorkspace(_)
1374 | ExpressionKind::AtCurrentWorkspace
1375 | ExpressionKind::DagRangeAll
1376 | ExpressionKind::RangeAll => Err(expr_error()),
1377 ExpressionKind::Unary(op, arg_node) => {
1378 let arg = expect_string_expression(diagnostics, arg_node)?;
1379 match op {
1380 UnaryOp::Negate => Ok(arg.negated()),
1381 UnaryOp::DagRangePre
1382 | UnaryOp::DagRangePost
1383 | UnaryOp::RangePre
1384 | UnaryOp::RangePost
1385 | UnaryOp::Parents
1386 | UnaryOp::Children => Err(expr_error()),
1387 }
1388 }
1389 ExpressionKind::Binary(op, lhs_node, rhs_node) => {
1390 let lhs = expect_string_expression(diagnostics, lhs_node)?;
1391 let rhs = expect_string_expression(diagnostics, rhs_node)?;
1392 match op {
1393 BinaryOp::Intersection => Ok(lhs.intersection(rhs)),
1394 BinaryOp::Difference => Ok(lhs.intersection(rhs.negated())),
1395 BinaryOp::DagRange | BinaryOp::Range => Err(expr_error()),
1396 }
1397 }
1398 ExpressionKind::UnionAll(nodes) => {
1399 let expressions = nodes
1400 .iter()
1401 .map(|node| expect_string_expression(diagnostics, node))
1402 .try_collect()?;
1403 Ok(StringExpression::union_all(expressions))
1404 }
1405 ExpressionKind::FunctionCall(_) => Err(expr_error()),
1406 ExpressionKind::AliasExpanded(..) => unreachable!(),
1407 }
1408 })
1409}
1410
1411pub fn expect_date_pattern(
1412 diagnostics: &mut RevsetDiagnostics,
1413 node: &ExpressionNode,
1414 context: &DatePatternContext,
1415) -> Result<DatePattern, RevsetParseError> {
1416 revset_parser::catch_aliases(diagnostics, node, |_diagnostics, node| {
1417 let (value, kind) = revset_parser::expect_string_pattern("date pattern", node)?;
1418 let kind = kind.ok_or_else(|| {
1419 RevsetParseError::expression("Date pattern must specify 'after' or 'before'", node.span)
1420 })?;
1421 context.parse_relative(value, kind).map_err(|err| {
1422 RevsetParseError::expression("Invalid date pattern", node.span).with_source(err)
1423 })
1424 })
1425}
1426
1427fn parse_remote_refs_arguments(
1428 diagnostics: &mut RevsetDiagnostics,
1429 function: &FunctionCallNode,
1430 context: &LoweringContext,
1431) -> Result<RemoteRefSymbolExpression, RevsetParseError> {
1432 let ([], [name_opt_arg, remote_opt_arg]) = function.expect_named_arguments(&["", "remote"])?;
1433 let name = if let Some(name_arg) = name_opt_arg {
1434 expect_string_expression(diagnostics, name_arg)?
1435 } else {
1436 StringExpression::all()
1437 };
1438 let remote = if let Some(remote_arg) = remote_opt_arg {
1439 expect_string_expression(diagnostics, remote_arg)?
1440 } else if let Some(remote) = context.default_ignored_remote {
1441 StringExpression::exact(remote).negated()
1442 } else {
1443 StringExpression::all()
1444 };
1445 Ok(RemoteRefSymbolExpression { name, remote })
1446}
1447
1448fn lower_function_call(
1450 diagnostics: &mut RevsetDiagnostics,
1451 function: &FunctionCallNode,
1452 context: &LoweringContext,
1453) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
1454 let function_map = &context.extensions.function_map;
1455 if let Some(func) = function_map.get(function.name) {
1456 func(diagnostics, function, context)
1457 } else {
1458 Err(RevsetParseError::with_span(
1459 RevsetParseErrorKind::NoSuchFunction {
1460 name: function.name.to_owned(),
1461 candidates: collect_similar(function.name, function_map.keys()),
1462 },
1463 function.name_span,
1464 ))
1465 }
1466}
1467
1468pub fn lower_expression(
1471 diagnostics: &mut RevsetDiagnostics,
1472 node: &ExpressionNode,
1473 context: &LoweringContext,
1474) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
1475 revset_parser::catch_aliases(diagnostics, node, |diagnostics, node| match &node.kind {
1476 ExpressionKind::Identifier(name) => Ok(RevsetExpression::symbol((*name).to_owned())),
1477 ExpressionKind::String(name) => Ok(RevsetExpression::symbol(name.to_owned())),
1478 ExpressionKind::Pattern(_) => Err(RevsetParseError::with_span(
1479 RevsetParseErrorKind::NotInfixOperator {
1480 op: ":".to_owned(),
1481 similar_op: "::".to_owned(),
1482 description: "DAG range".to_owned(),
1483 },
1484 node.span,
1485 )),
1486 ExpressionKind::RemoteSymbol(symbol) => Ok(RevsetExpression::remote_symbol(symbol.clone())),
1487 ExpressionKind::AtWorkspace(name) => Ok(RevsetExpression::working_copy(name.into())),
1488 ExpressionKind::AtCurrentWorkspace => {
1489 let ctx = context.workspace.as_ref().ok_or_else(|| {
1490 RevsetParseError::with_span(
1491 RevsetParseErrorKind::WorkingCopyWithoutWorkspace,
1492 node.span,
1493 )
1494 })?;
1495 Ok(RevsetExpression::working_copy(
1496 ctx.workspace_name.to_owned(),
1497 ))
1498 }
1499 ExpressionKind::DagRangeAll => Ok(RevsetExpression::all()),
1500 ExpressionKind::RangeAll => Ok(RevsetExpression::root().negated()),
1501 ExpressionKind::Unary(op, arg_node) => {
1502 let arg = lower_expression(diagnostics, arg_node, context)?;
1503 match op {
1504 UnaryOp::Negate => Ok(arg.negated()),
1505 UnaryOp::DagRangePre => Ok(arg.ancestors()),
1506 UnaryOp::DagRangePost => Ok(arg.descendants()),
1507 UnaryOp::RangePre => Ok(RevsetExpression::root().range(&arg)),
1508 UnaryOp::RangePost => Ok(arg.ancestors().negated()),
1509 UnaryOp::Parents => Ok(arg.parents()),
1510 UnaryOp::Children => Ok(arg.children()),
1511 }
1512 }
1513 ExpressionKind::Binary(op, lhs_node, rhs_node) => {
1514 let lhs = lower_expression(diagnostics, lhs_node, context)?;
1515 let rhs = lower_expression(diagnostics, rhs_node, context)?;
1516 match op {
1517 BinaryOp::Intersection => Ok(lhs.intersection(&rhs)),
1518 BinaryOp::Difference => Ok(lhs.minus(&rhs)),
1519 BinaryOp::DagRange => Ok(lhs.dag_range_to(&rhs)),
1520 BinaryOp::Range => Ok(lhs.range(&rhs)),
1521 }
1522 }
1523 ExpressionKind::UnionAll(nodes) => {
1524 let expressions: Vec<_> = nodes
1525 .iter()
1526 .map(|node| lower_expression(diagnostics, node, context))
1527 .try_collect()?;
1528 Ok(RevsetExpression::union_all(&expressions))
1529 }
1530 ExpressionKind::FunctionCall(function) => {
1531 lower_function_call(diagnostics, function, context)
1532 }
1533 ExpressionKind::AliasExpanded(..) => unreachable!(),
1534 })
1535}
1536
1537pub fn parse(
1538 diagnostics: &mut RevsetDiagnostics,
1539 revset_str: &str,
1540 context: &RevsetParseContext,
1541) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
1542 let node = parse_program(revset_str)?;
1543 let node =
1544 dsl_util::expand_aliases_with_locals(node, context.aliases_map, &context.local_variables)?;
1545 lower_expression(diagnostics, &node, &context.to_lowering_context())
1546 .map_err(|err| err.extend_function_candidates(context.aliases_map.function_names()))
1547}
1548
1549pub fn parse_string_expression(
1551 diagnostics: &mut RevsetDiagnostics,
1552 text: &str,
1553) -> Result<StringExpression, RevsetParseError> {
1554 let node = parse_program(text)?;
1555 expect_string_expression(diagnostics, &node)
1556}
1557
1558fn to_binary_expression<T: Clone>(
1561 expressions: &[T],
1562 unit: &impl Fn() -> T,
1563 binary: &impl Fn(&T, &T) -> T,
1564) -> T {
1565 match expressions {
1566 [] => unit(),
1567 [expression] => expression.clone(),
1568 _ => {
1569 let (left, right) = expressions.split_at(expressions.len() / 2);
1571 binary(
1572 &to_binary_expression(left, unit, binary),
1573 &to_binary_expression(right, unit, binary),
1574 )
1575 }
1576 }
1577}
1578
1579type TransformedExpression<St> = Option<Arc<RevsetExpression<St>>>;
1581type PreTransformedExpression<St> = ControlFlow<TransformedExpression<St>, ()>;
1584
1585fn transform_expression<St: ExpressionState>(
1587 expression: &Arc<RevsetExpression<St>>,
1588 mut pre: impl FnMut(&Arc<RevsetExpression<St>>) -> PreTransformedExpression<St>,
1589 mut post: impl FnMut(&Arc<RevsetExpression<St>>) -> TransformedExpression<St>,
1590) -> TransformedExpression<St> {
1591 let Ok(transformed) =
1592 try_transform_expression::<St, Infallible>(expression, |x| Ok(pre(x)), |x| Ok(post(x)));
1593 transformed
1594}
1595
1596fn transform_expression_bottom_up<St: ExpressionState>(
1598 expression: &Arc<RevsetExpression<St>>,
1599 post: impl FnMut(&Arc<RevsetExpression<St>>) -> TransformedExpression<St>,
1600) -> TransformedExpression<St> {
1601 transform_expression(expression, |_| ControlFlow::Continue(()), post)
1602}
1603
1604fn try_transform_expression<St: ExpressionState, E>(
1617 expression: &Arc<RevsetExpression<St>>,
1618 mut pre: impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1619 mut post: impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1620) -> Result<TransformedExpression<St>, E> {
1621 fn transform_child_rec<St: ExpressionState, E>(
1622 expression: &Arc<RevsetExpression<St>>,
1623 pre: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1624 post: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1625 ) -> Result<TransformedExpression<St>, E> {
1626 Ok(match expression.as_ref() {
1627 RevsetExpression::None => None,
1628 RevsetExpression::All => None,
1629 RevsetExpression::VisibleHeads => None,
1630 RevsetExpression::VisibleHeadsOrReferenced => None,
1631 RevsetExpression::Root => None,
1632 RevsetExpression::Commits(_) => None,
1633 RevsetExpression::CommitRef(_) => None,
1634 RevsetExpression::Ancestors {
1635 heads,
1636 generation,
1637 parents_range,
1638 } => transform_rec(heads, pre, post)?.map(|heads| RevsetExpression::Ancestors {
1639 heads,
1640 generation: generation.clone(),
1641 parents_range: parents_range.clone(),
1642 }),
1643 RevsetExpression::Descendants { roots, generation } => transform_rec(roots, pre, post)?
1644 .map(|roots| RevsetExpression::Descendants {
1645 roots,
1646 generation: generation.clone(),
1647 }),
1648 RevsetExpression::Range {
1649 roots,
1650 heads,
1651 generation,
1652 parents_range,
1653 } => transform_rec_pair((roots, heads), pre, post)?.map(|(roots, heads)| {
1654 RevsetExpression::Range {
1655 roots,
1656 heads,
1657 generation: generation.clone(),
1658 parents_range: parents_range.clone(),
1659 }
1660 }),
1661 RevsetExpression::DagRange { roots, heads } => {
1662 transform_rec_pair((roots, heads), pre, post)?
1663 .map(|(roots, heads)| RevsetExpression::DagRange { roots, heads })
1664 }
1665 RevsetExpression::Reachable { sources, domain } => {
1666 transform_rec_pair((sources, domain), pre, post)?
1667 .map(|(sources, domain)| RevsetExpression::Reachable { sources, domain })
1668 }
1669 RevsetExpression::Heads(candidates) => {
1670 transform_rec(candidates, pre, post)?.map(RevsetExpression::Heads)
1671 }
1672 RevsetExpression::HeadsRange {
1673 roots,
1674 heads,
1675 parents_range,
1676 filter,
1677 } => {
1678 let transformed_roots = transform_rec(roots, pre, post)?;
1679 let transformed_heads = transform_rec(heads, pre, post)?;
1680 let transformed_filter = transform_rec(filter, pre, post)?;
1681 (transformed_roots.is_some()
1682 || transformed_heads.is_some()
1683 || transformed_filter.is_some())
1684 .then(|| RevsetExpression::HeadsRange {
1685 roots: transformed_roots.unwrap_or_else(|| roots.clone()),
1686 heads: transformed_heads.unwrap_or_else(|| heads.clone()),
1687 parents_range: parents_range.clone(),
1688 filter: transformed_filter.unwrap_or_else(|| filter.clone()),
1689 })
1690 }
1691 RevsetExpression::Roots(candidates) => {
1692 transform_rec(candidates, pre, post)?.map(RevsetExpression::Roots)
1693 }
1694 RevsetExpression::Forks => None,
1695 RevsetExpression::ForkPoint(expression) => {
1696 transform_rec(expression, pre, post)?.map(RevsetExpression::ForkPoint)
1697 }
1698 RevsetExpression::MergePoint(expression) => {
1699 transform_rec(expression, pre, post)?.map(RevsetExpression::MergePoint)
1700 }
1701 RevsetExpression::Bisect(expression) => {
1702 transform_rec(expression, pre, post)?.map(RevsetExpression::Bisect)
1703 }
1704 RevsetExpression::HasSize { candidates, count } => {
1705 transform_rec(candidates, pre, post)?.map(|candidates| RevsetExpression::HasSize {
1706 candidates,
1707 count: *count,
1708 })
1709 }
1710 RevsetExpression::Latest { candidates, count } => transform_rec(candidates, pre, post)?
1711 .map(|candidates| RevsetExpression::Latest {
1712 candidates,
1713 count: *count,
1714 }),
1715 RevsetExpression::Filter(_) => None,
1716 RevsetExpression::AsFilter(candidates) => {
1717 transform_rec(candidates, pre, post)?.map(RevsetExpression::AsFilter)
1718 }
1719 RevsetExpression::Divergent => None,
1720 RevsetExpression::AtOperation {
1721 operation,
1722 candidates,
1723 } => transform_rec(candidates, pre, post)?.map(|candidates| {
1724 RevsetExpression::AtOperation {
1725 operation: operation.clone(),
1726 candidates,
1727 }
1728 }),
1729 RevsetExpression::WithinReference {
1730 candidates,
1731 commits,
1732 } => transform_rec(candidates, pre, post)?.map(|candidates| {
1733 RevsetExpression::WithinReference {
1734 candidates,
1735 commits: commits.clone(),
1736 }
1737 }),
1738 RevsetExpression::WithinVisibility {
1739 candidates,
1740 visible_heads,
1741 } => transform_rec(candidates, pre, post)?.map(|candidates| {
1742 RevsetExpression::WithinVisibility {
1743 candidates,
1744 visible_heads: visible_heads.clone(),
1745 }
1746 }),
1747 RevsetExpression::Coalesce(expression1, expression2) => transform_rec_pair(
1748 (expression1, expression2),
1749 pre,
1750 post,
1751 )?
1752 .map(|(expression1, expression2)| RevsetExpression::Coalesce(expression1, expression2)),
1753 RevsetExpression::Present(candidates) => {
1754 transform_rec(candidates, pre, post)?.map(RevsetExpression::Present)
1755 }
1756 RevsetExpression::NotIn(complement) => {
1757 transform_rec(complement, pre, post)?.map(RevsetExpression::NotIn)
1758 }
1759 RevsetExpression::Union(expression1, expression2) => {
1760 transform_rec_pair((expression1, expression2), pre, post)?.map(
1761 |(expression1, expression2)| RevsetExpression::Union(expression1, expression2),
1762 )
1763 }
1764 RevsetExpression::Intersection(expression1, expression2) => {
1765 transform_rec_pair((expression1, expression2), pre, post)?.map(
1766 |(expression1, expression2)| {
1767 RevsetExpression::Intersection(expression1, expression2)
1768 },
1769 )
1770 }
1771 RevsetExpression::Difference(expression1, expression2) => {
1772 transform_rec_pair((expression1, expression2), pre, post)?.map(
1773 |(expression1, expression2)| {
1774 RevsetExpression::Difference(expression1, expression2)
1775 },
1776 )
1777 }
1778 }
1779 .map(Arc::new))
1780 }
1781
1782 #[expect(clippy::type_complexity)]
1783 fn transform_rec_pair<St: ExpressionState, E>(
1784 (expression1, expression2): (&Arc<RevsetExpression<St>>, &Arc<RevsetExpression<St>>),
1785 pre: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1786 post: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1787 ) -> Result<Option<(Arc<RevsetExpression<St>>, Arc<RevsetExpression<St>>)>, E> {
1788 match (
1789 transform_rec(expression1, pre, post)?,
1790 transform_rec(expression2, pre, post)?,
1791 ) {
1792 (Some(new_expression1), Some(new_expression2)) => {
1793 Ok(Some((new_expression1, new_expression2)))
1794 }
1795 (Some(new_expression1), None) => Ok(Some((new_expression1, expression2.clone()))),
1796 (None, Some(new_expression2)) => Ok(Some((expression1.clone(), new_expression2))),
1797 (None, None) => Ok(None),
1798 }
1799 }
1800
1801 fn transform_rec<St: ExpressionState, E>(
1802 expression: &Arc<RevsetExpression<St>>,
1803 pre: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1804 post: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1805 ) -> Result<TransformedExpression<St>, E> {
1806 if let ControlFlow::Break(transformed) = pre(expression)? {
1807 return Ok(transformed);
1808 }
1809 if let Some(new_expression) = transform_child_rec(expression, pre, post)? {
1810 Ok(Some(post(&new_expression)?.unwrap_or(new_expression)))
1812 } else {
1813 post(expression)
1814 }
1815 }
1816
1817 transform_rec(expression, &mut pre, &mut post)
1818}
1819
1820trait ExpressionStateFolder<InSt: ExpressionState, OutSt: ExpressionState> {
1825 type Error;
1826
1827 fn fold_expression(
1830 &mut self,
1831 expression: &RevsetExpression<InSt>,
1832 ) -> Result<Arc<RevsetExpression<OutSt>>, Self::Error> {
1833 fold_child_expression_state(self, expression)
1834 }
1835
1836 fn fold_commit_ref(
1838 &mut self,
1839 commit_ref: &InSt::CommitRef,
1840 ) -> Result<Arc<RevsetExpression<OutSt>>, Self::Error>;
1841
1842 fn fold_at_operation(
1844 &mut self,
1845 operation: &InSt::Operation,
1846 candidates: &RevsetExpression<InSt>,
1847 ) -> Result<Arc<RevsetExpression<OutSt>>, Self::Error>;
1848}
1849
1850fn fold_child_expression_state<InSt, OutSt, F>(
1852 folder: &mut F,
1853 expression: &RevsetExpression<InSt>,
1854) -> Result<Arc<RevsetExpression<OutSt>>, F::Error>
1855where
1856 InSt: ExpressionState,
1857 OutSt: ExpressionState,
1858 F: ExpressionStateFolder<InSt, OutSt> + ?Sized,
1859{
1860 let expression: Arc<_> = match expression {
1861 RevsetExpression::None => RevsetExpression::None.into(),
1862 RevsetExpression::All => RevsetExpression::All.into(),
1863 RevsetExpression::VisibleHeads => RevsetExpression::VisibleHeads.into(),
1864 RevsetExpression::VisibleHeadsOrReferenced => {
1865 RevsetExpression::VisibleHeadsOrReferenced.into()
1866 }
1867 RevsetExpression::Root => RevsetExpression::Root.into(),
1868 RevsetExpression::Commits(ids) => RevsetExpression::Commits(ids.clone()).into(),
1869 RevsetExpression::CommitRef(commit_ref) => folder.fold_commit_ref(commit_ref)?,
1870 RevsetExpression::Ancestors {
1871 heads,
1872 generation,
1873 parents_range,
1874 } => {
1875 let heads = folder.fold_expression(heads)?;
1876 let generation = generation.clone();
1877 let parents_range = parents_range.clone();
1878 RevsetExpression::Ancestors {
1879 heads,
1880 generation,
1881 parents_range,
1882 }
1883 .into()
1884 }
1885 RevsetExpression::Descendants { roots, generation } => {
1886 let roots = folder.fold_expression(roots)?;
1887 let generation = generation.clone();
1888 RevsetExpression::Descendants { roots, generation }.into()
1889 }
1890 RevsetExpression::Range {
1891 roots,
1892 heads,
1893 generation,
1894 parents_range,
1895 } => {
1896 let roots = folder.fold_expression(roots)?;
1897 let heads = folder.fold_expression(heads)?;
1898 let generation = generation.clone();
1899 let parents_range = parents_range.clone();
1900 RevsetExpression::Range {
1901 roots,
1902 heads,
1903 generation,
1904 parents_range,
1905 }
1906 .into()
1907 }
1908 RevsetExpression::DagRange { roots, heads } => {
1909 let roots = folder.fold_expression(roots)?;
1910 let heads = folder.fold_expression(heads)?;
1911 RevsetExpression::DagRange { roots, heads }.into()
1912 }
1913 RevsetExpression::Reachable { sources, domain } => {
1914 let sources = folder.fold_expression(sources)?;
1915 let domain = folder.fold_expression(domain)?;
1916 RevsetExpression::Reachable { sources, domain }.into()
1917 }
1918 RevsetExpression::Heads(heads) => {
1919 let heads = folder.fold_expression(heads)?;
1920 RevsetExpression::Heads(heads).into()
1921 }
1922 RevsetExpression::HeadsRange {
1923 roots,
1924 heads,
1925 parents_range,
1926 filter,
1927 } => {
1928 let roots = folder.fold_expression(roots)?;
1929 let heads = folder.fold_expression(heads)?;
1930 let parents_range = parents_range.clone();
1931 let filter = folder.fold_expression(filter)?;
1932 RevsetExpression::HeadsRange {
1933 roots,
1934 heads,
1935 parents_range,
1936 filter,
1937 }
1938 .into()
1939 }
1940 RevsetExpression::Roots(roots) => {
1941 let roots = folder.fold_expression(roots)?;
1942 RevsetExpression::Roots(roots).into()
1943 }
1944 RevsetExpression::Forks => RevsetExpression::Forks.into(),
1945 RevsetExpression::ForkPoint(expression) => {
1946 let expression = folder.fold_expression(expression)?;
1947 RevsetExpression::ForkPoint(expression).into()
1948 }
1949 RevsetExpression::MergePoint(expression) => {
1950 let expression = folder.fold_expression(expression)?;
1951 RevsetExpression::MergePoint(expression).into()
1952 }
1953 RevsetExpression::Bisect(expression) => {
1954 let expression = folder.fold_expression(expression)?;
1955 RevsetExpression::Bisect(expression).into()
1956 }
1957 RevsetExpression::HasSize { candidates, count } => {
1958 let candidates = folder.fold_expression(candidates)?;
1959 let count = *count;
1960 RevsetExpression::HasSize { candidates, count }.into()
1961 }
1962 RevsetExpression::Latest { candidates, count } => {
1963 let candidates = folder.fold_expression(candidates)?;
1964 let count = *count;
1965 RevsetExpression::Latest { candidates, count }.into()
1966 }
1967 RevsetExpression::Filter(predicate) => RevsetExpression::Filter(predicate.clone()).into(),
1968 RevsetExpression::AsFilter(candidates) => {
1969 let candidates = folder.fold_expression(candidates)?;
1970 RevsetExpression::AsFilter(candidates).into()
1971 }
1972 RevsetExpression::Divergent => RevsetExpression::Divergent.into(),
1973 RevsetExpression::AtOperation {
1974 operation,
1975 candidates,
1976 } => folder.fold_at_operation(operation, candidates)?,
1977 RevsetExpression::WithinReference {
1978 candidates,
1979 commits,
1980 } => {
1981 let candidates = folder.fold_expression(candidates)?;
1982 let commits = commits.clone();
1983 RevsetExpression::WithinReference {
1984 candidates,
1985 commits,
1986 }
1987 .into()
1988 }
1989 RevsetExpression::WithinVisibility {
1990 candidates,
1991 visible_heads,
1992 } => {
1993 let candidates = folder.fold_expression(candidates)?;
1994 let visible_heads = visible_heads.clone();
1995 RevsetExpression::WithinVisibility {
1996 candidates,
1997 visible_heads,
1998 }
1999 .into()
2000 }
2001 RevsetExpression::Coalesce(expression1, expression2) => {
2002 let expression1 = folder.fold_expression(expression1)?;
2003 let expression2 = folder.fold_expression(expression2)?;
2004 RevsetExpression::Coalesce(expression1, expression2).into()
2005 }
2006 RevsetExpression::Present(candidates) => {
2007 let candidates = folder.fold_expression(candidates)?;
2008 RevsetExpression::Present(candidates).into()
2009 }
2010 RevsetExpression::NotIn(complement) => {
2011 let complement = folder.fold_expression(complement)?;
2012 RevsetExpression::NotIn(complement).into()
2013 }
2014 RevsetExpression::Union(expression1, expression2) => {
2015 let expression1 = folder.fold_expression(expression1)?;
2016 let expression2 = folder.fold_expression(expression2)?;
2017 RevsetExpression::Union(expression1, expression2).into()
2018 }
2019 RevsetExpression::Intersection(expression1, expression2) => {
2020 let expression1 = folder.fold_expression(expression1)?;
2021 let expression2 = folder.fold_expression(expression2)?;
2022 RevsetExpression::Intersection(expression1, expression2).into()
2023 }
2024 RevsetExpression::Difference(expression1, expression2) => {
2025 let expression1 = folder.fold_expression(expression1)?;
2026 let expression2 = folder.fold_expression(expression2)?;
2027 RevsetExpression::Difference(expression1, expression2).into()
2028 }
2029 };
2030 Ok(expression)
2031}
2032
2033fn resolve_referenced_commits<St: ExpressionState>(
2037 expression: &Arc<RevsetExpression<St>>,
2038) -> TransformedExpression<St> {
2039 let omit_empty = true;
2041 resolve_referenced_commits_sub(expression, omit_empty)
2042}
2043
2044fn resolve_referenced_commits_sub<St: ExpressionState>(
2045 expression: &Arc<RevsetExpression<St>>,
2046 omit_empty: bool,
2047) -> TransformedExpression<St> {
2048 if matches!(
2050 expression.as_ref(),
2051 RevsetExpression::WithinReference { .. }
2052 ) {
2053 return None;
2054 }
2055
2056 let mut inner_commits = Vec::new();
2058 let mut outer_commits = Vec::new();
2059 let transformed = transform_expression(
2060 expression,
2061 |expression| match expression.as_ref() {
2062 RevsetExpression::WithinReference { commits, .. } => {
2064 inner_commits.extend_from_slice(commits);
2065 ControlFlow::Break(None)
2066 }
2067 RevsetExpression::WithinVisibility {
2069 candidates,
2070 visible_heads,
2071 } => {
2072 inner_commits.extend_from_slice(visible_heads);
2074 let transformed = resolve_referenced_commits_sub(candidates, false);
2077 match transformed.as_deref().unwrap_or(candidates) {
2079 RevsetExpression::WithinReference { commits, .. } => {
2080 inner_commits.extend_from_slice(commits);
2081 }
2082 _ => unreachable!("WithinReference should never be omitted"),
2083 }
2084 ControlFlow::Break(transformed.map(|candidates| {
2085 Arc::new(RevsetExpression::WithinVisibility {
2086 candidates,
2087 visible_heads: visible_heads.clone(),
2088 })
2089 }))
2090 }
2091 _ => ControlFlow::Continue(()),
2092 },
2093 |expression| {
2094 if let RevsetExpression::Commits(commits) = expression.as_ref() {
2095 outer_commits.extend_from_slice(commits);
2096 }
2097 None
2098 },
2099 );
2100
2101 outer_commits.extend(inner_commits);
2104 if omit_empty && outer_commits.is_empty() {
2105 return transformed;
2106 }
2107 Some(Arc::new(RevsetExpression::WithinReference {
2108 candidates: transformed.unwrap_or_else(|| expression.clone()),
2109 commits: outer_commits,
2110 }))
2111}
2112
2113fn flatten_intersections<St: ExpressionState>(
2116 expression: &Arc<RevsetExpression<St>>,
2117) -> TransformedExpression<St> {
2118 fn flatten<St: ExpressionState>(
2119 expression1: &Arc<RevsetExpression<St>>,
2120 expression2: &Arc<RevsetExpression<St>>,
2121 ) -> TransformedExpression<St> {
2122 let recurse = |a, b| flatten(a, b).unwrap_or_else(|| a.intersection(b));
2123
2124 match expression2.as_ref() {
2125 RevsetExpression::Intersection(inner1, inner2) => {
2127 Some(recurse(expression1, inner1).intersection(inner2))
2128 }
2129 _ => None,
2130 }
2131 }
2132
2133 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2134 RevsetExpression::Intersection(expression1, expression2) => {
2135 flatten(expression1, expression2)
2136 }
2137 _ => None,
2138 })
2139}
2140
2141fn sort_intersection_by_key<St: ExpressionState, T: Ord>(
2145 base: &Arc<RevsetExpression<St>>,
2146 expression: &Arc<RevsetExpression<St>>,
2147 mut get_key: impl FnMut(&RevsetExpression<St>) -> T,
2148) -> TransformedExpression<St> {
2149 fn sort_intersection_helper<St: ExpressionState, T: Ord>(
2152 base: &Arc<RevsetExpression<St>>,
2153 expression: &Arc<RevsetExpression<St>>,
2154 expression_key: T,
2155 mut get_key: impl FnMut(&RevsetExpression<St>) -> T,
2156 ) -> TransformedExpression<St> {
2157 if let RevsetExpression::Intersection(inner1, inner2) = base.as_ref() {
2158 (expression_key < get_key(inner2)).then(|| {
2160 sort_intersection_helper(inner1, expression, expression_key, get_key)
2161 .unwrap_or_else(|| inner1.intersection(expression))
2162 .intersection(inner2)
2163 })
2164 } else {
2165 (expression_key < get_key(base)).then(|| expression.intersection(base))
2167 }
2168 }
2169
2170 sort_intersection_helper(base, expression, get_key(expression), get_key)
2171}
2172
2173fn sort_negations_and_ancestors<St: ExpressionState>(
2178 expression: &Arc<RevsetExpression<St>>,
2179) -> TransformedExpression<St> {
2180 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
2181 enum AncestorsOrder {
2182 NegatedAncestors,
2183 Ancestors,
2184 Other,
2185 NegatedOther,
2186 }
2187
2188 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2189 RevsetExpression::Intersection(expression1, expression2) => {
2190 sort_intersection_by_key(expression1, expression2, |expression| match expression {
2191 RevsetExpression::Ancestors {
2192 heads: _,
2193 generation: Range { end: u64::MAX, .. },
2194 parents_range: _,
2195 } => AncestorsOrder::Ancestors,
2196 RevsetExpression::NotIn(complement) => match complement.as_ref() {
2197 RevsetExpression::Ancestors {
2198 heads: _,
2199 generation: Range { end: u64::MAX, .. },
2200 parents_range: PARENTS_RANGE_FULL,
2203 } => AncestorsOrder::NegatedAncestors,
2204 _ => AncestorsOrder::NegatedOther,
2205 },
2206 _ => AncestorsOrder::Other,
2207 })
2208 }
2209 _ => None,
2210 })
2211}
2212
2213fn internalize_filter<St: ExpressionState>(
2222 expression: &Arc<RevsetExpression<St>>,
2223) -> TransformedExpression<St> {
2224 fn get_filter<St: ExpressionState>(
2225 expression: &Arc<RevsetExpression<St>>,
2226 ) -> Option<&Arc<RevsetExpression<St>>> {
2227 match expression.as_ref() {
2228 RevsetExpression::Filter(_) => Some(expression),
2229 RevsetExpression::AsFilter(candidates) => Some(candidates),
2230 _ => None,
2231 }
2232 }
2233
2234 fn mark_filter<St: ExpressionState>(
2235 expression: Arc<RevsetExpression<St>>,
2236 ) -> Arc<RevsetExpression<St>> {
2237 Arc::new(RevsetExpression::AsFilter(expression))
2238 }
2239
2240 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2241 RevsetExpression::Present(e) => get_filter(e).map(|f| mark_filter(f.present())),
2243 RevsetExpression::NotIn(e) => get_filter(e).map(|f| mark_filter(f.negated())),
2244 RevsetExpression::Union(e1, e2) => {
2245 let f1 = get_filter(e1);
2246 let f2 = get_filter(e2);
2247 (f1.is_some() || f2.is_some())
2248 .then(|| mark_filter(f1.unwrap_or(e1).union(f2.unwrap_or(e2))))
2249 }
2250 RevsetExpression::Intersection(e1, e2) => match (get_filter(e1), get_filter(e2)) {
2256 (Some(f1), Some(f2)) => Some(mark_filter(f1.intersection(f2))),
2258 (Some(_), None) => Some(e2.intersection(e1)),
2260 (None, Some(f2)) => match e1.as_ref() {
2262 RevsetExpression::Intersection(e1a, e1b) => {
2263 get_filter(e1b).map(|f1b| e1a.intersection(&mark_filter(f1b.intersection(f2))))
2264 }
2265 _ => None,
2266 },
2267 (None, None) => match e1.as_ref() {
2269 RevsetExpression::Intersection(e1a, e1b) => {
2270 get_filter(e1b).map(|_| e1a.intersection(e2).intersection(e1b))
2271 }
2272 _ => None,
2273 },
2274 },
2275 _ => None,
2277 })
2278}
2279
2280fn fold_redundant_expression<St: ExpressionState>(
2285 expression: &Arc<RevsetExpression<St>>,
2286) -> TransformedExpression<St> {
2287 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2288 RevsetExpression::Commits(commits) if commits.is_empty() => Some(RevsetExpression::none()),
2289 RevsetExpression::NotIn(outer) => match outer.as_ref() {
2290 RevsetExpression::NotIn(inner) => Some(inner.clone()),
2291 RevsetExpression::None => Some(RevsetExpression::all()),
2292 RevsetExpression::All => Some(RevsetExpression::none()),
2293 _ => None,
2294 },
2295 RevsetExpression::Union(expression1, expression2) => {
2296 match (expression1.as_ref(), expression2.as_ref()) {
2297 (_, RevsetExpression::None) => Some(expression1.clone()),
2298 (RevsetExpression::None, _) => Some(expression2.clone()),
2299 (RevsetExpression::All, _) => Some(RevsetExpression::all()),
2300 (_, RevsetExpression::All) => Some(RevsetExpression::all()),
2301 _ => None,
2302 }
2303 }
2304 RevsetExpression::Intersection(expression1, expression2) => {
2305 match (expression1.as_ref(), expression2.as_ref()) {
2306 (RevsetExpression::None, _) => Some(RevsetExpression::none()),
2307 (_, RevsetExpression::None) => Some(RevsetExpression::none()),
2308 (_, RevsetExpression::All) => Some(expression1.clone()),
2309 (RevsetExpression::All, _) => Some(expression2.clone()),
2310 _ => None,
2311 }
2312 }
2313 _ => None,
2314 })
2315}
2316
2317fn ancestors_to_heads<St: ExpressionState>(
2321 expression: &RevsetExpression<St>,
2322) -> Result<Arc<RevsetExpression<St>>, ()> {
2323 match ancestors_to_heads_and_parents_range(expression) {
2324 Ok((heads, PARENTS_RANGE_FULL)) => Ok(heads),
2325 _ => Err(()),
2326 }
2327}
2328
2329fn ancestors_to_heads_and_parents_range<St: ExpressionState>(
2330 expression: &RevsetExpression<St>,
2331) -> Result<(Arc<RevsetExpression<St>>, Range<u32>), ()> {
2332 match expression {
2333 RevsetExpression::Ancestors {
2334 heads,
2335 generation: GENERATION_RANGE_FULL,
2336 parents_range,
2337 } => Ok((heads.clone(), parents_range.clone())),
2338 RevsetExpression::Ancestors {
2339 heads,
2340 generation: Range {
2341 start,
2342 end: u64::MAX,
2343 },
2344 parents_range,
2345 } => Ok((
2346 Arc::new(RevsetExpression::Ancestors {
2347 heads: heads.clone(),
2348 generation: (*start)..start.saturating_add(1),
2349 parents_range: parents_range.clone(),
2350 }),
2351 parents_range.clone(),
2352 )),
2353 _ => Err(()),
2354 }
2355}
2356
2357fn fold_ancestors_union<St: ExpressionState>(
2362 expression: &Arc<RevsetExpression<St>>,
2363) -> TransformedExpression<St> {
2364 fn union_ancestors<St: ExpressionState>(
2365 expression1: &Arc<RevsetExpression<St>>,
2366 expression2: &Arc<RevsetExpression<St>>,
2367 ) -> TransformedExpression<St> {
2368 let heads1 = ancestors_to_heads(expression1).ok()?;
2369 let heads2 = ancestors_to_heads(expression2).ok()?;
2370 Some(heads1.union(&heads2).ancestors())
2371 }
2372
2373 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2374 RevsetExpression::Union(expression1, expression2) => {
2375 union_ancestors(expression1, expression2)
2377 }
2378 RevsetExpression::Intersection(expression1, expression2) => {
2379 match (expression1.as_ref(), expression2.as_ref()) {
2380 (RevsetExpression::NotIn(complement1), RevsetExpression::NotIn(complement2)) => {
2382 union_ancestors(complement1, complement2).map(|expression| expression.negated())
2383 }
2384 _ => None,
2385 }
2386 }
2387 _ => None,
2388 })
2389}
2390
2391fn fold_heads_range<St: ExpressionState>(
2398 expression: &Arc<RevsetExpression<St>>,
2399) -> TransformedExpression<St> {
2400 struct FilteredRange<St: ExpressionState> {
2402 roots: Arc<RevsetExpression<St>>,
2403 heads_and_parents_range: Option<(Arc<RevsetExpression<St>>, Range<u32>)>,
2404 filter: Arc<RevsetExpression<St>>,
2405 }
2406
2407 impl<St: ExpressionState> FilteredRange<St> {
2408 fn new(roots: Arc<RevsetExpression<St>>) -> Self {
2409 Self {
2411 roots,
2412 heads_and_parents_range: None,
2413 filter: RevsetExpression::all(),
2414 }
2415 }
2416
2417 fn add(mut self, expression: &Arc<RevsetExpression<St>>) -> Self {
2418 if self.heads_and_parents_range.is_none() {
2419 if let Ok(heads_and_parents_range) =
2421 ancestors_to_heads_and_parents_range(expression)
2422 {
2423 self.heads_and_parents_range = Some(heads_and_parents_range);
2424 return self;
2425 }
2426 }
2427 self.add_filter(expression)
2428 }
2429
2430 fn add_filter(mut self, expression: &Arc<RevsetExpression<St>>) -> Self {
2431 self.filter = if let RevsetExpression::All = self.filter.as_ref() {
2432 expression.clone()
2434 } else {
2435 self.filter.intersection(expression)
2436 };
2437 self
2438 }
2439 }
2440
2441 fn to_filtered_range<St: ExpressionState>(
2442 expression: &Arc<RevsetExpression<St>>,
2443 ) -> Option<FilteredRange<St>> {
2444 if let Ok(heads_and_parents_range) = ancestors_to_heads_and_parents_range(expression) {
2448 return Some(FilteredRange {
2449 roots: RevsetExpression::none(),
2450 heads_and_parents_range: Some(heads_and_parents_range),
2451 filter: RevsetExpression::all(),
2452 });
2453 }
2454 match expression.as_ref() {
2455 RevsetExpression::NotIn(complement) => {
2458 if let Ok(roots) = ancestors_to_heads(complement) {
2459 Some(FilteredRange::new(roots))
2460 } else {
2461 Some(FilteredRange::new(RevsetExpression::none()).add_filter(expression))
2464 }
2465 }
2466 RevsetExpression::All | RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_) => {
2468 Some(FilteredRange::new(RevsetExpression::none()).add_filter(expression))
2469 }
2470 RevsetExpression::Intersection(expression1, expression2) => {
2473 to_filtered_range(expression1).map(|filtered_range| filtered_range.add(expression2))
2474 }
2475 _ => None,
2476 }
2477 }
2478
2479 fn to_heads_range<St: ExpressionState>(
2480 candidates: &Arc<RevsetExpression<St>>,
2481 ) -> Option<Arc<RevsetExpression<St>>> {
2482 to_filtered_range(candidates).map(|filtered_range| {
2483 let (heads, parents_range) =
2484 filtered_range.heads_and_parents_range.unwrap_or_else(|| {
2485 (
2486 RevsetExpression::visible_heads_or_referenced(),
2487 PARENTS_RANGE_FULL,
2488 )
2489 });
2490 RevsetExpression::HeadsRange {
2491 roots: filtered_range.roots,
2492 heads,
2493 parents_range,
2494 filter: filtered_range.filter,
2495 }
2496 .into()
2497 })
2498 }
2499
2500 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2501 RevsetExpression::Ancestors {
2504 heads,
2505 generation: GENERATION_RANGE_FULL,
2508 parents_range: PARENTS_RANGE_FULL,
2509 } => to_heads_range(heads).map(|heads| heads.ancestors()),
2510 RevsetExpression::Heads(candidates) => to_heads_range(candidates),
2513 _ => None,
2514 })
2515}
2516
2517fn to_difference_range<St: ExpressionState>(
2518 expression: &Arc<RevsetExpression<St>>,
2519 complement: &Arc<RevsetExpression<St>>,
2520) -> TransformedExpression<St> {
2521 let RevsetExpression::Ancestors {
2522 heads,
2523 generation,
2524 parents_range,
2525 } = expression.as_ref()
2526 else {
2527 return None;
2528 };
2529 let roots = ancestors_to_heads(complement).ok()?;
2530 Some(Arc::new(RevsetExpression::Range {
2533 roots,
2534 heads: heads.clone(),
2535 generation: generation.clone(),
2536 parents_range: parents_range.clone(),
2537 }))
2538}
2539
2540fn fold_difference<St: ExpressionState>(
2543 expression: &Arc<RevsetExpression<St>>,
2544) -> TransformedExpression<St> {
2545 fn to_difference<St: ExpressionState>(
2546 expression: &Arc<RevsetExpression<St>>,
2547 complement: &Arc<RevsetExpression<St>>,
2548 ) -> Arc<RevsetExpression<St>> {
2549 to_difference_range(expression, complement).unwrap_or_else(|| expression.minus(complement))
2550 }
2551
2552 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2553 RevsetExpression::Intersection(expression1, expression2) => {
2554 match (expression1.as_ref(), expression2.as_ref()) {
2555 (_, RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_)) => None,
2557 (_, RevsetExpression::NotIn(complement)) => {
2558 Some(to_difference(expression1, complement))
2559 }
2560 (RevsetExpression::NotIn(complement), _) => {
2561 Some(to_difference(expression2, complement))
2562 }
2563 _ => None,
2564 }
2565 }
2566 _ => None,
2567 })
2568}
2569
2570fn fold_not_in_ancestors<St: ExpressionState>(
2575 expression: &Arc<RevsetExpression<St>>,
2576) -> TransformedExpression<St> {
2577 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2578 RevsetExpression::NotIn(complement)
2579 if matches!(complement.as_ref(), RevsetExpression::Ancestors { .. }) =>
2580 {
2581 to_difference_range(
2584 &RevsetExpression::visible_heads_or_referenced().ancestors(),
2585 complement,
2586 )
2587 }
2588 _ => None,
2589 })
2590}
2591
2592fn unfold_difference<St: ExpressionState>(
2597 expression: &Arc<RevsetExpression<St>>,
2598) -> TransformedExpression<St> {
2599 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2600 RevsetExpression::Range {
2602 roots,
2603 heads,
2604 parents_range,
2605 generation,
2606 } => {
2607 let heads_ancestors = Arc::new(RevsetExpression::Ancestors {
2608 heads: heads.clone(),
2609 generation: generation.clone(),
2610 parents_range: parents_range.clone(),
2611 });
2612 Some(heads_ancestors.intersection(&roots.ancestors().negated()))
2613 }
2614 RevsetExpression::Difference(expression1, expression2) => {
2615 Some(expression1.intersection(&expression2.negated()))
2616 }
2617 _ => None,
2618 })
2619}
2620
2621fn fold_generation<St: ExpressionState>(
2624 expression: &Arc<RevsetExpression<St>>,
2625) -> TransformedExpression<St> {
2626 fn add_generation(generation1: &Range<u64>, generation2: &Range<u64>) -> Range<u64> {
2627 if generation1.is_empty() || generation2.is_empty() {
2629 GENERATION_RANGE_EMPTY
2630 } else {
2631 let start = u64::saturating_add(generation1.start, generation2.start);
2632 let end = u64::saturating_add(generation1.end, generation2.end - 1);
2633 start..end
2634 }
2635 }
2636
2637 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2638 RevsetExpression::Ancestors {
2639 heads,
2640 generation: generation1,
2641 parents_range: parents1,
2642 } => {
2643 match heads.as_ref() {
2644 RevsetExpression::Ancestors {
2648 heads,
2649 generation: generation2,
2650 parents_range: parents2,
2651 } if parents2 == parents1 => Some(Arc::new(RevsetExpression::Ancestors {
2652 heads: heads.clone(),
2653 generation: add_generation(generation1, generation2),
2654 parents_range: parents1.clone(),
2655 })),
2656 _ => None,
2657 }
2658 }
2659 RevsetExpression::Descendants {
2660 roots,
2661 generation: generation1,
2662 } => {
2663 match roots.as_ref() {
2664 RevsetExpression::Descendants {
2668 roots,
2669 generation: generation2,
2670 } => Some(Arc::new(RevsetExpression::Descendants {
2671 roots: roots.clone(),
2672 generation: add_generation(generation1, generation2),
2673 })),
2674 _ => None,
2675 }
2676 }
2677 _ => None,
2679 })
2680}
2681
2682pub fn optimize<St: ExpressionState>(
2685 expression: Arc<RevsetExpression<St>>,
2686) -> Arc<RevsetExpression<St>> {
2687 let expression = resolve_referenced_commits(&expression).unwrap_or(expression);
2690 let expression = unfold_difference(&expression).unwrap_or(expression);
2691 let expression = fold_redundant_expression(&expression).unwrap_or(expression);
2692 let expression = fold_generation(&expression).unwrap_or(expression);
2693 let expression = flatten_intersections(&expression).unwrap_or(expression);
2694 let expression = sort_negations_and_ancestors(&expression).unwrap_or(expression);
2695 let expression = fold_ancestors_union(&expression).unwrap_or(expression);
2696 let expression = internalize_filter(&expression).unwrap_or(expression);
2697 let expression = fold_heads_range(&expression).unwrap_or(expression);
2698 let expression = fold_difference(&expression).unwrap_or(expression);
2699 fold_not_in_ancestors(&expression).unwrap_or(expression)
2700}
2701
2702pub fn walk_revs(
2706 repo: &dyn Repo,
2707 wanted: &[CommitId],
2708 unwanted: &[CommitId],
2709) -> Result<Box<dyn Revset>, RevsetEvaluationError> {
2710 RevsetExpression::commits(unwanted.to_vec())
2711 .range(&RevsetExpression::commits(wanted.to_vec()))
2712 .evaluate(repo)
2713}
2714
2715fn reload_repo_at_operation(
2716 repo: &dyn Repo,
2717 op_str: &str,
2718) -> Result<Arc<ReadonlyRepo>, RevsetResolutionError> {
2719 let base_repo = repo.base_repo();
2720 let operation = op_walk::resolve_op_with_repo(base_repo, op_str)
2721 .block_on()
2722 .map_err(|err| RevsetResolutionError::Other(err.into()))?;
2723 base_repo
2724 .reload_at(&operation)
2725 .block_on()
2726 .map_err(|err| match err {
2727 RepoLoaderError::Backend(err) => RevsetResolutionError::Backend(err),
2728 RepoLoaderError::Index(_)
2729 | RepoLoaderError::IndexStore(_)
2730 | RepoLoaderError::OpHeadsStoreError(_)
2731 | RepoLoaderError::OpStore(_)
2732 | RepoLoaderError::TransactionCommit(_) => RevsetResolutionError::Other(err.into()),
2733 })
2734}
2735
2736fn resolve_remote_symbol(
2737 repo: &dyn Repo,
2738 symbol: RemoteRefSymbol<'_>,
2739) -> Result<CommitId, RevsetResolutionError> {
2740 let remote_ref = repo.view().get_remote_tag(symbol);
2741 if let Some(id) = to_resolved_ref("remote_tag", symbol, &remote_ref.target)? {
2742 return Ok(id);
2743 }
2744 let remote_ref = repo.view().get_remote_bookmark(symbol);
2745 if let Some(id) = to_resolved_ref("remote_bookmark", symbol, &remote_ref.target)? {
2746 return Ok(id);
2747 }
2748 Err(make_no_such_symbol_error(repo, symbol.to_string()))
2749}
2750
2751fn to_resolved_ref(
2752 kind: &'static str,
2753 symbol: impl ToString,
2754 target: &RefTarget,
2755) -> Result<Option<CommitId>, RevsetResolutionError> {
2756 match target.as_resolved() {
2757 Some(Some(id)) => Ok(Some(id.clone())),
2758 Some(None) => Ok(None),
2759 None => Err(RevsetResolutionError::ConflictedRef {
2760 kind,
2761 symbol: symbol.to_string(),
2762 targets: target.present_adds().cloned().collect(),
2763 }),
2764 }
2765}
2766
2767fn all_formatted_ref_symbols<'a>(
2768 all_refs: impl Iterator<Item = (&'a RefName, LocalRemoteRefTarget<'a>)>,
2769 include_synced_remotes: bool,
2770) -> impl Iterator<Item = String> {
2771 all_refs.flat_map(move |(name, targets)| {
2772 let local_target = targets.local_target;
2773 let local_symbol = local_target
2774 .is_present()
2775 .then(|| format_symbol(name.as_str()));
2776 let remote_symbols = targets
2777 .remote_refs
2778 .into_iter()
2779 .filter(move |&(_, remote_ref)| {
2780 include_synced_remotes
2781 || !remote_ref.is_tracked()
2782 || remote_ref.target != *local_target
2783 })
2784 .map(move |(remote, _)| format_remote_symbol(name.as_str(), remote.as_str()));
2785 local_symbol.into_iter().chain(remote_symbols)
2786 })
2787}
2788
2789fn make_no_such_symbol_error(repo: &dyn Repo, name: String) -> RevsetResolutionError {
2790 let include_synced_remotes = name.contains('@');
2791 let tag_names = all_formatted_ref_symbols(repo.view().tags(), include_synced_remotes);
2792 let bookmark_names = all_formatted_ref_symbols(repo.view().bookmarks(), include_synced_remotes);
2793 let mut candidates = collect_similar(&name, itertools::chain(tag_names, bookmark_names));
2794 candidates.dedup(); RevsetResolutionError::NoSuchRevision { name, candidates }
2796}
2797
2798pub trait PartialSymbolResolver {
2802 fn resolve_symbol(
2803 &self,
2804 repo: &dyn Repo,
2805 symbol: &str,
2806 ) -> Result<Option<CommitId>, RevsetResolutionError>;
2807}
2808
2809struct TagResolver;
2810
2811impl PartialSymbolResolver for TagResolver {
2812 fn resolve_symbol(
2813 &self,
2814 repo: &dyn Repo,
2815 symbol: &str,
2816 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2817 let target = repo.view().get_local_tag(symbol.as_ref());
2818 to_resolved_ref("tag", symbol, target)
2819 }
2820}
2821
2822struct BookmarkResolver;
2823
2824impl PartialSymbolResolver for BookmarkResolver {
2825 fn resolve_symbol(
2826 &self,
2827 repo: &dyn Repo,
2828 symbol: &str,
2829 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2830 let target = repo.view().get_local_bookmark(symbol.as_ref());
2831 to_resolved_ref("bookmark", symbol, target)
2832 }
2833}
2834
2835const DEFAULT_RESOLVERS: &[&dyn PartialSymbolResolver] = &[&TagResolver, &BookmarkResolver];
2836
2837struct CommitPrefixResolver<'a> {
2838 context_repo: &'a dyn Repo,
2839 context: Option<&'a IdPrefixContext>,
2840}
2841
2842impl CommitPrefixResolver<'_> {
2843 fn try_resolve(
2844 &self,
2845 repo: &dyn Repo,
2846 prefix: &HexPrefix,
2847 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2848 let index = self
2849 .context
2850 .map(|ctx| ctx.populate(self.context_repo))
2851 .transpose()
2852 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2853 .unwrap_or(IdPrefixIndex::empty());
2854 match index
2855 .resolve_commit_prefix(repo, prefix)
2856 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2857 {
2858 PrefixResolution::AmbiguousMatch => {
2859 Err(RevsetResolutionError::AmbiguousCommitIdPrefix(prefix.hex()))
2860 }
2861 PrefixResolution::SingleMatch(id) => Ok(Some(id)),
2862 PrefixResolution::NoMatch => Ok(None),
2863 }
2864 }
2865}
2866
2867impl PartialSymbolResolver for CommitPrefixResolver<'_> {
2868 fn resolve_symbol(
2869 &self,
2870 repo: &dyn Repo,
2871 symbol: &str,
2872 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2873 if let Some(prefix) = HexPrefix::try_from_hex(symbol) {
2874 self.try_resolve(repo, &prefix)
2875 } else {
2876 Ok(None)
2877 }
2878 }
2879}
2880
2881struct ChangePrefixResolver<'a> {
2882 context_repo: &'a dyn Repo,
2883 context: Option<&'a IdPrefixContext>,
2884}
2885
2886impl ChangePrefixResolver<'_> {
2887 fn try_resolve(
2888 &self,
2889 repo: &dyn Repo,
2890 prefix: &HexPrefix,
2891 ) -> Result<Option<ResolvedChangeTargets>, RevsetResolutionError> {
2892 let index = self
2893 .context
2894 .map(|ctx| ctx.populate(self.context_repo))
2895 .transpose()
2896 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2897 .unwrap_or(IdPrefixIndex::empty());
2898 match index
2899 .resolve_change_prefix(repo, prefix)
2900 .block_on()
2901 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2902 {
2903 PrefixResolution::AmbiguousMatch => Err(
2904 RevsetResolutionError::AmbiguousChangeIdPrefix(prefix.reverse_hex()),
2905 ),
2906 PrefixResolution::SingleMatch(ids) => Ok(Some(ids)),
2907 PrefixResolution::NoMatch => Ok(None),
2908 }
2909 }
2910}
2911
2912impl PartialSymbolResolver for ChangePrefixResolver<'_> {
2913 fn resolve_symbol(
2914 &self,
2915 repo: &dyn Repo,
2916 symbol: &str,
2917 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2918 let (change_id, offset) = if let Some((prefix, suffix)) = symbol.split_once('/') {
2919 if prefix.is_empty() || suffix.is_empty() {
2920 return Ok(None);
2921 }
2922 let Ok(offset) = suffix.parse() else {
2923 return Ok(None);
2924 };
2925 (prefix, Some(offset))
2926 } else {
2927 (symbol, None)
2928 };
2929 let Some(prefix) = HexPrefix::try_from_reverse_hex(change_id) else {
2930 return Ok(None);
2931 };
2932 let Some(targets) = self.try_resolve(repo, &prefix)? else {
2933 return Ok(None);
2934 };
2935 if let Some(offset) = offset {
2936 return Ok(targets.at_offset(offset).cloned());
2937 }
2938 match targets.visible_with_offsets().at_most_one() {
2939 Ok(maybe_resolved) => Ok(maybe_resolved.map(|(_, target)| target.clone())),
2940 Err(visible_targets) => Err(RevsetResolutionError::DivergentChangeId {
2941 symbol: change_id.to_owned(),
2942 visible_targets: visible_targets
2943 .map(|(i, target)| (i, target.clone()))
2944 .collect_vec(),
2945 }),
2946 }
2947 }
2948}
2949
2950pub trait SymbolResolverExtension: Send + Sync {
2957 fn new_resolvers<'a>(
2962 &self,
2963 context_repo: &'a dyn Repo,
2964 ) -> Vec<Box<dyn PartialSymbolResolver + 'a>>;
2965}
2966
2967pub struct SymbolResolver<'a> {
2970 commit_id_resolver: CommitPrefixResolver<'a>,
2971 change_id_resolver: ChangePrefixResolver<'a>,
2972 extensions: Vec<Box<dyn PartialSymbolResolver + 'a>>,
2973}
2974
2975impl<'a> SymbolResolver<'a> {
2976 pub fn new(
2979 context_repo: &'a dyn Repo,
2980 extensions: &[impl AsRef<dyn SymbolResolverExtension>],
2981 ) -> Self {
2982 SymbolResolver {
2983 commit_id_resolver: CommitPrefixResolver {
2984 context_repo,
2985 context: None,
2986 },
2987 change_id_resolver: ChangePrefixResolver {
2988 context_repo,
2989 context: None,
2990 },
2991 extensions: extensions
2992 .iter()
2993 .flat_map(|ext| ext.as_ref().new_resolvers(context_repo))
2994 .collect(),
2995 }
2996 }
2997
2998 pub fn with_id_prefix_context(mut self, id_prefix_context: &'a IdPrefixContext) -> Self {
2999 self.commit_id_resolver.context = Some(id_prefix_context);
3000 self.change_id_resolver.context = Some(id_prefix_context);
3001 self
3002 }
3003
3004 fn partial_resolvers(&self) -> impl Iterator<Item = &(dyn PartialSymbolResolver + 'a)> {
3005 let prefix_resolvers: [&dyn PartialSymbolResolver; 2] =
3006 [&self.commit_id_resolver, &self.change_id_resolver];
3007 itertools::chain!(
3008 DEFAULT_RESOLVERS.iter().copied(),
3009 prefix_resolvers,
3010 self.extensions.iter().map(|e| e.as_ref())
3011 )
3012 }
3013
3014 pub fn resolve_symbol(
3016 &self,
3017 repo: &dyn Repo,
3018 symbol: &str,
3019 ) -> Result<CommitId, RevsetResolutionError> {
3020 if symbol.is_empty() {
3021 return Err(RevsetResolutionError::EmptyString);
3022 }
3023
3024 for partial_resolver in self.partial_resolvers() {
3025 if let Some(id) = partial_resolver.resolve_symbol(repo, symbol)? {
3026 return Ok(id);
3027 }
3028 }
3029
3030 Err(make_no_such_symbol_error(repo, format_symbol(symbol)))
3031 }
3032}
3033
3034fn resolve_commit_ref(
3035 repo: &dyn Repo,
3036 commit_ref: &RevsetCommitRef,
3037 symbol_resolver: &SymbolResolver,
3038) -> Result<Vec<CommitId>, RevsetResolutionError> {
3039 match commit_ref {
3040 RevsetCommitRef::Symbol(symbol) => {
3041 let commit_id = symbol_resolver.resolve_symbol(repo, symbol)?;
3042 Ok(vec![commit_id])
3043 }
3044 RevsetCommitRef::RemoteSymbol(symbol) => {
3045 let commit_id = resolve_remote_symbol(repo, symbol.as_ref())?;
3046 Ok(vec![commit_id])
3047 }
3048 RevsetCommitRef::WorkingCopy(name) => {
3049 if let Some(commit_id) = repo.view().get_wc_commit_id(name) {
3050 Ok(vec![commit_id.clone()])
3051 } else {
3052 Err(RevsetResolutionError::WorkspaceMissingWorkingCopy { name: name.clone() })
3053 }
3054 }
3055 RevsetCommitRef::WorkingCopies => {
3056 let wc_commits = repo.view().wc_commit_ids().values().cloned().collect_vec();
3057 Ok(wc_commits)
3058 }
3059 RevsetCommitRef::ChangeId(prefix) => {
3060 let resolver = &symbol_resolver.change_id_resolver;
3061 Ok(resolver
3062 .try_resolve(repo, prefix)?
3063 .and_then(ResolvedChangeTargets::into_visible)
3064 .unwrap_or_else(Vec::new))
3065 }
3066 RevsetCommitRef::CommitId(prefix) => {
3067 let resolver = &symbol_resolver.commit_id_resolver;
3068 Ok(resolver.try_resolve(repo, prefix)?.into_iter().collect())
3069 }
3070 RevsetCommitRef::Bookmarks(expression) => {
3071 let commit_ids = repo
3072 .view()
3073 .local_bookmarks_matching(&expression.to_matcher())
3074 .flat_map(|(_, target)| target.present_adds())
3075 .cloned()
3076 .collect();
3077 Ok(commit_ids)
3078 }
3079 RevsetCommitRef::RemoteBookmarks {
3080 symbol,
3081 remote_ref_state,
3082 } => {
3083 let name_matcher = symbol.name.to_matcher();
3084 let remote_matcher = symbol.remote.to_matcher();
3085 let commit_ids = repo
3086 .view()
3087 .remote_bookmarks_matching(&name_matcher, &remote_matcher)
3088 .filter(|(_, remote_ref)| {
3089 remote_ref_state.is_none_or(|state| remote_ref.state == state)
3090 })
3091 .flat_map(|(_, remote_ref)| remote_ref.target.present_adds())
3092 .cloned()
3093 .collect();
3094 Ok(commit_ids)
3095 }
3096 RevsetCommitRef::Tags(expression) => {
3097 let commit_ids = repo
3098 .view()
3099 .local_tags_matching(&expression.to_matcher())
3100 .flat_map(|(_, target)| target.present_adds())
3101 .cloned()
3102 .collect();
3103 Ok(commit_ids)
3104 }
3105 RevsetCommitRef::RemoteTags {
3106 symbol,
3107 remote_ref_state,
3108 } => {
3109 let name_matcher = symbol.name.to_matcher();
3110 let remote_matcher = symbol.remote.to_matcher();
3111 let commit_ids = repo
3112 .view()
3113 .remote_tags_matching(&name_matcher, &remote_matcher)
3114 .filter(|(_, remote_ref)| {
3115 remote_ref_state.is_none_or(|state| remote_ref.state == state)
3116 })
3117 .flat_map(|(_, remote_ref)| remote_ref.target.present_adds())
3118 .cloned()
3119 .collect();
3120 Ok(commit_ids)
3121 }
3122 }
3123}
3124
3125struct ExpressionSymbolResolver<'a, 'b> {
3127 base_repo: &'a dyn Repo,
3129 symbol_resolver: &'a SymbolResolver<'b>,
3130 repo_stack: Vec<Arc<ReadonlyRepo>>,
3132 other_repos: Vec<Arc<ReadonlyRepo>>,
3134 other_operations: HashSet<OperationId>,
3136}
3137
3138impl<'a, 'b> ExpressionSymbolResolver<'a, 'b> {
3139 fn new(base_repo: &'a dyn Repo, symbol_resolver: &'a SymbolResolver<'b>) -> Self {
3140 Self {
3141 base_repo,
3142 symbol_resolver,
3143 repo_stack: vec![],
3144 other_repos: vec![],
3145 other_operations: HashSet::new(),
3146 }
3147 }
3148
3149 fn repo(&self) -> &dyn Repo {
3150 self.repo_stack
3151 .last()
3152 .map_or(self.base_repo, |repo| repo.as_ref())
3153 }
3154}
3155
3156impl ExpressionStateFolder<UserExpressionState, ResolvedExpressionState>
3157 for ExpressionSymbolResolver<'_, '_>
3158{
3159 type Error = RevsetResolutionError;
3160
3161 fn fold_expression(
3162 &mut self,
3163 expression: &UserRevsetExpression,
3164 ) -> Result<Arc<ResolvedRevsetExpression>, Self::Error> {
3165 match expression {
3166 RevsetExpression::Present(candidates) => {
3168 self.fold_expression(candidates).or_else(|err| match err {
3169 RevsetResolutionError::NoSuchRevision { .. }
3170 | RevsetResolutionError::WorkspaceMissingWorkingCopy { .. } => {
3171 Ok(RevsetExpression::none())
3172 }
3173 RevsetResolutionError::EmptyString
3174 | RevsetResolutionError::AmbiguousCommitIdPrefix(_)
3175 | RevsetResolutionError::AmbiguousChangeIdPrefix(_)
3176 | RevsetResolutionError::DivergentChangeId { .. }
3177 | RevsetResolutionError::ConflictedRef { .. }
3178 | RevsetResolutionError::Backend(_)
3179 | RevsetResolutionError::Other(_) => Err(err),
3180 })
3181 }
3182 _ => fold_child_expression_state(self, expression),
3183 }
3184 }
3185
3186 fn fold_commit_ref(
3187 &mut self,
3188 commit_ref: &RevsetCommitRef,
3189 ) -> Result<Arc<ResolvedRevsetExpression>, Self::Error> {
3190 let commit_ids = resolve_commit_ref(self.repo(), commit_ref, self.symbol_resolver)?;
3191 Ok(RevsetExpression::commits(commit_ids))
3192 }
3193
3194 fn fold_at_operation(
3195 &mut self,
3196 operation: &String,
3197 candidates: &UserRevsetExpression,
3198 ) -> Result<Arc<ResolvedRevsetExpression>, Self::Error> {
3199 let at_op_repo = reload_repo_at_operation(self.repo(), operation)?;
3200 let at_op_operation = at_op_repo.op_id().clone();
3201 if self.other_operations.insert(at_op_operation) {
3202 self.other_repos.push(at_op_repo.clone());
3205 }
3206 self.repo_stack.push(at_op_repo);
3207 let candidates = self.fold_expression(candidates)?;
3208 let expression = candidates.within_visibility(self.repo());
3209 self.repo_stack.pop();
3210 Ok(expression)
3211 }
3212}
3213
3214fn resolve_visibility(
3224 repo: &dyn Repo,
3225 expression: &ResolvedRevsetExpression,
3226) -> ResolvedExpression {
3227 let context = VisibilityResolutionContext {
3228 referenced_commits: &[],
3229 visible_heads: &repo.view().heads().iter().cloned().collect_vec(),
3230 root: repo.store().root_commit_id(),
3231 is_heads_normalized: repo.view().is_heads_normalized(),
3232 };
3233 context.resolve(expression)
3234}
3235
3236#[derive(Clone, Debug)]
3237struct VisibilityResolutionContext<'a> {
3238 referenced_commits: &'a [CommitId],
3239 visible_heads: &'a [CommitId],
3240 root: &'a CommitId,
3241 is_heads_normalized: bool,
3242}
3243
3244impl VisibilityResolutionContext<'_> {
3245 fn resolve(&self, expression: &ResolvedRevsetExpression) -> ResolvedExpression {
3247 match expression {
3248 RevsetExpression::None => ResolvedExpression::Commits(vec![]),
3249 RevsetExpression::All => self.resolve_all(),
3250 RevsetExpression::VisibleHeads => self.resolve_visible_heads(),
3251 RevsetExpression::VisibleHeadsOrReferenced => {
3252 self.resolve_visible_heads_or_referenced()
3253 }
3254 RevsetExpression::Root => self.resolve_root(),
3255 RevsetExpression::Commits(commit_ids) => {
3256 ResolvedExpression::Commits(commit_ids.clone())
3257 }
3258 RevsetExpression::CommitRef(commit_ref) => match *commit_ref {},
3259 RevsetExpression::Ancestors {
3260 heads,
3261 generation,
3262 parents_range,
3263 } => ResolvedExpression::Ancestors {
3264 heads: self.resolve(heads).into(),
3265 generation: generation.clone(),
3266 parents_range: parents_range.clone(),
3267 },
3268 RevsetExpression::Descendants { roots, generation } => ResolvedExpression::DagRange {
3269 roots: self.resolve(roots).into(),
3270 heads: self.resolve_visible_heads_or_referenced().into(),
3271 generation_from_roots: generation.clone(),
3272 },
3273 RevsetExpression::Range {
3274 roots,
3275 heads,
3276 generation,
3277 parents_range,
3278 } => ResolvedExpression::Range {
3279 roots: self.resolve(roots).into(),
3280 heads: self.resolve(heads).into(),
3281 generation: generation.clone(),
3282 parents_range: parents_range.clone(),
3283 },
3284 RevsetExpression::DagRange { roots, heads } => ResolvedExpression::DagRange {
3285 roots: self.resolve(roots).into(),
3286 heads: self.resolve(heads).into(),
3287 generation_from_roots: GENERATION_RANGE_FULL,
3288 },
3289 RevsetExpression::Reachable { sources, domain } => ResolvedExpression::Reachable {
3290 sources: self.resolve(sources).into(),
3291 domain: self.resolve(domain).into(),
3292 },
3293 RevsetExpression::Heads(candidates) => {
3294 ResolvedExpression::Heads(self.resolve(candidates).into())
3295 }
3296 RevsetExpression::HeadsRange {
3297 roots,
3298 heads,
3299 parents_range,
3300 filter,
3301 } => ResolvedExpression::HeadsRange {
3302 roots: self.resolve(roots).into(),
3303 heads: self.resolve(heads).into(),
3304 parents_range: parents_range.clone(),
3305 filter: (!matches!(filter.as_ref(), RevsetExpression::All))
3306 .then(|| self.resolve_predicate(filter)),
3307 },
3308 RevsetExpression::Roots(candidates) => {
3309 ResolvedExpression::Roots(self.resolve(candidates).into())
3310 }
3311 RevsetExpression::Forks => ResolvedExpression::Forks {
3312 heads: self.resolve_visible_heads_or_referenced().into(),
3313 },
3314 RevsetExpression::ForkPoint(expression) => {
3315 ResolvedExpression::ForkPoint(self.resolve(expression).into())
3316 }
3317 RevsetExpression::MergePoint(expression) => ResolvedExpression::MergePoint {
3318 roots: self.resolve(expression).into(),
3319 visible_heads: self.resolve_visible_heads_or_referenced().into(),
3320 },
3321 RevsetExpression::Bisect(expression) => {
3322 ResolvedExpression::Bisect(self.resolve(expression).into())
3323 }
3324 RevsetExpression::Latest { candidates, count } => ResolvedExpression::Latest {
3325 candidates: self.resolve(candidates).into(),
3326 count: *count,
3327 },
3328 RevsetExpression::HasSize { candidates, count } => ResolvedExpression::HasSize {
3329 candidates: self.resolve(candidates).into(),
3330 count: *count,
3331 },
3332 RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_) => {
3333 ResolvedExpression::FilterWithin {
3336 candidates: self.resolve_all().into(),
3337 predicate: self.resolve_predicate(expression),
3338 }
3339 }
3340 RevsetExpression::Divergent => ResolvedExpression::FilterWithin {
3341 candidates: self.resolve_all().into(),
3342 predicate: ResolvedPredicateExpression::Divergent {
3343 visible_heads: self.visible_heads.to_owned(),
3344 },
3345 },
3346 RevsetExpression::AtOperation { operation, .. } => match *operation {},
3347 RevsetExpression::WithinReference {
3348 candidates,
3349 commits,
3350 } => {
3351 let context = VisibilityResolutionContext {
3352 referenced_commits: commits,
3353 visible_heads: self.visible_heads,
3354 root: self.root,
3355 is_heads_normalized: self.is_heads_normalized,
3356 };
3357 context.resolve(candidates)
3358 }
3359 RevsetExpression::WithinVisibility {
3360 candidates,
3361 visible_heads,
3362 } => {
3363 let context = VisibilityResolutionContext {
3364 referenced_commits: self.referenced_commits,
3365 visible_heads,
3366 root: self.root,
3367 is_heads_normalized: self.is_heads_normalized,
3368 };
3369 context.resolve(candidates)
3370 }
3371 RevsetExpression::Coalesce(expression1, expression2) => ResolvedExpression::Coalesce(
3372 self.resolve(expression1).into(),
3373 self.resolve(expression2).into(),
3374 ),
3375 RevsetExpression::Present(candidates) => self.resolve(candidates),
3377 RevsetExpression::NotIn(complement) => ResolvedExpression::Difference(
3378 self.resolve_all().into(),
3379 self.resolve(complement).into(),
3380 ),
3381 RevsetExpression::Union(expression1, expression2) => ResolvedExpression::Union(
3382 self.resolve(expression1).into(),
3383 self.resolve(expression2).into(),
3384 ),
3385 RevsetExpression::Intersection(expression1, expression2) => {
3386 match expression2.as_ref() {
3387 RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_) => {
3388 ResolvedExpression::FilterWithin {
3389 candidates: self.resolve(expression1).into(),
3390 predicate: self.resolve_predicate(expression2),
3391 }
3392 }
3393 _ => ResolvedExpression::Intersection(
3394 self.resolve(expression1).into(),
3395 self.resolve(expression2).into(),
3396 ),
3397 }
3398 }
3399 RevsetExpression::Difference(expression1, expression2) => {
3400 ResolvedExpression::Difference(
3401 self.resolve(expression1).into(),
3402 self.resolve(expression2).into(),
3403 )
3404 }
3405 }
3406 }
3407
3408 fn resolve_all(&self) -> ResolvedExpression {
3409 ResolvedExpression::Ancestors {
3410 heads: self.resolve_visible_heads_or_referenced().into(),
3411 generation: GENERATION_RANGE_FULL,
3412 parents_range: PARENTS_RANGE_FULL,
3413 }
3414 }
3415
3416 fn resolve_visible_heads(&self) -> ResolvedExpression {
3417 let visible_heads = ResolvedExpression::Commits(self.visible_heads.to_owned());
3418 if self.is_heads_normalized {
3419 visible_heads
3420 } else {
3421 ResolvedExpression::Heads(visible_heads.into())
3422 }
3423 }
3424
3425 fn resolve_visible_heads_or_referenced(&self) -> ResolvedExpression {
3426 let commits = itertools::chain(self.referenced_commits, self.visible_heads)
3431 .cloned()
3432 .collect();
3433 ResolvedExpression::Commits(commits)
3434 }
3435
3436 fn resolve_root(&self) -> ResolvedExpression {
3437 ResolvedExpression::Commits(vec![self.root.to_owned()])
3438 }
3439
3440 fn resolve_predicate(
3445 &self,
3446 expression: &ResolvedRevsetExpression,
3447 ) -> ResolvedPredicateExpression {
3448 match expression {
3449 RevsetExpression::None
3450 | RevsetExpression::All
3451 | RevsetExpression::VisibleHeads
3452 | RevsetExpression::VisibleHeadsOrReferenced
3453 | RevsetExpression::Root
3454 | RevsetExpression::Commits(_)
3455 | RevsetExpression::CommitRef(_)
3456 | RevsetExpression::Ancestors { .. }
3457 | RevsetExpression::Descendants { .. }
3458 | RevsetExpression::Range { .. }
3459 | RevsetExpression::DagRange { .. }
3460 | RevsetExpression::Reachable { .. }
3461 | RevsetExpression::Heads(_)
3462 | RevsetExpression::HeadsRange { .. }
3463 | RevsetExpression::Roots(_)
3464 | RevsetExpression::Forks
3465 | RevsetExpression::ForkPoint(_)
3466 | RevsetExpression::MergePoint(_)
3467 | RevsetExpression::Bisect(_)
3468 | RevsetExpression::HasSize { .. }
3469 | RevsetExpression::Latest { .. } => {
3470 ResolvedPredicateExpression::Set(self.resolve(expression).into())
3471 }
3472 RevsetExpression::Filter(predicate) => {
3473 ResolvedPredicateExpression::Filter(predicate.clone())
3474 }
3475 RevsetExpression::AsFilter(candidates) => self.resolve_predicate(candidates),
3476 RevsetExpression::Divergent => ResolvedPredicateExpression::Divergent {
3477 visible_heads: self.visible_heads.to_owned(),
3478 },
3479 RevsetExpression::AtOperation { operation, .. } => match *operation {},
3480 RevsetExpression::WithinReference { .. }
3482 | RevsetExpression::WithinVisibility { .. } => {
3483 ResolvedPredicateExpression::Set(self.resolve(expression).into())
3484 }
3485 RevsetExpression::Coalesce(_, _) => {
3486 ResolvedPredicateExpression::Set(self.resolve(expression).into())
3487 }
3488 RevsetExpression::Present(candidates) => self.resolve_predicate(candidates),
3490 RevsetExpression::NotIn(complement) => {
3491 ResolvedPredicateExpression::NotIn(self.resolve_predicate(complement).into())
3492 }
3493 RevsetExpression::Union(expression1, expression2) => {
3494 let predicate1 = self.resolve_predicate(expression1);
3495 let predicate2 = self.resolve_predicate(expression2);
3496 ResolvedPredicateExpression::Union(predicate1.into(), predicate2.into())
3497 }
3498 RevsetExpression::Intersection(expression1, expression2) => {
3499 let predicate1 = self.resolve_predicate(expression1);
3500 let predicate2 = self.resolve_predicate(expression2);
3501 ResolvedPredicateExpression::Intersection(predicate1.into(), predicate2.into())
3502 }
3503 RevsetExpression::Difference(expression1, expression2) => {
3504 let predicate1 = self.resolve_predicate(expression1);
3505 let predicate2 = self.resolve_predicate(expression2);
3506 let predicate2 = ResolvedPredicateExpression::NotIn(predicate2.into());
3507 ResolvedPredicateExpression::Intersection(predicate1.into(), predicate2.into())
3508 }
3509 }
3510 }
3511}
3512
3513pub trait Revset: fmt::Debug {
3514 fn stream<'a>(&self) -> LocalBoxStream<'a, Result<CommitId, RevsetEvaluationError>>
3517 where
3518 Self: 'a;
3519
3520 fn commit_change_ids<'a>(
3522 &self,
3523 ) -> LocalBoxStream<'a, Result<(CommitId, ChangeId), RevsetEvaluationError>>
3524 where
3525 Self: 'a;
3526
3527 fn stream_graph<'a>(
3530 &self,
3531 ) -> LocalBoxStream<'a, Result<GraphNode<CommitId>, RevsetEvaluationError>>
3532 where
3533 Self: 'a;
3534
3535 fn is_empty(&self) -> Result<bool, RevsetEvaluationError>;
3537
3538 fn count_estimate(&self) -> Result<(usize, Option<usize>), RevsetEvaluationError>;
3543
3544 fn containing_fn<'a>(&self) -> Box<RevsetContainingFn<'a>>
3550 where
3551 Self: 'a;
3552}
3553
3554pub type RevsetContainingFn<'a> =
3556 dyn Fn(&CommitId) -> LocalBoxFuture<'a, Result<bool, RevsetEvaluationError>> + 'a;
3557
3558pub trait RevsetStreamExt {
3559 fn commits(
3560 self,
3561 store: &Arc<Store>,
3562 ) -> impl Stream<Item = Result<Commit, RevsetEvaluationError>> + use<'_, Self>;
3563}
3564
3565impl<S: Stream<Item = Result<CommitId, RevsetEvaluationError>>> RevsetStreamExt for S {
3566 fn commits(
3567 self,
3568 store: &Arc<Store>,
3569 ) -> impl Stream<Item = Result<Commit, RevsetEvaluationError>> + use<'_, S> {
3570 self.map(async move |result| {
3571 let commit_id = result?;
3572 let commit = store
3573 .get_commit_async(&commit_id)
3574 .await
3575 .map_err(RevsetEvaluationError::Backend)?;
3576 Ok(commit)
3577 })
3578 .buffered(store.concurrency())
3579 }
3580}
3581
3582pub struct RevsetExtensions {
3584 symbol_resolvers: Vec<Box<dyn SymbolResolverExtension>>,
3585 function_map: HashMap<&'static str, RevsetFunction>,
3586}
3587
3588impl Default for RevsetExtensions {
3589 fn default() -> Self {
3590 Self::new()
3591 }
3592}
3593
3594impl RevsetExtensions {
3595 pub fn new() -> Self {
3596 Self {
3597 symbol_resolvers: vec![],
3598 function_map: BUILTIN_FUNCTION_MAP.clone(),
3599 }
3600 }
3601
3602 pub fn symbol_resolvers(&self) -> &[Box<dyn SymbolResolverExtension>] {
3603 &self.symbol_resolvers
3604 }
3605
3606 pub fn add_symbol_resolver(&mut self, symbol_resolver: Box<dyn SymbolResolverExtension>) {
3607 self.symbol_resolvers.push(symbol_resolver);
3608 }
3609
3610 pub fn add_custom_function(&mut self, name: &'static str, func: RevsetFunction) {
3611 match self.function_map.entry(name) {
3612 hash_map::Entry::Occupied(_) => {
3613 panic!("Conflict registering revset function '{name}'")
3614 }
3615 hash_map::Entry::Vacant(v) => v.insert(func),
3616 };
3617 }
3618}
3619
3620#[derive(Clone)]
3622pub struct RevsetParseContext<'a> {
3623 pub aliases_map: &'a RevsetAliasesMap,
3624 pub local_variables: HashMap<&'a str, ExpressionNode<'a>>,
3625 pub user_email: &'a str,
3626 pub date_pattern_context: DatePatternContext,
3627 pub default_ignored_remote: Option<&'a RemoteName>,
3629 pub fileset_aliases_map: &'a FilesetAliasesMap,
3630 pub extensions: &'a RevsetExtensions,
3631 pub workspace: Option<RevsetWorkspaceContext<'a>>,
3632}
3633
3634impl<'a> RevsetParseContext<'a> {
3635 fn to_lowering_context(&self) -> LoweringContext<'a> {
3636 let RevsetParseContext {
3637 aliases_map: _,
3638 local_variables: _,
3639 user_email,
3640 date_pattern_context,
3641 default_ignored_remote,
3642 fileset_aliases_map,
3643 extensions,
3644 workspace,
3645 } = *self;
3646 LoweringContext {
3647 user_email,
3648 date_pattern_context,
3649 default_ignored_remote,
3650 fileset_aliases_map,
3651 extensions,
3652 workspace,
3653 }
3654 }
3655}
3656
3657#[derive(Clone)]
3659pub struct LoweringContext<'a> {
3660 user_email: &'a str,
3661 date_pattern_context: DatePatternContext,
3662 default_ignored_remote: Option<&'a RemoteName>,
3663 fileset_aliases_map: &'a FilesetAliasesMap,
3664 extensions: &'a RevsetExtensions,
3665 workspace: Option<RevsetWorkspaceContext<'a>>,
3666}
3667
3668impl<'a> LoweringContext<'a> {
3669 pub fn user_email(&self) -> &'a str {
3670 self.user_email
3671 }
3672
3673 pub fn date_pattern_context(&self) -> &DatePatternContext {
3674 &self.date_pattern_context
3675 }
3676
3677 pub fn fileset_parse_context(&self) -> Option<FilesetParseContext<'_>> {
3678 Some(FilesetParseContext {
3679 aliases_map: self.fileset_aliases_map,
3680 path_converter: self.workspace?.path_converter,
3681 })
3682 }
3683
3684 pub fn symbol_resolvers(&self) -> &'a [impl AsRef<dyn SymbolResolverExtension> + use<>] {
3685 self.extensions.symbol_resolvers()
3686 }
3687}
3688
3689#[derive(Clone, Copy, Debug)]
3691pub struct RevsetWorkspaceContext<'a> {
3692 pub path_converter: &'a RepoPathUiConverter,
3693 pub workspace_name: &'a WorkspaceName,
3694}
3695
3696#[cfg(test)]
3697#[rustversion::attr(
3698 since(1.89),
3699 expect(clippy::cloned_ref_to_slice_refs, reason = "makes tests more readable")
3700)]
3701mod tests {
3702 use std::assert_matches;
3703 use std::path::PathBuf;
3704
3705 use super::*;
3706 use crate::tests::TestResult;
3707
3708 fn parse(revset_str: &str) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3709 parse_with_aliases(revset_str, [] as [(&str, &str); 0])
3710 }
3711
3712 fn parse_with_workspace(
3713 revset_str: &str,
3714 workspace_name: &WorkspaceName,
3715 ) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3716 parse_with_aliases_and_workspace(revset_str, [] as [(&str, &str); 0], workspace_name)
3717 }
3718
3719 fn parse_with_aliases(
3720 revset_str: &str,
3721 aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
3722 ) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3723 let mut aliases_map = RevsetAliasesMap::new();
3724 for (decl, defn) in aliases {
3725 aliases_map.insert(decl, defn, None)?;
3726 }
3727 let context = RevsetParseContext {
3728 aliases_map: &aliases_map,
3729 local_variables: HashMap::new(),
3730 user_email: "test.user@example.com",
3731 date_pattern_context: chrono::Utc::now().fixed_offset().into(),
3732 default_ignored_remote: Some("ignored".as_ref()),
3733 fileset_aliases_map: &FilesetAliasesMap::new(),
3734 extensions: &RevsetExtensions::default(),
3735 workspace: None,
3736 };
3737 super::parse(&mut RevsetDiagnostics::new(), revset_str, &context)
3738 }
3739
3740 fn parse_with_aliases_and_workspace(
3741 revset_str: &str,
3742 aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
3743 workspace_name: &WorkspaceName,
3744 ) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3745 let path_converter = RepoPathUiConverter::Fs {
3747 cwd: PathBuf::from("/"),
3748 base: PathBuf::from("/"),
3749 };
3750 let workspace_ctx = RevsetWorkspaceContext {
3751 path_converter: &path_converter,
3752 workspace_name,
3753 };
3754 let mut aliases_map = RevsetAliasesMap::new();
3755 for (decl, defn) in aliases {
3756 aliases_map.insert(decl, defn, None)?;
3757 }
3758 let context = RevsetParseContext {
3759 aliases_map: &aliases_map,
3760 local_variables: HashMap::new(),
3761 user_email: "test.user@example.com",
3762 date_pattern_context: chrono::Utc::now().fixed_offset().into(),
3763 default_ignored_remote: Some("ignored".as_ref()),
3764 fileset_aliases_map: &FilesetAliasesMap::new(),
3765 extensions: &RevsetExtensions::default(),
3766 workspace: Some(workspace_ctx),
3767 };
3768 super::parse(&mut RevsetDiagnostics::new(), revset_str, &context)
3769 }
3770
3771 fn insta_settings() -> insta::Settings {
3772 let mut settings = insta::Settings::clone_current();
3773 for _ in 0..4 {
3776 settings.add_filter(
3777 r"(?x)
3778 \b([A-Z]\w*)\(\n
3779 \s*(.{1,60}),\n
3780 \s*\)",
3781 "$1($2)",
3782 );
3783 }
3784 settings
3785 }
3786
3787 #[test]
3788 #[expect(clippy::redundant_clone)] fn test_revset_expression_building() {
3790 let settings = insta_settings();
3791 let _guard = settings.bind_to_scope();
3792 let current_wc = UserRevsetExpression::working_copy(WorkspaceName::DEFAULT.to_owned());
3793 let foo_symbol = UserRevsetExpression::symbol("foo".to_string());
3794 let bar_symbol = UserRevsetExpression::symbol("bar".to_string());
3795 let baz_symbol = UserRevsetExpression::symbol("baz".to_string());
3796
3797 insta::assert_debug_snapshot!(
3798 current_wc,
3799 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("default")))"#);
3800 insta::assert_debug_snapshot!(
3801 current_wc.heads(),
3802 @r#"Heads(CommitRef(WorkingCopy(WorkspaceNameBuf("default"))))"#);
3803 insta::assert_debug_snapshot!(
3804 current_wc.roots(),
3805 @r#"Roots(CommitRef(WorkingCopy(WorkspaceNameBuf("default"))))"#);
3806 insta::assert_debug_snapshot!(
3807 current_wc.parents(), @r#"
3808 Ancestors {
3809 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3810 generation: 1..2,
3811 parents_range: 0..4294967295,
3812 }
3813 "#);
3814 insta::assert_debug_snapshot!(
3815 current_wc.ancestors(), @r#"
3816 Ancestors {
3817 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3818 generation: 0..18446744073709551615,
3819 parents_range: 0..4294967295,
3820 }
3821 "#);
3822 insta::assert_debug_snapshot!(
3823 foo_symbol.children(), @r#"
3824 Descendants {
3825 roots: CommitRef(Symbol("foo")),
3826 generation: 1..2,
3827 }
3828 "#);
3829 insta::assert_debug_snapshot!(
3830 foo_symbol.descendants(), @r#"
3831 Descendants {
3832 roots: CommitRef(Symbol("foo")),
3833 generation: 0..18446744073709551615,
3834 }
3835 "#);
3836 insta::assert_debug_snapshot!(
3837 foo_symbol.dag_range_to(¤t_wc), @r#"
3838 DagRange {
3839 roots: CommitRef(Symbol("foo")),
3840 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3841 }
3842 "#);
3843 insta::assert_debug_snapshot!(
3844 foo_symbol.connected(), @r#"
3845 DagRange {
3846 roots: CommitRef(Symbol("foo")),
3847 heads: CommitRef(Symbol("foo")),
3848 }
3849 "#);
3850 insta::assert_debug_snapshot!(
3851 foo_symbol.range(¤t_wc), @r#"
3852 Range {
3853 roots: CommitRef(Symbol("foo")),
3854 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3855 generation: 0..18446744073709551615,
3856 parents_range: 0..4294967295,
3857 }
3858 "#);
3859 insta::assert_debug_snapshot!(
3860 foo_symbol.negated(),
3861 @r#"NotIn(CommitRef(Symbol("foo")))"#);
3862 insta::assert_debug_snapshot!(
3863 foo_symbol.union(¤t_wc), @r#"
3864 Union(
3865 CommitRef(Symbol("foo")),
3866 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3867 )
3868 "#);
3869 insta::assert_debug_snapshot!(
3870 UserRevsetExpression::union_all(&[]),
3871 @"None");
3872 insta::assert_debug_snapshot!(
3873 RevsetExpression::union_all(&[current_wc.clone()]),
3874 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("default")))"#);
3875 insta::assert_debug_snapshot!(
3876 RevsetExpression::union_all(&[current_wc.clone(), foo_symbol.clone()]),
3877 @r#"
3878 Union(
3879 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3880 CommitRef(Symbol("foo")),
3881 )
3882 "#);
3883 insta::assert_debug_snapshot!(
3884 RevsetExpression::union_all(&[
3885 current_wc.clone(),
3886 foo_symbol.clone(),
3887 bar_symbol.clone(),
3888 ]),
3889 @r#"
3890 Union(
3891 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3892 Union(
3893 CommitRef(Symbol("foo")),
3894 CommitRef(Symbol("bar")),
3895 ),
3896 )
3897 "#);
3898 insta::assert_debug_snapshot!(
3899 RevsetExpression::union_all(&[
3900 current_wc.clone(),
3901 foo_symbol.clone(),
3902 bar_symbol.clone(),
3903 baz_symbol.clone(),
3904 ]),
3905 @r#"
3906 Union(
3907 Union(
3908 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3909 CommitRef(Symbol("foo")),
3910 ),
3911 Union(
3912 CommitRef(Symbol("bar")),
3913 CommitRef(Symbol("baz")),
3914 ),
3915 )
3916 "#);
3917 insta::assert_debug_snapshot!(
3918 foo_symbol.intersection(¤t_wc), @r#"
3919 Intersection(
3920 CommitRef(Symbol("foo")),
3921 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3922 )
3923 "#);
3924 insta::assert_debug_snapshot!(
3925 foo_symbol.minus(¤t_wc), @r#"
3926 Difference(
3927 CommitRef(Symbol("foo")),
3928 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3929 )
3930 "#);
3931 insta::assert_debug_snapshot!(
3932 UserRevsetExpression::coalesce(&[]),
3933 @"None");
3934 insta::assert_debug_snapshot!(
3935 RevsetExpression::coalesce(&[current_wc.clone()]),
3936 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("default")))"#);
3937 insta::assert_debug_snapshot!(
3938 RevsetExpression::coalesce(&[current_wc.clone(), foo_symbol.clone()]),
3939 @r#"
3940 Coalesce(
3941 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3942 CommitRef(Symbol("foo")),
3943 )
3944 "#);
3945 insta::assert_debug_snapshot!(
3946 RevsetExpression::coalesce(&[
3947 current_wc.clone(),
3948 foo_symbol.clone(),
3949 bar_symbol.clone(),
3950 ]),
3951 @r#"
3952 Coalesce(
3953 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3954 Coalesce(
3955 CommitRef(Symbol("foo")),
3956 CommitRef(Symbol("bar")),
3957 ),
3958 )
3959 "#);
3960 }
3961
3962 #[test]
3963 fn test_parse_revset() -> TestResult {
3964 let settings = insta_settings();
3965 let _guard = settings.bind_to_scope();
3966 let main_workspace_name = WorkspaceNameBuf::from("main");
3967 let other_workspace_name = WorkspaceNameBuf::from("other");
3968
3969 insta::assert_debug_snapshot!(
3971 parse("@").unwrap_err().kind(),
3972 @"WorkingCopyWithoutWorkspace");
3973 insta::assert_debug_snapshot!(
3974 parse("main@")?,
3975 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("main")))"#);
3976 insta::assert_debug_snapshot!(
3977 parse_with_workspace("@", &main_workspace_name)?,
3978 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("main")))"#);
3979 insta::assert_debug_snapshot!(
3980 parse_with_workspace("main@", &other_workspace_name)?,
3981 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("main")))"#);
3982 insta::assert_debug_snapshot!(
3984 parse("author_name(foo@)").unwrap_err().kind(),
3985 @r#"Expression("Invalid string expression")"#);
3986 insta::assert_debug_snapshot!(
3987 parse(r#"author_name("foo@")"#)?,
3988 @r#"Filter(AuthorName(Pattern(Exact("foo@"))))"#);
3989 insta::assert_debug_snapshot!(
3991 parse("foo")?,
3992 @r#"CommitRef(Symbol("foo"))"#);
3993 insta::assert_debug_snapshot!(
3995 parse("bookmarks()")?,
3996 @r#"CommitRef(Bookmarks(Pattern(Substring(""))))"#);
3997 insta::assert_debug_snapshot!(
3999 parse("tags()")?,
4000 @r#"CommitRef(Tags(Pattern(Substring(""))))"#);
4001 insta::assert_debug_snapshot!(parse("remote_bookmarks()")?, @r#"
4002 CommitRef(
4003 RemoteBookmarks {
4004 symbol: RemoteRefSymbolExpression {
4005 name: Pattern(Substring("")),
4006 remote: NotIn(Pattern(Exact("ignored"))),
4007 },
4008 remote_ref_state: None,
4009 },
4010 )
4011 "#);
4012 insta::assert_debug_snapshot!(parse("tracked_remote_bookmarks()")?, @r#"
4013 CommitRef(
4014 RemoteBookmarks {
4015 symbol: RemoteRefSymbolExpression {
4016 name: Pattern(Substring("")),
4017 remote: NotIn(Pattern(Exact("ignored"))),
4018 },
4019 remote_ref_state: Some(Tracked),
4020 },
4021 )
4022 "#);
4023 insta::assert_debug_snapshot!(parse("untracked_remote_bookmarks()")?, @r#"
4024 CommitRef(
4025 RemoteBookmarks {
4026 symbol: RemoteRefSymbolExpression {
4027 name: Pattern(Substring("")),
4028 remote: NotIn(Pattern(Exact("ignored"))),
4029 },
4030 remote_ref_state: Some(New),
4031 },
4032 )
4033 "#);
4034 insta::assert_debug_snapshot!(parse("remote_tags()")?, @r#"
4035 CommitRef(
4036 RemoteTags {
4037 symbol: RemoteRefSymbolExpression {
4038 name: Pattern(Substring("")),
4039 remote: NotIn(Pattern(Exact("ignored"))),
4040 },
4041 remote_ref_state: None,
4042 },
4043 )
4044 "#);
4045 insta::assert_debug_snapshot!(parse("tracked_remote_tags()")?, @r#"
4046 CommitRef(
4047 RemoteTags {
4048 symbol: RemoteRefSymbolExpression {
4049 name: Pattern(Substring("")),
4050 remote: NotIn(Pattern(Exact("ignored"))),
4051 },
4052 remote_ref_state: Some(Tracked),
4053 },
4054 )
4055 "#);
4056 insta::assert_debug_snapshot!(parse("untracked_remote_tags()")?, @r#"
4057 CommitRef(
4058 RemoteTags {
4059 symbol: RemoteRefSymbolExpression {
4060 name: Pattern(Substring("")),
4061 remote: NotIn(Pattern(Exact("ignored"))),
4062 },
4063 remote_ref_state: Some(New),
4064 },
4065 )
4066 "#);
4067 insta::assert_debug_snapshot!(
4069 parse("'foo'")?,
4070 @r#"CommitRef(Symbol("foo"))"#);
4071 insta::assert_debug_snapshot!(parse("foo-")?, @r#"
4073 Ancestors {
4074 heads: CommitRef(Symbol("foo")),
4075 generation: 1..2,
4076 parents_range: 0..4294967295,
4077 }
4078 "#);
4079 insta::assert_debug_snapshot!(parse("foo+")?, @r#"
4081 Descendants {
4082 roots: CommitRef(Symbol("foo")),
4083 generation: 1..2,
4084 }
4085 "#);
4086 insta::assert_debug_snapshot!(parse("::foo")?, @r#"
4088 Ancestors {
4089 heads: CommitRef(Symbol("foo")),
4090 generation: 0..18446744073709551615,
4091 parents_range: 0..4294967295,
4092 }
4093 "#);
4094 insta::assert_debug_snapshot!(parse("foo::")?, @r#"
4096 Descendants {
4097 roots: CommitRef(Symbol("foo")),
4098 generation: 0..18446744073709551615,
4099 }
4100 "#);
4101 insta::assert_debug_snapshot!(parse("foo::bar")?, @r#"
4103 DagRange {
4104 roots: CommitRef(Symbol("foo")),
4105 heads: CommitRef(Symbol("bar")),
4106 }
4107 "#);
4108 insta::assert_debug_snapshot!(parse("::")?, @"All");
4110 insta::assert_debug_snapshot!(parse("..foo")?, @r#"
4112 Range {
4113 roots: Root,
4114 heads: CommitRef(Symbol("foo")),
4115 generation: 0..18446744073709551615,
4116 parents_range: 0..4294967295,
4117 }
4118 "#);
4119 insta::assert_debug_snapshot!(parse("foo..")?, @r#"
4120 NotIn(
4121 Ancestors {
4122 heads: CommitRef(Symbol("foo")),
4123 generation: 0..18446744073709551615,
4124 parents_range: 0..4294967295,
4125 },
4126 )
4127 "#);
4128 insta::assert_debug_snapshot!(parse("foo..bar")?, @r#"
4129 Range {
4130 roots: CommitRef(Symbol("foo")),
4131 heads: CommitRef(Symbol("bar")),
4132 generation: 0..18446744073709551615,
4133 parents_range: 0..4294967295,
4134 }
4135 "#);
4136 insta::assert_debug_snapshot!(parse("..")?, @"NotIn(Root)");
4138 insta::assert_debug_snapshot!(
4140 parse("~ foo")?,
4141 @r#"NotIn(CommitRef(Symbol("foo")))"#);
4142 insta::assert_debug_snapshot!(parse("foo & bar")?, @r#"
4144 Intersection(
4145 CommitRef(Symbol("foo")),
4146 CommitRef(Symbol("bar")),
4147 )
4148 "#);
4149 insta::assert_debug_snapshot!(parse("foo | bar")?, @r#"
4151 Union(
4152 CommitRef(Symbol("foo")),
4153 CommitRef(Symbol("bar")),
4154 )
4155 "#);
4156 insta::assert_debug_snapshot!(parse("foo ~ bar")?, @r#"
4158 Difference(
4159 CommitRef(Symbol("foo")),
4160 CommitRef(Symbol("bar")),
4161 )
4162 "#);
4163 Ok(())
4164 }
4165
4166 #[test]
4167 fn test_parse_string_pattern() -> TestResult {
4168 let settings = insta_settings();
4169 let _guard = settings.bind_to_scope();
4170
4171 insta::assert_debug_snapshot!(
4172 parse(r#"bookmarks("foo")"#)?,
4173 @r#"CommitRef(Bookmarks(Pattern(Exact("foo"))))"#);
4174 insta::assert_debug_snapshot!(
4175 parse(r#"bookmarks(exact:"foo")"#)?,
4176 @r#"CommitRef(Bookmarks(Pattern(Exact("foo"))))"#);
4177 insta::assert_debug_snapshot!(
4178 parse(r#"bookmarks(substring:"foo")"#)?,
4179 @r#"CommitRef(Bookmarks(Pattern(Substring("foo"))))"#);
4180 insta::assert_debug_snapshot!(
4181 parse(r#"bookmarks(bad:"foo")"#).unwrap_err().kind(),
4182 @r#"Expression("Invalid string pattern")"#);
4183 insta::assert_debug_snapshot!(
4184 parse(r#"bookmarks(exact::"foo")"#).unwrap_err().kind(),
4185 @r#"Expression("Invalid string expression")"#);
4186 insta::assert_debug_snapshot!(
4187 parse(r#"bookmarks(exact:"foo"+)"#).unwrap_err().kind(),
4188 @r#"Expression("Expected string")"#);
4189
4190 insta::assert_debug_snapshot!(
4191 parse(r#"tags("foo")"#)?,
4192 @r#"CommitRef(Tags(Pattern(Exact("foo"))))"#);
4193 insta::assert_debug_snapshot!(
4194 parse(r#"tags(exact:"foo")"#)?,
4195 @r#"CommitRef(Tags(Pattern(Exact("foo"))))"#);
4196 insta::assert_debug_snapshot!(
4197 parse(r#"tags(substring:"foo")"#)?,
4198 @r#"CommitRef(Tags(Pattern(Substring("foo"))))"#);
4199 insta::assert_debug_snapshot!(
4200 parse(r#"tags(bad:"foo")"#).unwrap_err().kind(),
4201 @r#"Expression("Invalid string pattern")"#);
4202 insta::assert_debug_snapshot!(
4203 parse(r#"tags(exact::"foo")"#).unwrap_err().kind(),
4204 @r#"Expression("Invalid string expression")"#);
4205 insta::assert_debug_snapshot!(
4206 parse(r#"tags(exact:"foo"+)"#).unwrap_err().kind(),
4207 @r#"Expression("Expected string")"#);
4208
4209 assert_matches!(
4211 parse(r#"(exact:"foo")"#).unwrap_err().kind(),
4212 RevsetParseErrorKind::NotInfixOperator { .. }
4213 );
4214 Ok(())
4215 }
4216
4217 #[test]
4218 fn test_parse_compound_string_expression() -> TestResult {
4219 let settings = insta_settings();
4220 let _guard = settings.bind_to_scope();
4221
4222 insta::assert_debug_snapshot!(
4223 parse(r#"tags(~a)"#)?,
4224 @r#"
4225 CommitRef(
4226 Tags(NotIn(Pattern(Exact("a")))),
4227 )
4228 "#);
4229 insta::assert_debug_snapshot!(
4230 parse(r#"tags(a|b&c)"#)?,
4231 @r#"
4232 CommitRef(
4233 Tags(
4234 Union(
4235 Pattern(Exact("a")),
4236 Intersection(
4237 Pattern(Exact("b")),
4238 Pattern(Exact("c")),
4239 ),
4240 ),
4241 ),
4242 )
4243 "#);
4244 insta::assert_debug_snapshot!(
4245 parse(r#"tags(a|b|c)"#)?,
4246 @r#"
4247 CommitRef(
4248 Tags(
4249 Union(
4250 Pattern(Exact("a")),
4251 Union(
4252 Pattern(Exact("b")),
4253 Pattern(Exact("c")),
4254 ),
4255 ),
4256 ),
4257 )
4258 "#);
4259 insta::assert_debug_snapshot!(
4260 parse(r#"tags(a~(b|c))"#)?,
4261 @r#"
4262 CommitRef(
4263 Tags(
4264 Intersection(
4265 Pattern(Exact("a")),
4266 NotIn(
4267 Union(
4268 Pattern(Exact("b")),
4269 Pattern(Exact("c")),
4270 ),
4271 ),
4272 ),
4273 ),
4274 )
4275 "#);
4276 Ok(())
4277 }
4278
4279 #[test]
4280 fn test_parse_revset_function() -> TestResult {
4281 let settings = insta_settings();
4282 let _guard = settings.bind_to_scope();
4283
4284 insta::assert_debug_snapshot!(
4285 parse("parents(foo)")?, @r#"
4286 Ancestors {
4287 heads: CommitRef(Symbol("foo")),
4288 generation: 1..2,
4289 parents_range: 0..4294967295,
4290 }
4291 "#);
4292 insta::assert_debug_snapshot!(
4293 parse("parents(\"foo\")")?, @r#"
4294 Ancestors {
4295 heads: CommitRef(Symbol("foo")),
4296 generation: 1..2,
4297 parents_range: 0..4294967295,
4298 }
4299 "#);
4300 insta::assert_debug_snapshot!(
4301 parse("ancestors(parents(foo))")?, @r#"
4302 Ancestors {
4303 heads: Ancestors {
4304 heads: CommitRef(Symbol("foo")),
4305 generation: 1..2,
4306 parents_range: 0..4294967295,
4307 },
4308 generation: 0..18446744073709551615,
4309 parents_range: 0..4294967295,
4310 }
4311 "#);
4312 insta::assert_debug_snapshot!(
4313 parse("parents(foo, bar, baz)").unwrap_err().kind(), @r#"
4314 InvalidFunctionArguments {
4315 name: "parents",
4316 message: "Expected 1 to 2 arguments",
4317 }
4318 "#);
4319 insta::assert_debug_snapshot!(
4320 parse("parents(foo, 2)")?, @r#"
4321 Ancestors {
4322 heads: CommitRef(Symbol("foo")),
4323 generation: 2..3,
4324 parents_range: 0..4294967295,
4325 }
4326 "#);
4327 insta::assert_debug_snapshot!(
4328 parse("root()")?,
4329 @"Root");
4330 assert!(parse("root(a)").is_err());
4331 insta::assert_debug_snapshot!(
4332 parse(r#"description("")"#)?,
4333 @r#"Filter(Description(Pattern(Exact(""))))"#);
4334 insta::assert_debug_snapshot!(
4335 parse("description(foo)")?,
4336 @r#"Filter(Description(Pattern(Exact("foo"))))"#);
4337 insta::assert_debug_snapshot!(
4338 parse("description(visible_heads())").unwrap_err().kind(),
4339 @r#"Expression("Invalid string expression")"#);
4340 insta::assert_debug_snapshot!(
4341 parse("description(\"(foo)\")")?,
4342 @r#"Filter(Description(Pattern(Exact("(foo)"))))"#);
4343 assert!(parse("mine(foo)").is_err());
4344 insta::assert_debug_snapshot!(
4345 parse_with_workspace("empty()", WorkspaceName::DEFAULT)?,
4346 @"NotIn(Filter(File(All)))");
4347 assert!(parse_with_workspace("empty(foo)", WorkspaceName::DEFAULT).is_err());
4348 assert!(parse_with_workspace("file()", WorkspaceName::DEFAULT).is_err());
4349 insta::assert_debug_snapshot!(
4350 parse_with_workspace("files(foo)", WorkspaceName::DEFAULT)?,
4351 @r#"Filter(File(Pattern(PrefixPath("foo"))))"#);
4352 insta::assert_debug_snapshot!(
4353 parse_with_workspace("files(all())", WorkspaceName::DEFAULT)?,
4354 @"Filter(File(All))");
4355 insta::assert_debug_snapshot!(
4356 parse_with_workspace(r#"files(file:"foo")"#, WorkspaceName::DEFAULT)?,
4357 @r#"Filter(File(Pattern(FilePath("foo"))))"#);
4358 insta::assert_debug_snapshot!(
4359 parse_with_workspace("files(foo|bar&baz)", WorkspaceName::DEFAULT)?, @r#"
4360 Filter(
4361 File(
4362 UnionAll(
4363 [
4364 Pattern(PrefixPath("foo")),
4365 Intersection(
4366 Pattern(PrefixPath("bar")),
4367 Pattern(PrefixPath("baz")),
4368 ),
4369 ],
4370 ),
4371 ),
4372 )
4373 "#);
4374 insta::assert_debug_snapshot!(
4375 parse_with_workspace(r#"files(~(foo))"#, WorkspaceName::DEFAULT)?,
4376 @r#"
4377 Filter(
4378 File(
4379 Difference(
4380 All,
4381 Pattern(PrefixPath("foo")),
4382 ),
4383 ),
4384 )
4385 "#);
4386 insta::assert_debug_snapshot!(parse("signed()")?, @"Filter(Signed)");
4387 Ok(())
4388 }
4389
4390 #[test]
4391 fn test_parse_revset_change_commit_id_functions() -> TestResult {
4392 let settings = insta_settings();
4393 let _guard = settings.bind_to_scope();
4394
4395 insta::assert_debug_snapshot!(
4396 parse("change_id(z)")?,
4397 @r#"CommitRef(ChangeId(HexPrefix("0")))"#);
4398 insta::assert_debug_snapshot!(
4399 parse("change_id('zk')")?,
4400 @r#"CommitRef(ChangeId(HexPrefix("0f")))"#);
4401 insta::assert_debug_snapshot!(
4402 parse("change_id(01234)").unwrap_err().kind(),
4403 @r#"Expression("Invalid change ID prefix")"#);
4404
4405 insta::assert_debug_snapshot!(
4406 parse("commit_id(0)")?,
4407 @r#"CommitRef(CommitId(HexPrefix("0")))"#);
4408 insta::assert_debug_snapshot!(
4409 parse("commit_id('0f')")?,
4410 @r#"CommitRef(CommitId(HexPrefix("0f")))"#);
4411 insta::assert_debug_snapshot!(
4412 parse("commit_id(xyzzy)").unwrap_err().kind(),
4413 @r#"Expression("Invalid commit ID prefix")"#);
4414 Ok(())
4415 }
4416
4417 #[test]
4418 fn test_parse_revset_author_committer_functions() -> TestResult {
4419 let settings = insta_settings();
4420 let _guard = settings.bind_to_scope();
4421
4422 insta::assert_debug_snapshot!(
4423 parse("author(foo)")?, @r#"
4424 Union(
4425 Filter(AuthorName(Pattern(Exact("foo")))),
4426 Filter(AuthorEmail(Pattern(Exact("foo")))),
4427 )
4428 "#);
4429 insta::assert_debug_snapshot!(
4430 parse("author_name(foo)")?,
4431 @r#"Filter(AuthorName(Pattern(Exact("foo"))))"#);
4432 insta::assert_debug_snapshot!(
4433 parse("author_email(foo)")?,
4434 @r#"Filter(AuthorEmail(Pattern(Exact("foo"))))"#);
4435
4436 insta::assert_debug_snapshot!(
4437 parse("committer(foo)")?, @r#"
4438 Union(
4439 Filter(CommitterName(Pattern(Exact("foo")))),
4440 Filter(CommitterEmail(Pattern(Exact("foo")))),
4441 )
4442 "#);
4443 insta::assert_debug_snapshot!(
4444 parse("committer_name(foo)")?,
4445 @r#"Filter(CommitterName(Pattern(Exact("foo"))))"#);
4446 insta::assert_debug_snapshot!(
4447 parse("committer_email(foo)")?,
4448 @r#"Filter(CommitterEmail(Pattern(Exact("foo"))))"#);
4449
4450 insta::assert_debug_snapshot!(
4451 parse("mine()")?,
4452 @r#"Filter(AuthorEmail(Pattern(ExactI("test.user@example.com"))))"#);
4453 Ok(())
4454 }
4455
4456 #[test]
4457 fn test_parse_revset_keyword_arguments() -> TestResult {
4458 let settings = insta_settings();
4459 let _guard = settings.bind_to_scope();
4460
4461 insta::assert_debug_snapshot!(
4462 parse("remote_bookmarks(remote=foo)")?, @r#"
4463 CommitRef(
4464 RemoteBookmarks {
4465 symbol: RemoteRefSymbolExpression {
4466 name: Pattern(Substring("")),
4467 remote: Pattern(Exact("foo")),
4468 },
4469 remote_ref_state: None,
4470 },
4471 )
4472 "#);
4473 insta::assert_debug_snapshot!(
4474 parse("remote_bookmarks(foo, remote=bar)")?, @r#"
4475 CommitRef(
4476 RemoteBookmarks {
4477 symbol: RemoteRefSymbolExpression {
4478 name: Pattern(Exact("foo")),
4479 remote: Pattern(Exact("bar")),
4480 },
4481 remote_ref_state: None,
4482 },
4483 )
4484 "#);
4485 insta::assert_debug_snapshot!(
4486 parse("tracked_remote_bookmarks(foo, remote=bar)")?, @r#"
4487 CommitRef(
4488 RemoteBookmarks {
4489 symbol: RemoteRefSymbolExpression {
4490 name: Pattern(Exact("foo")),
4491 remote: Pattern(Exact("bar")),
4492 },
4493 remote_ref_state: Some(Tracked),
4494 },
4495 )
4496 "#);
4497 insta::assert_debug_snapshot!(
4498 parse("untracked_remote_bookmarks(foo, remote=bar)")?, @r#"
4499 CommitRef(
4500 RemoteBookmarks {
4501 symbol: RemoteRefSymbolExpression {
4502 name: Pattern(Exact("foo")),
4503 remote: Pattern(Exact("bar")),
4504 },
4505 remote_ref_state: Some(New),
4506 },
4507 )
4508 "#);
4509 insta::assert_debug_snapshot!(
4510 parse(r#"remote_bookmarks(remote=foo, bar)"#).unwrap_err().kind(),
4511 @r#"
4512 InvalidFunctionArguments {
4513 name: "remote_bookmarks",
4514 message: "Positional argument follows keyword argument",
4515 }
4516 "#);
4517 insta::assert_debug_snapshot!(
4518 parse(r#"remote_bookmarks("", foo, remote=bar)"#).unwrap_err().kind(),
4519 @r#"
4520 InvalidFunctionArguments {
4521 name: "remote_bookmarks",
4522 message: "Got multiple values for keyword \"remote\"",
4523 }
4524 "#);
4525 insta::assert_debug_snapshot!(
4526 parse(r#"remote_bookmarks(remote=bar, remote=bar)"#).unwrap_err().kind(),
4527 @r#"
4528 InvalidFunctionArguments {
4529 name: "remote_bookmarks",
4530 message: "Got multiple values for keyword \"remote\"",
4531 }
4532 "#);
4533 insta::assert_debug_snapshot!(
4534 parse(r#"remote_bookmarks(unknown=bar)"#).unwrap_err().kind(),
4535 @r#"
4536 InvalidFunctionArguments {
4537 name: "remote_bookmarks",
4538 message: "Unexpected keyword argument \"unknown\"",
4539 }
4540 "#);
4541 Ok(())
4542 }
4543
4544 #[test]
4545 fn test_expand_symbol_alias() -> TestResult {
4546 let settings = insta_settings();
4547 let _guard = settings.bind_to_scope();
4548
4549 insta::assert_debug_snapshot!(
4550 parse_with_aliases("AB|c", [("AB", "a|b")])?, @r#"
4551 Union(
4552 Union(
4553 CommitRef(Symbol("a")),
4554 CommitRef(Symbol("b")),
4555 ),
4556 CommitRef(Symbol("c")),
4557 )
4558 "#);
4559
4560 insta::assert_debug_snapshot!(
4562 parse_with_aliases_and_workspace("files(A)", [("A", "a")], WorkspaceName::DEFAULT)
4563 ?,
4564 @r#"Filter(File(Pattern(PrefixPath("a"))))"#);
4565
4566 insta::assert_debug_snapshot!(
4568 parse_with_aliases("author_name(A)", [("A", "a")])?,
4569 @r#"Filter(AuthorName(Pattern(Exact("a"))))"#);
4570 insta::assert_debug_snapshot!(
4571 parse_with_aliases("author_name(A)", [("A", "exact:a")])?,
4572 @r#"Filter(AuthorName(Pattern(Exact("a"))))"#);
4573 Ok(())
4574 }
4575
4576 #[test]
4577 fn test_expand_function_alias() -> TestResult {
4578 let settings = insta_settings();
4579 let _guard = settings.bind_to_scope();
4580
4581 insta::assert_debug_snapshot!(
4583 parse_with_aliases("F(a)", [("F(x)", "author_name(x)|committer_name(x)")])?,
4584 @r#"
4585 Union(
4586 Filter(AuthorName(Pattern(Exact("a")))),
4587 Filter(CommitterName(Pattern(Exact("a")))),
4588 )
4589 "#);
4590 Ok(())
4591 }
4592
4593 #[test]
4594 fn test_transform_expression() {
4595 let settings = insta_settings();
4596 let _guard = settings.bind_to_scope();
4597
4598 insta::assert_debug_snapshot!(
4600 transform_expression(
4601 &ResolvedRevsetExpression::root(),
4602 |_| ControlFlow::Break(None),
4603 |_| Some(RevsetExpression::none()),
4604 ), @"None");
4605
4606 insta::assert_debug_snapshot!(
4608 transform_expression(
4609 &ResolvedRevsetExpression::root(),
4610 |_| ControlFlow::Break(Some(RevsetExpression::all())),
4611 |_| Some(RevsetExpression::none()),
4612 ), @"Some(All)");
4613
4614 insta::assert_debug_snapshot!(
4616 transform_expression(
4617 &ResolvedRevsetExpression::root().heads(),
4618 |_| ControlFlow::Continue(()),
4619 |x| match x.as_ref() {
4620 RevsetExpression::Root => Some(RevsetExpression::none()),
4621 _ => None,
4622 },
4623 ), @"Some(Heads(None))");
4624
4625 insta::assert_debug_snapshot!(
4627 transform_expression(
4628 &ResolvedRevsetExpression::root().heads(),
4629 |_| ControlFlow::Continue(()),
4630 |x| match x.as_ref() {
4631 RevsetExpression::Heads(y) => Some(y.clone()),
4632 _ => None,
4633 },
4634 ), @"Some(Root)");
4635 }
4636
4637 #[test]
4638 fn test_resolve_referenced_commits() {
4639 let settings = insta_settings();
4640 let _guard = settings.bind_to_scope();
4641
4642 let visibility1 = Arc::new(ResolvedRevsetExpression::WithinVisibility {
4643 candidates: RevsetExpression::commit(CommitId::from_hex("100001")),
4644 visible_heads: vec![CommitId::from_hex("100000")],
4645 });
4646 let visibility2 = Arc::new(ResolvedRevsetExpression::WithinVisibility {
4647 candidates: RevsetExpression::filter(RevsetFilterPredicate::HasConflict),
4648 visible_heads: vec![CommitId::from_hex("200000")],
4649 });
4650 let commit3 = RevsetExpression::commit(CommitId::from_hex("000003"));
4651
4652 insta::assert_debug_snapshot!(
4655 resolve_referenced_commits(&visibility1.intersection(&RevsetExpression::all())), @r#"
4656 Some(
4657 WithinReference {
4658 candidates: Intersection(
4659 WithinVisibility {
4660 candidates: WithinReference {
4661 candidates: Commits(
4662 [
4663 CommitId("100001"),
4664 ],
4665 ),
4666 commits: [
4667 CommitId("100001"),
4668 ],
4669 },
4670 visible_heads: [
4671 CommitId("100000"),
4672 ],
4673 },
4674 All,
4675 ),
4676 commits: [
4677 CommitId("100000"),
4678 CommitId("100001"),
4679 ],
4680 },
4681 )
4682 "#);
4683
4684 insta::assert_debug_snapshot!(
4688 resolve_referenced_commits(
4689 &visibility2
4690 .intersection(&RevsetExpression::all())
4691 .union(&commit3),
4692 ), @r#"
4693 Some(
4694 WithinReference {
4695 candidates: Union(
4696 Intersection(
4697 WithinVisibility {
4698 candidates: WithinReference {
4699 candidates: Filter(HasConflict),
4700 commits: [],
4701 },
4702 visible_heads: [
4703 CommitId("200000"),
4704 ],
4705 },
4706 All,
4707 ),
4708 Commits(
4709 [
4710 CommitId("000003"),
4711 ],
4712 ),
4713 ),
4714 commits: [
4715 CommitId("000003"),
4716 CommitId("200000"),
4717 ],
4718 },
4719 )
4720 "#);
4721
4722 insta::assert_debug_snapshot!(
4724 resolve_referenced_commits(
4725 &visibility1
4726 .union(&visibility2)
4727 .union(&commit3)
4728 .intersection(&RevsetExpression::all())
4729 ), @r#"
4730 Some(
4731 WithinReference {
4732 candidates: Intersection(
4733 Union(
4734 Union(
4735 WithinVisibility {
4736 candidates: WithinReference {
4737 candidates: Commits(
4738 [
4739 CommitId("100001"),
4740 ],
4741 ),
4742 commits: [
4743 CommitId("100001"),
4744 ],
4745 },
4746 visible_heads: [
4747 CommitId("100000"),
4748 ],
4749 },
4750 WithinVisibility {
4751 candidates: WithinReference {
4752 candidates: Filter(HasConflict),
4753 commits: [],
4754 },
4755 visible_heads: [
4756 CommitId("200000"),
4757 ],
4758 },
4759 ),
4760 Commits(
4761 [
4762 CommitId("000003"),
4763 ],
4764 ),
4765 ),
4766 All,
4767 ),
4768 commits: [
4769 CommitId("000003"),
4770 CommitId("100000"),
4771 CommitId("100001"),
4772 CommitId("200000"),
4773 ],
4774 },
4775 )
4776 "#);
4777
4778 insta::assert_debug_snapshot!(
4780 resolve_referenced_commits(&Arc::new(ResolvedRevsetExpression::WithinVisibility {
4781 candidates: visibility1.clone(),
4782 visible_heads: vec![CommitId::from_hex("400000")],
4783 })), @r#"
4784 Some(
4785 WithinReference {
4786 candidates: WithinVisibility {
4787 candidates: WithinReference {
4788 candidates: WithinVisibility {
4789 candidates: WithinReference {
4790 candidates: Commits(
4791 [
4792 CommitId("100001"),
4793 ],
4794 ),
4795 commits: [
4796 CommitId("100001"),
4797 ],
4798 },
4799 visible_heads: [
4800 CommitId("100000"),
4801 ],
4802 },
4803 commits: [
4804 CommitId("100000"),
4805 CommitId("100001"),
4806 ],
4807 },
4808 visible_heads: [
4809 CommitId("400000"),
4810 ],
4811 },
4812 commits: [
4813 CommitId("400000"),
4814 CommitId("100000"),
4815 CommitId("100001"),
4816 ],
4817 },
4818 )
4819 "#);
4820
4821 let resolved = Arc::new(ResolvedRevsetExpression::WithinReference {
4823 candidates: RevsetExpression::none(),
4826 commits: vec![CommitId::from_hex("100000")],
4827 });
4828 insta::assert_debug_snapshot!(
4829 resolve_referenced_commits(&resolved), @"None");
4830 insta::assert_debug_snapshot!(
4831 resolve_referenced_commits(&resolved.intersection(&RevsetExpression::all())), @r#"
4832 Some(
4833 WithinReference {
4834 candidates: Intersection(
4835 WithinReference {
4836 candidates: None,
4837 commits: [
4838 CommitId("100000"),
4839 ],
4840 },
4841 All,
4842 ),
4843 commits: [
4844 CommitId("100000"),
4845 ],
4846 },
4847 )
4848 "#);
4849 insta::assert_debug_snapshot!(
4850 resolve_referenced_commits(&Arc::new(ResolvedRevsetExpression::WithinVisibility {
4851 candidates: resolved.clone(),
4852 visible_heads: vec![CommitId::from_hex("400000")],
4853 })), @r#"
4854 Some(
4855 WithinReference {
4856 candidates: WithinVisibility {
4857 candidates: WithinReference {
4858 candidates: None,
4859 commits: [
4860 CommitId("100000"),
4861 ],
4862 },
4863 visible_heads: [
4864 CommitId("400000"),
4865 ],
4866 },
4867 commits: [
4868 CommitId("400000"),
4869 CommitId("100000"),
4870 ],
4871 },
4872 )
4873 "#);
4874 }
4875
4876 #[test]
4877 fn test_optimize_subtree() -> TestResult {
4878 let settings = insta_settings();
4879 let _guard = settings.bind_to_scope();
4880
4881 insta::assert_debug_snapshot!(
4885 optimize(parse("parents(bookmarks() & all())")?), @r#"
4886 Ancestors {
4887 heads: CommitRef(Bookmarks(Pattern(Substring("")))),
4888 generation: 1..2,
4889 parents_range: 0..4294967295,
4890 }
4891 "#);
4892 insta::assert_debug_snapshot!(
4893 optimize(parse("children(bookmarks() & all())")?), @r#"
4894 Descendants {
4895 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4896 generation: 1..2,
4897 }
4898 "#);
4899 insta::assert_debug_snapshot!(
4900 optimize(parse("ancestors(bookmarks() & all())")?), @r#"
4901 Ancestors {
4902 heads: CommitRef(Bookmarks(Pattern(Substring("")))),
4903 generation: 0..18446744073709551615,
4904 parents_range: 0..4294967295,
4905 }
4906 "#);
4907 insta::assert_debug_snapshot!(
4908 optimize(parse("descendants(bookmarks() & all())")?), @r#"
4909 Descendants {
4910 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4911 generation: 0..18446744073709551615,
4912 }
4913 "#);
4914
4915 insta::assert_debug_snapshot!(
4916 optimize(parse("(bookmarks() & all())..(all() & tags())")?), @r#"
4917 Range {
4918 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4919 heads: CommitRef(Tags(Pattern(Substring("")))),
4920 generation: 0..18446744073709551615,
4921 parents_range: 0..4294967295,
4922 }
4923 "#);
4924 insta::assert_debug_snapshot!(
4925 optimize(parse("(bookmarks() & all())::(all() & tags())")?), @r#"
4926 DagRange {
4927 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4928 heads: CommitRef(Tags(Pattern(Substring("")))),
4929 }
4930 "#);
4931
4932 insta::assert_debug_snapshot!(
4933 optimize(parse("heads(bookmarks() & all())")?),
4934 @r#"
4935 Heads(
4936 CommitRef(Bookmarks(Pattern(Substring("")))),
4937 )
4938 "#);
4939 insta::assert_debug_snapshot!(
4940 optimize(parse("roots(bookmarks() & all())")?),
4941 @r#"
4942 Roots(
4943 CommitRef(Bookmarks(Pattern(Substring("")))),
4944 )
4945 "#);
4946
4947 insta::assert_debug_snapshot!(
4948 optimize(parse("latest(bookmarks() & all(), 2)")?), @r#"
4949 Latest {
4950 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
4951 count: 2,
4952 }
4953 "#);
4954
4955 insta::assert_debug_snapshot!(
4956 optimize(parse("present(foo ~ bar)")?), @r#"
4957 Present(
4958 Difference(
4959 CommitRef(Symbol("foo")),
4960 CommitRef(Symbol("bar")),
4961 ),
4962 )
4963 "#);
4964 insta::assert_debug_snapshot!(
4965 optimize(parse("present(bookmarks() & all())")?),
4966 @r#"
4967 Present(
4968 CommitRef(Bookmarks(Pattern(Substring("")))),
4969 )
4970 "#);
4971
4972 insta::assert_debug_snapshot!(
4973 optimize(parse("at_operation(@-, bookmarks() & all())")?), @r#"
4974 AtOperation {
4975 operation: "@-",
4976 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
4977 }
4978 "#);
4979 insta::assert_debug_snapshot!(
4980 optimize(Arc::new(RevsetExpression::WithinReference {
4981 candidates: parse("bookmarks() & all()")?,
4982 commits: vec![CommitId::from_hex("012345")],
4983 })), @r#"
4984 WithinReference {
4985 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
4986 commits: [
4987 CommitId("012345"),
4988 ],
4989 }
4990 "#);
4991 insta::assert_debug_snapshot!(
4992 optimize(Arc::new(RevsetExpression::WithinVisibility {
4993 candidates: parse("bookmarks() & all()")?,
4994 visible_heads: vec![CommitId::from_hex("012345")],
4995 })), @r#"
4996 WithinReference {
4997 candidates: WithinVisibility {
4998 candidates: WithinReference {
4999 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
5000 commits: [],
5001 },
5002 visible_heads: [
5003 CommitId("012345"),
5004 ],
5005 },
5006 commits: [
5007 CommitId("012345"),
5008 ],
5009 }
5010 "#);
5011
5012 insta::assert_debug_snapshot!(
5013 optimize(parse("~bookmarks() & all()")?),
5014 @r#"
5015 NotIn(
5016 CommitRef(Bookmarks(Pattern(Substring("")))),
5017 )
5018 "#);
5019 insta::assert_debug_snapshot!(
5020 optimize(parse("(bookmarks() & all()) | (all() & tags())")?), @r#"
5021 Union(
5022 CommitRef(Bookmarks(Pattern(Substring("")))),
5023 CommitRef(Tags(Pattern(Substring("")))),
5024 )
5025 "#);
5026 insta::assert_debug_snapshot!(
5027 optimize(parse("(bookmarks() & all()) & (all() & tags())")?), @r#"
5028 Intersection(
5029 CommitRef(Bookmarks(Pattern(Substring("")))),
5030 CommitRef(Tags(Pattern(Substring("")))),
5031 )
5032 "#);
5033 insta::assert_debug_snapshot!(
5034 optimize(parse("(bookmarks() & all()) ~ (all() & tags())")?), @r#"
5035 Difference(
5036 CommitRef(Bookmarks(Pattern(Substring("")))),
5037 CommitRef(Tags(Pattern(Substring("")))),
5038 )
5039 "#);
5040 Ok(())
5041 }
5042
5043 #[test]
5044 fn test_optimize_unchanged_subtree() -> TestResult {
5045 fn unwrap_union(
5046 expression: &UserRevsetExpression,
5047 ) -> (&Arc<UserRevsetExpression>, &Arc<UserRevsetExpression>) {
5048 match expression {
5049 RevsetExpression::Union(left, right) => (left, right),
5050 _ => panic!("unexpected expression: {expression:?}"),
5051 }
5052 }
5053
5054 let parsed = parse("foo-")?;
5056 let optimized = optimize(parsed.clone());
5057 assert!(Arc::ptr_eq(&parsed, &optimized));
5058
5059 let parsed = parse("bookmarks() | tags()")?;
5060 let optimized = optimize(parsed.clone());
5061 assert!(Arc::ptr_eq(&parsed, &optimized));
5062
5063 let parsed = parse("bookmarks() & tags()")?;
5064 let optimized = optimize(parsed.clone());
5065 assert!(Arc::ptr_eq(&parsed, &optimized));
5066
5067 let parsed = parse("(bookmarks() & all()) | tags()")?;
5069 let optimized = optimize(parsed.clone());
5070 assert_matches!(
5071 unwrap_union(&optimized).0.as_ref(),
5072 RevsetExpression::CommitRef(RevsetCommitRef::Bookmarks(_))
5073 );
5074 assert!(Arc::ptr_eq(
5075 unwrap_union(&parsed).1,
5076 unwrap_union(&optimized).1
5077 ));
5078
5079 let parsed = parse("bookmarks() | (all() & tags())")?;
5081 let optimized = optimize(parsed.clone());
5082 assert!(Arc::ptr_eq(
5083 unwrap_union(&parsed).0,
5084 unwrap_union(&optimized).0
5085 ));
5086 assert_matches!(
5087 unwrap_union(&optimized).1.as_ref(),
5088 RevsetExpression::CommitRef(RevsetCommitRef::Tags(_))
5089 );
5090 Ok(())
5091 }
5092
5093 #[test]
5094 fn test_optimize_basic() -> TestResult {
5095 let settings = insta_settings();
5096 let _guard = settings.bind_to_scope();
5097
5098 insta::assert_debug_snapshot!(optimize(parse("all() | none()")?), @"All");
5099 insta::assert_debug_snapshot!(optimize(parse("all() & none()")?), @"None");
5100 insta::assert_debug_snapshot!(optimize(parse("root() | all()")?), @"All");
5101 insta::assert_debug_snapshot!(optimize(parse("root() & all()")?), @"Root");
5102 insta::assert_debug_snapshot!(optimize(parse("none() | root()")?), @"Root");
5103 insta::assert_debug_snapshot!(optimize(parse("none() & root()")?), @"None");
5104 insta::assert_debug_snapshot!(optimize(parse("~none()")?), @"All");
5105 insta::assert_debug_snapshot!(optimize(parse("~~none()")?), @"None");
5106 insta::assert_debug_snapshot!(optimize(parse("~all()")?), @"None");
5107 insta::assert_debug_snapshot!(optimize(parse("~~all()")?), @"All");
5108 insta::assert_debug_snapshot!(optimize(parse("~~foo")?), @r#"CommitRef(Symbol("foo"))"#);
5109 insta::assert_debug_snapshot!(
5110 optimize(parse("(root() | none()) & (visible_heads() | ~~all())")?), @"Root");
5111 insta::assert_debug_snapshot!(
5112 optimize(UserRevsetExpression::commits(vec![])), @"None");
5113 insta::assert_debug_snapshot!(
5114 optimize(UserRevsetExpression::commits(vec![]).negated()), @"All");
5115 Ok(())
5116 }
5117
5118 #[test]
5119 fn test_optimize_difference() -> TestResult {
5120 let settings = insta_settings();
5121 let _guard = settings.bind_to_scope();
5122
5123 insta::assert_debug_snapshot!(optimize(parse("foo & ~bar")?), @r#"
5124 Difference(
5125 CommitRef(Symbol("foo")),
5126 CommitRef(Symbol("bar")),
5127 )
5128 "#);
5129 insta::assert_debug_snapshot!(optimize(parse("~foo & bar")?), @r#"
5130 Difference(
5131 CommitRef(Symbol("bar")),
5132 CommitRef(Symbol("foo")),
5133 )
5134 "#);
5135 insta::assert_debug_snapshot!(optimize(parse("~foo & bar & ~baz")?), @r#"
5136 Difference(
5137 Difference(
5138 CommitRef(Symbol("bar")),
5139 CommitRef(Symbol("foo")),
5140 ),
5141 CommitRef(Symbol("baz")),
5142 )
5143 "#);
5144 insta::assert_debug_snapshot!(optimize(parse("(all() & ~foo) & bar")?), @r#"
5145 Difference(
5146 CommitRef(Symbol("bar")),
5147 CommitRef(Symbol("foo")),
5148 )
5149 "#);
5150
5151 insta::assert_debug_snapshot!(
5153 optimize(parse("all() ~ foo")?),
5154 @r#"NotIn(CommitRef(Symbol("foo")))"#);
5155 insta::assert_debug_snapshot!(optimize(parse("foo ~ bar")?), @r#"
5156 Difference(
5157 CommitRef(Symbol("foo")),
5158 CommitRef(Symbol("bar")),
5159 )
5160 "#);
5161 insta::assert_debug_snapshot!(optimize(parse("(all() ~ foo) & bar")?), @r#"
5162 Difference(
5163 CommitRef(Symbol("bar")),
5164 CommitRef(Symbol("foo")),
5165 )
5166 "#);
5167
5168 insta::assert_debug_snapshot!(optimize(parse("::foo & ~::bar")?), @r#"
5170 Range {
5171 roots: CommitRef(Symbol("bar")),
5172 heads: CommitRef(Symbol("foo")),
5173 generation: 0..18446744073709551615,
5174 parents_range: 0..4294967295,
5175 }
5176 "#);
5177 insta::assert_debug_snapshot!(optimize(parse("~::foo & ::bar")?), @r#"
5178 Range {
5179 roots: CommitRef(Symbol("foo")),
5180 heads: CommitRef(Symbol("bar")),
5181 generation: 0..18446744073709551615,
5182 parents_range: 0..4294967295,
5183 }
5184 "#);
5185 insta::assert_debug_snapshot!(optimize(parse("foo..")?), @r#"
5186 Range {
5187 roots: CommitRef(Symbol("foo")),
5188 heads: VisibleHeadsOrReferenced,
5189 generation: 0..18446744073709551615,
5190 parents_range: 0..4294967295,
5191 }
5192 "#);
5193 insta::assert_debug_snapshot!(optimize(parse("foo..bar")?), @r#"
5194 Range {
5195 roots: CommitRef(Symbol("foo")),
5196 heads: CommitRef(Symbol("bar")),
5197 generation: 0..18446744073709551615,
5198 parents_range: 0..4294967295,
5199 }
5200 "#);
5201 insta::assert_debug_snapshot!(optimize(parse("foo.. & ::bar")?), @r#"
5202 Range {
5203 roots: CommitRef(Symbol("foo")),
5204 heads: CommitRef(Symbol("bar")),
5205 generation: 0..18446744073709551615,
5206 parents_range: 0..4294967295,
5207 }
5208 "#);
5209 insta::assert_debug_snapshot!(optimize(parse("foo.. & first_ancestors(bar)")?), @r#"
5210 Range {
5211 roots: CommitRef(Symbol("foo")),
5212 heads: CommitRef(Symbol("bar")),
5213 generation: 0..18446744073709551615,
5214 parents_range: 0..1,
5215 }
5216 "#);
5217
5218 insta::assert_debug_snapshot!(optimize(parse("foo & ~~bar")?), @r#"
5220 Intersection(
5221 CommitRef(Symbol("foo")),
5222 CommitRef(Symbol("bar")),
5223 )
5224 "#);
5225 insta::assert_debug_snapshot!(optimize(parse("foo & ~~~bar")?), @r#"
5226 Difference(
5227 CommitRef(Symbol("foo")),
5228 CommitRef(Symbol("bar")),
5229 )
5230 "#);
5231 insta::assert_debug_snapshot!(optimize(parse("~(all() & ~foo) & bar")?), @r#"
5232 Intersection(
5233 CommitRef(Symbol("foo")),
5234 CommitRef(Symbol("bar")),
5235 )
5236 "#);
5237
5238 insta::assert_debug_snapshot!(optimize(parse("~foo & ~bar")?), @r#"
5240 Difference(
5241 NotIn(CommitRef(Symbol("foo"))),
5242 CommitRef(Symbol("bar")),
5243 )
5244 "#);
5245
5246 insta::assert_debug_snapshot!(optimize(parse("a..b & c..d")?), @r#"
5248 Intersection(
5249 Range {
5250 roots: Union(
5251 CommitRef(Symbol("a")),
5252 CommitRef(Symbol("c")),
5253 ),
5254 heads: CommitRef(Symbol("b")),
5255 generation: 0..18446744073709551615,
5256 parents_range: 0..4294967295,
5257 },
5258 Ancestors {
5259 heads: CommitRef(Symbol("d")),
5260 generation: 0..18446744073709551615,
5261 parents_range: 0..4294967295,
5262 },
5263 )
5264 "#);
5265
5266 insta::assert_debug_snapshot!(optimize(parse("foo..bar ~ first_ancestors(baz)")?), @r#"
5268 Difference(
5269 Range {
5270 roots: CommitRef(Symbol("foo")),
5271 heads: CommitRef(Symbol("bar")),
5272 generation: 0..18446744073709551615,
5273 parents_range: 0..4294967295,
5274 },
5275 Ancestors {
5276 heads: CommitRef(Symbol("baz")),
5277 generation: 0..18446744073709551615,
5278 parents_range: 0..1,
5279 },
5280 )
5281 "#);
5282
5283 insta::assert_debug_snapshot!(optimize(parse("foo ~ ::a & (::b & bar & ::c) & (baz ~ ::d)")?), @r#"
5286 Intersection(
5287 Intersection(
5288 Intersection(
5289 Intersection(
5290 Range {
5291 roots: Union(
5292 CommitRef(Symbol("a")),
5293 CommitRef(Symbol("d")),
5294 ),
5295 heads: CommitRef(Symbol("b")),
5296 generation: 0..18446744073709551615,
5297 parents_range: 0..4294967295,
5298 },
5299 Ancestors {
5300 heads: CommitRef(Symbol("c")),
5301 generation: 0..18446744073709551615,
5302 parents_range: 0..4294967295,
5303 },
5304 ),
5305 CommitRef(Symbol("foo")),
5306 ),
5307 CommitRef(Symbol("bar")),
5308 ),
5309 CommitRef(Symbol("baz")),
5310 )
5311 "#);
5312 Ok(())
5313 }
5314
5315 #[test]
5316 fn test_optimize_not_in_ancestors() -> TestResult {
5317 let settings = insta_settings();
5318 let _guard = settings.bind_to_scope();
5319
5320 insta::assert_debug_snapshot!(optimize(parse("~(::foo)")?), @r#"
5322 Range {
5323 roots: CommitRef(Symbol("foo")),
5324 heads: VisibleHeadsOrReferenced,
5325 generation: 0..18446744073709551615,
5326 parents_range: 0..4294967295,
5327 }
5328 "#);
5329
5330 insta::assert_debug_snapshot!(optimize(parse("~(::foo-)")?), @r#"
5332 Range {
5333 roots: Ancestors {
5334 heads: CommitRef(Symbol("foo")),
5335 generation: 1..2,
5336 parents_range: 0..4294967295,
5337 },
5338 heads: VisibleHeadsOrReferenced,
5339 generation: 0..18446744073709551615,
5340 parents_range: 0..4294967295,
5341 }
5342 "#);
5343 insta::assert_debug_snapshot!(optimize(parse("~(::foo--)")?), @r#"
5344 Range {
5345 roots: Ancestors {
5346 heads: CommitRef(Symbol("foo")),
5347 generation: 2..3,
5348 parents_range: 0..4294967295,
5349 },
5350 heads: VisibleHeadsOrReferenced,
5351 generation: 0..18446744073709551615,
5352 parents_range: 0..4294967295,
5353 }
5354 "#);
5355
5356 insta::assert_debug_snapshot!(optimize(parse("~ancestors(foo, 1)")?), @r#"
5358 NotIn(
5359 Ancestors {
5360 heads: CommitRef(Symbol("foo")),
5361 generation: 0..1,
5362 parents_range: 0..4294967295,
5363 },
5364 )
5365 "#);
5366 insta::assert_debug_snapshot!(optimize(parse("~ancestors(foo-, 1)")?), @r#"
5367 NotIn(
5368 Ancestors {
5369 heads: CommitRef(Symbol("foo")),
5370 generation: 1..2,
5371 parents_range: 0..4294967295,
5372 },
5373 )
5374 "#);
5375 Ok(())
5376 }
5377
5378 #[test]
5379 fn test_optimize_filter_difference() -> TestResult {
5380 let settings = insta_settings();
5381 let _guard = settings.bind_to_scope();
5382
5383 insta::assert_debug_snapshot!(optimize(parse("~empty()")?), @"Filter(File(All))");
5385
5386 insta::assert_debug_snapshot!(
5388 optimize(parse("(author_name(foo) & ~bar) & baz")?), @r#"
5389 Intersection(
5390 Difference(
5391 CommitRef(Symbol("baz")),
5392 CommitRef(Symbol("bar")),
5393 ),
5394 Filter(AuthorName(Pattern(Exact("foo")))),
5395 )
5396 "#);
5397
5398 insta::assert_debug_snapshot!(
5400 optimize(parse("~foo & author_name(bar)")?), @r#"
5401 Intersection(
5402 NotIn(CommitRef(Symbol("foo"))),
5403 Filter(AuthorName(Pattern(Exact("bar")))),
5404 )
5405 "#);
5406 insta::assert_debug_snapshot!(
5407 optimize(parse("~foo & (author_name(bar) | baz)")?), @r#"
5408 Intersection(
5409 NotIn(CommitRef(Symbol("foo"))),
5410 AsFilter(
5411 Union(
5412 Filter(AuthorName(Pattern(Exact("bar")))),
5413 CommitRef(Symbol("baz")),
5414 ),
5415 ),
5416 )
5417 "#);
5418
5419 insta::assert_debug_snapshot!(
5421 optimize(parse("author_name(foo) ~ bar")?), @r#"
5422 Intersection(
5423 NotIn(CommitRef(Symbol("bar"))),
5424 Filter(AuthorName(Pattern(Exact("foo")))),
5425 )
5426 "#);
5427 Ok(())
5428 }
5429
5430 #[test]
5431 fn test_optimize_filter_intersection() -> TestResult {
5432 let settings = insta_settings();
5433 let _guard = settings.bind_to_scope();
5434
5435 insta::assert_debug_snapshot!(
5436 optimize(parse("author_name(foo)")?),
5437 @r#"Filter(AuthorName(Pattern(Exact("foo"))))"#);
5438
5439 insta::assert_debug_snapshot!(optimize(parse("foo & description(bar)")?), @r#"
5440 Intersection(
5441 CommitRef(Symbol("foo")),
5442 Filter(Description(Pattern(Exact("bar")))),
5443 )
5444 "#);
5445 insta::assert_debug_snapshot!(optimize(parse("author_name(foo) & bar")?), @r#"
5446 Intersection(
5447 CommitRef(Symbol("bar")),
5448 Filter(AuthorName(Pattern(Exact("foo")))),
5449 )
5450 "#);
5451 insta::assert_debug_snapshot!(
5452 optimize(parse("author_name(foo) & committer_name(bar)")?), @r#"
5453 AsFilter(
5454 Intersection(
5455 Filter(AuthorName(Pattern(Exact("foo")))),
5456 Filter(CommitterName(Pattern(Exact("bar")))),
5457 ),
5458 )
5459 "#);
5460 insta::assert_debug_snapshot!(optimize(parse("divergent() & foo")?), @r#"
5461 Intersection(
5462 CommitRef(Symbol("foo")),
5463 AsFilter(Divergent),
5464 )
5465 "#);
5466
5467 insta::assert_debug_snapshot!(
5468 optimize(parse("foo & description(bar) & author_name(baz)")?), @r#"
5469 Intersection(
5470 CommitRef(Symbol("foo")),
5471 AsFilter(
5472 Intersection(
5473 Filter(Description(Pattern(Exact("bar")))),
5474 Filter(AuthorName(Pattern(Exact("baz")))),
5475 ),
5476 ),
5477 )
5478 "#);
5479 insta::assert_debug_snapshot!(
5480 optimize(parse("committer_name(foo) & bar & author_name(baz)")?), @r#"
5481 Intersection(
5482 CommitRef(Symbol("bar")),
5483 AsFilter(
5484 Intersection(
5485 Filter(CommitterName(Pattern(Exact("foo")))),
5486 Filter(AuthorName(Pattern(Exact("baz")))),
5487 ),
5488 ),
5489 )
5490 "#);
5491 insta::assert_debug_snapshot!(
5492 optimize(parse_with_workspace(
5493 "committer_name(foo) & files(bar) & baz",
5494 WorkspaceName::DEFAULT)?,
5495 ), @r#"
5496 Intersection(
5497 CommitRef(Symbol("baz")),
5498 AsFilter(
5499 Intersection(
5500 Filter(CommitterName(Pattern(Exact("foo")))),
5501 Filter(File(Pattern(PrefixPath("bar")))),
5502 ),
5503 ),
5504 )
5505 "#);
5506 insta::assert_debug_snapshot!(
5507 optimize(parse_with_workspace(
5508 "committer_name(foo) & files(bar) & author_name(baz)",
5509 WorkspaceName::DEFAULT)?,
5510 ), @r#"
5511 AsFilter(
5512 Intersection(
5513 Intersection(
5514 Filter(CommitterName(Pattern(Exact("foo")))),
5515 Filter(File(Pattern(PrefixPath("bar")))),
5516 ),
5517 Filter(AuthorName(Pattern(Exact("baz")))),
5518 ),
5519 )
5520 "#);
5521 insta::assert_debug_snapshot!(
5522 optimize(parse_with_workspace(
5523 "foo & files(bar) & baz",
5524 WorkspaceName::DEFAULT)?,
5525 ), @r#"
5526 Intersection(
5527 Intersection(
5528 CommitRef(Symbol("foo")),
5529 CommitRef(Symbol("baz")),
5530 ),
5531 Filter(File(Pattern(PrefixPath("bar")))),
5532 )
5533 "#);
5534
5535 insta::assert_debug_snapshot!(
5536 optimize(parse("foo & description(bar) & author_name(baz) & qux")?), @r#"
5537 Intersection(
5538 Intersection(
5539 CommitRef(Symbol("foo")),
5540 CommitRef(Symbol("qux")),
5541 ),
5542 AsFilter(
5543 Intersection(
5544 Filter(Description(Pattern(Exact("bar")))),
5545 Filter(AuthorName(Pattern(Exact("baz")))),
5546 ),
5547 ),
5548 )
5549 "#);
5550 insta::assert_debug_snapshot!(
5551 optimize(parse("foo & description(bar) & parents(author_name(baz)) & qux")?),
5552 @r#"
5553 Intersection(
5554 Intersection(
5555 Intersection(
5556 CommitRef(Symbol("foo")),
5557 Ancestors {
5558 heads: Filter(AuthorName(Pattern(Exact("baz")))),
5559 generation: 1..2,
5560 parents_range: 0..4294967295,
5561 },
5562 ),
5563 CommitRef(Symbol("qux")),
5564 ),
5565 Filter(Description(Pattern(Exact("bar")))),
5566 )
5567 "#);
5568 insta::assert_debug_snapshot!(
5569 optimize(parse("foo & description(bar) & parents(author_name(baz) & qux)")?),
5570 @r#"
5571 Intersection(
5572 Intersection(
5573 CommitRef(Symbol("foo")),
5574 Ancestors {
5575 heads: Intersection(
5576 CommitRef(Symbol("qux")),
5577 Filter(AuthorName(Pattern(Exact("baz")))),
5578 ),
5579 generation: 1..2,
5580 parents_range: 0..4294967295,
5581 },
5582 ),
5583 Filter(Description(Pattern(Exact("bar")))),
5584 )
5585 "#);
5586
5587 insta::assert_debug_snapshot!(
5589 optimize(parse("(a & author_name(A)) & (b & author_name(B)) & (c & author_name(C))")?),
5590 @r#"
5591 Intersection(
5592 Intersection(
5593 Intersection(
5594 CommitRef(Symbol("a")),
5595 CommitRef(Symbol("b")),
5596 ),
5597 CommitRef(Symbol("c")),
5598 ),
5599 AsFilter(
5600 Intersection(
5601 Intersection(
5602 Filter(AuthorName(Pattern(Exact("A")))),
5603 Filter(AuthorName(Pattern(Exact("B")))),
5604 ),
5605 Filter(AuthorName(Pattern(Exact("C")))),
5606 ),
5607 ),
5608 )
5609 "#);
5610 insta::assert_debug_snapshot!(
5611 optimize(parse("(a & author_name(A)) & ((b & author_name(B)) & (c & author_name(C))) & d")?),
5612 @r#"
5613 Intersection(
5614 Intersection(
5615 Intersection(
5616 Intersection(
5617 CommitRef(Symbol("a")),
5618 CommitRef(Symbol("b")),
5619 ),
5620 CommitRef(Symbol("c")),
5621 ),
5622 CommitRef(Symbol("d")),
5623 ),
5624 AsFilter(
5625 Intersection(
5626 Intersection(
5627 Filter(AuthorName(Pattern(Exact("A")))),
5628 Filter(AuthorName(Pattern(Exact("B")))),
5629 ),
5630 Filter(AuthorName(Pattern(Exact("C")))),
5631 ),
5632 ),
5633 )
5634 "#);
5635
5636 insta::assert_debug_snapshot!(
5638 optimize(parse("foo & (all() & description(bar)) & (author_name(baz) & all())")?),
5639 @r#"
5640 Intersection(
5641 CommitRef(Symbol("foo")),
5642 AsFilter(
5643 Intersection(
5644 Filter(Description(Pattern(Exact("bar")))),
5645 Filter(AuthorName(Pattern(Exact("baz")))),
5646 ),
5647 ),
5648 )
5649 "#);
5650
5651 insta::assert_debug_snapshot!(
5653 optimize(parse("author_name(foo) & bar & at_operation(@-, committer_name(baz))")?),
5654 @r#"
5655 Intersection(
5656 Intersection(
5657 CommitRef(Symbol("bar")),
5658 AtOperation {
5659 operation: "@-",
5660 candidates: Filter(CommitterName(Pattern(Exact("baz")))),
5661 },
5662 ),
5663 Filter(AuthorName(Pattern(Exact("foo")))),
5664 )
5665 "#);
5666 Ok(())
5667 }
5668
5669 #[test]
5670 fn test_optimize_filter_subtree() -> TestResult {
5671 let settings = insta_settings();
5672 let _guard = settings.bind_to_scope();
5673
5674 insta::assert_debug_snapshot!(
5675 optimize(parse("(author_name(foo) | bar) & baz")?), @r#"
5676 Intersection(
5677 CommitRef(Symbol("baz")),
5678 AsFilter(
5679 Union(
5680 Filter(AuthorName(Pattern(Exact("foo")))),
5681 CommitRef(Symbol("bar")),
5682 ),
5683 ),
5684 )
5685 "#);
5686
5687 insta::assert_debug_snapshot!(
5689 optimize(parse("merges() & foo | bar")?), @r#"
5690 Union(
5691 Intersection(
5692 CommitRef(Symbol("foo")),
5693 Filter(ParentCount(2..4294967295)),
5694 ),
5695 CommitRef(Symbol("bar")),
5696 )
5697 "#);
5698
5699 insta::assert_debug_snapshot!(
5702 optimize(parse("merges() & foo | conflicts()")?), @r#"
5703 AsFilter(
5704 Union(
5705 Intersection(
5706 CommitRef(Symbol("foo")),
5707 Filter(ParentCount(2..4294967295)),
5708 ),
5709 Filter(HasConflict),
5710 ),
5711 )
5712 "#);
5713
5714 insta::assert_debug_snapshot!(
5716 optimize(parse("foo | conflicts() & merges() & signed()")?), @r#"
5717 AsFilter(
5718 Union(
5719 CommitRef(Symbol("foo")),
5720 Intersection(
5721 Intersection(
5722 Filter(HasConflict),
5723 Filter(ParentCount(2..4294967295)),
5724 ),
5725 Filter(Signed),
5726 ),
5727 ),
5728 )
5729 "#);
5730
5731 insta::assert_debug_snapshot!(
5732 optimize(parse("(foo | committer_name(bar)) & description(baz) & qux")?), @r#"
5733 Intersection(
5734 CommitRef(Symbol("qux")),
5735 AsFilter(
5736 Intersection(
5737 Union(
5738 CommitRef(Symbol("foo")),
5739 Filter(CommitterName(Pattern(Exact("bar")))),
5740 ),
5741 Filter(Description(Pattern(Exact("baz")))),
5742 ),
5743 ),
5744 )
5745 "#);
5746
5747 insta::assert_debug_snapshot!(
5748 optimize(parse(
5749 "(~present(author_name(foo) & description(bar)) | baz) & qux")?), @r#"
5750 Intersection(
5751 CommitRef(Symbol("qux")),
5752 AsFilter(
5753 Union(
5754 NotIn(
5755 Present(
5756 Intersection(
5757 Filter(AuthorName(Pattern(Exact("foo")))),
5758 Filter(Description(Pattern(Exact("bar")))),
5759 ),
5760 ),
5761 ),
5762 CommitRef(Symbol("baz")),
5763 ),
5764 ),
5765 )
5766 "#);
5767
5768 insta::assert_debug_snapshot!(
5770 optimize(parse(
5771 "(a & (author_name(A) | 0)) & (b & (author_name(B) | 1)) & (c & (author_name(C) | 2))")?),
5772 @r#"
5773 Intersection(
5774 Intersection(
5775 Intersection(
5776 CommitRef(Symbol("a")),
5777 CommitRef(Symbol("b")),
5778 ),
5779 CommitRef(Symbol("c")),
5780 ),
5781 AsFilter(
5782 Intersection(
5783 Intersection(
5784 Union(
5785 Filter(AuthorName(Pattern(Exact("A")))),
5786 CommitRef(Symbol("0")),
5787 ),
5788 Union(
5789 Filter(AuthorName(Pattern(Exact("B")))),
5790 CommitRef(Symbol("1")),
5791 ),
5792 ),
5793 Union(
5794 Filter(AuthorName(Pattern(Exact("C")))),
5795 CommitRef(Symbol("2")),
5796 ),
5797 ),
5798 ),
5799 )
5800 "#);
5801
5802 insta::assert_debug_snapshot!(optimize(parse("::foo | ::author_name(bar)")?), @r#"
5804 Ancestors {
5805 heads: HeadsRange {
5806 roots: None,
5807 heads: VisibleHeadsOrReferenced,
5808 parents_range: 0..4294967295,
5809 filter: AsFilter(
5810 Union(
5811 CommitRef(Symbol("foo")),
5812 Filter(AuthorName(Pattern(Exact("bar")))),
5813 ),
5814 ),
5815 },
5816 generation: 0..18446744073709551615,
5817 parents_range: 0..4294967295,
5818 }
5819 "#);
5820 Ok(())
5821 }
5822
5823 #[test]
5824 fn test_optimize_ancestors() -> TestResult {
5825 let settings = insta_settings();
5826 let _guard = settings.bind_to_scope();
5827
5828 insta::assert_debug_snapshot!(optimize(parse("foo--")?), @r#"
5830 Ancestors {
5831 heads: CommitRef(Symbol("foo")),
5832 generation: 2..3,
5833 parents_range: 0..4294967295,
5834 }
5835 "#);
5836 insta::assert_debug_snapshot!(optimize(parse("::(foo---)")?), @r#"
5837 Ancestors {
5838 heads: CommitRef(Symbol("foo")),
5839 generation: 3..18446744073709551615,
5840 parents_range: 0..4294967295,
5841 }
5842 "#);
5843 insta::assert_debug_snapshot!(optimize(parse("(::foo)---")?), @r#"
5844 Ancestors {
5845 heads: CommitRef(Symbol("foo")),
5846 generation: 3..18446744073709551615,
5847 parents_range: 0..4294967295,
5848 }
5849 "#);
5850
5851 insta::assert_debug_snapshot!(optimize(parse("foo---+")?), @r#"
5853 Descendants {
5854 roots: Ancestors {
5855 heads: CommitRef(Symbol("foo")),
5856 generation: 3..4,
5857 parents_range: 0..4294967295,
5858 },
5859 generation: 1..2,
5860 }
5861 "#);
5862
5863 insta::assert_debug_snapshot!(optimize(parse("foo..(bar--)")?), @r#"
5865 Range {
5866 roots: CommitRef(Symbol("foo")),
5867 heads: CommitRef(Symbol("bar")),
5868 generation: 2..18446744073709551615,
5869 parents_range: 0..4294967295,
5870 }
5871 "#);
5872 insta::assert_debug_snapshot!(optimize(parse("(foo--)..(bar---)")?), @r#"
5874 Range {
5875 roots: Ancestors {
5876 heads: CommitRef(Symbol("foo")),
5877 generation: 2..3,
5878 parents_range: 0..4294967295,
5879 },
5880 heads: CommitRef(Symbol("bar")),
5881 generation: 3..18446744073709551615,
5882 parents_range: 0..4294967295,
5883 }
5884 "#);
5885 insta::assert_debug_snapshot!(
5887 optimize(parse("~ancestors(foo, 2) & ::bar")?), @r#"
5888 Difference(
5889 Ancestors {
5890 heads: CommitRef(Symbol("bar")),
5891 generation: 0..18446744073709551615,
5892 parents_range: 0..4294967295,
5893 },
5894 Ancestors {
5895 heads: CommitRef(Symbol("foo")),
5896 generation: 0..2,
5897 parents_range: 0..4294967295,
5898 },
5899 )
5900 "#);
5901
5902 insta::assert_debug_snapshot!(optimize(parse("(foo..bar)--")?), @r#"
5905 Ancestors {
5906 heads: Range {
5907 roots: CommitRef(Symbol("foo")),
5908 heads: CommitRef(Symbol("bar")),
5909 generation: 0..18446744073709551615,
5910 parents_range: 0..4294967295,
5911 },
5912 generation: 2..3,
5913 parents_range: 0..4294967295,
5914 }
5915 "#);
5916 insta::assert_debug_snapshot!(optimize(parse("foo..(bar..baz)")?), @r#"
5917 Range {
5918 roots: CommitRef(Symbol("foo")),
5919 heads: HeadsRange {
5920 roots: CommitRef(Symbol("bar")),
5921 heads: CommitRef(Symbol("baz")),
5922 parents_range: 0..4294967295,
5923 filter: All,
5924 },
5925 generation: 0..18446744073709551615,
5926 parents_range: 0..4294967295,
5927 }
5928 "#);
5929
5930 insta::assert_debug_snapshot!(
5932 optimize(parse("ancestors(ancestors(foo), 0)")?), @r#"
5933 Ancestors {
5934 heads: CommitRef(Symbol("foo")),
5935 generation: 0..0,
5936 parents_range: 0..4294967295,
5937 }
5938 "#
5939 );
5940 insta::assert_debug_snapshot!(
5941 optimize(parse("ancestors(ancestors(foo, 0))")?), @r#"
5942 Ancestors {
5943 heads: CommitRef(Symbol("foo")),
5944 generation: 0..0,
5945 parents_range: 0..4294967295,
5946 }
5947 "#
5948 );
5949
5950 insta::assert_debug_snapshot!(
5952 optimize(parse("first_ancestors(first_ancestors(foo, 5), 5)")?), @r#"
5953 Ancestors {
5954 heads: CommitRef(Symbol("foo")),
5955 generation: 0..9,
5956 parents_range: 0..1,
5957 }
5958 "#
5959 );
5960 insta::assert_debug_snapshot!(
5961 optimize(parse("first_ancestors(first_parent(foo), 5)")?), @r#"
5962 Ancestors {
5963 heads: CommitRef(Symbol("foo")),
5964 generation: 1..6,
5965 parents_range: 0..1,
5966 }
5967 "#
5968 );
5969 insta::assert_debug_snapshot!(
5970 optimize(parse("first_ancestors(ancestors(foo, 5), 5)")?), @r#"
5971 Ancestors {
5972 heads: Ancestors {
5973 heads: CommitRef(Symbol("foo")),
5974 generation: 0..5,
5975 parents_range: 0..4294967295,
5976 },
5977 generation: 0..5,
5978 parents_range: 0..1,
5979 }
5980 "#
5981 );
5982 insta::assert_debug_snapshot!(
5983 optimize(parse("ancestors(first_ancestors(foo, 5), 5)")?), @r#"
5984 Ancestors {
5985 heads: Ancestors {
5986 heads: CommitRef(Symbol("foo")),
5987 generation: 0..5,
5988 parents_range: 0..1,
5989 },
5990 generation: 0..5,
5991 parents_range: 0..4294967295,
5992 }
5993 "#
5994 );
5995 Ok(())
5996 }
5997
5998 #[test]
5999 fn test_optimize_descendants() -> TestResult {
6000 let settings = insta_settings();
6001 let _guard = settings.bind_to_scope();
6002
6003 insta::assert_debug_snapshot!(optimize(parse("foo++")?), @r#"
6005 Descendants {
6006 roots: CommitRef(Symbol("foo")),
6007 generation: 2..3,
6008 }
6009 "#);
6010 insta::assert_debug_snapshot!(optimize(parse("(foo+++)::")?), @r#"
6011 Descendants {
6012 roots: CommitRef(Symbol("foo")),
6013 generation: 3..18446744073709551615,
6014 }
6015 "#);
6016 insta::assert_debug_snapshot!(optimize(parse("(foo::)+++")?), @r#"
6017 Descendants {
6018 roots: CommitRef(Symbol("foo")),
6019 generation: 3..18446744073709551615,
6020 }
6021 "#);
6022
6023 insta::assert_debug_snapshot!(optimize(parse("foo+++-")?), @r#"
6025 Ancestors {
6026 heads: Descendants {
6027 roots: CommitRef(Symbol("foo")),
6028 generation: 3..4,
6029 },
6030 generation: 1..2,
6031 parents_range: 0..4294967295,
6032 }
6033 "#);
6034
6035 insta::assert_debug_snapshot!(optimize(parse("(foo++)::bar")?), @r#"
6039 DagRange {
6040 roots: Descendants {
6041 roots: CommitRef(Symbol("foo")),
6042 generation: 2..3,
6043 },
6044 heads: CommitRef(Symbol("bar")),
6045 }
6046 "#);
6047 Ok(())
6048 }
6049
6050 #[test]
6051 fn test_optimize_flatten_intersection() -> TestResult {
6052 let settings = insta_settings();
6053 let _guard = settings.bind_to_scope();
6054
6055 insta::assert_debug_snapshot!(optimize(parse("a & ((b & c) & (d & e))")?), @r#"
6057 Intersection(
6058 Intersection(
6059 Intersection(
6060 Intersection(
6061 CommitRef(Symbol("a")),
6062 CommitRef(Symbol("b")),
6063 ),
6064 CommitRef(Symbol("c")),
6065 ),
6066 CommitRef(Symbol("d")),
6067 ),
6068 CommitRef(Symbol("e")),
6069 )
6070 "#);
6071 Ok(())
6072 }
6073
6074 #[test]
6075 fn test_optimize_ancestors_union() -> TestResult {
6076 let settings = insta_settings();
6077 let _guard = settings.bind_to_scope();
6078
6079 insta::assert_debug_snapshot!(optimize(parse("::a | ::b | ::c | ::d")?), @r#"
6081 Ancestors {
6082 heads: Union(
6083 Union(
6084 CommitRef(Symbol("a")),
6085 CommitRef(Symbol("b")),
6086 ),
6087 Union(
6088 CommitRef(Symbol("c")),
6089 CommitRef(Symbol("d")),
6090 ),
6091 ),
6092 generation: 0..18446744073709551615,
6093 parents_range: 0..4294967295,
6094 }
6095 "#);
6096 insta::assert_debug_snapshot!(optimize(parse("ancestors(a-) | ancestors(b)")?), @r#"
6097 Ancestors {
6098 heads: Union(
6099 Ancestors {
6100 heads: CommitRef(Symbol("a")),
6101 generation: 1..2,
6102 parents_range: 0..4294967295,
6103 },
6104 CommitRef(Symbol("b")),
6105 ),
6106 generation: 0..18446744073709551615,
6107 parents_range: 0..4294967295,
6108 }
6109 "#);
6110
6111 insta::assert_debug_snapshot!(optimize(parse("~::a- & ~::b & ~::c & ::d")?), @r#"
6113 Range {
6114 roots: Union(
6115 Union(
6116 Ancestors {
6117 heads: CommitRef(Symbol("a")),
6118 generation: 1..2,
6119 parents_range: 0..4294967295,
6120 },
6121 CommitRef(Symbol("b")),
6122 ),
6123 CommitRef(Symbol("c")),
6124 ),
6125 heads: CommitRef(Symbol("d")),
6126 generation: 0..18446744073709551615,
6127 parents_range: 0..4294967295,
6128 }
6129 "#);
6130 insta::assert_debug_snapshot!(optimize(parse("a..b ~ ::c- ~ ::d")?), @r#"
6131 Range {
6132 roots: Union(
6133 Union(
6134 CommitRef(Symbol("a")),
6135 Ancestors {
6136 heads: CommitRef(Symbol("c")),
6137 generation: 1..2,
6138 parents_range: 0..4294967295,
6139 },
6140 ),
6141 CommitRef(Symbol("d")),
6142 ),
6143 heads: CommitRef(Symbol("b")),
6144 generation: 0..18446744073709551615,
6145 parents_range: 0..4294967295,
6146 }
6147 "#);
6148
6149 insta::assert_debug_snapshot!(optimize(parse("ancestors(a, 2) | ancestors(b)")?), @r#"
6151 Union(
6152 Ancestors {
6153 heads: CommitRef(Symbol("a")),
6154 generation: 0..2,
6155 parents_range: 0..4294967295,
6156 },
6157 Ancestors {
6158 heads: CommitRef(Symbol("b")),
6159 generation: 0..18446744073709551615,
6160 parents_range: 0..4294967295,
6161 },
6162 )
6163 "#);
6164 Ok(())
6165 }
6166
6167 #[test]
6168 fn test_optimize_sort_negations_and_ancestors() -> TestResult {
6169 let settings = insta_settings();
6170 let _guard = settings.bind_to_scope();
6171
6172 insta::assert_debug_snapshot!(optimize(parse("~a & ::b & ~::c & d ~ e & f & ::g & ~::h")?), @r#"
6175 Difference(
6176 Difference(
6177 Intersection(
6178 Intersection(
6179 Intersection(
6180 Range {
6181 roots: Union(
6182 CommitRef(Symbol("c")),
6183 CommitRef(Symbol("h")),
6184 ),
6185 heads: CommitRef(Symbol("b")),
6186 generation: 0..18446744073709551615,
6187 parents_range: 0..4294967295,
6188 },
6189 Ancestors {
6190 heads: CommitRef(Symbol("g")),
6191 generation: 0..18446744073709551615,
6192 parents_range: 0..4294967295,
6193 },
6194 ),
6195 CommitRef(Symbol("d")),
6196 ),
6197 CommitRef(Symbol("f")),
6198 ),
6199 CommitRef(Symbol("a")),
6200 ),
6201 CommitRef(Symbol("e")),
6202 )
6203 "#);
6204 Ok(())
6205 }
6206
6207 #[test]
6208 fn test_optimize_heads_range() -> TestResult {
6209 let settings = insta_settings();
6210 let _guard = settings.bind_to_scope();
6211
6212 insta::assert_debug_snapshot!(optimize(parse("heads(::)")?), @"
6214 HeadsRange {
6215 roots: None,
6216 heads: VisibleHeadsOrReferenced,
6217 parents_range: 0..4294967295,
6218 filter: All,
6219 }
6220 ");
6221 insta::assert_debug_snapshot!(optimize(parse("heads(::foo)")?), @r#"
6222 HeadsRange {
6223 roots: None,
6224 heads: CommitRef(Symbol("foo")),
6225 parents_range: 0..4294967295,
6226 filter: All,
6227 }
6228 "#);
6229 insta::assert_debug_snapshot!(optimize(parse("heads(..)")?), @"
6232 HeadsRange {
6233 roots: None,
6234 heads: VisibleHeadsOrReferenced,
6235 parents_range: 0..4294967295,
6236 filter: NotIn(Root),
6237 }
6238 ");
6239 insta::assert_debug_snapshot!(optimize(parse("heads(foo..)")?), @r#"
6240 HeadsRange {
6241 roots: CommitRef(Symbol("foo")),
6242 heads: VisibleHeadsOrReferenced,
6243 parents_range: 0..4294967295,
6244 filter: All,
6245 }
6246 "#);
6247 insta::assert_debug_snapshot!(optimize(parse("heads(..bar)")?), @r#"
6248 HeadsRange {
6249 roots: Root,
6250 heads: CommitRef(Symbol("bar")),
6251 parents_range: 0..4294967295,
6252 filter: All,
6253 }
6254 "#);
6255 insta::assert_debug_snapshot!(optimize(parse("heads(foo..bar)")?), @r#"
6256 HeadsRange {
6257 roots: CommitRef(Symbol("foo")),
6258 heads: CommitRef(Symbol("bar")),
6259 parents_range: 0..4294967295,
6260 filter: All,
6261 }
6262 "#);
6263 insta::assert_debug_snapshot!(optimize(parse("heads(~::foo & ::bar)")?), @r#"
6264 HeadsRange {
6265 roots: CommitRef(Symbol("foo")),
6266 heads: CommitRef(Symbol("bar")),
6267 parents_range: 0..4294967295,
6268 filter: All,
6269 }
6270 "#);
6271 insta::assert_debug_snapshot!(optimize(parse("heads(~::foo)")?), @r#"
6272 HeadsRange {
6273 roots: CommitRef(Symbol("foo")),
6274 heads: VisibleHeadsOrReferenced,
6275 parents_range: 0..4294967295,
6276 filter: All,
6277 }
6278 "#);
6279 insta::assert_debug_snapshot!(optimize(parse("heads(a..b & c..d)")?), @r#"
6280 HeadsRange {
6281 roots: Union(
6282 CommitRef(Symbol("a")),
6283 CommitRef(Symbol("c")),
6284 ),
6285 heads: CommitRef(Symbol("b")),
6286 parents_range: 0..4294967295,
6287 filter: Ancestors {
6288 heads: CommitRef(Symbol("d")),
6289 generation: 0..18446744073709551615,
6290 parents_range: 0..4294967295,
6291 },
6292 }
6293 "#);
6294
6295 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(foo))")?), @r#"
6297 HeadsRange {
6298 roots: None,
6299 heads: CommitRef(Symbol("foo")),
6300 parents_range: 0..1,
6301 filter: All,
6302 }
6303 "#);
6304 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(foo) & bar..)")?), @r#"
6305 HeadsRange {
6306 roots: CommitRef(Symbol("bar")),
6307 heads: CommitRef(Symbol("foo")),
6308 parents_range: 0..1,
6309 filter: All,
6310 }
6311 "#);
6312 insta::assert_debug_snapshot!(optimize(parse("heads(foo.. & first_ancestors(bar) & ::baz)")?), @r#"
6313 HeadsRange {
6314 roots: CommitRef(Symbol("foo")),
6315 heads: CommitRef(Symbol("bar")),
6316 parents_range: 0..1,
6317 filter: Ancestors {
6318 heads: CommitRef(Symbol("baz")),
6319 generation: 0..18446744073709551615,
6320 parents_range: 0..4294967295,
6321 },
6322 }
6323 "#);
6324
6325 insta::assert_debug_snapshot!(optimize(parse("heads(ancestors(foo, 2))")?), @r#"
6327 Heads(
6328 Ancestors {
6329 heads: CommitRef(Symbol("foo")),
6330 generation: 0..2,
6331 parents_range: 0..4294967295,
6332 },
6333 )
6334 "#);
6335 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(foo, 2))")?), @r#"
6336 Heads(
6337 Ancestors {
6338 heads: CommitRef(Symbol("foo")),
6339 generation: 0..2,
6340 parents_range: 0..1,
6341 },
6342 )
6343 "#);
6344
6345 insta::assert_debug_snapshot!(optimize(parse("heads(ancestors(foo--))")?), @r#"
6347 HeadsRange {
6348 roots: None,
6349 heads: Ancestors {
6350 heads: CommitRef(Symbol("foo")),
6351 generation: 2..3,
6352 parents_range: 0..4294967295,
6353 },
6354 parents_range: 0..4294967295,
6355 filter: All,
6356 }
6357 "#);
6358 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(first_parent(foo, 2)))")?), @r#"
6359 HeadsRange {
6360 roots: None,
6361 heads: Ancestors {
6362 heads: CommitRef(Symbol("foo")),
6363 generation: 2..3,
6364 parents_range: 0..1,
6365 },
6366 parents_range: 0..1,
6367 filter: All,
6368 }
6369 "#);
6370
6371 insta::assert_debug_snapshot!(optimize(parse("heads(author_name(A) | author_name(B))")?), @r#"
6373 HeadsRange {
6374 roots: None,
6375 heads: VisibleHeadsOrReferenced,
6376 parents_range: 0..4294967295,
6377 filter: AsFilter(
6378 Union(
6379 Filter(AuthorName(Pattern(Exact("A")))),
6380 Filter(AuthorName(Pattern(Exact("B")))),
6381 ),
6382 ),
6383 }
6384 "#);
6385 insta::assert_debug_snapshot!(optimize(parse("heads(~author_name(A))")?), @r#"
6386 HeadsRange {
6387 roots: None,
6388 heads: VisibleHeadsOrReferenced,
6389 parents_range: 0..4294967295,
6390 filter: AsFilter(
6391 NotIn(
6392 Filter(AuthorName(Pattern(Exact("A")))),
6393 ),
6394 ),
6395 }
6396 "#);
6397 insta::assert_debug_snapshot!(optimize(parse("heads(~foo)")?), @r#"
6398 HeadsRange {
6399 roots: None,
6400 heads: VisibleHeadsOrReferenced,
6401 parents_range: 0..4294967295,
6402 filter: NotIn(CommitRef(Symbol("foo"))),
6403 }
6404 "#);
6405
6406 insta::assert_debug_snapshot!(optimize(parse("heads(author_name(A) & ::foo ~ author_name(B))")?), @r#"
6408 HeadsRange {
6409 roots: None,
6410 heads: CommitRef(Symbol("foo")),
6411 parents_range: 0..4294967295,
6412 filter: AsFilter(
6413 Difference(
6414 Filter(AuthorName(Pattern(Exact("A")))),
6415 Filter(AuthorName(Pattern(Exact("B")))),
6416 ),
6417 ),
6418 }
6419 "#);
6420
6421 insta::assert_debug_snapshot!(optimize(parse("heads(~foo & ~roots(bar) & ::baz)")?), @r#"
6423 HeadsRange {
6424 roots: None,
6425 heads: CommitRef(Symbol("baz")),
6426 parents_range: 0..4294967295,
6427 filter: Difference(
6428 NotIn(CommitRef(Symbol("foo"))),
6429 Roots(CommitRef(Symbol("bar"))),
6430 ),
6431 }
6432 "#);
6433 Ok(())
6434 }
6435
6436 #[test]
6437 fn test_optimize_ancestors_heads_range() -> TestResult {
6438 insta::assert_debug_snapshot!(optimize(parse("::description(bar)")?), @r#"
6440 Ancestors {
6441 heads: HeadsRange {
6442 roots: None,
6443 heads: VisibleHeadsOrReferenced,
6444 parents_range: 0..4294967295,
6445 filter: Filter(
6446 Description(
6447 Pattern(
6448 Exact(
6449 "bar",
6450 ),
6451 ),
6452 ),
6453 ),
6454 },
6455 generation: 0..18446744073709551615,
6456 parents_range: 0..4294967295,
6457 }
6458 "#);
6459 insta::assert_debug_snapshot!(optimize(parse("author_name(foo)..")?), @r#"
6460 Range {
6461 roots: HeadsRange {
6462 roots: None,
6463 heads: VisibleHeadsOrReferenced,
6464 parents_range: 0..4294967295,
6465 filter: Filter(
6466 AuthorName(
6467 Pattern(
6468 Exact(
6469 "foo",
6470 ),
6471 ),
6472 ),
6473 ),
6474 },
6475 heads: VisibleHeadsOrReferenced,
6476 generation: 0..18446744073709551615,
6477 parents_range: 0..4294967295,
6478 }
6479 "#);
6480
6481 insta::assert_debug_snapshot!(optimize(parse("::(foo..bar)")?), @r#"
6483 Ancestors {
6484 heads: HeadsRange {
6485 roots: CommitRef(
6486 Symbol(
6487 "foo",
6488 ),
6489 ),
6490 heads: CommitRef(
6491 Symbol(
6492 "bar",
6493 ),
6494 ),
6495 parents_range: 0..4294967295,
6496 filter: All,
6497 },
6498 generation: 0..18446744073709551615,
6499 parents_range: 0..4294967295,
6500 }
6501 "#);
6502
6503 insta::assert_debug_snapshot!(optimize(parse("ancestors(author_name(foo), 5)")?), @r#"
6505 Ancestors {
6506 heads: Filter(
6507 AuthorName(
6508 Pattern(
6509 Exact(
6510 "foo",
6511 ),
6512 ),
6513 ),
6514 ),
6515 generation: 0..5,
6516 parents_range: 0..4294967295,
6517 }
6518 "#);
6519 insta::assert_debug_snapshot!(optimize(parse("first_ancestors(author_name(foo))")?), @r#"
6520 Ancestors {
6521 heads: Filter(
6522 AuthorName(
6523 Pattern(
6524 Exact(
6525 "foo",
6526 ),
6527 ),
6528 ),
6529 ),
6530 generation: 0..18446744073709551615,
6531 parents_range: 0..1,
6532 }
6533 "#);
6534 Ok(())
6535 }
6536}