revsets: add revset yielding merge commits

This commit is contained in:
Martin von Zweigbergk 2021-05-01 14:29:43 -07:00
parent aef27d5701
commit 7065cecfdc
2 changed files with 100 additions and 1 deletions

View File

@ -15,6 +15,7 @@
use std::cmp::{Ordering, Reverse};
use std::collections::HashSet;
use std::iter::Peekable;
use std::ops::Range;
use std::rc::Rc;
use pest::iterators::Pairs;
@ -122,6 +123,10 @@ pub enum RevsetExpression {
PublicHeads,
GitRefs,
NonObsoleteHeads(Rc<RevsetExpression>),
ParentCount {
candidates: Rc<RevsetExpression>,
parent_count_range: Range<u32>,
},
Description {
needle: String,
candidates: Rc<RevsetExpression>,
@ -422,6 +427,25 @@ fn parse_function_expression(
})
}
}
"merges" => {
if arg_count > 1 {
return Err(RevsetParseError::InvalidFunctionArguments {
name,
message: "Expected 0 or 1 arguments".to_string(),
});
}
let candidates = if arg_count == 0 {
RevsetExpression::non_obsolete_commits()
} else {
Rc::new(parse_expression_rule(
argument_pairs.next().unwrap().into_inner(),
)?)
};
Ok(RevsetExpression::ParentCount {
candidates,
parent_count_range: 2..u32::MAX,
})
}
"description" => {
if !(1..=2).contains(&arg_count) {
return Err(RevsetParseError::InvalidFunctionArguments {
@ -586,6 +610,39 @@ impl<'repo> Iterator for ChildrenRevsetIterator<'_, 'repo> {
}
}
// TODO: Generalize this to be take a predicate to apply to an &IndexEntry.
struct ParentCountRevset<'revset, 'repo: 'revset> {
candidates: Box<dyn Revset<'repo> + 'revset>,
parent_count_range: Range<u32>,
}
impl<'repo> Revset<'repo> for ParentCountRevset<'_, 'repo> {
fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
RevsetIterator::new(Box::new(ParentCountRevsetIterator {
iter: self.candidates.iter(),
parent_count_range: self.parent_count_range.clone(),
}))
}
}
struct ParentCountRevsetIterator<'revset, 'repo> {
iter: RevsetIterator<'revset, 'repo>,
parent_count_range: Range<u32>,
}
impl<'revset, 'repo> Iterator for ParentCountRevsetIterator<'revset, 'repo> {
type Item = IndexEntry<'repo>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(next) = self.iter.next() {
if self.parent_count_range.contains(&next.num_parents()) {
return Some(next);
}
}
None
}
}
struct UnionRevset<'revset, 'repo: 'revset> {
set1: Box<dyn Revset<'repo> + 'revset>,
set2: Box<dyn Revset<'repo> + 'revset>,
@ -808,6 +865,16 @@ pub fn evaluate_expression<'revset, 'repo: 'revset>(
let base_set = evaluate_expression(repo, base_expression.as_ref())?;
Ok(non_obsolete_heads(repo, base_set))
}
RevsetExpression::ParentCount {
candidates,
parent_count_range,
} => {
let candidates = evaluate_expression(repo, candidates.as_ref())?;
Ok(Box::new(ParentCountRevset {
candidates,
parent_count_range: parent_count_range.clone(),
}))
}
RevsetExpression::PublicHeads => {
let index = repo.index();
let heads = repo.view().public_heads();

View File

@ -769,6 +769,38 @@ fn test_evaluate_expression_obsolete(use_git: bool) {
tx.discard();
}
#[test_case(false ; "local store")]
#[test_case(true ; "git store")]
fn test_evaluate_expression_merges(use_git: bool) {
let settings = testutils::user_settings();
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
let mut tx = repo.start_transaction("test");
let mut_repo = tx.mut_repo();
let mut graph_builder = CommitGraphBuilder::new(&settings, mut_repo);
let commit1 = graph_builder.initial_commit();
let commit2 = graph_builder.initial_commit();
let commit3 = graph_builder.initial_commit();
let commit4 = graph_builder.commit_with_parents(&[&commit1, &commit2]);
let commit5 = graph_builder.commit_with_parents(&[&commit1, &commit2, &commit3]);
// Finds all merges by default
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "merges()"),
vec![commit5.id().clone(), commit4.id().clone(),]
);
// Searches only among candidates if specified
assert_eq!(
resolve_commit_ids(
mut_repo.as_repo_ref(),
&format!("merges(,,{})", commit5.id().hex())
),
vec![commit5.id().clone()]
);
tx.discard();
}
#[test_case(false ; "local store")]
#[test_case(true ; "git store")]
fn test_evaluate_expression_description(use_git: bool) {
@ -804,7 +836,7 @@ fn test_evaluate_expression_description(use_git: bool) {
resolve_commit_ids(mut_repo.as_repo_ref(), "description(\"commit 2\")"),
vec![commit2.id().clone()]
);
// Searches only in given base set if specified
// Searches only among candidates if specified
assert_eq!(
resolve_commit_ids(
mut_repo.as_repo_ref(),