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