mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-18 18:27:38 +00:00
revset: move internal_evaluate()
onto new context type
I'm about to replace the `&dyn Repo` argument by several smaller types, and it's easier to collect those in a single context type than to pass them separately as arguments. I also moved `revset_for_commit_ids()` and `take_latest_revset()` onto the new type because it was easy. `build_predicate_fn()` and `has_diff_from_parent()` ran into some lifetime issue when I tried.
This commit is contained in:
parent
3ff1ab520b
commit
002ec1ac68
1 changed files with 214 additions and 208 deletions
|
@ -496,230 +496,236 @@ pub fn evaluate<'index>(
|
||||||
index: CompositeIndex<'index>,
|
index: CompositeIndex<'index>,
|
||||||
expression: &RevsetExpression,
|
expression: &RevsetExpression,
|
||||||
) -> Result<RevsetImpl<'index>, RevsetError> {
|
) -> Result<RevsetImpl<'index>, RevsetError> {
|
||||||
let internal_revset = internal_evaluate(repo, expression)?;
|
let context = EvaluationContext { repo };
|
||||||
|
let internal_revset = context.evaluate(expression)?;
|
||||||
Ok(RevsetImpl::new(internal_revset, index))
|
Ok(RevsetImpl::new(internal_revset, index))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_evaluate<'index>(
|
struct EvaluationContext<'index> {
|
||||||
repo: &'index dyn Repo,
|
repo: &'index dyn Repo,
|
||||||
expression: &RevsetExpression,
|
}
|
||||||
) -> Result<Box<dyn InternalRevset<'index> + 'index>, RevsetError> {
|
|
||||||
match expression {
|
impl<'index> EvaluationContext<'index> {
|
||||||
RevsetExpression::Symbol(_)
|
fn evaluate(
|
||||||
| RevsetExpression::Branches(_)
|
&self,
|
||||||
| RevsetExpression::RemoteBranches { .. }
|
expression: &RevsetExpression,
|
||||||
| RevsetExpression::Tags
|
) -> Result<Box<dyn InternalRevset<'index> + 'index>, RevsetError> {
|
||||||
| RevsetExpression::GitRefs
|
match expression {
|
||||||
| RevsetExpression::GitHead => {
|
RevsetExpression::Symbol(_)
|
||||||
panic!("Expression '{expression:?}' should have been resolved by caller");
|
| RevsetExpression::Branches(_)
|
||||||
}
|
| RevsetExpression::RemoteBranches { .. }
|
||||||
RevsetExpression::None => Ok(Box::new(EagerRevset::empty())),
|
| RevsetExpression::Tags
|
||||||
RevsetExpression::All => {
|
| RevsetExpression::GitRefs
|
||||||
// Since `all()` does not include hidden commits, some of the logical
|
| RevsetExpression::GitHead => {
|
||||||
// transformation rules may subtly change the evaluated set. For example,
|
panic!("Expression '{expression:?}' should have been resolved by caller");
|
||||||
// `all() & x` is not `x` if `x` is hidden. This wouldn't matter in practice,
|
|
||||||
// but if it does, the heads set could be extended to include the commits
|
|
||||||
// (and `remote_branches()`) specified in the revset expression. Alternatively,
|
|
||||||
// some optimization rules could be removed, but that means `author(_) & x`
|
|
||||||
// would have to test `:heads() & x`.
|
|
||||||
internal_evaluate(repo, &RevsetExpression::visible_heads().ancestors())
|
|
||||||
}
|
|
||||||
RevsetExpression::Commits(commit_ids) => Ok(revset_for_commit_ids(repo, commit_ids)),
|
|
||||||
RevsetExpression::Children(roots) => {
|
|
||||||
let root_set = internal_evaluate(repo, roots)?;
|
|
||||||
let candidates_expression = roots.descendants();
|
|
||||||
let candidate_set = internal_evaluate(repo, &candidates_expression)?;
|
|
||||||
Ok(Box::new(ChildrenRevset {
|
|
||||||
root_set,
|
|
||||||
candidate_set,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
RevsetExpression::Ancestors { heads, generation } => {
|
|
||||||
let range_expression = RevsetExpression::Range {
|
|
||||||
roots: RevsetExpression::none(),
|
|
||||||
heads: heads.clone(),
|
|
||||||
generation: generation.clone(),
|
|
||||||
};
|
|
||||||
internal_evaluate(repo, &range_expression)
|
|
||||||
}
|
|
||||||
RevsetExpression::Range {
|
|
||||||
roots,
|
|
||||||
heads,
|
|
||||||
generation,
|
|
||||||
} => {
|
|
||||||
let root_set = internal_evaluate(repo, roots)?;
|
|
||||||
let root_ids = root_set.iter().map(|entry| entry.commit_id()).collect_vec();
|
|
||||||
let head_set = internal_evaluate(repo, heads)?;
|
|
||||||
let head_ids = head_set.iter().map(|entry| entry.commit_id()).collect_vec();
|
|
||||||
let walk = repo.index().walk_revs(&head_ids, &root_ids);
|
|
||||||
if generation == &GENERATION_RANGE_FULL {
|
|
||||||
Ok(Box::new(RevWalkRevset { walk }))
|
|
||||||
} else {
|
|
||||||
let walk = walk.filter_by_generation(generation.clone());
|
|
||||||
Ok(Box::new(RevWalkRevset { walk }))
|
|
||||||
}
|
}
|
||||||
}
|
RevsetExpression::None => Ok(Box::new(EagerRevset::empty())),
|
||||||
RevsetExpression::DagRange { roots, heads } => {
|
RevsetExpression::All => {
|
||||||
let root_set = internal_evaluate(repo, roots)?;
|
// Since `all()` does not include hidden commits, some of the logical
|
||||||
let candidate_set = internal_evaluate(repo, &heads.ancestors())?;
|
// transformation rules may subtly change the evaluated set. For example,
|
||||||
let mut reachable: HashSet<_> = root_set.iter().map(|entry| entry.position()).collect();
|
// `all() & x` is not `x` if `x` is hidden. This wouldn't matter in practice,
|
||||||
let mut result = vec![];
|
// but if it does, the heads set could be extended to include the commits
|
||||||
let candidates = candidate_set.iter().collect_vec();
|
// (and `remote_branches()`) specified in the revset expression. Alternatively,
|
||||||
for candidate in candidates.into_iter().rev() {
|
// some optimization rules could be removed, but that means `author(_) & x`
|
||||||
if reachable.contains(&candidate.position())
|
// would have to test `:heads() & x`.
|
||||||
|| candidate
|
self.evaluate(&RevsetExpression::visible_heads().ancestors())
|
||||||
|
}
|
||||||
|
RevsetExpression::Commits(commit_ids) => Ok(self.revset_for_commit_ids(commit_ids)),
|
||||||
|
RevsetExpression::Children(roots) => {
|
||||||
|
let root_set = self.evaluate(roots)?;
|
||||||
|
let candidates_expression = roots.descendants();
|
||||||
|
let candidate_set = self.evaluate(&candidates_expression)?;
|
||||||
|
Ok(Box::new(ChildrenRevset {
|
||||||
|
root_set,
|
||||||
|
candidate_set,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
RevsetExpression::Ancestors { heads, generation } => {
|
||||||
|
let range_expression = RevsetExpression::Range {
|
||||||
|
roots: RevsetExpression::none(),
|
||||||
|
heads: heads.clone(),
|
||||||
|
generation: generation.clone(),
|
||||||
|
};
|
||||||
|
self.evaluate(&range_expression)
|
||||||
|
}
|
||||||
|
RevsetExpression::Range {
|
||||||
|
roots,
|
||||||
|
heads,
|
||||||
|
generation,
|
||||||
|
} => {
|
||||||
|
let root_set = self.evaluate(roots)?;
|
||||||
|
let root_ids = root_set.iter().map(|entry| entry.commit_id()).collect_vec();
|
||||||
|
let head_set = self.evaluate(heads)?;
|
||||||
|
let head_ids = head_set.iter().map(|entry| entry.commit_id()).collect_vec();
|
||||||
|
let walk = self.repo.index().walk_revs(&head_ids, &root_ids);
|
||||||
|
if generation == &GENERATION_RANGE_FULL {
|
||||||
|
Ok(Box::new(RevWalkRevset { walk }))
|
||||||
|
} else {
|
||||||
|
let walk = walk.filter_by_generation(generation.clone());
|
||||||
|
Ok(Box::new(RevWalkRevset { walk }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RevsetExpression::DagRange { roots, heads } => {
|
||||||
|
let root_set = self.evaluate(roots)?;
|
||||||
|
let candidate_set = self.evaluate(&heads.ancestors())?;
|
||||||
|
let mut reachable: HashSet<_> =
|
||||||
|
root_set.iter().map(|entry| entry.position()).collect();
|
||||||
|
let mut result = vec![];
|
||||||
|
let candidates = candidate_set.iter().collect_vec();
|
||||||
|
for candidate in candidates.into_iter().rev() {
|
||||||
|
if reachable.contains(&candidate.position())
|
||||||
|
|| candidate
|
||||||
|
.parent_positions()
|
||||||
|
.iter()
|
||||||
|
.any(|parent_pos| reachable.contains(parent_pos))
|
||||||
|
{
|
||||||
|
reachable.insert(candidate.position());
|
||||||
|
result.push(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.reverse();
|
||||||
|
Ok(Box::new(EagerRevset {
|
||||||
|
index_entries: result,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
RevsetExpression::VisibleHeads => {
|
||||||
|
Ok(self
|
||||||
|
.revset_for_commit_ids(&self.repo.view().heads().iter().cloned().collect_vec()))
|
||||||
|
}
|
||||||
|
RevsetExpression::Heads(candidates) => {
|
||||||
|
let candidate_set = self.evaluate(candidates)?;
|
||||||
|
let candidate_ids = candidate_set
|
||||||
|
.iter()
|
||||||
|
.map(|entry| entry.commit_id())
|
||||||
|
.collect_vec();
|
||||||
|
Ok(self.revset_for_commit_ids(&self.repo.index().heads(&mut candidate_ids.iter())))
|
||||||
|
}
|
||||||
|
RevsetExpression::Roots(candidates) => {
|
||||||
|
let connected_set = self.evaluate(&candidates.connected())?;
|
||||||
|
let filled: HashSet<_> =
|
||||||
|
connected_set.iter().map(|entry| entry.position()).collect();
|
||||||
|
let mut index_entries = vec![];
|
||||||
|
let candidate_set = self.evaluate(candidates)?;
|
||||||
|
for candidate in candidate_set.iter() {
|
||||||
|
if !candidate
|
||||||
.parent_positions()
|
.parent_positions()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|parent_pos| reachable.contains(parent_pos))
|
.any(|parent| filled.contains(parent))
|
||||||
{
|
{
|
||||||
reachable.insert(candidate.position());
|
index_entries.push(candidate);
|
||||||
result.push(candidate);
|
}
|
||||||
|
}
|
||||||
|
Ok(Box::new(EagerRevset { index_entries }))
|
||||||
|
}
|
||||||
|
RevsetExpression::Latest { candidates, count } => {
|
||||||
|
let candidate_set = self.evaluate(candidates)?;
|
||||||
|
Ok(self.take_latest_revset(candidate_set.as_ref(), *count))
|
||||||
|
}
|
||||||
|
RevsetExpression::Filter(predicate) => Ok(Box::new(FilterRevset {
|
||||||
|
candidates: self.evaluate(&RevsetExpression::All)?,
|
||||||
|
predicate: build_predicate_fn(self.repo, predicate),
|
||||||
|
})),
|
||||||
|
RevsetExpression::AsFilter(candidates) => self.evaluate(candidates),
|
||||||
|
RevsetExpression::Present(candidates) => match self.evaluate(candidates) {
|
||||||
|
Ok(set) => Ok(set),
|
||||||
|
Err(RevsetError::NoSuchRevision(_)) => Ok(Box::new(EagerRevset::empty())),
|
||||||
|
r @ Err(RevsetError::AmbiguousIdPrefix(_) | RevsetError::StoreError(_)) => r,
|
||||||
|
},
|
||||||
|
RevsetExpression::NotIn(complement) => {
|
||||||
|
let set1 = self.evaluate(&RevsetExpression::All)?;
|
||||||
|
let set2 = self.evaluate(complement)?;
|
||||||
|
Ok(Box::new(DifferenceRevset { set1, set2 }))
|
||||||
|
}
|
||||||
|
RevsetExpression::Union(expression1, expression2) => {
|
||||||
|
let set1 = self.evaluate(expression1)?;
|
||||||
|
let set2 = self.evaluate(expression2)?;
|
||||||
|
Ok(Box::new(UnionRevset { set1, set2 }))
|
||||||
|
}
|
||||||
|
RevsetExpression::Intersection(expression1, expression2) => {
|
||||||
|
match expression2.as_ref() {
|
||||||
|
RevsetExpression::Filter(predicate) => Ok(Box::new(FilterRevset {
|
||||||
|
candidates: self.evaluate(expression1)?,
|
||||||
|
predicate: build_predicate_fn(self.repo, predicate),
|
||||||
|
})),
|
||||||
|
RevsetExpression::AsFilter(expression2) => Ok(Box::new(FilterRevset {
|
||||||
|
candidates: self.evaluate(expression1)?,
|
||||||
|
predicate: self.evaluate(expression2)?,
|
||||||
|
})),
|
||||||
|
_ => {
|
||||||
|
// TODO: 'set2' can be turned into a predicate, and use FilterRevset
|
||||||
|
// if a predicate function can terminate the 'set1' iterator early.
|
||||||
|
let set1 = self.evaluate(expression1)?;
|
||||||
|
let set2 = self.evaluate(expression2)?;
|
||||||
|
Ok(Box::new(IntersectionRevset { set1, set2 }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.reverse();
|
RevsetExpression::Difference(expression1, expression2) => {
|
||||||
Ok(Box::new(EagerRevset {
|
let set1 = self.evaluate(expression1)?;
|
||||||
index_entries: result,
|
let set2 = self.evaluate(expression2)?;
|
||||||
}))
|
Ok(Box::new(DifferenceRevset { set1, set2 }))
|
||||||
}
|
|
||||||
RevsetExpression::VisibleHeads => Ok(revset_for_commit_ids(
|
|
||||||
repo,
|
|
||||||
&repo.view().heads().iter().cloned().collect_vec(),
|
|
||||||
)),
|
|
||||||
RevsetExpression::Heads(candidates) => {
|
|
||||||
let candidate_set = internal_evaluate(repo, candidates)?;
|
|
||||||
let candidate_ids = candidate_set
|
|
||||||
.iter()
|
|
||||||
.map(|entry| entry.commit_id())
|
|
||||||
.collect_vec();
|
|
||||||
Ok(revset_for_commit_ids(
|
|
||||||
repo,
|
|
||||||
&repo.index().heads(&mut candidate_ids.iter()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
RevsetExpression::Roots(candidates) => {
|
|
||||||
let connected_set = internal_evaluate(repo, &candidates.connected())?;
|
|
||||||
let filled: HashSet<_> = connected_set.iter().map(|entry| entry.position()).collect();
|
|
||||||
let mut index_entries = vec![];
|
|
||||||
let candidate_set = internal_evaluate(repo, candidates)?;
|
|
||||||
for candidate in candidate_set.iter() {
|
|
||||||
if !candidate
|
|
||||||
.parent_positions()
|
|
||||||
.iter()
|
|
||||||
.any(|parent| filled.contains(parent))
|
|
||||||
{
|
|
||||||
index_entries.push(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Box::new(EagerRevset { index_entries }))
|
|
||||||
}
|
|
||||||
RevsetExpression::Latest { candidates, count } => {
|
|
||||||
let candidate_set = internal_evaluate(repo, candidates)?;
|
|
||||||
Ok(take_latest_revset(repo, candidate_set.as_ref(), *count))
|
|
||||||
}
|
|
||||||
RevsetExpression::Filter(predicate) => Ok(Box::new(FilterRevset {
|
|
||||||
candidates: internal_evaluate(repo, &RevsetExpression::All)?,
|
|
||||||
predicate: build_predicate_fn(repo, predicate),
|
|
||||||
})),
|
|
||||||
RevsetExpression::AsFilter(candidates) => internal_evaluate(repo, candidates),
|
|
||||||
RevsetExpression::Present(candidates) => match internal_evaluate(repo, candidates) {
|
|
||||||
Ok(set) => Ok(set),
|
|
||||||
Err(RevsetError::NoSuchRevision(_)) => Ok(Box::new(EagerRevset::empty())),
|
|
||||||
r @ Err(RevsetError::AmbiguousIdPrefix(_) | RevsetError::StoreError(_)) => r,
|
|
||||||
},
|
|
||||||
RevsetExpression::NotIn(complement) => {
|
|
||||||
let set1 = internal_evaluate(repo, &RevsetExpression::All)?;
|
|
||||||
let set2 = internal_evaluate(repo, complement)?;
|
|
||||||
Ok(Box::new(DifferenceRevset { set1, set2 }))
|
|
||||||
}
|
|
||||||
RevsetExpression::Union(expression1, expression2) => {
|
|
||||||
let set1 = internal_evaluate(repo, expression1)?;
|
|
||||||
let set2 = internal_evaluate(repo, expression2)?;
|
|
||||||
Ok(Box::new(UnionRevset { set1, set2 }))
|
|
||||||
}
|
|
||||||
RevsetExpression::Intersection(expression1, expression2) => {
|
|
||||||
match expression2.as_ref() {
|
|
||||||
RevsetExpression::Filter(predicate) => Ok(Box::new(FilterRevset {
|
|
||||||
candidates: internal_evaluate(repo, expression1)?,
|
|
||||||
predicate: build_predicate_fn(repo, predicate),
|
|
||||||
})),
|
|
||||||
RevsetExpression::AsFilter(expression2) => Ok(Box::new(FilterRevset {
|
|
||||||
candidates: internal_evaluate(repo, expression1)?,
|
|
||||||
predicate: internal_evaluate(repo, expression2)?,
|
|
||||||
})),
|
|
||||||
_ => {
|
|
||||||
// TODO: 'set2' can be turned into a predicate, and use FilterRevset
|
|
||||||
// if a predicate function can terminate the 'set1' iterator early.
|
|
||||||
let set1 = internal_evaluate(repo, expression1)?;
|
|
||||||
let set2 = internal_evaluate(repo, expression2)?;
|
|
||||||
Ok(Box::new(IntersectionRevset { set1, set2 }))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RevsetExpression::Difference(expression1, expression2) => {
|
}
|
||||||
let set1 = internal_evaluate(repo, expression1)?;
|
|
||||||
let set2 = internal_evaluate(repo, expression2)?;
|
fn revset_for_commit_ids(
|
||||||
Ok(Box::new(DifferenceRevset { set1, set2 }))
|
&self,
|
||||||
|
commit_ids: &[CommitId],
|
||||||
|
) -> Box<dyn InternalRevset<'index> + 'index> {
|
||||||
|
let index = self.repo.index();
|
||||||
|
let mut index_entries = vec![];
|
||||||
|
for id in commit_ids {
|
||||||
|
index_entries.push(index.entry_by_id(id).unwrap());
|
||||||
}
|
}
|
||||||
}
|
index_entries.sort_unstable_by_key(|b| Reverse(b.position()));
|
||||||
}
|
index_entries.dedup();
|
||||||
|
Box::new(EagerRevset { index_entries })
|
||||||
fn revset_for_commit_ids<'index>(
|
|
||||||
repo: &'index dyn Repo,
|
|
||||||
commit_ids: &[CommitId],
|
|
||||||
) -> Box<dyn InternalRevset<'index> + 'index> {
|
|
||||||
let index = repo.index();
|
|
||||||
let mut index_entries = vec![];
|
|
||||||
for id in commit_ids {
|
|
||||||
index_entries.push(index.entry_by_id(id).unwrap());
|
|
||||||
}
|
|
||||||
index_entries.sort_unstable_by_key(|b| Reverse(b.position()));
|
|
||||||
index_entries.dedup();
|
|
||||||
Box::new(EagerRevset { index_entries })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take_latest_revset<'index>(
|
|
||||||
repo: &dyn Repo,
|
|
||||||
candidate_set: &dyn InternalRevset<'index>,
|
|
||||||
count: usize,
|
|
||||||
) -> Box<dyn InternalRevset<'index> + 'index> {
|
|
||||||
if count == 0 {
|
|
||||||
return Box::new(EagerRevset::empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
|
fn take_latest_revset(
|
||||||
struct Item<'a> {
|
&self,
|
||||||
timestamp: MillisSinceEpoch,
|
candidate_set: &dyn InternalRevset<'index>,
|
||||||
entry: IndexEntryByPosition<'a>, // tie-breaker
|
count: usize,
|
||||||
}
|
) -> Box<dyn InternalRevset<'index> + 'index> {
|
||||||
|
if count == 0 {
|
||||||
let store = repo.store();
|
return Box::new(EagerRevset::empty());
|
||||||
let make_rev_item = |entry: IndexEntry<'index>| {
|
|
||||||
let commit = store.get_commit(&entry.commit_id()).unwrap();
|
|
||||||
Reverse(Item {
|
|
||||||
timestamp: commit.committer().timestamp.timestamp.clone(),
|
|
||||||
entry: IndexEntryByPosition(entry),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Maintain min-heap containing the latest (greatest) count items. For small
|
|
||||||
// count and large candidate set, this is probably cheaper than building vec
|
|
||||||
// and applying selection algorithm.
|
|
||||||
let mut candidate_iter = candidate_set.iter().map(make_rev_item).fuse();
|
|
||||||
let mut latest_items = BinaryHeap::from_iter(candidate_iter.by_ref().take(count));
|
|
||||||
for item in candidate_iter {
|
|
||||||
let mut earliest = latest_items.peek_mut().unwrap();
|
|
||||||
if earliest.0 < item.0 {
|
|
||||||
*earliest = item;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
assert!(latest_items.len() <= count);
|
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
let mut index_entries = latest_items
|
struct Item<'a> {
|
||||||
.into_iter()
|
timestamp: MillisSinceEpoch,
|
||||||
.map(|item| item.0.entry.0)
|
entry: IndexEntryByPosition<'a>, // tie-breaker
|
||||||
.collect_vec();
|
}
|
||||||
index_entries.sort_unstable_by_key(|b| Reverse(b.position()));
|
|
||||||
Box::new(EagerRevset { index_entries })
|
let store = self.repo.store();
|
||||||
|
let make_rev_item = |entry: IndexEntry<'index>| {
|
||||||
|
let commit = store.get_commit(&entry.commit_id()).unwrap();
|
||||||
|
Reverse(Item {
|
||||||
|
timestamp: commit.committer().timestamp.timestamp.clone(),
|
||||||
|
entry: IndexEntryByPosition(entry),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maintain min-heap containing the latest (greatest) count items. For small
|
||||||
|
// count and large candidate set, this is probably cheaper than building vec
|
||||||
|
// and applying selection algorithm.
|
||||||
|
let mut candidate_iter = candidate_set.iter().map(make_rev_item).fuse();
|
||||||
|
let mut latest_items = BinaryHeap::from_iter(candidate_iter.by_ref().take(count));
|
||||||
|
for item in candidate_iter {
|
||||||
|
let mut earliest = latest_items.peek_mut().unwrap();
|
||||||
|
if earliest.0 < item.0 {
|
||||||
|
*earliest = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(latest_items.len() <= count);
|
||||||
|
let mut index_entries = latest_items
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| item.0.entry.0)
|
||||||
|
.collect_vec();
|
||||||
|
index_entries.sort_unstable_by_key(|b| Reverse(b.position()));
|
||||||
|
Box::new(EagerRevset { index_entries })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurePredicateFn<'index> = Box<dyn Fn(&IndexEntry<'index>) -> bool + 'index>;
|
type PurePredicateFn<'index> = Box<dyn Fn(&IndexEntry<'index>) -> bool + 'index>;
|
||||||
|
|
Loading…
Reference in a new issue