mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-27 06:27:43 +00:00
revsets: add intersection operator
This commit is contained in:
parent
332580918c
commit
3a65c1d2ab
3 changed files with 109 additions and 1 deletions
|
@ -21,8 +21,9 @@ ancestors = { "*:" }
|
||||||
prefix_operator = _{ parents | ancestors }
|
prefix_operator = _{ parents | ancestors }
|
||||||
|
|
||||||
union = { "|" }
|
union = { "|" }
|
||||||
|
intersection = { "&" }
|
||||||
difference = { "-" }
|
difference = { "-" }
|
||||||
infix_operator = _{ union| difference }
|
infix_operator = _{ union| intersection | difference }
|
||||||
|
|
||||||
function_name = @{ (ASCII_ALPHANUMERIC | "_")+ }
|
function_name = @{ (ASCII_ALPHANUMERIC | "_")+ }
|
||||||
// The grammar accepts a string literal or an expression for function
|
// The grammar accepts a string literal or an expression for function
|
||||||
|
|
|
@ -108,6 +108,7 @@ pub enum RevsetExpression {
|
||||||
base_expression: Box<RevsetExpression>,
|
base_expression: Box<RevsetExpression>,
|
||||||
},
|
},
|
||||||
Union(Box<RevsetExpression>, Box<RevsetExpression>),
|
Union(Box<RevsetExpression>, Box<RevsetExpression>),
|
||||||
|
Intersection(Box<RevsetExpression>, Box<RevsetExpression>),
|
||||||
Difference(Box<RevsetExpression>, Box<RevsetExpression>),
|
Difference(Box<RevsetExpression>, Box<RevsetExpression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +136,10 @@ fn parse_infix_expression_rule(
|
||||||
Rule::union => {
|
Rule::union => {
|
||||||
expression1 = RevsetExpression::Union(Box::new(expression1), Box::new(expression2))
|
expression1 = RevsetExpression::Union(Box::new(expression1), Box::new(expression2))
|
||||||
}
|
}
|
||||||
|
Rule::intersection => {
|
||||||
|
expression1 =
|
||||||
|
RevsetExpression::Intersection(Box::new(expression1), Box::new(expression2))
|
||||||
|
}
|
||||||
Rule::difference => {
|
Rule::difference => {
|
||||||
expression1 =
|
expression1 =
|
||||||
RevsetExpression::Difference(Box::new(expression1), Box::new(expression2))
|
RevsetExpression::Difference(Box::new(expression1), Box::new(expression2))
|
||||||
|
@ -440,6 +445,54 @@ impl<'revset, 'repo> Iterator for UnionRevsetIterator<'revset, 'repo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct IntersectionRevset<'revset, 'repo: 'revset> {
|
||||||
|
set1: Box<dyn Revset<'repo> + 'revset>,
|
||||||
|
set2: Box<dyn Revset<'repo> + 'revset>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'repo> Revset<'repo> for IntersectionRevset<'_, 'repo> {
|
||||||
|
fn iter<'revset>(&'revset self) -> Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset> {
|
||||||
|
Box::new(IntersectionRevsetIterator {
|
||||||
|
iter1: self.set1.iter().peekable(),
|
||||||
|
iter2: self.set2.iter().peekable(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IntersectionRevsetIterator<'revset, 'repo> {
|
||||||
|
iter1: Peekable<Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>>,
|
||||||
|
iter2: Peekable<Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'revset, 'repo> Iterator for IntersectionRevsetIterator<'revset, 'repo> {
|
||||||
|
type Item = IndexEntry<'repo>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
match (self.iter1.peek(), self.iter2.peek()) {
|
||||||
|
(None, _) => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
(_, None) => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
(Some(entry1), Some(entry2)) => match entry1.position().cmp(&entry2.position()) {
|
||||||
|
Ordering::Less => {
|
||||||
|
self.iter2.next();
|
||||||
|
}
|
||||||
|
Ordering::Equal => {
|
||||||
|
self.iter1.next();
|
||||||
|
return self.iter2.next();
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
self.iter1.next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct DifferenceRevset<'revset, 'repo: 'revset> {
|
struct DifferenceRevset<'revset, 'repo: 'revset> {
|
||||||
// The minuend (what to subtract from)
|
// The minuend (what to subtract from)
|
||||||
set1: Box<dyn Revset<'repo> + 'revset>,
|
set1: Box<dyn Revset<'repo> + 'revset>,
|
||||||
|
@ -559,6 +612,11 @@ pub fn evaluate_expression<'revset, 'repo: 'revset>(
|
||||||
let set2 = evaluate_expression(repo, expression2.as_ref())?;
|
let set2 = evaluate_expression(repo, expression2.as_ref())?;
|
||||||
Ok(Box::new(UnionRevset { set1, set2 }))
|
Ok(Box::new(UnionRevset { set1, set2 }))
|
||||||
}
|
}
|
||||||
|
RevsetExpression::Intersection(expression1, expression2) => {
|
||||||
|
let set1 = evaluate_expression(repo, expression1.as_ref())?;
|
||||||
|
let set2 = evaluate_expression(repo, expression2.as_ref())?;
|
||||||
|
Ok(Box::new(IntersectionRevset { set1, set2 }))
|
||||||
|
}
|
||||||
RevsetExpression::Difference(expression1, expression2) => {
|
RevsetExpression::Difference(expression1, expression2) => {
|
||||||
let set1 = evaluate_expression(repo, expression1.as_ref())?;
|
let set1 = evaluate_expression(repo, expression1.as_ref())?;
|
||||||
let set2 = evaluate_expression(repo, expression2.as_ref())?;
|
let set2 = evaluate_expression(repo, expression2.as_ref())?;
|
||||||
|
|
|
@ -695,6 +695,55 @@ fn test_evaluate_expression_union(use_git: bool) {
|
||||||
tx.discard();
|
tx.discard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(false ; "local store")]
|
||||||
|
#[test_case(true ; "git store")]
|
||||||
|
fn test_evaluate_expression_intersection(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 root_commit = repo.store().root_commit();
|
||||||
|
let commit1 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo);
|
||||||
|
let commit2 = testutils::create_random_commit(&settings, &repo)
|
||||||
|
.set_parents(vec![commit1.id().clone()])
|
||||||
|
.write_to_repo(mut_repo);
|
||||||
|
let commit3 = testutils::create_random_commit(&settings, &repo)
|
||||||
|
.set_parents(vec![commit2.id().clone()])
|
||||||
|
.write_to_repo(mut_repo);
|
||||||
|
let commit4 = testutils::create_random_commit(&settings, &repo)
|
||||||
|
.set_parents(vec![commit3.id().clone()])
|
||||||
|
.write_to_repo(mut_repo);
|
||||||
|
let commit5 = testutils::create_random_commit(&settings, &repo)
|
||||||
|
.set_parents(vec![commit2.id().clone()])
|
||||||
|
.write_to_repo(mut_repo);
|
||||||
|
|
||||||
|
// Intersection between ancestors
|
||||||
|
assert_eq!(
|
||||||
|
resolve_commit_ids(
|
||||||
|
mut_repo.as_repo_ref(),
|
||||||
|
&format!("*:{} & *:{}", commit4.id().hex(), commit5.id().hex())
|
||||||
|
),
|
||||||
|
vec![
|
||||||
|
commit2.id().clone(),
|
||||||
|
commit1.id().clone(),
|
||||||
|
root_commit.id().clone()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Intersection of disjoint sets
|
||||||
|
assert_eq!(
|
||||||
|
resolve_commit_ids(
|
||||||
|
mut_repo.as_repo_ref(),
|
||||||
|
&format!("{} & {}", commit4.id().hex(), commit2.id().hex())
|
||||||
|
),
|
||||||
|
vec![]
|
||||||
|
);
|
||||||
|
|
||||||
|
tx.discard();
|
||||||
|
}
|
||||||
|
|
||||||
#[test_case(false ; "local store")]
|
#[test_case(false ; "local store")]
|
||||||
#[test_case(true ; "git store")]
|
#[test_case(true ; "git store")]
|
||||||
fn test_evaluate_expression_difference(use_git: bool) {
|
fn test_evaluate_expression_difference(use_git: bool) {
|
||||||
|
|
Loading…
Reference in a new issue