mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-29 23:57:51 +00:00
revset: introduce trait that turns evaluated revset into predicate function
This allows us to evaluate 's1 & (f() | s2)' as 's1.iter().filter(f || s2)' instead of 's1 & (all.iter().filter(f) | s2)'.
This commit is contained in:
parent
f64f96251f
commit
f2e7a5ad03
1 changed files with 155 additions and 10 deletions
|
@ -1262,7 +1262,7 @@ pub fn optimize(expression: Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
||||||
fold_difference(&expression).unwrap_or(expression)
|
fold_difference(&expression).unwrap_or(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Revset<'repo> {
|
pub trait Revset<'repo>: ToPredicateFn<'repo> {
|
||||||
// All revsets currently iterate in order of descending index position
|
// All revsets currently iterate in order of descending index position
|
||||||
fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo>;
|
fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo>;
|
||||||
|
|
||||||
|
@ -1271,6 +1271,23 @@ pub trait Revset<'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This trait is implementation detail, which can be hidden in private module.
|
||||||
|
pub trait ToPredicateFn<'repo> {
|
||||||
|
/// Creates function that tests if the given entry is included in the set.
|
||||||
|
///
|
||||||
|
/// The predicate function is evaluated in order of `RevsetIterator`.
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'repo, T> ToPredicateFn<'repo> for Box<T>
|
||||||
|
where
|
||||||
|
T: ToPredicateFn<'repo> + ?Sized,
|
||||||
|
{
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
<T as ToPredicateFn<'repo>>::to_predicate_fn(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RevsetIterator<'revset, 'repo: 'revset> {
|
pub struct RevsetIterator<'revset, 'repo: 'revset> {
|
||||||
inner: Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>,
|
inner: Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>,
|
||||||
}
|
}
|
||||||
|
@ -1300,6 +1317,16 @@ impl<'revset, 'repo> RevsetIterator<'revset, 'repo> {
|
||||||
pub fn graph(self) -> RevsetGraphIterator<'revset, 'repo> {
|
pub fn graph(self) -> RevsetGraphIterator<'revset, 'repo> {
|
||||||
RevsetGraphIterator::new(self)
|
RevsetGraphIterator::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_predicate_fn(self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + 'revset> {
|
||||||
|
let mut iter = self.fuse().peekable();
|
||||||
|
Box::new(move |entry| {
|
||||||
|
while iter.next_if(|e| e.position() > entry.position()).is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
iter.next_if(|e| e.position() == entry.position()).is_some()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'repo> Iterator for RevsetIterator<'_, 'repo> {
|
impl<'repo> Iterator for RevsetIterator<'_, 'repo> {
|
||||||
|
@ -1367,6 +1394,12 @@ impl<'repo> Revset<'repo> for EagerRevset<'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'repo> ToPredicateFn<'repo> for EagerRevset<'repo> {
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
self.iter().into_predicate_fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct RevWalkRevset<'repo> {
|
struct RevWalkRevset<'repo> {
|
||||||
walk: RevWalk<'repo>,
|
walk: RevWalk<'repo>,
|
||||||
}
|
}
|
||||||
|
@ -1377,6 +1410,12 @@ impl<'repo> Revset<'repo> for RevWalkRevset<'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'repo> ToPredicateFn<'repo> for RevWalkRevset<'repo> {
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
self.iter().into_predicate_fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ChildrenRevset<'revset, 'repo: 'revset> {
|
struct ChildrenRevset<'revset, 'repo: 'revset> {
|
||||||
// The revisions we want to find children for
|
// The revisions we want to find children for
|
||||||
root_set: Box<dyn Revset<'repo> + 'revset>,
|
root_set: Box<dyn Revset<'repo> + 'revset>,
|
||||||
|
@ -1403,14 +1442,37 @@ impl<'repo> Revset<'repo> for ChildrenRevset<'_, 'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FilterRevset<'revset, 'repo: 'revset> {
|
impl<'repo> ToPredicateFn<'repo> for ChildrenRevset<'_, 'repo> {
|
||||||
candidates: Box<dyn Revset<'repo> + 'revset>,
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
predicate: Box<dyn Fn(&IndexEntry<'repo>) -> bool + 'repo>,
|
// TODO: can be optimized if candidate_set contains all heads
|
||||||
|
self.iter().into_predicate_fn()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'repo> Revset<'repo> for FilterRevset<'_, 'repo> {
|
struct FilterRevset<'revset, 'repo: 'revset, P> {
|
||||||
|
candidates: Box<dyn Revset<'repo> + 'revset>,
|
||||||
|
predicate: P,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'repo, P> Revset<'repo> for FilterRevset<'_, 'repo, P>
|
||||||
|
where
|
||||||
|
P: ToPredicateFn<'repo>,
|
||||||
|
{
|
||||||
fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
|
fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
|
||||||
RevsetIterator::new(Box::new(self.candidates.iter().filter(&self.predicate)))
|
let p = self.predicate.to_predicate_fn();
|
||||||
|
RevsetIterator::new(Box::new(self.candidates.iter().filter(p)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'repo, P> ToPredicateFn<'repo> for FilterRevset<'_, 'repo, P>
|
||||||
|
where
|
||||||
|
P: ToPredicateFn<'repo>,
|
||||||
|
{
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
// TODO: optimize 'p1' out if candidates = All
|
||||||
|
let mut p1 = self.candidates.to_predicate_fn();
|
||||||
|
let mut p2 = self.predicate.to_predicate_fn();
|
||||||
|
Box::new(move |entry| p1(entry) && p2(entry))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1428,6 +1490,14 @@ impl<'repo> Revset<'repo> for UnionRevset<'_, 'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'repo> ToPredicateFn<'repo> for UnionRevset<'_, 'repo> {
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
let mut p1 = self.set1.to_predicate_fn();
|
||||||
|
let mut p2 = self.set2.to_predicate_fn();
|
||||||
|
Box::new(move |entry| p1(entry) || p2(entry))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct UnionRevsetIterator<'revset, 'repo> {
|
struct UnionRevsetIterator<'revset, 'repo> {
|
||||||
iter1: Peekable<RevsetIterator<'revset, 'repo>>,
|
iter1: Peekable<RevsetIterator<'revset, 'repo>>,
|
||||||
iter2: Peekable<RevsetIterator<'revset, 'repo>>,
|
iter2: Peekable<RevsetIterator<'revset, 'repo>>,
|
||||||
|
@ -1466,6 +1536,14 @@ impl<'repo> Revset<'repo> for IntersectionRevset<'_, 'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'repo> ToPredicateFn<'repo> for IntersectionRevset<'_, 'repo> {
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
let mut p1 = self.set1.to_predicate_fn();
|
||||||
|
let mut p2 = self.set2.to_predicate_fn();
|
||||||
|
Box::new(move |entry| p1(entry) && p2(entry))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct IntersectionRevsetIterator<'revset, 'repo> {
|
struct IntersectionRevsetIterator<'revset, 'repo> {
|
||||||
iter1: Peekable<RevsetIterator<'revset, 'repo>>,
|
iter1: Peekable<RevsetIterator<'revset, 'repo>>,
|
||||||
iter2: Peekable<RevsetIterator<'revset, 'repo>>,
|
iter2: Peekable<RevsetIterator<'revset, 'repo>>,
|
||||||
|
@ -1516,6 +1594,15 @@ impl<'repo> Revset<'repo> for DifferenceRevset<'_, 'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'repo> ToPredicateFn<'repo> for DifferenceRevset<'_, 'repo> {
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
// TODO: optimize 'p1' out for unary negate?
|
||||||
|
let mut p1 = self.set1.to_predicate_fn();
|
||||||
|
let mut p2 = self.set2.to_predicate_fn();
|
||||||
|
Box::new(move |entry| p1(entry) && !p2(entry))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct DifferenceRevsetIterator<'revset, 'repo> {
|
struct DifferenceRevsetIterator<'revset, 'repo> {
|
||||||
iter1: Peekable<RevsetIterator<'revset, 'repo>>,
|
iter1: Peekable<RevsetIterator<'revset, 'repo>>,
|
||||||
iter2: Peekable<RevsetIterator<'revset, 'repo>>,
|
iter2: Peekable<RevsetIterator<'revset, 'repo>>,
|
||||||
|
@ -1779,10 +1866,18 @@ pub fn revset_for_commits<'revset, 'repo: 'revset>(
|
||||||
Box::new(EagerRevset { index_entries })
|
Box::new(EagerRevset { index_entries })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PurePredicateFn<'repo> = Box<dyn Fn(&IndexEntry<'repo>) -> bool + 'repo>;
|
||||||
|
|
||||||
|
impl<'repo> ToPredicateFn<'repo> for PurePredicateFn<'repo> {
|
||||||
|
fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_predicate_fn<'repo>(
|
fn build_predicate_fn<'repo>(
|
||||||
repo: RepoRef<'repo>,
|
repo: RepoRef<'repo>,
|
||||||
predicate: &RevsetFilterPredicate,
|
predicate: &RevsetFilterPredicate,
|
||||||
) -> Box<dyn Fn(&IndexEntry<'repo>) -> bool + 'repo> {
|
) -> PurePredicateFn<'repo> {
|
||||||
match predicate {
|
match predicate {
|
||||||
RevsetFilterPredicate::ParentCount(parent_count_range) => {
|
RevsetFilterPredicate::ParentCount(parent_count_range) => {
|
||||||
let parent_count_range = parent_count_range.clone();
|
let parent_count_range = parent_count_range.clone();
|
||||||
|
@ -1833,7 +1928,7 @@ pub fn filter_by_diff<'revset, 'repo: 'revset>(
|
||||||
matcher: impl Borrow<dyn Matcher + 'repo> + 'repo,
|
matcher: impl Borrow<dyn Matcher + 'repo> + 'repo,
|
||||||
candidates: Box<dyn Revset<'repo> + 'revset>,
|
candidates: Box<dyn Revset<'repo> + 'revset>,
|
||||||
) -> Box<dyn Revset<'repo> + 'revset> {
|
) -> Box<dyn Revset<'repo> + 'revset> {
|
||||||
Box::new(FilterRevset {
|
Box::new(FilterRevset::<PurePredicateFn> {
|
||||||
candidates,
|
candidates,
|
||||||
predicate: Box::new(move |entry| has_diff_from_parent(repo, entry, matcher.borrow())),
|
predicate: Box::new(move |entry| has_diff_from_parent(repo, entry, matcher.borrow())),
|
||||||
})
|
})
|
||||||
|
@ -2885,16 +2980,48 @@ mod tests {
|
||||||
|
|
||||||
let get_entry = |id: &CommitId| index.entry_by_id(id).unwrap();
|
let get_entry = |id: &CommitId| index.entry_by_id(id).unwrap();
|
||||||
let make_entries = |ids: &[&CommitId]| ids.iter().map(|id| get_entry(id)).collect_vec();
|
let make_entries = |ids: &[&CommitId]| ids.iter().map(|id| get_entry(id)).collect_vec();
|
||||||
let make_set = |ids: &[&CommitId]| -> Box<dyn Revset<'_>> {
|
let make_set = |ids: &[&CommitId]| -> Box<dyn Revset<'_> + '_> {
|
||||||
let index_entries = make_entries(ids);
|
let index_entries = make_entries(ids);
|
||||||
Box::new(EagerRevset { index_entries })
|
Box::new(EagerRevset { index_entries })
|
||||||
};
|
};
|
||||||
|
|
||||||
let set = FilterRevset {
|
let set = make_set(&[&id_4, &id_3, &id_2, &id_0]);
|
||||||
|
let mut p = set.to_predicate_fn();
|
||||||
|
assert!(p(&get_entry(&id_4)));
|
||||||
|
assert!(p(&get_entry(&id_3)));
|
||||||
|
assert!(p(&get_entry(&id_2)));
|
||||||
|
assert!(!p(&get_entry(&id_1)));
|
||||||
|
assert!(p(&get_entry(&id_0)));
|
||||||
|
// Uninteresting entries can be skipped
|
||||||
|
let mut p = set.to_predicate_fn();
|
||||||
|
assert!(p(&get_entry(&id_3)));
|
||||||
|
assert!(!p(&get_entry(&id_1)));
|
||||||
|
assert!(p(&get_entry(&id_0)));
|
||||||
|
|
||||||
|
let set = FilterRevset::<PurePredicateFn> {
|
||||||
candidates: make_set(&[&id_4, &id_2, &id_0]),
|
candidates: make_set(&[&id_4, &id_2, &id_0]),
|
||||||
predicate: Box::new(|entry| entry.commit_id() != id_4),
|
predicate: Box::new(|entry| entry.commit_id() != id_4),
|
||||||
};
|
};
|
||||||
assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2, &id_0]));
|
assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2, &id_0]));
|
||||||
|
let mut p = set.to_predicate_fn();
|
||||||
|
assert!(!p(&get_entry(&id_4)));
|
||||||
|
assert!(!p(&get_entry(&id_3)));
|
||||||
|
assert!(p(&get_entry(&id_2)));
|
||||||
|
assert!(!p(&get_entry(&id_1)));
|
||||||
|
assert!(p(&get_entry(&id_0)));
|
||||||
|
|
||||||
|
// Intersection by FilterRevset
|
||||||
|
let set = FilterRevset {
|
||||||
|
candidates: make_set(&[&id_4, &id_2, &id_0]),
|
||||||
|
predicate: make_set(&[&id_3, &id_2, &id_1]),
|
||||||
|
};
|
||||||
|
assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2]));
|
||||||
|
let mut p = set.to_predicate_fn();
|
||||||
|
assert!(!p(&get_entry(&id_4)));
|
||||||
|
assert!(!p(&get_entry(&id_3)));
|
||||||
|
assert!(p(&get_entry(&id_2)));
|
||||||
|
assert!(!p(&get_entry(&id_1)));
|
||||||
|
assert!(!p(&get_entry(&id_0)));
|
||||||
|
|
||||||
let set = UnionRevset {
|
let set = UnionRevset {
|
||||||
set1: make_set(&[&id_4, &id_2]),
|
set1: make_set(&[&id_4, &id_2]),
|
||||||
|
@ -2904,17 +3031,35 @@ mod tests {
|
||||||
set.iter().collect_vec(),
|
set.iter().collect_vec(),
|
||||||
make_entries(&[&id_4, &id_3, &id_2, &id_1])
|
make_entries(&[&id_4, &id_3, &id_2, &id_1])
|
||||||
);
|
);
|
||||||
|
let mut p = set.to_predicate_fn();
|
||||||
|
assert!(p(&get_entry(&id_4)));
|
||||||
|
assert!(p(&get_entry(&id_3)));
|
||||||
|
assert!(p(&get_entry(&id_2)));
|
||||||
|
assert!(p(&get_entry(&id_1)));
|
||||||
|
assert!(!p(&get_entry(&id_0)));
|
||||||
|
|
||||||
let set = IntersectionRevset {
|
let set = IntersectionRevset {
|
||||||
set1: make_set(&[&id_4, &id_2, &id_0]),
|
set1: make_set(&[&id_4, &id_2, &id_0]),
|
||||||
set2: make_set(&[&id_3, &id_2, &id_1]),
|
set2: make_set(&[&id_3, &id_2, &id_1]),
|
||||||
};
|
};
|
||||||
assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2]));
|
assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2]));
|
||||||
|
let mut p = set.to_predicate_fn();
|
||||||
|
assert!(!p(&get_entry(&id_4)));
|
||||||
|
assert!(!p(&get_entry(&id_3)));
|
||||||
|
assert!(p(&get_entry(&id_2)));
|
||||||
|
assert!(!p(&get_entry(&id_1)));
|
||||||
|
assert!(!p(&get_entry(&id_0)));
|
||||||
|
|
||||||
let set = DifferenceRevset {
|
let set = DifferenceRevset {
|
||||||
set1: make_set(&[&id_4, &id_2, &id_0]),
|
set1: make_set(&[&id_4, &id_2, &id_0]),
|
||||||
set2: make_set(&[&id_3, &id_2, &id_1]),
|
set2: make_set(&[&id_3, &id_2, &id_1]),
|
||||||
};
|
};
|
||||||
assert_eq!(set.iter().collect_vec(), make_entries(&[&id_4, &id_0]));
|
assert_eq!(set.iter().collect_vec(), make_entries(&[&id_4, &id_0]));
|
||||||
|
let mut p = set.to_predicate_fn();
|
||||||
|
assert!(p(&get_entry(&id_4)));
|
||||||
|
assert!(!p(&get_entry(&id_3)));
|
||||||
|
assert!(!p(&get_entry(&id_2)));
|
||||||
|
assert!(!p(&get_entry(&id_1)));
|
||||||
|
assert!(p(&get_entry(&id_0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue