diff --git a/lib/src/default_revset_engine.rs b/lib/src/default_revset_engine.rs index dcaf48ba5..bfa9ba4fd 100644 --- a/lib/src/default_revset_engine.rs +++ b/lib/src/default_revset_engine.rs @@ -618,18 +618,28 @@ impl<'index> EvaluationContext<'index> { heads, generation_from_roots, } => { - let mut root_set = self.evaluate(roots)?; + let root_set = self.evaluate(roots)?; let head_set = self.evaluate(heads)?; - // TODO: implement generic evaluation path for generation filter - for _ in 0..generation_from_roots.start { - root_set = Box::new(self.walk_children(&*root_set, &*head_set)); - } - if generation_from_roots.end == generation_from_roots.start + 1 { - Ok(root_set) - } else { - assert_eq!(generation_from_roots.end, u64::MAX); // TODO + if generation_from_roots == &(1..2) { + Ok(Box::new(self.walk_children(&*root_set, &*head_set))) + } else if generation_from_roots == &GENERATION_RANGE_FULL { let (dag_range_set, _) = self.collect_dag_range(&*root_set, &*head_set); Ok(Box::new(dag_range_set)) + } else { + // For small generation range, it might be better to build a reachable map + // with generation bit set, which can be calculated incrementally from roots: + // reachable[pos] = (reachable[parent_pos] | ...) << 1 + let root_positions = + root_set.iter().map(|entry| entry.position()).collect_vec(); + let walk = self + .walk_ancestors(&*head_set) + .descendants_filtered_by_generation( + &root_positions, + to_u32_generation_range(generation_from_roots)?, + ); + let mut index_entries = walk.collect_vec(); + index_entries.reverse(); + Ok(Box::new(EagerRevset { index_entries })) } } ResolvedExpression::Heads(candidates) => { diff --git a/testing/bench-revsets-git.txt b/testing/bench-revsets-git.txt index 72013f2e3..cfab1baf1 100644 --- a/testing/bench-revsets-git.txt +++ b/testing/bench-revsets-git.txt @@ -13,7 +13,6 @@ v2.40.0 v2.39.0..v2.40.0 :v2.40.0 ~ :v2.39.0 v2.39.0:v2.40.0 -v2.40.0+ # Tags and branches tags() branches() @@ -42,16 +41,26 @@ heads(:v2.40.0) v1.0.0- v1.0.0--- :v1.0.0--- +# Children and descendants of old commit +v1.0.0+ +v1.0.0+++ +v1.0.0+++: # Parents and ancestors of recent commit v2.40.0- v2.40.0--- :v2.40.0--- +# Children and descendants of recent commit +v2.40.0+ +v2.40.0+++ +v2.40.0+++: # Parents and ancestors of small subset tags()- tags()--- :tags()--- -# Children of small subset +# Children and descendants of small subset tags()+ +tags()+++ +tags()+++: # Filter that doesn't read commit object merges() ~merges()