revset: introduce a trait for resolving symbols

I'd like to make the symbol resolution more flexible, both so we can
support customizing it (in custom `jj` binaries) and so we can use it
for resolving short prefixes within a small revset.
This commit is contained in:
Martin von Zweigbergk 2023-05-05 11:33:39 -07:00 committed by Martin von Zweigbergk
parent ac31c83e13
commit 5e7c57c527

View file

@ -436,7 +436,9 @@ impl RevsetExpression {
self: Rc<Self>, self: Rc<Self>,
repo: &dyn Repo, repo: &dyn Repo,
) -> Result<ResolvedExpression, RevsetResolutionError> { ) -> Result<ResolvedExpression, RevsetResolutionError> {
resolve_symbols(repo, self, None).map(|expression| resolve_visibility(repo, &expression)) let symbol_resolver = DefaultSymbolResolver::new(repo, None);
resolve_symbols(repo, self, &symbol_resolver)
.map(|expression| resolve_visibility(repo, &expression))
} }
pub fn resolve_in_workspace( pub fn resolve_in_workspace(
@ -444,7 +446,8 @@ impl RevsetExpression {
repo: &dyn Repo, repo: &dyn Repo,
workspace_ctx: &RevsetWorkspaceContext, workspace_ctx: &RevsetWorkspaceContext,
) -> Result<ResolvedExpression, RevsetResolutionError> { ) -> Result<ResolvedExpression, RevsetResolutionError> {
resolve_symbols(repo, self, Some(workspace_ctx)) let symbol_resolver = DefaultSymbolResolver::new(repo, Some(workspace_ctx.workspace_id));
resolve_symbols(repo, self, &symbol_resolver)
.map(|expression| resolve_visibility(repo, &expression)) .map(|expression| resolve_visibility(repo, &expression))
} }
} }
@ -1663,14 +1666,31 @@ fn resolve_change_id(
} }
} }
pub fn resolve_symbol( pub trait SymbolResolver {
repo: &dyn Repo, fn resolve_symbol(&self, symbol: &str) -> Result<Vec<CommitId>, RevsetResolutionError>;
symbol: &str, }
workspace_id: Option<&WorkspaceId>,
) -> Result<Vec<CommitId>, RevsetResolutionError> { /// Resolves the "root" and "@" symbols, branches, remote branches, tags, git
/// refs, and full and abbreviated commit and change ids.
pub struct DefaultSymbolResolver<'a> {
repo: &'a dyn Repo,
workspace_id: Option<&'a WorkspaceId>,
}
impl DefaultSymbolResolver<'_> {
pub fn new<'a>(
repo: &'a dyn Repo,
workspace_id: Option<&'a WorkspaceId>,
) -> DefaultSymbolResolver<'a> {
DefaultSymbolResolver { repo, workspace_id }
}
}
impl SymbolResolver for DefaultSymbolResolver<'_> {
fn resolve_symbol(&self, symbol: &str) -> Result<Vec<CommitId>, RevsetResolutionError> {
if symbol.ends_with('@') { if symbol.ends_with('@') {
let target_workspace = if symbol == "@" { let target_workspace = if symbol == "@" {
if let Some(workspace_id) = workspace_id { if let Some(workspace_id) = self.workspace_id {
workspace_id.clone() workspace_id.clone()
} else { } else {
return Err(RevsetResolutionError::NoSuchRevision(symbol.to_owned())); return Err(RevsetResolutionError::NoSuchRevision(symbol.to_owned()));
@ -1678,57 +1698,64 @@ pub fn resolve_symbol(
} else { } else {
WorkspaceId::new(symbol.strip_suffix('@').unwrap().to_string()) WorkspaceId::new(symbol.strip_suffix('@').unwrap().to_string())
}; };
if let Some(commit_id) = repo.view().get_wc_commit_id(&target_workspace) { if let Some(commit_id) = self.repo.view().get_wc_commit_id(&target_workspace) {
Ok(vec![commit_id.clone()]) Ok(vec![commit_id.clone()])
} else { } else {
Err(RevsetResolutionError::NoSuchRevision(symbol.to_owned())) Err(RevsetResolutionError::NoSuchRevision(symbol.to_owned()))
} }
} else if symbol == "root" { } else if symbol == "root" {
Ok(vec![repo.store().root_commit_id().clone()]) Ok(vec![self.repo.store().root_commit_id().clone()])
} else { } else {
// Try to resolve as a tag // Try to resolve as a tag
if let Some(target) = repo.view().tags().get(symbol) { if let Some(target) = self.repo.view().tags().get(symbol) {
return Ok(target.adds()); return Ok(target.adds());
} }
// Try to resolve as a branch // Try to resolve as a branch
if let Some(ids) = resolve_branch(repo, symbol) { if let Some(ids) = resolve_branch(self.repo, symbol) {
return Ok(ids); return Ok(ids);
} }
// Try to resolve as a git ref // Try to resolve as a git ref
if let Some(ids) = resolve_git_ref(repo, symbol) { if let Some(ids) = resolve_git_ref(self.repo, symbol) {
return Ok(ids); return Ok(ids);
} }
// Try to resolve as a full commit id. // Try to resolve as a full commit id.
if let Some(ids) = resolve_full_commit_id(repo, symbol)? { if let Some(ids) = resolve_full_commit_id(self.repo, symbol)? {
return Ok(ids); return Ok(ids);
} }
// Try to resolve as a commit id. // Try to resolve as a commit id.
if let Some(ids) = resolve_short_commit_id(repo, symbol)? { if let Some(ids) = resolve_short_commit_id(self.repo, symbol)? {
return Ok(ids); return Ok(ids);
} }
// Try to resolve as a change id. // Try to resolve as a change id.
if let Some(ids) = resolve_change_id(repo, symbol)? { if let Some(ids) = resolve_change_id(self.repo, symbol)? {
return Ok(ids); return Ok(ids);
} }
Err(RevsetResolutionError::NoSuchRevision(symbol.to_owned())) Err(RevsetResolutionError::NoSuchRevision(symbol.to_owned()))
} }
}
}
pub fn resolve_symbol(
repo: &dyn Repo,
symbol: &str,
workspace_id: Option<&WorkspaceId>,
) -> Result<Vec<CommitId>, RevsetResolutionError> {
DefaultSymbolResolver::new(repo, workspace_id).resolve_symbol(symbol)
} }
fn resolve_commit_ref( fn resolve_commit_ref(
repo: &dyn Repo, repo: &dyn Repo,
commit_ref: &RevsetCommitRef, commit_ref: &RevsetCommitRef,
workspace_ctx: Option<&RevsetWorkspaceContext>, symbol_resolver: &dyn SymbolResolver,
) -> Result<Vec<CommitId>, RevsetResolutionError> { ) -> Result<Vec<CommitId>, RevsetResolutionError> {
match commit_ref { match commit_ref {
RevsetCommitRef::Symbol(symbol) => { RevsetCommitRef::Symbol(symbol) => symbol_resolver.resolve_symbol(symbol),
resolve_symbol(repo, symbol, workspace_ctx.map(|ctx| ctx.workspace_id))
}
RevsetCommitRef::VisibleHeads => Ok(repo.view().heads().iter().cloned().collect_vec()), RevsetCommitRef::VisibleHeads => Ok(repo.view().heads().iter().cloned().collect_vec()),
RevsetCommitRef::Branches(needle) => { RevsetCommitRef::Branches(needle) => {
let mut commit_ids = vec![]; let mut commit_ids = vec![];
@ -1786,14 +1813,14 @@ fn resolve_commit_ref(
fn resolve_symbols( fn resolve_symbols(
repo: &dyn Repo, repo: &dyn Repo,
expression: Rc<RevsetExpression>, expression: Rc<RevsetExpression>,
workspace_ctx: Option<&RevsetWorkspaceContext>, symbol_resolver: &dyn SymbolResolver,
) -> Result<Rc<RevsetExpression>, RevsetResolutionError> { ) -> Result<Rc<RevsetExpression>, RevsetResolutionError> {
Ok(try_transform_expression( Ok(try_transform_expression(
&expression, &expression,
|expression| match expression.as_ref() { |expression| match expression.as_ref() {
// 'present(x)' opens new symbol resolution scope to map error to 'none()'. // 'present(x)' opens new symbol resolution scope to map error to 'none()'.
RevsetExpression::Present(candidates) => { RevsetExpression::Present(candidates) => {
resolve_symbols(repo, candidates.clone(), workspace_ctx) resolve_symbols(repo, candidates.clone(), symbol_resolver)
.or_else(|err| match err { .or_else(|err| match err {
RevsetResolutionError::NoSuchRevision(_) => Ok(RevsetExpression::none()), RevsetResolutionError::NoSuchRevision(_) => Ok(RevsetExpression::none()),
RevsetResolutionError::AmbiguousIdPrefix(_) RevsetResolutionError::AmbiguousIdPrefix(_)
@ -1806,7 +1833,7 @@ fn resolve_symbols(
}, },
|expression| match expression.as_ref() { |expression| match expression.as_ref() {
RevsetExpression::CommitRef(commit_ref) => { RevsetExpression::CommitRef(commit_ref) => {
let commit_ids = resolve_commit_ref(repo, commit_ref, workspace_ctx)?; let commit_ids = resolve_commit_ref(repo, commit_ref, symbol_resolver)?;
Ok(Some(RevsetExpression::commits(commit_ids))) Ok(Some(RevsetExpression::commits(commit_ids)))
} }
_ => Ok(None), _ => Ok(None),