Skip to content

Commit

Permalink
revset: add tracked/untracked_remote_branches()
Browse files Browse the repository at this point in the history
Adds support for revsets `tracked_remote_branches()` and
`untracked_remote_branches()`. I think this would be especially useful
for configuring `immutable_heads()` because rewriting untracked remote
branches usually wouldn't be desirable (since it wouldn't update the
remote branch). It also makes it easy to hide branches that you don't
care about from the log, since you could hide untracked branches and
then only track branches that you care about.
  • Loading branch information
scott2000 committed Jul 13, 2024
1 parent 5aa0804 commit 6a54579
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
address unconditionally. Only ASCII case folding is currently implemented,
but this will likely change in the future.

* New `tracked_remote_branches()` and `untracked_remote_branches()` revset
functions can be used to select tracked/untracked remote branches.

### Fixed bugs

## [0.19.0] - 2024-07-03
Expand Down
1 change: 1 addition & 0 deletions cli/src/commands/git/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ fn find_branches_targeted_by_revisions<'a>(
let current_branches_expression = RevsetExpression::remote_branches(
StringPattern::everything(),
StringPattern::exact(remote_name),
None,
)
.range(&RevsetExpression::commit(wc_commit_id))
.intersection(&RevsetExpression::branches(StringPattern::everything()));
Expand Down
4 changes: 4 additions & 0 deletions docs/revsets.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ revsets (expressions) as arguments.
While Git-tracking branches can be selected by `<name>@git`, these branches
aren't included in `remote_branches()`.

* `tracked_remote_branches()`: All targets of tracked remote branches.

* `untracked_remote_branches()`: All targets of untracked remote branches.

* `tags()`: All tag targets. If a tag is in a conflicted state, all its
possible targets are included.

Expand Down
43 changes: 41 additions & 2 deletions lib/src/revset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use crate::graph::GraphEdge;
use crate::hex_util::to_forward_hex;
use crate::id_prefix::IdPrefixContext;
use crate::object_id::{HexPrefix, PrefixResolution};
use crate::op_store::WorkspaceId;
use crate::op_store::{RemoteRefState, WorkspaceId};
use crate::repo::Repo;
use crate::repo_path::RepoPathUiConverter;
pub use crate::revset_parser::{
Expand Down Expand Up @@ -107,6 +107,7 @@ pub enum RevsetCommitRef {
RemoteBranches {
branch_pattern: StringPattern,
remote_pattern: StringPattern,
remote_ref_state: Option<RemoteRefState>,
},
Tags,
GitRefs,
Expand Down Expand Up @@ -239,11 +240,13 @@ impl RevsetExpression {
pub fn remote_branches(
branch_pattern: StringPattern,
remote_pattern: StringPattern,
remote_ref_state: Option<RemoteRefState>,
) -> Rc<RevsetExpression> {
Rc::new(RevsetExpression::CommitRef(
RevsetCommitRef::RemoteBranches {
branch_pattern,
remote_pattern,
remote_ref_state,
},
))
}
Expand Down Expand Up @@ -641,6 +644,23 @@ static BUILTIN_FUNCTION_MAP: Lazy<HashMap<&'static str, RevsetFunction>> = Lazy:
Ok(RevsetExpression::remote_branches(
branch_pattern,
remote_pattern,
None,
))
});
map.insert("tracked_remote_branches", |function, _context| {
function.expect_no_arguments()?;
Ok(RevsetExpression::remote_branches(
StringPattern::everything(),
StringPattern::everything(),
Some(RemoteRefState::Tracking),
))
});
map.insert("untracked_remote_branches", |function, _context| {
function.expect_no_arguments()?;
Ok(RevsetExpression::remote_branches(
StringPattern::everything(),
StringPattern::everything(),
Some(RemoteRefState::New),
))
});
map.insert("tags", |function, _context| {
Expand Down Expand Up @@ -1609,11 +1629,17 @@ fn resolve_commit_ref(
RevsetCommitRef::RemoteBranches {
branch_pattern,
remote_pattern,
remote_ref_state,
} => {
// TODO: should we allow to select @git branches explicitly?
let commit_ids = repo
.view()
.remote_branches_matching(branch_pattern, remote_pattern)
.filter(|(_, remote_ref)| {
remote_ref_state
.map(|state| remote_ref.state == state)
.unwrap_or(true)
})
.filter(|&((_, remote_name), _)| {
#[cfg(feature = "git")]
{
Expand Down Expand Up @@ -2313,14 +2339,25 @@ mod tests {
RemoteBranches {
branch_pattern: Substring(""),
remote_pattern: Substring(""),
remote_ref_state: None,
},
)
"###);
insta::assert_debug_snapshot!(parse("remote_branches()").unwrap(), @r###"
insta::assert_debug_snapshot!(parse("tracked_remote_branches()").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring(""),
remote_pattern: Substring(""),
remote_ref_state: Some(Tracking),
},
)
"###);
insta::assert_debug_snapshot!(parse("untracked_remote_branches()").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring(""),
remote_pattern: Substring(""),
remote_ref_state: Some(New),
},
)
"###);
Expand Down Expand Up @@ -2566,6 +2603,7 @@ mod tests {
RemoteBranches {
branch_pattern: Substring(""),
remote_pattern: Substring("foo"),
remote_ref_state: None,
},
)
"###);
Expand All @@ -2575,6 +2613,7 @@ mod tests {
RemoteBranches {
branch_pattern: Substring("foo"),
remote_pattern: Substring("bar"),
remote_ref_state: None,
},
)
"###);
Expand Down
24 changes: 21 additions & 3 deletions lib/tests/test_revset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2061,7 +2061,7 @@ fn test_evaluate_expression_remote_branches() {
let repo = &test_repo.repo;
let remote_ref = |target| RemoteRef {
target,
state: RemoteRefState::Tracking, // doesn't matter
state: RemoteRefState::Tracking,
};
let normal_remote_ref = |id: &CommitId| remote_ref(RefTarget::normal(id.clone()));

Expand All @@ -2076,15 +2076,24 @@ fn test_evaluate_expression_remote_branches() {

// Can get branches when there are none
assert_eq!(resolve_commit_ids(mut_repo, "remote_branches()"), vec![]);
// Can get a few branches
mut_repo.set_remote_branch("branch1", "origin", normal_remote_ref(commit1.id()));
// Branch 1 is untracked on remote origin
mut_repo.set_remote_branch(
"branch1",
"origin",
RemoteRef {
target: RefTarget::normal(commit1.id().clone()),
state: RemoteRefState::New,
},
);
// Branch 2 is tracked on remote private
mut_repo.set_remote_branch("branch2", "private", normal_remote_ref(commit2.id()));
// Git-tracking branches aren't included
mut_repo.set_remote_branch(
"branch",
git::REMOTE_NAME_FOR_LOCAL_GIT_REPO,
normal_remote_ref(commit_git_remote.id()),
);
// Can get a few branches
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches()"),
vec![commit2.id().clone(), commit1.id().clone()]
Expand Down Expand Up @@ -2128,6 +2137,15 @@ fn test_evaluate_expression_remote_branches() {
resolve_commit_ids(mut_repo, r#"remote_branches(exact:branch1, exact:origin)"#),
vec![commit1.id().clone()]
);
// Can filter branches by tracked and untracked
assert_eq!(
resolve_commit_ids(mut_repo, "tracked_remote_branches()"),
vec![commit2.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "untracked_remote_branches()"),
vec![commit1.id().clone()]
);
// Can silently resolve to an empty set if there's no matches
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches(branch3)"),
Expand Down

0 comments on commit 6a54579

Please sign in to comment.