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