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