forked from mirrors/jj
revsets: add revset yielding merge commits
This commit is contained in:
parent
aef27d5701
commit
7065cecfdc
2 changed files with 100 additions and 1 deletions
|
@ -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();
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in a new issue