forked from mirrors/jj
revsets: add "ancestor difference" range operator (like git's ..
)
This commit is contained in:
parent
49173de423
commit
9dc18524fc
3 changed files with 103 additions and 4 deletions
|
@ -28,6 +28,7 @@ children_op = { ":" }
|
|||
ancestors_op = { ",," }
|
||||
descendants_op = { ",," }
|
||||
dag_range_op = { ",," }
|
||||
range_op = { ",,," }
|
||||
|
||||
union_op = { "|" }
|
||||
intersection_op = { "&" }
|
||||
|
@ -52,6 +53,7 @@ children_expression = { parents_expression ~ children_op* }
|
|||
|
||||
range_expression = {
|
||||
children_expression ~ dag_range_op ~ children_expression
|
||||
| children_expression ~ range_op ~ children_expression
|
||||
| ancestors_op ~ children_expression
|
||||
| children_expression ~ descendants_op
|
||||
| children_expression
|
||||
|
|
|
@ -99,6 +99,7 @@ pub enum RevsetParseError {
|
|||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum RevsetExpression {
|
||||
None,
|
||||
Symbol(String),
|
||||
Parents(Rc<RevsetExpression>),
|
||||
Children {
|
||||
|
@ -106,6 +107,12 @@ pub enum RevsetExpression {
|
|||
heads: Rc<RevsetExpression>,
|
||||
},
|
||||
Ancestors(Rc<RevsetExpression>),
|
||||
// Commits that are ancestors of "heads" but not ancestors of "roots"
|
||||
Range {
|
||||
roots: Rc<RevsetExpression>,
|
||||
heads: Rc<RevsetExpression>,
|
||||
},
|
||||
// Commits that are descendants of "roots" and ancestors of "heads"
|
||||
DagRange {
|
||||
roots: Rc<RevsetExpression>,
|
||||
heads: Rc<RevsetExpression>,
|
||||
|
@ -213,6 +220,14 @@ fn parse_range_expression_rule(
|
|||
heads: Rc::new(heads_expression),
|
||||
};
|
||||
}
|
||||
Rule::range_op => {
|
||||
let expression2 =
|
||||
parse_children_expression_rule(pairs.next().unwrap().into_inner())?;
|
||||
expression = RevsetExpression::Range {
|
||||
roots: Rc::new(expression),
|
||||
heads: Rc::new(expression2),
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
panic!("unxpected revset range operator rule {:?}", next.as_rule());
|
||||
}
|
||||
|
@ -678,6 +693,9 @@ pub fn evaluate_expression<'revset, 'repo: 'revset>(
|
|||
expression: &RevsetExpression,
|
||||
) -> Result<Box<dyn Revset<'repo> + 'revset>, RevsetError> {
|
||||
match expression {
|
||||
RevsetExpression::None => Ok(Box::new(EagerRevset {
|
||||
index_entries: vec![],
|
||||
})),
|
||||
RevsetExpression::Symbol(symbol) => {
|
||||
let commit_id = resolve_symbol(repo, &symbol)?.id().clone();
|
||||
Ok(Box::new(EagerRevset {
|
||||
|
@ -704,10 +722,19 @@ pub fn evaluate_expression<'revset, 'repo: 'revset>(
|
|||
candidate_set,
|
||||
}))
|
||||
}
|
||||
RevsetExpression::Ancestors(base_expression) => {
|
||||
let base_set = evaluate_expression(repo, base_expression.as_ref())?;
|
||||
let base_ids: Vec<_> = base_set.iter().map(|entry| entry.commit_id()).collect();
|
||||
let walk = repo.index().walk_revs(&base_ids, &[]);
|
||||
RevsetExpression::Ancestors(base_expression) => evaluate_expression(
|
||||
repo,
|
||||
&RevsetExpression::Range {
|
||||
roots: Rc::new(RevsetExpression::None),
|
||||
heads: base_expression.clone(),
|
||||
},
|
||||
),
|
||||
RevsetExpression::Range { roots, heads } => {
|
||||
let root_set = evaluate_expression(repo, roots.as_ref())?;
|
||||
let root_ids: Vec<_> = root_set.iter().map(|entry| entry.commit_id()).collect();
|
||||
let head_set = evaluate_expression(repo, heads.as_ref())?;
|
||||
let head_ids: Vec<_> = head_set.iter().map(|entry| entry.commit_id()).collect();
|
||||
let walk = repo.index().walk_revs(&head_ids, &root_ids);
|
||||
Ok(Box::new(RevWalkRevset { walk }))
|
||||
}
|
||||
RevsetExpression::DagRange { roots, heads } => {
|
||||
|
|
|
@ -439,6 +439,76 @@ fn test_evaluate_expression_ancestors(use_git: bool) {
|
|||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_evaluate_expression_range(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 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![commit1.id().clone(), commit3.id().clone()])
|
||||
.write_to_repo(mut_repo);
|
||||
|
||||
// The range from the root to the root is empty (because the left side of the
|
||||
// range is exclusive)
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo.as_repo_ref(), "root,,,root"),
|
||||
vec![]
|
||||
);
|
||||
|
||||
// Linear range
|
||||
assert_eq!(
|
||||
resolve_commit_ids(
|
||||
mut_repo.as_repo_ref(),
|
||||
&format!("{},,,{}", commit1.id().hex(), commit3.id().hex())
|
||||
),
|
||||
vec![commit3.id().clone(), commit2.id().clone()]
|
||||
);
|
||||
|
||||
// Empty range (descendant first)
|
||||
assert_eq!(
|
||||
resolve_commit_ids(
|
||||
mut_repo.as_repo_ref(),
|
||||
&format!("{},,,{}", commit3.id().hex(), commit1.id().hex())
|
||||
),
|
||||
vec![]
|
||||
);
|
||||
|
||||
// Range including a merge
|
||||
assert_eq!(
|
||||
resolve_commit_ids(
|
||||
mut_repo.as_repo_ref(),
|
||||
&format!("{},,,{}", commit1.id().hex(), commit4.id().hex())
|
||||
),
|
||||
vec![
|
||||
commit4.id().clone(),
|
||||
commit3.id().clone(),
|
||||
commit2.id().clone()
|
||||
]
|
||||
);
|
||||
|
||||
// Sibling commits
|
||||
assert_eq!(
|
||||
resolve_commit_ids(
|
||||
mut_repo.as_repo_ref(),
|
||||
&format!("{},,,{}", commit2.id().hex(), commit3.id().hex())
|
||||
),
|
||||
vec![commit3.id().clone()]
|
||||
);
|
||||
|
||||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_evaluate_expression_dag_range(use_git: bool) {
|
||||
|
|
Loading…
Reference in a new issue