jj_lib/default_index/
revset_engine.rs

1// Copyright 2023 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![allow(missing_docs)]
16
17use std::cell::RefCell;
18use std::cmp::Ordering;
19use std::cmp::Reverse;
20use std::collections::BinaryHeap;
21use std::collections::HashSet;
22use std::convert::Infallible;
23use std::fmt;
24use std::iter;
25use std::ops::Range;
26use std::rc::Rc;
27use std::str;
28use std::sync::Arc;
29
30use bstr::BString;
31use futures::StreamExt as _;
32use itertools::Itertools as _;
33use pollster::FutureExt as _;
34
35use super::rev_walk::EagerRevWalk;
36use super::rev_walk::PeekableRevWalk;
37use super::rev_walk::RevWalk;
38use super::rev_walk::RevWalkBuilder;
39use super::revset_graph_iterator::RevsetGraphWalk;
40use crate::backend::BackendResult;
41use crate::backend::ChangeId;
42use crate::backend::CommitId;
43use crate::backend::MillisSinceEpoch;
44use crate::commit::Commit;
45use crate::conflicts::materialize_tree_value;
46use crate::conflicts::MaterializedTreeValue;
47use crate::default_index::AsCompositeIndex;
48use crate::default_index::CompositeIndex;
49use crate::default_index::IndexPosition;
50use crate::diff::Diff;
51use crate::diff::DiffHunkKind;
52use crate::files;
53use crate::graph::GraphNode;
54use crate::matchers::Matcher;
55use crate::matchers::Visit;
56use crate::merge::Merge;
57use crate::merged_tree::resolve_file_values;
58use crate::object_id::ObjectId as _;
59use crate::repo_path::RepoPath;
60use crate::revset::ResolvedExpression;
61use crate::revset::ResolvedPredicateExpression;
62use crate::revset::Revset;
63use crate::revset::RevsetContainingFn;
64use crate::revset::RevsetEvaluationError;
65use crate::revset::RevsetFilterPredicate;
66use crate::revset::GENERATION_RANGE_FULL;
67use crate::rewrite;
68use crate::store::Store;
69use crate::str_util::StringPattern;
70use crate::union_find;
71
72type BoxedPredicateFn<'a> =
73    Box<dyn FnMut(&CompositeIndex, IndexPosition) -> Result<bool, RevsetEvaluationError> + 'a>;
74pub(super) type BoxedRevWalk<'a> =
75    Box<dyn RevWalk<CompositeIndex, Item = Result<IndexPosition, RevsetEvaluationError>> + 'a>;
76
77trait ToPredicateFn: fmt::Debug {
78    /// Creates function that tests if the given entry is included in the set.
79    ///
80    /// The predicate function is evaluated in order of `RevsetIterator`.
81    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
82    where
83        Self: 'a;
84}
85
86impl<T: ToPredicateFn + ?Sized> ToPredicateFn for Box<T> {
87    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
88    where
89        Self: 'a,
90    {
91        <T as ToPredicateFn>::to_predicate_fn(self)
92    }
93}
94
95trait InternalRevset: fmt::Debug + ToPredicateFn {
96    // All revsets currently iterate in order of descending index position
97    fn positions<'a>(&self) -> BoxedRevWalk<'a>
98    where
99        Self: 'a;
100
101    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
102    where
103        Self: 'a;
104}
105
106impl<T: InternalRevset + ?Sized> InternalRevset for Box<T> {
107    fn positions<'a>(&self) -> BoxedRevWalk<'a>
108    where
109        Self: 'a,
110    {
111        <T as InternalRevset>::positions(self)
112    }
113
114    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
115    where
116        Self: 'a,
117    {
118        <T as InternalRevset>::into_predicate(*self)
119    }
120}
121
122pub struct RevsetImpl<I> {
123    inner: Box<dyn InternalRevset>,
124    index: I,
125}
126
127impl<I: AsCompositeIndex + Clone> RevsetImpl<I> {
128    fn new(inner: Box<dyn InternalRevset>, index: I) -> Self {
129        Self { inner, index }
130    }
131
132    fn positions(
133        &self,
134    ) -> impl Iterator<Item = Result<IndexPosition, RevsetEvaluationError>> + use<'_, I> {
135        self.inner.positions().attach(self.index.as_composite())
136    }
137
138    pub fn iter_graph_impl(
139        &self,
140        skip_transitive_edges: bool,
141    ) -> impl Iterator<Item = Result<GraphNode<CommitId>, RevsetEvaluationError>> + use<I> {
142        let index = self.index.clone();
143        let walk = self.inner.positions();
144        let mut graph_walk = RevsetGraphWalk::new(walk, skip_transitive_edges);
145        iter::from_fn(move || graph_walk.next(index.as_composite()))
146    }
147}
148
149impl<I> fmt::Debug for RevsetImpl<I> {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        f.debug_struct("RevsetImpl")
152            .field("inner", &self.inner)
153            .finish_non_exhaustive()
154    }
155}
156
157impl<I: AsCompositeIndex + Clone> Revset for RevsetImpl<I> {
158    fn iter<'a>(&self) -> Box<dyn Iterator<Item = Result<CommitId, RevsetEvaluationError>> + 'a>
159    where
160        Self: 'a,
161    {
162        let index = self.index.clone();
163        let mut walk = self
164            .inner
165            .positions()
166            .map(|index, pos| Ok(index.entry_by_pos(pos?).commit_id()));
167        Box::new(iter::from_fn(move || walk.next(index.as_composite())))
168    }
169
170    fn commit_change_ids<'a>(
171        &self,
172    ) -> Box<dyn Iterator<Item = Result<(CommitId, ChangeId), RevsetEvaluationError>> + 'a>
173    where
174        Self: 'a,
175    {
176        let index = self.index.clone();
177        let mut walk = self.inner.positions().map(|index, pos| {
178            let entry = index.entry_by_pos(pos?);
179            Ok((entry.commit_id(), entry.change_id()))
180        });
181        Box::new(iter::from_fn(move || walk.next(index.as_composite())))
182    }
183
184    fn iter_graph<'a>(
185        &self,
186    ) -> Box<dyn Iterator<Item = Result<GraphNode<CommitId>, RevsetEvaluationError>> + 'a>
187    where
188        Self: 'a,
189    {
190        let skip_transitive_edges = true;
191        Box::new(self.iter_graph_impl(skip_transitive_edges))
192    }
193
194    fn is_empty(&self) -> bool {
195        self.positions().next().is_none()
196    }
197
198    fn count_estimate(&self) -> Result<(usize, Option<usize>), RevsetEvaluationError> {
199        if cfg!(feature = "testing") {
200            // Exercise the estimation feature in tests. (If we ever have a Revset
201            // implementation in production code that returns estimates, we can probably
202            // remove this and rewrite the associated tests.)
203            let count = self
204                .positions()
205                .take(10)
206                .process_results(|iter| iter.count())?;
207            if count < 10 {
208                Ok((count, Some(count)))
209            } else {
210                Ok((10, None))
211            }
212        } else {
213            let count = self.positions().process_results(|iter| iter.count())?;
214            Ok((count, Some(count)))
215        }
216    }
217
218    fn containing_fn<'a>(&self) -> Box<RevsetContainingFn<'a>>
219    where
220        Self: 'a,
221    {
222        let positions = PositionsAccumulator::new(self.index.clone(), self.inner.positions());
223        Box::new(move |commit_id| positions.contains(commit_id))
224    }
225}
226
227/// Incrementally consumes `RevWalk` of the revset collecting positions.
228struct PositionsAccumulator<'a, I> {
229    index: I,
230    inner: RefCell<PositionsAccumulatorInner<'a>>,
231}
232
233impl<'a, I: AsCompositeIndex> PositionsAccumulator<'a, I> {
234    fn new(index: I, walk: BoxedRevWalk<'a>) -> Self {
235        let inner = RefCell::new(PositionsAccumulatorInner {
236            walk,
237            consumed_positions: Vec::new(),
238        });
239        PositionsAccumulator { index, inner }
240    }
241
242    /// Checks whether the commit is in the revset.
243    fn contains(&self, commit_id: &CommitId) -> Result<bool, RevsetEvaluationError> {
244        let index = self.index.as_composite();
245        let Some(position) = index.commit_id_to_pos(commit_id) else {
246            return Ok(false);
247        };
248
249        let mut inner = self.inner.borrow_mut();
250        inner.consume_to(index, position)?;
251        let found = inner
252            .consumed_positions
253            .binary_search_by(|p| p.cmp(&position).reverse())
254            .is_ok();
255        Ok(found)
256    }
257
258    #[cfg(test)]
259    fn consumed_len(&self) -> usize {
260        self.inner.borrow().consumed_positions.len()
261    }
262}
263
264/// Helper struct for [`PositionsAccumulator`] to simplify interior mutability.
265struct PositionsAccumulatorInner<'a> {
266    walk: BoxedRevWalk<'a>,
267    consumed_positions: Vec<IndexPosition>,
268}
269
270impl PositionsAccumulatorInner<'_> {
271    /// Consumes `RevWalk` to a desired position but not deeper.
272    fn consume_to(
273        &mut self,
274        index: &CompositeIndex,
275        desired_position: IndexPosition,
276    ) -> Result<(), RevsetEvaluationError> {
277        let last_position = self.consumed_positions.last();
278        if last_position.is_some_and(|&pos| pos <= desired_position) {
279            return Ok(());
280        }
281        while let Some(position) = self.walk.next(index).transpose()? {
282            self.consumed_positions.push(position);
283            if position <= desired_position {
284                return Ok(());
285            }
286        }
287        Ok(())
288    }
289}
290
291/// Adapter for precomputed `IndexPosition`s.
292#[derive(Debug)]
293struct EagerRevset {
294    positions: Vec<IndexPosition>,
295}
296
297impl EagerRevset {
298    pub const fn empty() -> Self {
299        EagerRevset {
300            positions: Vec::new(),
301        }
302    }
303}
304
305impl InternalRevset for EagerRevset {
306    fn positions<'a>(&self) -> BoxedRevWalk<'a>
307    where
308        Self: 'a,
309    {
310        let walk = EagerRevWalk::new(self.positions.clone().into_iter());
311        Box::new(walk.map(|_index, pos| Ok(pos)))
312    }
313
314    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
315    where
316        Self: 'a,
317    {
318        self
319    }
320}
321
322impl ToPredicateFn for EagerRevset {
323    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
324    where
325        Self: 'a,
326    {
327        let walk = EagerRevWalk::new(self.positions.clone().into_iter());
328        predicate_fn_from_rev_walk(walk)
329    }
330}
331
332/// Adapter for infallible `RevWalk` of `IndexPosition`s.
333struct RevWalkRevset<W> {
334    walk: W,
335}
336
337impl<W> fmt::Debug for RevWalkRevset<W> {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        f.debug_struct("RevWalkRevset").finish_non_exhaustive()
340    }
341}
342
343impl<W> InternalRevset for RevWalkRevset<W>
344where
345    W: RevWalk<CompositeIndex, Item = IndexPosition> + Clone,
346{
347    fn positions<'a>(&self) -> BoxedRevWalk<'a>
348    where
349        Self: 'a,
350    {
351        Box::new(self.walk.clone().map(|_index, pos| Ok(pos)))
352    }
353
354    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
355    where
356        Self: 'a,
357    {
358        self
359    }
360}
361
362impl<W> ToPredicateFn for RevWalkRevset<W>
363where
364    W: RevWalk<CompositeIndex, Item = IndexPosition> + Clone,
365{
366    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
367    where
368        Self: 'a,
369    {
370        predicate_fn_from_rev_walk(self.walk.clone())
371    }
372}
373
374fn predicate_fn_from_rev_walk<'a, W>(walk: W) -> BoxedPredicateFn<'a>
375where
376    W: RevWalk<CompositeIndex, Item = IndexPosition> + 'a,
377{
378    let mut walk = walk.peekable();
379    Box::new(move |index, entry_pos| {
380        while walk.next_if(index, |&pos| pos > entry_pos).is_some() {
381            continue;
382        }
383        Ok(walk.next_if(index, |&pos| pos == entry_pos).is_some())
384    })
385}
386
387#[derive(Debug)]
388struct FilterRevset<S, P> {
389    candidates: S,
390    predicate: P,
391}
392
393impl<S, P> InternalRevset for FilterRevset<S, P>
394where
395    S: InternalRevset,
396    P: ToPredicateFn,
397{
398    fn positions<'a>(&self) -> BoxedRevWalk<'a>
399    where
400        Self: 'a,
401    {
402        let mut p = self.predicate.to_predicate_fn();
403        Box::new(self.candidates.positions().filter_map(move |index, pos| {
404            pos.and_then(|pos| Ok(p(index, pos)?.then_some(pos)))
405                .transpose()
406        }))
407    }
408
409    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
410    where
411        Self: 'a,
412    {
413        self
414    }
415}
416
417impl<S, P> ToPredicateFn for FilterRevset<S, P>
418where
419    S: ToPredicateFn,
420    P: ToPredicateFn,
421{
422    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
423    where
424        Self: 'a,
425    {
426        let mut p1 = self.candidates.to_predicate_fn();
427        let mut p2 = self.predicate.to_predicate_fn();
428        Box::new(move |index, pos| Ok(p1(index, pos)? && p2(index, pos)?))
429    }
430}
431
432#[derive(Debug)]
433struct NotInPredicate<S>(S);
434
435impl<S: ToPredicateFn> ToPredicateFn for NotInPredicate<S> {
436    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
437    where
438        Self: 'a,
439    {
440        let mut p = self.0.to_predicate_fn();
441        Box::new(move |index, pos| Ok(!p(index, pos)?))
442    }
443}
444
445#[derive(Debug)]
446struct UnionRevset<S1, S2> {
447    set1: S1,
448    set2: S2,
449}
450
451impl<S1, S2> InternalRevset for UnionRevset<S1, S2>
452where
453    S1: InternalRevset,
454    S2: InternalRevset,
455{
456    fn positions<'a>(&self) -> BoxedRevWalk<'a>
457    where
458        Self: 'a,
459    {
460        Box::new(union_by(
461            self.set1.positions(),
462            self.set2.positions(),
463            |pos1, pos2| pos1.cmp(pos2).reverse(),
464        ))
465    }
466
467    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
468    where
469        Self: 'a,
470    {
471        self
472    }
473}
474
475impl<S1, S2> ToPredicateFn for UnionRevset<S1, S2>
476where
477    S1: ToPredicateFn,
478    S2: ToPredicateFn,
479{
480    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
481    where
482        Self: 'a,
483    {
484        let mut p1 = self.set1.to_predicate_fn();
485        let mut p2 = self.set2.to_predicate_fn();
486        Box::new(move |index, pos| Ok(p1(index, pos)? || p2(index, pos)?))
487    }
488}
489
490/// `RevWalk` node that merges two sorted walk nodes.
491///
492/// The input items should be sorted in ascending order by the `cmp` function.
493struct UnionRevWalk<I: ?Sized, W1: RevWalk<I>, W2: RevWalk<I>, C> {
494    walk1: PeekableRevWalk<I, W1>,
495    walk2: PeekableRevWalk<I, W2>,
496    cmp: C,
497}
498
499impl<I, T, E, W1, W2, C> RevWalk<I> for UnionRevWalk<I, W1, W2, C>
500where
501    I: ?Sized,
502    W1: RevWalk<I, Item = Result<T, E>>,
503    W2: RevWalk<I, Item = Result<T, E>>,
504    C: FnMut(&T, &T) -> Ordering,
505{
506    type Item = W1::Item;
507
508    fn next(&mut self, index: &I) -> Option<Self::Item> {
509        match (self.walk1.peek(index), self.walk2.peek(index)) {
510            (None, _) => self.walk2.next(index),
511            (_, None) => self.walk1.next(index),
512            (Some(Ok(item1)), Some(Ok(item2))) => match (self.cmp)(item1, item2) {
513                Ordering::Less => self.walk1.next(index),
514                Ordering::Equal => {
515                    self.walk2.next(index);
516                    self.walk1.next(index)
517                }
518                Ordering::Greater => self.walk2.next(index),
519            },
520            (Some(Err(_)), _) => self.walk1.next(index),
521            (_, Some(Err(_))) => self.walk2.next(index),
522        }
523    }
524}
525
526fn union_by<I, T, E, W1, W2, C>(walk1: W1, walk2: W2, cmp: C) -> UnionRevWalk<I, W1, W2, C>
527where
528    I: ?Sized,
529    W1: RevWalk<I, Item = Result<T, E>>,
530    W2: RevWalk<I, Item = Result<T, E>>,
531    C: FnMut(&T, &T) -> Ordering,
532{
533    UnionRevWalk {
534        walk1: walk1.peekable(),
535        walk2: walk2.peekable(),
536        cmp,
537    }
538}
539
540#[derive(Debug)]
541struct IntersectionRevset<S1, S2> {
542    set1: S1,
543    set2: S2,
544}
545
546impl<S1, S2> InternalRevset for IntersectionRevset<S1, S2>
547where
548    S1: InternalRevset,
549    S2: InternalRevset,
550{
551    fn positions<'a>(&self) -> BoxedRevWalk<'a>
552    where
553        Self: 'a,
554    {
555        Box::new(intersection_by(
556            self.set1.positions(),
557            self.set2.positions(),
558            |pos1, pos2| pos1.cmp(pos2).reverse(),
559        ))
560    }
561
562    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
563    where
564        Self: 'a,
565    {
566        self
567    }
568}
569
570impl<S1, S2> ToPredicateFn for IntersectionRevset<S1, S2>
571where
572    S1: ToPredicateFn,
573    S2: ToPredicateFn,
574{
575    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
576    where
577        Self: 'a,
578    {
579        let mut p1 = self.set1.to_predicate_fn();
580        let mut p2 = self.set2.to_predicate_fn();
581        Box::new(move |index, pos| Ok(p1(index, pos)? && p2(index, pos)?))
582    }
583}
584
585/// `RevWalk` node that intersects two sorted walk nodes.
586///
587/// The input items should be sorted in ascending order by the `cmp` function.
588struct IntersectionRevWalk<I: ?Sized, W1: RevWalk<I>, W2: RevWalk<I>, C> {
589    walk1: PeekableRevWalk<I, W1>,
590    walk2: PeekableRevWalk<I, W2>,
591    cmp: C,
592}
593
594impl<I, T, E, W1, W2, C> RevWalk<I> for IntersectionRevWalk<I, W1, W2, C>
595where
596    I: ?Sized,
597    W1: RevWalk<I, Item = Result<T, E>>,
598    W2: RevWalk<I, Item = Result<T, E>>,
599    C: FnMut(&T, &T) -> Ordering,
600{
601    type Item = W1::Item;
602
603    fn next(&mut self, index: &I) -> Option<Self::Item> {
604        loop {
605            match (self.walk1.peek(index), self.walk2.peek(index)) {
606                (None, _) => {
607                    return None;
608                }
609                (_, None) => {
610                    return None;
611                }
612                (Some(Ok(item1)), Some(Ok(item2))) => match (self.cmp)(item1, item2) {
613                    Ordering::Less => {
614                        self.walk1.next(index);
615                    }
616                    Ordering::Equal => {
617                        self.walk2.next(index);
618                        return self.walk1.next(index);
619                    }
620                    Ordering::Greater => {
621                        self.walk2.next(index);
622                    }
623                },
624                (Some(Err(_)), _) => {
625                    return self.walk1.next(index);
626                }
627                (_, Some(Err(_))) => {
628                    return self.walk2.next(index);
629                }
630            }
631        }
632    }
633}
634
635fn intersection_by<I, T, E, W1, W2, C>(
636    walk1: W1,
637    walk2: W2,
638    cmp: C,
639) -> IntersectionRevWalk<I, W1, W2, C>
640where
641    I: ?Sized,
642    W1: RevWalk<I, Item = Result<T, E>>,
643    W2: RevWalk<I, Item = Result<T, E>>,
644    C: FnMut(&T, &T) -> Ordering,
645{
646    IntersectionRevWalk {
647        walk1: walk1.peekable(),
648        walk2: walk2.peekable(),
649        cmp,
650    }
651}
652
653#[derive(Debug)]
654struct DifferenceRevset<S1, S2> {
655    // The minuend (what to subtract from)
656    set1: S1,
657    // The subtrahend (what to subtract)
658    set2: S2,
659}
660
661impl<S1, S2> InternalRevset for DifferenceRevset<S1, S2>
662where
663    S1: InternalRevset,
664    S2: InternalRevset,
665{
666    fn positions<'a>(&self) -> BoxedRevWalk<'a>
667    where
668        Self: 'a,
669    {
670        Box::new(difference_by(
671            self.set1.positions(),
672            self.set2.positions(),
673            |pos1, pos2| pos1.cmp(pos2).reverse(),
674        ))
675    }
676
677    fn into_predicate<'a>(self: Box<Self>) -> Box<dyn ToPredicateFn + 'a>
678    where
679        Self: 'a,
680    {
681        self
682    }
683}
684
685impl<S1, S2> ToPredicateFn for DifferenceRevset<S1, S2>
686where
687    S1: ToPredicateFn,
688    S2: ToPredicateFn,
689{
690    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
691    where
692        Self: 'a,
693    {
694        let mut p1 = self.set1.to_predicate_fn();
695        let mut p2 = self.set2.to_predicate_fn();
696        Box::new(move |index, pos| Ok(p1(index, pos)? && !p2(index, pos)?))
697    }
698}
699
700/// `RevWalk` node that subtracts `walk2` items from `walk1`.
701///
702/// The input items should be sorted in ascending order by the `cmp` function.
703struct DifferenceRevWalk<I: ?Sized, W1: RevWalk<I>, W2: RevWalk<I>, C> {
704    walk1: PeekableRevWalk<I, W1>,
705    walk2: PeekableRevWalk<I, W2>,
706    cmp: C,
707}
708
709impl<I, T, E, W1, W2, C> RevWalk<I> for DifferenceRevWalk<I, W1, W2, C>
710where
711    I: ?Sized,
712    W1: RevWalk<I, Item = Result<T, E>>,
713    W2: RevWalk<I, Item = Result<T, E>>,
714    C: FnMut(&T, &T) -> Ordering,
715{
716    type Item = W1::Item;
717
718    fn next(&mut self, index: &I) -> Option<Self::Item> {
719        loop {
720            match (self.walk1.peek(index), self.walk2.peek(index)) {
721                (None, _) => {
722                    return None;
723                }
724                (_, None) => {
725                    return self.walk1.next(index);
726                }
727                (Some(Ok(item1)), Some(Ok(item2))) => match (self.cmp)(item1, item2) {
728                    Ordering::Less => {
729                        return self.walk1.next(index);
730                    }
731                    Ordering::Equal => {
732                        self.walk2.next(index);
733                        self.walk1.next(index);
734                    }
735                    Ordering::Greater => {
736                        self.walk2.next(index);
737                    }
738                },
739                (Some(Err(_)), _) => {
740                    return self.walk1.next(index);
741                }
742                (_, Some(Err(_))) => {
743                    return self.walk2.next(index);
744                }
745            }
746        }
747    }
748}
749
750fn difference_by<I, T, E, W1, W2, C>(
751    walk1: W1,
752    walk2: W2,
753    cmp: C,
754) -> DifferenceRevWalk<I, W1, W2, C>
755where
756    I: ?Sized,
757    W1: RevWalk<I, Item = Result<T, E>>,
758    W2: RevWalk<I, Item = Result<T, E>>,
759    C: FnMut(&T, &T) -> Ordering,
760{
761    DifferenceRevWalk {
762        walk1: walk1.peekable(),
763        walk2: walk2.peekable(),
764        cmp,
765    }
766}
767
768pub fn evaluate<I: AsCompositeIndex + Clone>(
769    expression: &ResolvedExpression,
770    store: &Arc<Store>,
771    index: I,
772) -> Result<RevsetImpl<I>, RevsetEvaluationError> {
773    let context = EvaluationContext {
774        store: store.clone(),
775        index: index.as_composite(),
776    };
777    let internal_revset = context.evaluate(expression)?;
778    Ok(RevsetImpl::new(internal_revset, index))
779}
780
781struct EvaluationContext<'index> {
782    store: Arc<Store>,
783    index: &'index CompositeIndex,
784}
785
786fn to_u32_generation_range(range: &Range<u64>) -> Result<Range<u32>, RevsetEvaluationError> {
787    let start = range.start.try_into().map_err(|_| {
788        RevsetEvaluationError::Other(
789            format!("Lower bound of generation ({}) is too large", range.start).into(),
790        )
791    })?;
792    let end = range.end.try_into().unwrap_or(u32::MAX);
793    Ok(start..end)
794}
795
796impl EvaluationContext<'_> {
797    fn evaluate(
798        &self,
799        expression: &ResolvedExpression,
800    ) -> Result<Box<dyn InternalRevset>, RevsetEvaluationError> {
801        let index = self.index;
802        match expression {
803            ResolvedExpression::Commits(commit_ids) => {
804                Ok(Box::new(self.revset_for_commit_ids(commit_ids)?))
805            }
806            ResolvedExpression::Ancestors { heads, generation } => {
807                let head_set = self.evaluate(heads)?;
808                let head_positions = head_set.positions().attach(index);
809                let builder =
810                    RevWalkBuilder::new(index).wanted_heads(head_positions.try_collect()?);
811                if generation == &GENERATION_RANGE_FULL {
812                    let walk = builder.ancestors().detach();
813                    Ok(Box::new(RevWalkRevset { walk }))
814                } else {
815                    let generation = to_u32_generation_range(generation)?;
816                    let walk = builder
817                        .ancestors_filtered_by_generation(generation)
818                        .detach();
819                    Ok(Box::new(RevWalkRevset { walk }))
820                }
821            }
822            ResolvedExpression::Range {
823                roots,
824                heads,
825                generation,
826            } => {
827                let root_set = self.evaluate(roots)?;
828                let root_positions: Vec<_> = root_set.positions().attach(index).try_collect()?;
829                // Pre-filter heads so queries like 'immutable_heads()..' can
830                // terminate early. immutable_heads() usually includes some
831                // visible heads, which can be trivially rejected.
832                let head_set = self.evaluate(heads)?;
833                let head_positions = difference_by(
834                    head_set.positions(),
835                    EagerRevWalk::new(root_positions.iter().copied().map(Ok)),
836                    |pos1, pos2| pos1.cmp(pos2).reverse(),
837                )
838                .attach(index);
839                let builder = RevWalkBuilder::new(index)
840                    .wanted_heads(head_positions.try_collect()?)
841                    .unwanted_roots(root_positions);
842                if generation == &GENERATION_RANGE_FULL {
843                    let walk = builder.ancestors().detach();
844                    Ok(Box::new(RevWalkRevset { walk }))
845                } else {
846                    let generation = to_u32_generation_range(generation)?;
847                    let walk = builder
848                        .ancestors_filtered_by_generation(generation)
849                        .detach();
850                    Ok(Box::new(RevWalkRevset { walk }))
851                }
852            }
853            ResolvedExpression::DagRange {
854                roots,
855                heads,
856                generation_from_roots,
857            } => {
858                let root_set = self.evaluate(roots)?;
859                let root_positions = root_set.positions().attach(index);
860                let head_set = self.evaluate(heads)?;
861                let head_positions = head_set.positions().attach(index);
862                let builder =
863                    RevWalkBuilder::new(index).wanted_heads(head_positions.try_collect()?);
864                if generation_from_roots == &(1..2) {
865                    let root_positions: HashSet<_> = root_positions.try_collect()?;
866                    let walk = builder
867                        .ancestors_until_roots(root_positions.iter().copied())
868                        .detach();
869                    let candidates = RevWalkRevset { walk };
870                    let predicate = as_pure_predicate_fn(move |index, pos| {
871                        Ok(index
872                            .entry_by_pos(pos)
873                            .parent_positions()
874                            .iter()
875                            .any(|parent_pos| root_positions.contains(parent_pos)))
876                    });
877                    // TODO: Suppose heads include all visible heads, ToPredicateFn version can be
878                    // optimized to only test the predicate()
879                    Ok(Box::new(FilterRevset {
880                        candidates,
881                        predicate,
882                    }))
883                } else if generation_from_roots == &GENERATION_RANGE_FULL {
884                    let mut positions = builder
885                        .descendants(root_positions.try_collect()?)
886                        .collect_vec();
887                    positions.reverse();
888                    Ok(Box::new(EagerRevset { positions }))
889                } else {
890                    // For small generation range, it might be better to build a reachable map
891                    // with generation bit set, which can be calculated incrementally from roots:
892                    //   reachable[pos] = (reachable[parent_pos] | ...) << 1
893                    let mut positions = builder
894                        .descendants_filtered_by_generation(
895                            root_positions.try_collect()?,
896                            to_u32_generation_range(generation_from_roots)?,
897                        )
898                        .map(|Reverse(pos)| pos)
899                        .collect_vec();
900                    positions.reverse();
901                    Ok(Box::new(EagerRevset { positions }))
902                }
903            }
904            ResolvedExpression::Reachable { sources, domain } => {
905                let mut sets = union_find::UnionFind::<IndexPosition>::new();
906
907                // Compute all reachable subgraphs.
908                let domain_revset = self.evaluate(domain)?;
909                let domain_vec: Vec<_> = domain_revset.positions().attach(index).try_collect()?;
910                let domain_set: HashSet<_> = domain_vec.iter().copied().collect();
911                for pos in &domain_set {
912                    for parent_pos in index.entry_by_pos(*pos).parent_positions() {
913                        if domain_set.contains(&parent_pos) {
914                            sets.union(*pos, parent_pos);
915                        }
916                    }
917                }
918
919                // Identify disjoint sets reachable from sources.
920                let set_reps: HashSet<_> = intersection_by(
921                    self.evaluate(sources)?.positions(),
922                    EagerRevWalk::new(domain_vec.iter().copied().map(Ok)),
923                    |pos1, pos2| pos1.cmp(pos2).reverse(),
924                )
925                .attach(index)
926                .map_ok(|pos| sets.find(pos))
927                .try_collect()?;
928
929                let positions = domain_vec
930                    .into_iter()
931                    .filter(|pos| set_reps.contains(&sets.find(*pos)))
932                    .collect_vec();
933                Ok(Box::new(EagerRevset { positions }))
934            }
935            ResolvedExpression::Heads(candidates) => {
936                let candidate_set = self.evaluate(candidates)?;
937                let positions =
938                    index.heads_pos(candidate_set.positions().attach(index).try_collect()?);
939                Ok(Box::new(EagerRevset { positions }))
940            }
941            ResolvedExpression::HeadsRange {
942                roots,
943                heads,
944                filter,
945            } => {
946                let root_set = self.evaluate(roots)?;
947                let root_positions: Vec<_> = root_set.positions().attach(index).try_collect()?;
948                // Pre-filter heads so queries like 'immutable_heads()..' can
949                // terminate early. immutable_heads() usually includes some
950                // visible heads, which can be trivially rejected.
951                let head_set = self.evaluate(heads)?;
952                let head_positions = difference_by(
953                    head_set.positions(),
954                    EagerRevWalk::new(root_positions.iter().copied().map(Ok)),
955                    |pos1, pos2| pos1.cmp(pos2).reverse(),
956                )
957                .attach(index)
958                .try_collect()?;
959                let positions = if let Some(filter) = filter {
960                    let mut filter = self.evaluate_predicate(filter)?.to_predicate_fn();
961                    index.heads_from_range_and_filter(root_positions, head_positions, |pos| {
962                        filter(index, pos)
963                    })?
964                } else {
965                    let Ok(positions) = index.heads_from_range_and_filter::<Infallible>(
966                        root_positions,
967                        head_positions,
968                        |_| Ok(true),
969                    );
970                    positions
971                };
972                Ok(Box::new(EagerRevset { positions }))
973            }
974            ResolvedExpression::Roots(candidates) => {
975                let mut positions: Vec<_> = self
976                    .evaluate(candidates)?
977                    .positions()
978                    .attach(index)
979                    .try_collect()?;
980                let filled = RevWalkBuilder::new(index)
981                    .wanted_heads(positions.clone())
982                    .descendants(positions.iter().copied().collect())
983                    .collect_positions_set();
984                positions.retain(|&pos| {
985                    !index
986                        .entry_by_pos(pos)
987                        .parent_positions()
988                        .iter()
989                        .any(|parent| filled.contains(parent))
990                });
991                Ok(Box::new(EagerRevset { positions }))
992            }
993            ResolvedExpression::ForkPoint(expression) => {
994                let expression_set = self.evaluate(expression)?;
995                let mut expression_positions_iter = expression_set.positions().attach(index);
996                let Some(position) = expression_positions_iter.next() else {
997                    return Ok(Box::new(EagerRevset::empty()));
998                };
999                let mut positions = vec![position?];
1000                for position in expression_positions_iter {
1001                    positions = index.common_ancestors_pos(positions, vec![position?]);
1002                }
1003                Ok(Box::new(EagerRevset { positions }))
1004            }
1005            ResolvedExpression::Latest { candidates, count } => {
1006                let candidate_set = self.evaluate(candidates)?;
1007                Ok(Box::new(self.take_latest_revset(&*candidate_set, *count)?))
1008            }
1009            ResolvedExpression::Coalesce(expression1, expression2) => {
1010                let set1 = self.evaluate(expression1)?;
1011                if set1.positions().attach(index).next().is_some() {
1012                    Ok(set1)
1013                } else {
1014                    self.evaluate(expression2)
1015                }
1016            }
1017            ResolvedExpression::Union(expression1, expression2) => {
1018                let set1 = self.evaluate(expression1)?;
1019                let set2 = self.evaluate(expression2)?;
1020                Ok(Box::new(UnionRevset { set1, set2 }))
1021            }
1022            ResolvedExpression::FilterWithin {
1023                candidates,
1024                predicate,
1025            } => Ok(Box::new(FilterRevset {
1026                candidates: self.evaluate(candidates)?,
1027                predicate: self.evaluate_predicate(predicate)?,
1028            })),
1029            ResolvedExpression::Intersection(expression1, expression2) => {
1030                let set1 = self.evaluate(expression1)?;
1031                let set2 = self.evaluate(expression2)?;
1032                Ok(Box::new(IntersectionRevset { set1, set2 }))
1033            }
1034            ResolvedExpression::Difference(expression1, expression2) => {
1035                let set1 = self.evaluate(expression1)?;
1036                let set2 = self.evaluate(expression2)?;
1037                Ok(Box::new(DifferenceRevset { set1, set2 }))
1038            }
1039        }
1040    }
1041
1042    fn evaluate_predicate(
1043        &self,
1044        expression: &ResolvedPredicateExpression,
1045    ) -> Result<Box<dyn ToPredicateFn>, RevsetEvaluationError> {
1046        match expression {
1047            ResolvedPredicateExpression::Filter(predicate) => {
1048                Ok(build_predicate_fn(self.store.clone(), predicate))
1049            }
1050            ResolvedPredicateExpression::Set(expression) => {
1051                Ok(self.evaluate(expression)?.into_predicate())
1052            }
1053            ResolvedPredicateExpression::NotIn(complement) => {
1054                let set = self.evaluate_predicate(complement)?;
1055                Ok(Box::new(NotInPredicate(set)))
1056            }
1057            ResolvedPredicateExpression::Union(expression1, expression2) => {
1058                let set1 = self.evaluate_predicate(expression1)?;
1059                let set2 = self.evaluate_predicate(expression2)?;
1060                Ok(Box::new(UnionRevset { set1, set2 }))
1061            }
1062            ResolvedPredicateExpression::Intersection(expression1, expression2) => {
1063                let set1 = self.evaluate_predicate(expression1)?;
1064                let set2 = self.evaluate_predicate(expression2)?;
1065                Ok(Box::new(IntersectionRevset { set1, set2 }))
1066            }
1067        }
1068    }
1069
1070    fn revset_for_commit_ids(
1071        &self,
1072        commit_ids: &[CommitId],
1073    ) -> Result<EagerRevset, RevsetEvaluationError> {
1074        let mut positions: Vec<_> = commit_ids
1075            .iter()
1076            .map(|id| {
1077                // Invalid commit IDs should be rejected by the revset frontend,
1078                // but there are a few edge cases that break the precondition.
1079                // For example, in jj <= 0.22, the root commit doesn't exist in
1080                // the root operation.
1081                self.index.commit_id_to_pos(id).ok_or_else(|| {
1082                    RevsetEvaluationError::Other(
1083                        format!(
1084                            "Commit ID {} not found in index (index or view might be corrupted)",
1085                            id.hex()
1086                        )
1087                        .into(),
1088                    )
1089                })
1090            })
1091            .try_collect()?;
1092        positions.sort_unstable_by_key(|&pos| Reverse(pos));
1093        positions.dedup();
1094        Ok(EagerRevset { positions })
1095    }
1096
1097    fn take_latest_revset(
1098        &self,
1099        candidate_set: &dyn InternalRevset,
1100        count: usize,
1101    ) -> Result<EagerRevset, RevsetEvaluationError> {
1102        if count == 0 {
1103            return Ok(EagerRevset::empty());
1104        }
1105
1106        #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
1107        struct Item {
1108            timestamp: MillisSinceEpoch,
1109            pos: IndexPosition, // tie-breaker
1110        }
1111
1112        let make_rev_item = |pos| -> Result<_, RevsetEvaluationError> {
1113            let entry = self.index.entry_by_pos(pos?);
1114            let commit = self.store.get_commit(&entry.commit_id())?;
1115            Ok(Reverse(Item {
1116                timestamp: commit.committer().timestamp.timestamp,
1117                pos: entry.position(),
1118            }))
1119        };
1120
1121        // Maintain min-heap containing the latest (greatest) count items. For small
1122        // count and large candidate set, this is probably cheaper than building vec
1123        // and applying selection algorithm.
1124        let mut candidate_iter = candidate_set
1125            .positions()
1126            .attach(self.index)
1127            .map(make_rev_item)
1128            .fuse();
1129        let mut latest_items: BinaryHeap<_> = candidate_iter.by_ref().take(count).try_collect()?;
1130        for item in candidate_iter {
1131            let item = item?;
1132            let mut earliest = latest_items.peek_mut().unwrap();
1133            if earliest.0 < item.0 {
1134                *earliest = item;
1135            }
1136        }
1137
1138        assert!(latest_items.len() <= count);
1139        let mut positions = latest_items
1140            .into_iter()
1141            .map(|item| item.0.pos)
1142            .collect_vec();
1143        positions.sort_unstable_by_key(|&pos| Reverse(pos));
1144        Ok(EagerRevset { positions })
1145    }
1146}
1147
1148struct PurePredicateFn<F>(F);
1149
1150impl<F> fmt::Debug for PurePredicateFn<F> {
1151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152        f.debug_struct("PurePredicateFn").finish_non_exhaustive()
1153    }
1154}
1155
1156impl<F> ToPredicateFn for PurePredicateFn<F>
1157where
1158    F: Fn(&CompositeIndex, IndexPosition) -> Result<bool, RevsetEvaluationError> + Clone,
1159{
1160    fn to_predicate_fn<'a>(&self) -> BoxedPredicateFn<'a>
1161    where
1162        Self: 'a,
1163    {
1164        Box::new(self.0.clone())
1165    }
1166}
1167
1168fn as_pure_predicate_fn<F>(f: F) -> PurePredicateFn<F>
1169where
1170    F: Fn(&CompositeIndex, IndexPosition) -> Result<bool, RevsetEvaluationError> + Clone,
1171{
1172    PurePredicateFn(f)
1173}
1174
1175fn box_pure_predicate_fn<'a>(
1176    f: impl Fn(&CompositeIndex, IndexPosition) -> Result<bool, RevsetEvaluationError> + Clone + 'a,
1177) -> Box<dyn ToPredicateFn + 'a> {
1178    Box::new(PurePredicateFn(f))
1179}
1180
1181fn build_predicate_fn(
1182    store: Arc<Store>,
1183    predicate: &RevsetFilterPredicate,
1184) -> Box<dyn ToPredicateFn> {
1185    match predicate {
1186        RevsetFilterPredicate::ParentCount(parent_count_range) => {
1187            let parent_count_range = parent_count_range.clone();
1188            box_pure_predicate_fn(move |index, pos| {
1189                let entry = index.entry_by_pos(pos);
1190                Ok(parent_count_range.contains(&entry.num_parents()))
1191            })
1192        }
1193        RevsetFilterPredicate::Description(pattern) => {
1194            let pattern = pattern.clone();
1195            box_pure_predicate_fn(move |index, pos| {
1196                let entry = index.entry_by_pos(pos);
1197                let commit = store.get_commit(&entry.commit_id())?;
1198                Ok(pattern.matches(commit.description()))
1199            })
1200        }
1201        RevsetFilterPredicate::Subject(pattern) => {
1202            let pattern = pattern.clone();
1203            box_pure_predicate_fn(move |index, pos| {
1204                let entry = index.entry_by_pos(pos);
1205                let commit = store.get_commit(&entry.commit_id())?;
1206                Ok(pattern.matches(commit.description().lines().next().unwrap_or_default()))
1207            })
1208        }
1209        RevsetFilterPredicate::AuthorName(pattern) => {
1210            let pattern = pattern.clone();
1211            box_pure_predicate_fn(move |index, pos| {
1212                let entry = index.entry_by_pos(pos);
1213                let commit = store.get_commit(&entry.commit_id())?;
1214                Ok(pattern.matches(&commit.author().name))
1215            })
1216        }
1217        RevsetFilterPredicate::AuthorEmail(pattern) => {
1218            let pattern = pattern.clone();
1219            box_pure_predicate_fn(move |index, pos| {
1220                let entry = index.entry_by_pos(pos);
1221                let commit = store.get_commit(&entry.commit_id())?;
1222                Ok(pattern.matches(&commit.author().email))
1223            })
1224        }
1225        RevsetFilterPredicate::AuthorDate(expression) => {
1226            let expression = *expression;
1227            box_pure_predicate_fn(move |index, pos| {
1228                let entry = index.entry_by_pos(pos);
1229                let commit = store.get_commit(&entry.commit_id())?;
1230                let author_date = &commit.author().timestamp;
1231                Ok(expression.matches(author_date))
1232            })
1233        }
1234        RevsetFilterPredicate::CommitterName(pattern) => {
1235            let pattern = pattern.clone();
1236            box_pure_predicate_fn(move |index, pos| {
1237                let entry = index.entry_by_pos(pos);
1238                let commit = store.get_commit(&entry.commit_id())?;
1239                Ok(pattern.matches(&commit.committer().name))
1240            })
1241        }
1242        RevsetFilterPredicate::CommitterEmail(pattern) => {
1243            let pattern = pattern.clone();
1244            box_pure_predicate_fn(move |index, pos| {
1245                let entry = index.entry_by_pos(pos);
1246                let commit = store.get_commit(&entry.commit_id())?;
1247                Ok(pattern.matches(&commit.committer().email))
1248            })
1249        }
1250        RevsetFilterPredicate::CommitterDate(expression) => {
1251            let expression = *expression;
1252            box_pure_predicate_fn(move |index, pos| {
1253                let entry = index.entry_by_pos(pos);
1254                let commit = store.get_commit(&entry.commit_id())?;
1255                let committer_date = &commit.committer().timestamp;
1256                Ok(expression.matches(committer_date))
1257            })
1258        }
1259        RevsetFilterPredicate::File(expr) => {
1260            let matcher: Rc<dyn Matcher> = expr.to_matcher().into();
1261            box_pure_predicate_fn(move |index, pos| {
1262                let entry = index.entry_by_pos(pos);
1263                let commit = store.get_commit(&entry.commit_id())?;
1264                Ok(has_diff_from_parent(&store, index, &commit, &*matcher).block_on()?)
1265            })
1266        }
1267        RevsetFilterPredicate::DiffContains { text, files } => {
1268            let text_pattern = text.clone();
1269            let files_matcher: Rc<dyn Matcher> = files.to_matcher().into();
1270            box_pure_predicate_fn(move |index, pos| {
1271                let entry = index.entry_by_pos(pos);
1272                let commit = store.get_commit(&entry.commit_id())?;
1273                Ok(
1274                    matches_diff_from_parent(
1275                        &store,
1276                        index,
1277                        &commit,
1278                        &text_pattern,
1279                        &*files_matcher,
1280                    )
1281                    .block_on()?,
1282                )
1283            })
1284        }
1285        RevsetFilterPredicate::HasConflict => box_pure_predicate_fn(move |index, pos| {
1286            let entry = index.entry_by_pos(pos);
1287            let commit = store.get_commit(&entry.commit_id())?;
1288            Ok(commit.has_conflict()?)
1289        }),
1290        RevsetFilterPredicate::Signed => box_pure_predicate_fn(move |index, pos| {
1291            let entry = index.entry_by_pos(pos);
1292            let commit = store.get_commit(&entry.commit_id())?;
1293            Ok(commit.is_signed())
1294        }),
1295        RevsetFilterPredicate::Extension(ext) => {
1296            let ext = ext.clone();
1297            box_pure_predicate_fn(move |index, pos| {
1298                let entry = index.entry_by_pos(pos);
1299                let commit = store.get_commit(&entry.commit_id())?;
1300                Ok(ext.matches_commit(&commit))
1301            })
1302        }
1303    }
1304}
1305
1306async fn has_diff_from_parent(
1307    store: &Arc<Store>,
1308    index: &CompositeIndex,
1309    commit: &Commit,
1310    matcher: &dyn Matcher,
1311) -> BackendResult<bool> {
1312    let parents: Vec<_> = commit.parents().try_collect()?;
1313    if let [parent] = parents.as_slice() {
1314        // Fast path: no need to load the root tree
1315        let unchanged = commit.tree_id() == parent.tree_id();
1316        if matcher.visit(RepoPath::root()) == Visit::AllRecursively {
1317            return Ok(!unchanged);
1318        } else if unchanged {
1319            return Ok(false);
1320        }
1321    }
1322
1323    // Conflict resolution is expensive, try that only for matched files.
1324    let from_tree = rewrite::merge_commit_trees_no_resolve_without_repo(store, &index, &parents)?;
1325    let to_tree = commit.tree()?;
1326    // TODO: handle copy tracking
1327    let mut tree_diff = from_tree.diff_stream(&to_tree, matcher);
1328    // TODO: Resolve values concurrently
1329    while let Some(entry) = tree_diff.next().await {
1330        let (from_value, to_value) = entry.values?;
1331        let from_value = resolve_file_values(store, &entry.path, from_value).await?;
1332        if from_value == to_value {
1333            continue;
1334        }
1335        return Ok(true);
1336    }
1337    Ok(false)
1338}
1339
1340async fn matches_diff_from_parent(
1341    store: &Arc<Store>,
1342    index: &CompositeIndex,
1343    commit: &Commit,
1344    text_pattern: &StringPattern,
1345    files_matcher: &dyn Matcher,
1346) -> BackendResult<bool> {
1347    let parents: Vec<_> = commit.parents().try_collect()?;
1348    // Conflict resolution is expensive, try that only for matched files.
1349    let from_tree = rewrite::merge_commit_trees_no_resolve_without_repo(store, &index, &parents)?;
1350    let to_tree = commit.tree()?;
1351    // TODO: handle copy tracking
1352    let mut tree_diff = from_tree.diff_stream(&to_tree, files_matcher);
1353    // TODO: Resolve values concurrently
1354    while let Some(entry) = tree_diff.next().await {
1355        let (left_value, right_value) = entry.values?;
1356        let left_value = resolve_file_values(store, &entry.path, left_value).await?;
1357        if left_value == right_value {
1358            continue;
1359        }
1360        let left_future = materialize_tree_value(store, &entry.path, left_value);
1361        let right_future = materialize_tree_value(store, &entry.path, right_value);
1362        let (left_value, right_value) = futures::try_join!(left_future, right_future)?;
1363        let left_contents = to_file_content(&entry.path, left_value).await?;
1364        let right_contents = to_file_content(&entry.path, right_value).await?;
1365        if diff_match_lines(&left_contents, &right_contents, text_pattern)? {
1366            return Ok(true);
1367        }
1368    }
1369    Ok(false)
1370}
1371
1372fn diff_match_lines(
1373    lefts: &Merge<BString>,
1374    rights: &Merge<BString>,
1375    pattern: &StringPattern,
1376) -> BackendResult<bool> {
1377    // Filter lines prior to comparison. This might produce inferior hunks due
1378    // to lack of contexts, but is way faster than full diff.
1379    if let (Some(left), Some(right)) = (lefts.as_resolved(), rights.as_resolved()) {
1380        let left_lines = match_lines(left, pattern);
1381        let right_lines = match_lines(right, pattern);
1382        Ok(left_lines.ne(right_lines))
1383    } else {
1384        let lefts: Merge<BString> = lefts.map(|text| match_lines(text, pattern).collect());
1385        let rights: Merge<BString> = rights.map(|text| match_lines(text, pattern).collect());
1386        let lefts = files::merge(&lefts);
1387        let rights = files::merge(&rights);
1388        let diff = Diff::by_line(lefts.iter().chain(rights.iter()));
1389        let different = files::conflict_diff_hunks(diff.hunks(), lefts.as_slice().len())
1390            .any(|hunk| hunk.kind == DiffHunkKind::Different);
1391        Ok(different)
1392    }
1393}
1394
1395fn match_lines<'a, 'b>(
1396    text: &'a [u8],
1397    pattern: &'b StringPattern,
1398) -> impl Iterator<Item = &'a [u8]> + use<'a, 'b> {
1399    // The pattern is matched line by line so that it can be anchored to line
1400    // start/end. For example, exact:"" will match blank lines.
1401    text.split_inclusive(|b| *b == b'\n').filter(|line| {
1402        let line = line.strip_suffix(b"\n").unwrap_or(line);
1403        // TODO: add .matches_bytes() or .to_bytes_matcher()
1404        str::from_utf8(line).is_ok_and(|line| pattern.matches(line))
1405    })
1406}
1407
1408async fn to_file_content(
1409    path: &RepoPath,
1410    value: MaterializedTreeValue,
1411) -> BackendResult<Merge<BString>> {
1412    let empty = || Merge::resolved(BString::default());
1413    match value {
1414        MaterializedTreeValue::Absent => Ok(empty()),
1415        MaterializedTreeValue::AccessDenied(_) => Ok(empty()),
1416        MaterializedTreeValue::File(mut file) => {
1417            Ok(Merge::resolved(file.read_all(path).await?.into()))
1418        }
1419        MaterializedTreeValue::Symlink { id: _, target } => Ok(Merge::resolved(target.into())),
1420        MaterializedTreeValue::GitSubmodule(_) => Ok(empty()),
1421        MaterializedTreeValue::FileConflict(file) => Ok(file.contents),
1422        MaterializedTreeValue::OtherConflict { .. } => Ok(empty()),
1423        MaterializedTreeValue::Tree(id) => {
1424            panic!("Unexpected tree with id {id:?} in diff at path {path:?}");
1425        }
1426    }
1427}
1428
1429#[cfg(test)]
1430#[rustversion::attr(
1431    since(1.89),
1432    expect(clippy::cloned_ref_to_slice_refs, reason = "makes tests more readable")
1433)]
1434mod tests {
1435    use indoc::indoc;
1436
1437    use super::*;
1438    use crate::default_index::DefaultMutableIndex;
1439
1440    /// Generator of unique 16-byte ChangeId excluding root id
1441    fn change_id_generator() -> impl FnMut() -> ChangeId {
1442        let mut iter = (1_u128..).map(|n| ChangeId::new(n.to_le_bytes().into()));
1443        move || iter.next().unwrap()
1444    }
1445
1446    fn try_collect_vec<T, E>(iter: impl IntoIterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
1447        iter.into_iter().collect()
1448    }
1449
1450    #[test]
1451    fn test_revset_combinator() {
1452        let mut new_change_id = change_id_generator();
1453        let mut index = DefaultMutableIndex::full(3, 16);
1454        let id_0 = CommitId::from_hex("000000");
1455        let id_1 = CommitId::from_hex("111111");
1456        let id_2 = CommitId::from_hex("222222");
1457        let id_3 = CommitId::from_hex("333333");
1458        let id_4 = CommitId::from_hex("444444");
1459        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1460        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1461        index.add_commit_data(id_2.clone(), new_change_id(), &[id_1.clone()]);
1462        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
1463        index.add_commit_data(id_4.clone(), new_change_id(), &[id_3.clone()]);
1464
1465        let index = index.as_composite();
1466        let get_pos = |id: &CommitId| index.commit_id_to_pos(id).unwrap();
1467        let make_positions = |ids: &[&CommitId]| ids.iter().copied().map(get_pos).collect_vec();
1468        let make_set = |ids: &[&CommitId]| -> Box<dyn InternalRevset> {
1469            let positions = make_positions(ids);
1470            Box::new(EagerRevset { positions })
1471        };
1472
1473        let set = make_set(&[&id_4, &id_3, &id_2, &id_0]);
1474        let mut p = set.to_predicate_fn();
1475        assert!(p(index, get_pos(&id_4)).unwrap());
1476        assert!(p(index, get_pos(&id_3)).unwrap());
1477        assert!(p(index, get_pos(&id_2)).unwrap());
1478        assert!(!p(index, get_pos(&id_1)).unwrap());
1479        assert!(p(index, get_pos(&id_0)).unwrap());
1480        // Uninteresting entries can be skipped
1481        let mut p = set.to_predicate_fn();
1482        assert!(p(index, get_pos(&id_3)).unwrap());
1483        assert!(!p(index, get_pos(&id_1)).unwrap());
1484        assert!(p(index, get_pos(&id_0)).unwrap());
1485
1486        let set = FilterRevset {
1487            candidates: make_set(&[&id_4, &id_2, &id_0]),
1488            predicate: as_pure_predicate_fn(|index, pos| {
1489                Ok(index.entry_by_pos(pos).commit_id() != id_4)
1490            }),
1491        };
1492        assert_eq!(
1493            try_collect_vec(set.positions().attach(index)).unwrap(),
1494            make_positions(&[&id_2, &id_0])
1495        );
1496        let mut p = set.to_predicate_fn();
1497        assert!(!p(index, get_pos(&id_4)).unwrap());
1498        assert!(!p(index, get_pos(&id_3)).unwrap());
1499        assert!(p(index, get_pos(&id_2)).unwrap());
1500        assert!(!p(index, get_pos(&id_1)).unwrap());
1501        assert!(p(index, get_pos(&id_0)).unwrap());
1502
1503        // Intersection by FilterRevset
1504        let set = FilterRevset {
1505            candidates: make_set(&[&id_4, &id_2, &id_0]),
1506            predicate: make_set(&[&id_3, &id_2, &id_1]),
1507        };
1508        assert_eq!(
1509            try_collect_vec(set.positions().attach(index)).unwrap(),
1510            make_positions(&[&id_2])
1511        );
1512        let mut p = set.to_predicate_fn();
1513        assert!(!p(index, get_pos(&id_4)).unwrap());
1514        assert!(!p(index, get_pos(&id_3)).unwrap());
1515        assert!(p(index, get_pos(&id_2)).unwrap());
1516        assert!(!p(index, get_pos(&id_1)).unwrap());
1517        assert!(!p(index, get_pos(&id_0)).unwrap());
1518
1519        let set = UnionRevset {
1520            set1: make_set(&[&id_4, &id_2]),
1521            set2: make_set(&[&id_3, &id_2, &id_1]),
1522        };
1523        assert_eq!(
1524            try_collect_vec(set.positions().attach(index)).unwrap(),
1525            make_positions(&[&id_4, &id_3, &id_2, &id_1])
1526        );
1527        let mut p = set.to_predicate_fn();
1528        assert!(p(index, get_pos(&id_4)).unwrap());
1529        assert!(p(index, get_pos(&id_3)).unwrap());
1530        assert!(p(index, get_pos(&id_2)).unwrap());
1531        assert!(p(index, get_pos(&id_1)).unwrap());
1532        assert!(!p(index, get_pos(&id_0)).unwrap());
1533
1534        let set = IntersectionRevset {
1535            set1: make_set(&[&id_4, &id_2, &id_0]),
1536            set2: make_set(&[&id_3, &id_2, &id_1]),
1537        };
1538        assert_eq!(
1539            try_collect_vec(set.positions().attach(index)).unwrap(),
1540            make_positions(&[&id_2])
1541        );
1542        let mut p = set.to_predicate_fn();
1543        assert!(!p(index, get_pos(&id_4)).unwrap());
1544        assert!(!p(index, get_pos(&id_3)).unwrap());
1545        assert!(p(index, get_pos(&id_2)).unwrap());
1546        assert!(!p(index, get_pos(&id_1)).unwrap());
1547        assert!(!p(index, get_pos(&id_0)).unwrap());
1548
1549        let set = DifferenceRevset {
1550            set1: make_set(&[&id_4, &id_2, &id_0]),
1551            set2: make_set(&[&id_3, &id_2, &id_1]),
1552        };
1553        assert_eq!(
1554            try_collect_vec(set.positions().attach(index)).unwrap(),
1555            make_positions(&[&id_4, &id_0])
1556        );
1557        let mut p = set.to_predicate_fn();
1558        assert!(p(index, get_pos(&id_4)).unwrap());
1559        assert!(!p(index, get_pos(&id_3)).unwrap());
1560        assert!(!p(index, get_pos(&id_2)).unwrap());
1561        assert!(!p(index, get_pos(&id_1)).unwrap());
1562        assert!(p(index, get_pos(&id_0)).unwrap());
1563    }
1564
1565    #[test]
1566    fn test_revset_combinator_error_propagation() {
1567        let mut new_change_id = change_id_generator();
1568        let mut index = DefaultMutableIndex::full(3, 16);
1569        let id_0 = CommitId::from_hex("000000");
1570        let id_1 = CommitId::from_hex("111111");
1571        let id_2 = CommitId::from_hex("222222");
1572        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1573        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1574        index.add_commit_data(id_2.clone(), new_change_id(), &[id_1.clone()]);
1575
1576        let index = index.as_composite();
1577        let get_pos = |id: &CommitId| index.commit_id_to_pos(id).unwrap();
1578        let make_positions = |ids: &[&CommitId]| ids.iter().copied().map(get_pos).collect_vec();
1579        let make_good_set = |ids: &[&CommitId]| -> Box<dyn InternalRevset> {
1580            let positions = make_positions(ids);
1581            Box::new(EagerRevset { positions })
1582        };
1583        let make_bad_set = |ids: &[&CommitId], bad_id: &CommitId| -> Box<dyn InternalRevset> {
1584            let positions = make_positions(ids);
1585            let bad_id = bad_id.clone();
1586            Box::new(FilterRevset {
1587                candidates: EagerRevset { positions },
1588                predicate: as_pure_predicate_fn(move |index, pos| {
1589                    if index.entry_by_pos(pos).commit_id() == bad_id {
1590                        Err(RevsetEvaluationError::Other("bad".into()))
1591                    } else {
1592                        Ok(true)
1593                    }
1594                }),
1595            })
1596        };
1597
1598        // Error from filter predicate
1599        let set = make_bad_set(&[&id_2, &id_1, &id_0], &id_1);
1600        assert_eq!(
1601            try_collect_vec(set.positions().attach(index).take(1)).unwrap(),
1602            make_positions(&[&id_2])
1603        );
1604        assert!(try_collect_vec(set.positions().attach(index).take(2)).is_err());
1605        let mut p = set.to_predicate_fn();
1606        assert!(p(index, get_pos(&id_2)).unwrap());
1607        assert!(p(index, get_pos(&id_1)).is_err());
1608
1609        // Error from filter candidates
1610        let set = FilterRevset {
1611            candidates: make_bad_set(&[&id_2, &id_1, &id_0], &id_1),
1612            predicate: as_pure_predicate_fn(|_, _| Ok(true)),
1613        };
1614        assert_eq!(
1615            try_collect_vec(set.positions().attach(index).take(1)).unwrap(),
1616            make_positions(&[&id_2])
1617        );
1618        assert!(try_collect_vec(set.positions().attach(index).take(2)).is_err());
1619        let mut p = set.to_predicate_fn();
1620        assert!(p(index, get_pos(&id_2)).unwrap());
1621        assert!(p(index, get_pos(&id_1)).is_err());
1622
1623        // Error from left side of union, immediately
1624        let set = UnionRevset {
1625            set1: make_bad_set(&[&id_1], &id_1),
1626            set2: make_good_set(&[&id_2, &id_1]),
1627        };
1628        assert!(try_collect_vec(set.positions().attach(index).take(1)).is_err());
1629        let mut p = set.to_predicate_fn();
1630        assert!(p(index, get_pos(&id_2)).unwrap()); // works because bad id isn't visited
1631        assert!(p(index, get_pos(&id_1)).is_err());
1632
1633        // Error from right side of union, lazily
1634        let set = UnionRevset {
1635            set1: make_good_set(&[&id_2, &id_1]),
1636            set2: make_bad_set(&[&id_1, &id_0], &id_0),
1637        };
1638        assert_eq!(
1639            try_collect_vec(set.positions().attach(index).take(2)).unwrap(),
1640            make_positions(&[&id_2, &id_1])
1641        );
1642        assert!(try_collect_vec(set.positions().attach(index).take(3)).is_err());
1643        let mut p = set.to_predicate_fn();
1644        assert!(p(index, get_pos(&id_2)).unwrap());
1645        assert!(p(index, get_pos(&id_1)).unwrap());
1646        assert!(p(index, get_pos(&id_0)).is_err());
1647
1648        // Error from left side of intersection, immediately
1649        let set = IntersectionRevset {
1650            set1: make_bad_set(&[&id_1], &id_1),
1651            set2: make_good_set(&[&id_2, &id_1]),
1652        };
1653        assert!(try_collect_vec(set.positions().attach(index).take(1)).is_err());
1654        let mut p = set.to_predicate_fn();
1655        assert!(!p(index, get_pos(&id_2)).unwrap());
1656        assert!(p(index, get_pos(&id_1)).is_err());
1657
1658        // Error from right side of intersection, lazily
1659        let set = IntersectionRevset {
1660            set1: make_good_set(&[&id_2, &id_1, &id_0]),
1661            set2: make_bad_set(&[&id_1, &id_0], &id_0),
1662        };
1663        assert_eq!(
1664            try_collect_vec(set.positions().attach(index).take(1)).unwrap(),
1665            make_positions(&[&id_1])
1666        );
1667        assert!(try_collect_vec(set.positions().attach(index).take(2)).is_err());
1668        let mut p = set.to_predicate_fn();
1669        assert!(!p(index, get_pos(&id_2)).unwrap());
1670        assert!(p(index, get_pos(&id_1)).unwrap());
1671        assert!(p(index, get_pos(&id_0)).is_err());
1672
1673        // Error from left side of difference, immediately
1674        let set = DifferenceRevset {
1675            set1: make_bad_set(&[&id_1], &id_1),
1676            set2: make_good_set(&[&id_2, &id_1]),
1677        };
1678        assert!(try_collect_vec(set.positions().attach(index).take(1)).is_err());
1679        let mut p = set.to_predicate_fn();
1680        assert!(!p(index, get_pos(&id_2)).unwrap());
1681        assert!(p(index, get_pos(&id_1)).is_err());
1682
1683        // Error from right side of difference, lazily
1684        let set = DifferenceRevset {
1685            set1: make_good_set(&[&id_2, &id_1, &id_0]),
1686            set2: make_bad_set(&[&id_1, &id_0], &id_0),
1687        };
1688        assert_eq!(
1689            try_collect_vec(set.positions().attach(index).take(1)).unwrap(),
1690            make_positions(&[&id_2])
1691        );
1692        assert!(try_collect_vec(set.positions().attach(index).take(2)).is_err());
1693        let mut p = set.to_predicate_fn();
1694        assert!(p(index, get_pos(&id_2)).unwrap());
1695        assert!(!p(index, get_pos(&id_1)).unwrap());
1696        assert!(p(index, get_pos(&id_0)).is_err());
1697    }
1698
1699    #[test]
1700    fn test_positions_accumulator() {
1701        let mut new_change_id = change_id_generator();
1702        let mut index = DefaultMutableIndex::full(3, 16);
1703        let id_0 = CommitId::from_hex("000000");
1704        let id_1 = CommitId::from_hex("111111");
1705        let id_2 = CommitId::from_hex("222222");
1706        let id_3 = CommitId::from_hex("333333");
1707        let id_4 = CommitId::from_hex("444444");
1708        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1709        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1710        index.add_commit_data(id_2.clone(), new_change_id(), &[id_1.clone()]);
1711        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
1712        index.add_commit_data(id_4.clone(), new_change_id(), &[id_3.clone()]);
1713
1714        let index = index.as_composite();
1715        let get_pos = |id: &CommitId| index.commit_id_to_pos(id).unwrap();
1716        let make_positions = |ids: &[&CommitId]| ids.iter().copied().map(get_pos).collect_vec();
1717        let make_set = |ids: &[&CommitId]| -> Box<dyn InternalRevset> {
1718            let positions = make_positions(ids);
1719            Box::new(EagerRevset { positions })
1720        };
1721
1722        let full_set = make_set(&[&id_4, &id_3, &id_2, &id_1, &id_0]);
1723
1724        // Consumes entries incrementally
1725        let positions_accum = PositionsAccumulator::new(index, full_set.positions());
1726
1727        assert!(positions_accum.contains(&id_3).unwrap());
1728        assert_eq!(positions_accum.consumed_len(), 2);
1729
1730        assert!(positions_accum.contains(&id_0).unwrap());
1731        assert_eq!(positions_accum.consumed_len(), 5);
1732
1733        assert!(positions_accum.contains(&id_3).unwrap());
1734        assert_eq!(positions_accum.consumed_len(), 5);
1735
1736        // Does not consume positions for unknown commits
1737        let positions_accum = PositionsAccumulator::new(index, full_set.positions());
1738
1739        assert!(!positions_accum
1740            .contains(&CommitId::from_hex("999999"))
1741            .unwrap());
1742        assert_eq!(positions_accum.consumed_len(), 0);
1743
1744        // Does not consume without necessity
1745        let set = make_set(&[&id_3, &id_2, &id_1]);
1746        let positions_accum = PositionsAccumulator::new(index, set.positions());
1747
1748        assert!(!positions_accum.contains(&id_4).unwrap());
1749        assert_eq!(positions_accum.consumed_len(), 1);
1750
1751        assert!(positions_accum.contains(&id_3).unwrap());
1752        assert_eq!(positions_accum.consumed_len(), 1);
1753
1754        assert!(!positions_accum.contains(&id_0).unwrap());
1755        assert_eq!(positions_accum.consumed_len(), 3);
1756
1757        assert!(positions_accum.contains(&id_1).unwrap());
1758    }
1759
1760    fn diff_match_lines_samples() -> (Merge<BString>, Merge<BString>) {
1761        // left2      left1      base       right1      right2
1762        // ---------- ---------- ---------- ----------- -----------
1763        // "left 1.1" "line 1"   "line 1"   "line 1"    "line 1"
1764        // "line 2"   "line 2"   "line 2"   "line 2"    "line 2"
1765        // "left 3.1" "left 3.1" "line 3"   "right 3.1" "right 3.1"
1766        // "left 3.2" "left 3.2"
1767        // "left 3.3"
1768        // "line 4"   "line 4"   "line 4"   "line 4"    "line 4"
1769        // "line 5"   "line 5"              "line 5"
1770        let base = indoc! {"
1771            line 1
1772            line 2
1773            line 3
1774            line 4
1775        "};
1776        let left1 = indoc! {"
1777            line 1
1778            line 2
1779            left 3.1
1780            left 3.2
1781            line 4
1782            line 5
1783        "};
1784        let left2 = indoc! {"
1785            left 1.1
1786            line 2
1787            left 3.1
1788            left 3.2
1789            left 3.3
1790            line 4
1791            line 5
1792        "};
1793        let right1 = indoc! {"
1794            line 1
1795            line 2
1796            right 3.1
1797            line 4
1798            line 5
1799        "};
1800        let right2 = indoc! {"
1801            line 1
1802            line 2
1803            right 3.1
1804            line 4
1805        "};
1806
1807        let conflict1 = Merge::from_vec([left1, base, right1].map(BString::from).to_vec());
1808        let conflict2 = Merge::from_vec([left2, base, right2].map(BString::from).to_vec());
1809        (conflict1, conflict2)
1810    }
1811
1812    #[test]
1813    fn test_diff_match_lines_between_resolved() {
1814        let (conflict1, conflict2) = diff_match_lines_samples();
1815        let left1 = Merge::resolved(conflict1.first().clone());
1816        let left2 = Merge::resolved(conflict2.first().clone());
1817        let diff = |needle: &str| {
1818            let pattern = StringPattern::substring(needle);
1819            diff_match_lines(&left1, &left2, &pattern).unwrap()
1820        };
1821
1822        assert!(diff(""));
1823        assert!(!diff("no match"));
1824        assert!(diff("line "));
1825        assert!(diff(" 1"));
1826        assert!(!diff(" 2"));
1827        assert!(diff(" 3"));
1828        assert!(!diff(" 3.1"));
1829        assert!(!diff(" 3.2"));
1830        assert!(diff(" 3.3"));
1831        assert!(!diff(" 4"));
1832        assert!(!diff(" 5"));
1833    }
1834
1835    #[test]
1836    fn test_diff_match_lines_between_conflicts() {
1837        let (conflict1, conflict2) = diff_match_lines_samples();
1838        let diff = |needle: &str| {
1839            let pattern = StringPattern::substring(needle);
1840            diff_match_lines(&conflict1, &conflict2, &pattern).unwrap()
1841        };
1842
1843        assert!(diff(""));
1844        assert!(!diff("no match"));
1845        assert!(diff("line "));
1846        assert!(diff(" 1"));
1847        assert!(!diff(" 2"));
1848        assert!(diff(" 3"));
1849        // " 3.1" and " 3.2" could be considered different because the hunk
1850        // includes a changed line " 3.3". However, we filters out unmatched
1851        // lines first, therefore the changed line is omitted from the hunk.
1852        assert!(!diff(" 3.1"));
1853        assert!(!diff(" 3.2"));
1854        assert!(diff(" 3.3"));
1855        assert!(!diff(" 4"));
1856        assert!(!diff(" 5")); // per A-B+A=A rule
1857    }
1858
1859    #[test]
1860    fn test_diff_match_lines_between_resolved_and_conflict() {
1861        let (_conflict1, conflict2) = diff_match_lines_samples();
1862        let base = Merge::resolved(conflict2.get_remove(0).unwrap().clone());
1863        let diff = |needle: &str| {
1864            let pattern = StringPattern::substring(needle);
1865            diff_match_lines(&base, &conflict2, &pattern).unwrap()
1866        };
1867
1868        assert!(diff(""));
1869        assert!(!diff("no match"));
1870        assert!(diff("line "));
1871        assert!(diff(" 1"));
1872        assert!(!diff(" 2"));
1873        assert!(diff(" 3"));
1874        assert!(diff(" 3.1"));
1875        assert!(diff(" 3.2"));
1876        assert!(!diff(" 4"));
1877        assert!(diff(" 5"));
1878    }
1879}