revset: split out a new remote_branches() from branches()

It seems useful to be able to use `remote_branches()` in revsets
e.g. for filtering out commits known on remotes.
This commit is contained in:
Martin von Zweigbergk 2021-10-10 09:39:40 -07:00
parent ebcd946732
commit 8465a578be
2 changed files with 169 additions and 0 deletions

View file

@ -198,6 +198,7 @@ pub enum RevsetExpression {
Heads,
PublicHeads,
Branches,
RemoteBranches,
Tags,
GitRefs,
ParentCount {
@ -246,6 +247,10 @@ impl RevsetExpression {
Rc::new(RevsetExpression::Branches)
}
pub fn remote_branches() -> Rc<RevsetExpression> {
Rc::new(RevsetExpression::RemoteBranches)
}
pub fn tags() -> Rc<RevsetExpression> {
Rc::new(RevsetExpression::Tags)
}
@ -577,6 +582,16 @@ fn parse_function_expression(
})
}
}
"remote_branches" => {
if arg_count == 0 {
Ok(RevsetExpression::remote_branches())
} else {
Err(RevsetParseError::InvalidFunctionArguments {
name,
message: "Expected 0 arguments".to_string(),
})
}
}
"tags" => {
if arg_count == 0 {
Ok(RevsetExpression::tags())
@ -1098,6 +1113,15 @@ pub fn evaluate_expression<'repo>(
index_entries.push(index.entry_by_id(&id).unwrap());
}
}
}
index_entries.sort_by_key(|b| Reverse(b.position()));
index_entries.dedup();
Ok(Box::new(EagerRevset { index_entries }))
}
RevsetExpression::RemoteBranches => {
let index = repo.index();
let mut index_entries = vec![];
for branch_target in repo.view().branches().values() {
for remote_target in branch_target.remote_targets.values() {
for id in remote_target.adds() {
index_entries.push(index.entry_by_id(&id).unwrap());

View file

@ -890,6 +890,151 @@ fn test_evaluate_expression_git_refs(use_git: bool) {
tx.discard();
}
#[test_case(false ; "local backend")]
#[test_case(true ; "git backend")]
fn test_evaluate_expression_branches(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).write_to_repo(mut_repo);
let commit3 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo);
let commit4 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo);
// Can get branches when there are none
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "branches()"),
vec![]
);
// Can get a few branches
mut_repo.set_local_branch(
"branch1".to_string(),
RefTarget::Normal(commit1.id().clone()),
);
mut_repo.set_local_branch(
"branch2".to_string(),
RefTarget::Normal(commit2.id().clone()),
);
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "branches()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Two branches pointing to the same commit does not result in a duplicate in
// the revset
mut_repo.set_local_branch(
"branch3".to_string(),
RefTarget::Normal(commit2.id().clone()),
);
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "branches()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Can get branches when there are conflicted refs
mut_repo.set_local_branch(
"branch1".to_string(),
RefTarget::Conflict {
removes: vec![commit1.id().clone()],
adds: vec![commit2.id().clone(), commit3.id().clone()],
},
);
mut_repo.set_local_branch(
"branch2".to_string(),
RefTarget::Conflict {
removes: vec![commit2.id().clone()],
adds: vec![commit3.id().clone(), commit4.id().clone()],
},
);
mut_repo.remove_local_branch("branch3");
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "branches()"),
vec![
commit4.id().clone(),
commit3.id().clone(),
commit2.id().clone()
]
);
tx.discard();
}
#[test_case(false ; "local backend")]
#[test_case(true ; "git backend")]
fn test_evaluate_expression_remote_branches(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).write_to_repo(mut_repo);
let commit3 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo);
let commit4 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo);
// Can get branches when there are none
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "remote_branches()"),
vec![]
);
// Can get a few branches
mut_repo.set_remote_branch(
"branch1".to_string(),
"origin".to_string(),
RefTarget::Normal(commit1.id().clone()),
);
mut_repo.set_remote_branch(
"branch2".to_string(),
"private".to_string(),
RefTarget::Normal(commit2.id().clone()),
);
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "remote_branches()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Two branches pointing to the same commit does not result in a duplicate in
// the revset
mut_repo.set_remote_branch(
"branch3".to_string(),
"origin".to_string(),
RefTarget::Normal(commit2.id().clone()),
);
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "remote_branches()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Can get branches when there are conflicted refs
mut_repo.set_remote_branch(
"branch1".to_string(),
"origin".to_string(),
RefTarget::Conflict {
removes: vec![commit1.id().clone()],
adds: vec![commit2.id().clone(), commit3.id().clone()],
},
);
mut_repo.set_remote_branch(
"branch2".to_string(),
"private".to_string(),
RefTarget::Conflict {
removes: vec![commit2.id().clone()],
adds: vec![commit3.id().clone(), commit4.id().clone()],
},
);
mut_repo.remove_remote_branch("branch3", "origin");
assert_eq!(
resolve_commit_ids(mut_repo.as_repo_ref(), "remote_branches()"),
vec![
commit4.id().clone(),
commit3.id().clone(),
commit2.id().clone()
]
);
tx.discard();
}
#[test_case(false ; "local backend")]
#[test_case(true ; "git backend")]
fn test_evaluate_expression_merges(use_git: bool) {