id_prefix: remove repo field from IdPrefixContext

By passing the repo as argument to the methods instead, we can remove
the `repo` field and the associated lifetime. Thanks to Yuya for the
suggestion.
This commit is contained in:
Martin von Zweigbergk 2023-05-11 22:09:48 -07:00 committed by Martin von Zweigbergk
parent eab5218fe5
commit 916b00c33e
5 changed files with 124 additions and 70 deletions

View file

@ -66,19 +66,12 @@ impl DisambiguationData {
} }
} }
pub struct IdPrefixContext<'repo> { #[derive(Default)]
repo: &'repo dyn Repo, pub struct IdPrefixContext {
disambiguation: Option<DisambiguationData>, disambiguation: Option<DisambiguationData>,
} }
impl IdPrefixContext<'_> { impl IdPrefixContext {
pub fn new(repo: &dyn Repo) -> IdPrefixContext {
IdPrefixContext {
repo,
disambiguation: None,
}
}
pub fn disambiguate_within( pub fn disambiguate_within(
mut self, mut self,
expression: Rc<RevsetExpression>, expression: Rc<RevsetExpression>,
@ -92,59 +85,65 @@ impl IdPrefixContext<'_> {
self self
} }
fn disambiguation_indexes(&self) -> Option<&Indexes> { fn disambiguation_indexes(&self, repo: &dyn Repo) -> Option<&Indexes> {
// TODO: propagate errors instead of treating them as if no revset was specified // TODO: propagate errors instead of treating them as if no revset was specified
self.disambiguation self.disambiguation
.as_ref() .as_ref()
.and_then(|disambiguation| disambiguation.indexes(self.repo).ok()) .and_then(|disambiguation| disambiguation.indexes(repo).ok())
} }
/// Resolve an unambiguous commit ID prefix. /// Resolve an unambiguous commit ID prefix.
pub fn resolve_commit_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId> { pub fn resolve_commit_prefix(
if let Some(indexes) = self.disambiguation_indexes() { &self,
repo: &dyn Repo,
prefix: &HexPrefix,
) -> PrefixResolution<CommitId> {
if let Some(indexes) = self.disambiguation_indexes(repo) {
let resolution = indexes.commit_index.resolve_prefix(prefix); let resolution = indexes.commit_index.resolve_prefix(prefix);
if let PrefixResolution::SingleMatch(mut ids) = resolution { if let PrefixResolution::SingleMatch(mut ids) = resolution {
assert_eq!(ids.len(), 1); assert_eq!(ids.len(), 1);
return PrefixResolution::SingleMatch(ids.pop().unwrap()); return PrefixResolution::SingleMatch(ids.pop().unwrap());
} }
} }
self.repo.index().resolve_prefix(prefix) repo.index().resolve_prefix(prefix)
} }
/// Returns the shortest length of a prefix of `commit_id` that /// Returns the shortest length of a prefix of `commit_id` that
/// can still be resolved by `resolve_commit_prefix()`. /// can still be resolved by `resolve_commit_prefix()`.
pub fn shortest_commit_prefix_len(&self, commit_id: &CommitId) -> usize { pub fn shortest_commit_prefix_len(&self, repo: &dyn Repo, commit_id: &CommitId) -> usize {
if let Some(indexes) = self.disambiguation_indexes() { if let Some(indexes) = self.disambiguation_indexes(repo) {
// TODO: Avoid the double lookup here (has_key() + shortest_unique_prefix_len()) // TODO: Avoid the double lookup here (has_key() + shortest_unique_prefix_len())
if indexes.commit_index.has_key(commit_id) { if indexes.commit_index.has_key(commit_id) {
return indexes.commit_index.shortest_unique_prefix_len(commit_id); return indexes.commit_index.shortest_unique_prefix_len(commit_id);
} }
} }
self.repo repo.index().shortest_unique_commit_id_prefix_len(commit_id)
.index()
.shortest_unique_commit_id_prefix_len(commit_id)
} }
/// Resolve an unambiguous change ID prefix to the commit IDs in the revset. /// Resolve an unambiguous change ID prefix to the commit IDs in the revset.
pub fn resolve_change_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<Vec<CommitId>> { pub fn resolve_change_prefix(
if let Some(indexes) = self.disambiguation_indexes() { &self,
repo: &dyn Repo,
prefix: &HexPrefix,
) -> PrefixResolution<Vec<CommitId>> {
if let Some(indexes) = self.disambiguation_indexes(repo) {
let resolution = indexes.change_index.resolve_prefix(prefix); let resolution = indexes.change_index.resolve_prefix(prefix);
if let PrefixResolution::SingleMatch(ids) = resolution { if let PrefixResolution::SingleMatch(ids) = resolution {
return PrefixResolution::SingleMatch(ids); return PrefixResolution::SingleMatch(ids);
} }
} }
self.repo.resolve_change_id_prefix(prefix) repo.resolve_change_id_prefix(prefix)
} }
/// Returns the shortest length of a prefix of `change_id` that /// Returns the shortest length of a prefix of `change_id` that
/// can still be resolved by `resolve_change_prefix()`. /// can still be resolved by `resolve_change_prefix()`.
pub fn shortest_change_prefix_len(&self, change_id: &ChangeId) -> usize { pub fn shortest_change_prefix_len(&self, repo: &dyn Repo, change_id: &ChangeId) -> usize {
if let Some(indexes) = self.disambiguation_indexes() { if let Some(indexes) = self.disambiguation_indexes(repo) {
if indexes.change_index.has_key(change_id) { if indexes.change_index.has_key(change_id) {
return indexes.change_index.shortest_unique_prefix_len(change_id); return indexes.change_index.shortest_unique_prefix_len(change_id);
} }
} }
self.repo.shortest_unique_change_id_prefix_len(change_id) repo.shortest_unique_change_id_prefix_len(change_id)
} }
} }

View file

@ -1647,7 +1647,7 @@ impl SymbolResolver for FailingSymbolResolver {
} }
} }
pub type PrefixResolver<'a, T> = Box<dyn Fn(&HexPrefix) -> PrefixResolution<T> + 'a>; pub type PrefixResolver<'a, T> = Box<dyn Fn(&dyn Repo, &HexPrefix) -> PrefixResolution<T> + 'a>;
/// Resolves the "root" and "@" symbols, branches, remote branches, tags, git /// Resolves the "root" and "@" symbols, branches, remote branches, tags, git
/// refs, and full and abbreviated commit and change ids. /// refs, and full and abbreviated commit and change ids.
@ -1728,7 +1728,7 @@ impl SymbolResolver for DefaultSymbolResolver<'_> {
// Try to resolve as a commit id. // Try to resolve as a commit id.
if let Some(prefix) = HexPrefix::new(symbol) { if let Some(prefix) = HexPrefix::new(symbol) {
let prefix_resolution = if let Some(commit_id_resolver) = &self.commit_id_resolver { let prefix_resolution = if let Some(commit_id_resolver) = &self.commit_id_resolver {
commit_id_resolver(&prefix) commit_id_resolver(self.repo, &prefix)
} else { } else {
self.repo.index().resolve_prefix(&prefix) self.repo.index().resolve_prefix(&prefix)
}; };
@ -1748,7 +1748,7 @@ impl SymbolResolver for DefaultSymbolResolver<'_> {
// Try to resolve as a change id. // Try to resolve as a change id.
if let Some(prefix) = to_forward_hex(symbol).as_deref().and_then(HexPrefix::new) { if let Some(prefix) = to_forward_hex(symbol).as_deref().and_then(HexPrefix::new) {
let prefix_resolution = if let Some(change_id_resolver) = &self.change_id_resolver { let prefix_resolution = if let Some(change_id_resolver) = &self.change_id_resolver {
change_id_resolver(&prefix) change_id_resolver(self.repo, &prefix)
} else { } else {
self.repo.resolve_change_id_prefix(&prefix) self.repo.resolve_change_id_prefix(&prefix)
}; };

View file

@ -130,25 +130,55 @@ fn test_id_prefix() {
// Without a disambiguation revset // Without a disambiguation revset
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
let c = IdPrefixContext::new(repo.as_ref()); let c = IdPrefixContext::default();
assert_eq!(c.shortest_commit_prefix_len(commits[2].id()), 2);
assert_eq!(c.shortest_commit_prefix_len(commits[5].id()), 1);
assert_eq!(c.resolve_commit_prefix(&prefix("2")), AmbiguousMatch);
assert_eq!( assert_eq!(
c.resolve_commit_prefix(&prefix("2a")), c.shortest_commit_prefix_len(repo.as_ref(), commits[2].id()),
2
);
assert_eq!(
c.shortest_commit_prefix_len(repo.as_ref(), commits[5].id()),
1
);
assert_eq!(
c.resolve_commit_prefix(repo.as_ref(), &prefix("2")),
AmbiguousMatch
);
assert_eq!(
c.resolve_commit_prefix(repo.as_ref(), &prefix("2a")),
SingleMatch(commits[2].id().clone()) SingleMatch(commits[2].id().clone())
); );
assert_eq!(c.resolve_commit_prefix(&prefix("20")), NoMatch);
assert_eq!(c.resolve_commit_prefix(&prefix("2a0")), NoMatch);
assert_eq!(c.shortest_change_prefix_len(commits[0].change_id()), 2);
assert_eq!(c.shortest_change_prefix_len(commits[6].change_id()), 1);
assert_eq!(c.resolve_change_prefix(&prefix("7")), AmbiguousMatch);
assert_eq!( assert_eq!(
c.resolve_change_prefix(&prefix("78")), c.resolve_commit_prefix(repo.as_ref(), &prefix("20")),
NoMatch
);
assert_eq!(
c.resolve_commit_prefix(repo.as_ref(), &prefix("2a0")),
NoMatch
);
assert_eq!(
c.shortest_change_prefix_len(repo.as_ref(), commits[0].change_id()),
2
);
assert_eq!(
c.shortest_change_prefix_len(repo.as_ref(), commits[6].change_id()),
1
);
assert_eq!(
c.resolve_change_prefix(repo.as_ref(), &prefix("7")),
AmbiguousMatch
);
assert_eq!(
c.resolve_change_prefix(repo.as_ref(), &prefix("78")),
SingleMatch(vec![commits[0].id().clone()]) SingleMatch(vec![commits[0].id().clone()])
); );
assert_eq!(c.resolve_change_prefix(&prefix("70")), NoMatch); assert_eq!(
assert_eq!(c.resolve_change_prefix(&prefix("780")), NoMatch); c.resolve_change_prefix(repo.as_ref(), &prefix("70")),
NoMatch
);
assert_eq!(
c.resolve_change_prefix(repo.as_ref(), &prefix("780")),
NoMatch
);
// Disambiguate within a revset // Disambiguate within a revset
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
@ -156,20 +186,26 @@ fn test_id_prefix() {
RevsetExpression::commits(vec![commits[0].id().clone(), commits[2].id().clone()]); RevsetExpression::commits(vec![commits[0].id().clone(), commits[2].id().clone()]);
let c = c.disambiguate_within(expression, None); let c = c.disambiguate_within(expression, None);
// The prefix is now shorter // The prefix is now shorter
assert_eq!(c.shortest_commit_prefix_len(commits[2].id()), 1); assert_eq!(
c.shortest_commit_prefix_len(repo.as_ref(), commits[2].id()),
1
);
// Shorter prefix within the set can be used // Shorter prefix within the set can be used
assert_eq!( assert_eq!(
c.resolve_commit_prefix(&prefix("2")), c.resolve_commit_prefix(repo.as_ref(), &prefix("2")),
SingleMatch(commits[2].id().clone()) SingleMatch(commits[2].id().clone())
); );
// Can still resolve commits outside the set // Can still resolve commits outside the set
assert_eq!( assert_eq!(
c.resolve_commit_prefix(&prefix("21")), c.resolve_commit_prefix(repo.as_ref(), &prefix("21")),
SingleMatch(commits[24].id().clone()) SingleMatch(commits[24].id().clone())
); );
assert_eq!(c.shortest_change_prefix_len(commits[0].change_id()), 1);
assert_eq!( assert_eq!(
c.resolve_change_prefix(&prefix("7")), c.shortest_change_prefix_len(repo.as_ref(), commits[0].change_id()),
1
);
assert_eq!(
c.resolve_change_prefix(repo.as_ref(), &prefix("7")),
SingleMatch(vec![commits[0].id().clone()]) SingleMatch(vec![commits[0].id().clone()])
); );
@ -178,16 +214,28 @@ fn test_id_prefix() {
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
let expression = RevsetExpression::commit(root_commit_id.clone()); let expression = RevsetExpression::commit(root_commit_id.clone());
let c = c.disambiguate_within(expression, None); let c = c.disambiguate_within(expression, None);
assert_eq!(c.shortest_commit_prefix_len(root_commit_id), 1);
assert_eq!(c.resolve_commit_prefix(&prefix("")), AmbiguousMatch);
assert_eq!( assert_eq!(
c.resolve_commit_prefix(&prefix("0")), c.shortest_commit_prefix_len(repo.as_ref(), root_commit_id),
1
);
assert_eq!(
c.resolve_commit_prefix(repo.as_ref(), &prefix("")),
AmbiguousMatch
);
assert_eq!(
c.resolve_commit_prefix(repo.as_ref(), &prefix("0")),
SingleMatch(root_commit_id.clone()) SingleMatch(root_commit_id.clone())
); );
assert_eq!(c.shortest_change_prefix_len(root_change_id), 1);
assert_eq!(c.resolve_change_prefix(&prefix("")), AmbiguousMatch);
assert_eq!( assert_eq!(
c.resolve_change_prefix(&prefix("0")), c.shortest_change_prefix_len(repo.as_ref(), root_change_id),
1
);
assert_eq!(
c.resolve_change_prefix(repo.as_ref(), &prefix("")),
AmbiguousMatch
);
assert_eq!(
c.resolve_change_prefix(repo.as_ref(), &prefix("0")),
SingleMatch(vec![root_commit_id.clone()]) SingleMatch(vec![root_commit_id.clone()])
); );
@ -196,6 +244,12 @@ fn test_id_prefix() {
// TODO: Should be an error // TODO: Should be an error
let expression = RevsetExpression::symbol("nonexistent".to_string()); let expression = RevsetExpression::symbol("nonexistent".to_string());
let context = c.disambiguate_within(expression, None); let context = c.disambiguate_within(expression, None);
assert_eq!(context.shortest_commit_prefix_len(commits[2].id()), 2); assert_eq!(
assert_eq!(context.resolve_commit_prefix(&prefix("2")), AmbiguousMatch); context.shortest_commit_prefix_len(repo.as_ref(), commits[2].id()),
2
);
assert_eq!(
context.resolve_commit_prefix(repo.as_ref(), &prefix("2")),
AmbiguousMatch
);
} }

View file

@ -548,7 +548,7 @@ impl CommandHelper {
/// data is lazily loaded. /// data is lazily loaded.
struct ReadonlyUserRepo { struct ReadonlyUserRepo {
repo: Arc<ReadonlyRepo>, repo: Arc<ReadonlyRepo>,
id_prefix_context: OnceCell<IdPrefixContext<'static>>, id_prefix_context: OnceCell<IdPrefixContext>,
} }
impl ReadonlyUserRepo { impl ReadonlyUserRepo {
@ -590,7 +590,7 @@ impl WorkspaceCommandHelper {
// Parse commit_summary template early to report error before starting mutable // Parse commit_summary template early to report error before starting mutable
// operation. // operation.
// TODO: Parsed template can be cached if it doesn't capture repo // TODO: Parsed template can be cached if it doesn't capture repo
let id_prefix_context = IdPrefixContext::new(repo.as_ref()); let id_prefix_context = IdPrefixContext::default();
parse_commit_summary_template( parse_commit_summary_template(
repo.as_ref(), repo.as_ref(),
workspace.workspace_id(), workspace.workspace_id(),
@ -925,18 +925,18 @@ impl WorkspaceCommandHelper {
pub(crate) fn revset_symbol_resolver(&self) -> DefaultSymbolResolver<'_> { pub(crate) fn revset_symbol_resolver(&self) -> DefaultSymbolResolver<'_> {
let id_prefix_context = self.id_prefix_context(); let id_prefix_context = self.id_prefix_context();
let commit_id_resolver: revset::PrefixResolver<'_, CommitId> = let commit_id_resolver: revset::PrefixResolver<CommitId> =
Box::new(|prefix| id_prefix_context.resolve_commit_prefix(prefix)); Box::new(|repo, prefix| id_prefix_context.resolve_commit_prefix(repo, prefix));
let change_id_resolver: revset::PrefixResolver<'_, Vec<CommitId>> = let change_id_resolver: revset::PrefixResolver<Vec<CommitId>> =
Box::new(|prefix| id_prefix_context.resolve_change_prefix(prefix)); Box::new(|repo, prefix| id_prefix_context.resolve_change_prefix(repo, prefix));
DefaultSymbolResolver::new(self.repo().as_ref(), Some(self.workspace_id())) DefaultSymbolResolver::new(self.repo().as_ref(), Some(self.workspace_id()))
.with_commit_id_resolver(commit_id_resolver) .with_commit_id_resolver(commit_id_resolver)
.with_change_id_resolver(change_id_resolver) .with_change_id_resolver(change_id_resolver)
} }
pub fn id_prefix_context(&self) -> &IdPrefixContext<'_> { pub fn id_prefix_context(&self) -> &IdPrefixContext {
self.user_repo.id_prefix_context.get_or_init(|| { self.user_repo.id_prefix_context.get_or_init(|| {
let mut context: IdPrefixContext<'_> = IdPrefixContext::new(self.repo().as_ref()); let mut context: IdPrefixContext = IdPrefixContext::default();
let revset_string: String = self let revset_string: String = self
.settings .settings
.config() .config()
@ -947,7 +947,6 @@ impl WorkspaceCommandHelper {
context = context context = context
.disambiguate_within(disambiguation_revset, Some(self.workspace_id().clone())); .disambiguate_within(disambiguation_revset, Some(self.workspace_id().clone()));
} }
let context: IdPrefixContext<'static> = unsafe { std::mem::transmute(context) };
context context
}) })
} }
@ -1295,7 +1294,7 @@ impl WorkspaceCommandTransaction<'_> {
commit: &Commit, commit: &Commit,
) -> std::io::Result<()> { ) -> std::io::Result<()> {
// TODO: Use the disambiguation revset // TODO: Use the disambiguation revset
let id_prefix_context = IdPrefixContext::new(self.tx.repo()); let id_prefix_context = IdPrefixContext::default();
let template = parse_commit_summary_template( let template = parse_commit_summary_template(
self.tx.repo(), self.tx.repo(),
self.helper.workspace_id(), self.helper.workspace_id(),
@ -1734,7 +1733,7 @@ fn load_template_aliases(
fn parse_commit_summary_template<'a>( fn parse_commit_summary_template<'a>(
repo: &'a dyn Repo, repo: &'a dyn Repo,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
id_prefix_context: &'a IdPrefixContext<'a>, id_prefix_context: &'a IdPrefixContext,
aliases_map: &TemplateAliasesMap, aliases_map: &TemplateAliasesMap,
settings: &UserSettings, settings: &UserSettings,
) -> Result<Box<dyn Template<Commit> + 'a>, CommandError> { ) -> Result<Box<dyn Template<Commit> + 'a>, CommandError> {

View file

@ -40,7 +40,7 @@ use crate::text_util;
struct CommitTemplateLanguage<'repo, 'b> { struct CommitTemplateLanguage<'repo, 'b> {
repo: &'repo dyn Repo, repo: &'repo dyn Repo,
workspace_id: &'b WorkspaceId, workspace_id: &'b WorkspaceId,
id_prefix_context: &'repo IdPrefixContext<'repo>, id_prefix_context: &'repo IdPrefixContext,
} }
impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo, '_> { impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo, '_> {
@ -387,13 +387,14 @@ impl CommitOrChangeId {
/// length of the shortest unique prefix /// length of the shortest unique prefix
pub fn shortest( pub fn shortest(
&self, &self,
repo: &dyn Repo,
id_prefix_context: &IdPrefixContext, id_prefix_context: &IdPrefixContext,
total_len: usize, total_len: usize,
) -> ShortestIdPrefix { ) -> ShortestIdPrefix {
let mut hex = self.hex(); let mut hex = self.hex();
let prefix_len = match self { let prefix_len = match self {
CommitOrChangeId::Commit(id) => id_prefix_context.shortest_commit_prefix_len(id), CommitOrChangeId::Commit(id) => id_prefix_context.shortest_commit_prefix_len(repo, id),
CommitOrChangeId::Change(id) => id_prefix_context.shortest_change_prefix_len(id), CommitOrChangeId::Change(id) => id_prefix_context.shortest_change_prefix_len(repo, id),
}; };
hex.truncate(max(prefix_len, total_len)); hex.truncate(max(prefix_len, total_len));
let rest = hex.split_off(prefix_len); let rest = hex.split_off(prefix_len);
@ -434,6 +435,7 @@ fn build_commit_or_change_id_method<'repo>(
(self_property, len_property), (self_property, len_property),
|(id, len)| { |(id, len)| {
id.shortest( id.shortest(
language.repo,
id_prefix_context, id_prefix_context,
len.and_then(|l| l.try_into().ok()).unwrap_or(0), len.and_then(|l| l.try_into().ok()).unwrap_or(0),
) )
@ -515,7 +517,7 @@ fn build_shortest_id_prefix_method<'repo>(
pub fn parse<'repo>( pub fn parse<'repo>(
repo: &'repo dyn Repo, repo: &'repo dyn Repo,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
id_prefix_context: &'repo IdPrefixContext<'repo>, id_prefix_context: &'repo IdPrefixContext,
template_text: &str, template_text: &str,
aliases_map: &TemplateAliasesMap, aliases_map: &TemplateAliasesMap,
) -> TemplateParseResult<Box<dyn Template<Commit> + 'repo>> { ) -> TemplateParseResult<Box<dyn Template<Commit> + 'repo>> {