diff --git a/lib/src/id_prefix.rs b/lib/src/id_prefix.rs new file mode 100644 index 000000000..ec9cc1471 --- /dev/null +++ b/lib/src/id_prefix.rs @@ -0,0 +1,51 @@ +// Copyright 2023 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::backend::{ChangeId, CommitId}; +use crate::index::{HexPrefix, PrefixResolution}; +use crate::repo::Repo; + +pub struct IdPrefixContext<'repo> { + repo: &'repo dyn Repo, +} + +impl IdPrefixContext<'_> { + pub fn new(repo: &dyn Repo) -> IdPrefixContext { + IdPrefixContext { repo } + } + + /// Resolve an unambiguous commit ID prefix. + pub fn resolve_commit_prefix(&self, prefix: &HexPrefix) -> PrefixResolution { + self.repo.index().resolve_prefix(prefix) + } + + /// Returns the shortest length of a prefix of `commit_id` that + /// can still be resolved by `resolve_commit_prefix()`. + pub fn shortest_commit_prefix_len(&self, commit_id: &CommitId) -> usize { + self.repo + .index() + .shortest_unique_commit_id_prefix_len(commit_id) + } + + /// Resolve an unambiguous change ID prefix to the commit IDs in the revset. + pub fn resolve_change_prefix(&self, prefix: &HexPrefix) -> PrefixResolution> { + self.repo.resolve_change_id_prefix(prefix) + } + + /// Returns the shortest length of a prefix of `change_id` that + /// can still be resolved by `resolve_change_prefix()`. + pub fn shortest_change_prefix_len(&self, change_id: &ChangeId) -> usize { + self.repo.shortest_unique_change_id_prefix_len(change_id) + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 2cbf7e7ef..affc1c073 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -32,6 +32,7 @@ pub mod git; pub mod git_backend; pub mod gitignore; pub mod hex_util; +pub mod id_prefix; pub mod index; pub mod local_backend; pub mod lock; diff --git a/src/cli_util.rs b/src/cli_util.rs index 023fb910a..c99824570 100644 --- a/src/cli_util.rs +++ b/src/cli_util.rs @@ -34,6 +34,7 @@ use jujutsu_lib::commit::Commit; use jujutsu_lib::git::{GitExportError, GitImportError}; use jujutsu_lib::gitignore::GitIgnoreFile; use jujutsu_lib::hex_util::to_reverse_hex; +use jujutsu_lib::id_prefix::IdPrefixContext; use jujutsu_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher, Visit}; use jujutsu_lib::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore}; use jujutsu_lib::op_store::{OpStore, OpStoreError, OperationId, RefTarget, WorkspaceId}; @@ -56,6 +57,7 @@ use jujutsu_lib::working_copy::{ }; use jujutsu_lib::workspace::{Workspace, WorkspaceInitError, WorkspaceLoadError, WorkspaceLoader}; use jujutsu_lib::{dag_walk, file_util, git, revset}; +use once_cell::unsync::OnceCell; use thiserror::Error; use toml_edit; use tracing_subscriber::prelude::*; @@ -546,11 +548,15 @@ impl CommandHelper { /// data is lazily loaded. struct ReadonlyUserRepo { repo: Arc, + id_prefix_context: OnceCell>, } impl ReadonlyUserRepo { fn new(repo: Arc) -> Self { - Self { repo } + Self { + repo, + id_prefix_context: OnceCell::new(), + } } } @@ -584,9 +590,11 @@ impl WorkspaceCommandHelper { // Parse commit_summary template early to report error before starting mutable // operation. // TODO: Parsed template can be cached if it doesn't capture repo + let id_prefix_context = IdPrefixContext::new(repo.as_ref()); parse_commit_summary_template( repo.as_ref(), workspace.workspace_id(), + &id_prefix_context, &template_aliases_map, &settings, )?; @@ -919,6 +927,14 @@ impl WorkspaceCommandHelper { DefaultSymbolResolver::new(self.repo().as_ref(), Some(self.workspace_id())) } + pub fn id_prefix_context(&self) -> &IdPrefixContext<'_> { + self.user_repo.id_prefix_context.get_or_init(|| { + let context: IdPrefixContext<'_> = IdPrefixContext::new(self.user_repo.repo.as_ref()); + let context: IdPrefixContext<'static> = unsafe { std::mem::transmute(context) }; + context + }) + } + pub fn template_aliases_map(&self) -> &TemplateAliasesMap { &self.template_aliases_map } @@ -930,6 +946,7 @@ impl WorkspaceCommandHelper { commit_templater::parse( self.repo().as_ref(), self.workspace_id(), + self.id_prefix_context(), template_text, &self.template_aliases_map, ) @@ -952,6 +969,7 @@ impl WorkspaceCommandHelper { let template = parse_commit_summary_template( self.repo().as_ref(), self.workspace_id(), + self.id_prefix_context(), &self.template_aliases_map, &self.settings, ) @@ -1259,9 +1277,11 @@ impl WorkspaceCommandTransaction<'_> { formatter: &mut dyn Formatter, commit: &Commit, ) -> std::io::Result<()> { + let id_prefix_context = IdPrefixContext::new(self.tx.repo()); let template = parse_commit_summary_template( self.tx.repo(), self.helper.workspace_id(), + &id_prefix_context, &self.helper.template_aliases_map, &self.helper.settings, ) @@ -1696,6 +1716,7 @@ fn load_template_aliases( fn parse_commit_summary_template<'a>( repo: &'a dyn Repo, workspace_id: &WorkspaceId, + id_prefix_context: &'a IdPrefixContext<'a>, aliases_map: &TemplateAliasesMap, settings: &UserSettings, ) -> Result + 'a>, CommandError> { @@ -1703,6 +1724,7 @@ fn parse_commit_summary_template<'a>( Ok(commit_templater::parse( repo, workspace_id, + id_prefix_context, &template_text, aliases_map, )?) diff --git a/src/commit_templater.rs b/src/commit_templater.rs index 27c9d2419..db679c60a 100644 --- a/src/commit_templater.rs +++ b/src/commit_templater.rs @@ -19,6 +19,7 @@ use itertools::Itertools as _; use jujutsu_lib::backend::{ChangeId, CommitId, ObjectId as _}; use jujutsu_lib::commit::Commit; use jujutsu_lib::hex_util::to_reverse_hex; +use jujutsu_lib::id_prefix::IdPrefixContext; use jujutsu_lib::op_store::WorkspaceId; use jujutsu_lib::repo::Repo; use jujutsu_lib::rewrite; @@ -39,6 +40,7 @@ use crate::text_util; struct CommitTemplateLanguage<'repo, 'b> { repo: &'repo dyn Repo, workspace_id: &'b WorkspaceId, + id_prefix_context: &'repo IdPrefixContext<'repo>, } impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo, '_> { @@ -383,11 +385,15 @@ impl CommitOrChangeId { /// The length of the id printed will be the maximum of `total_len` and the /// length of the shortest unique prefix - pub fn shortest(&self, repo: &dyn Repo, total_len: usize) -> ShortestIdPrefix { + pub fn shortest( + &self, + id_prefix_context: &IdPrefixContext, + total_len: usize, + ) -> ShortestIdPrefix { let mut hex = self.hex(); let prefix_len = match self { - CommitOrChangeId::Commit(id) => repo.index().shortest_unique_commit_id_prefix_len(id), - CommitOrChangeId::Change(id) => repo.shortest_unique_change_id_prefix_len(id), + CommitOrChangeId::Commit(id) => id_prefix_context.shortest_commit_prefix_len(id), + CommitOrChangeId::Change(id) => id_prefix_context.shortest_change_prefix_len(id), }; hex.truncate(max(prefix_len, total_len)); let rest = hex.split_off(prefix_len); @@ -422,11 +428,16 @@ fn build_commit_or_change_id_method<'repo>( )) } "shortest" => { - let repo = language.repo; + let id_prefix_context = &language.id_prefix_context; let len_property = parse_optional_integer(function)?; language.wrap_shortest_id_prefix(TemplateFunction::new( (self_property, len_property), - |(id, len)| id.shortest(repo, len.and_then(|l| l.try_into().ok()).unwrap_or(0)), + |(id, len)| { + id.shortest( + id_prefix_context, + len.and_then(|l| l.try_into().ok()).unwrap_or(0), + ) + }, )) } _ => { @@ -504,10 +515,15 @@ fn build_shortest_id_prefix_method<'repo>( pub fn parse<'repo>( repo: &'repo dyn Repo, workspace_id: &WorkspaceId, + id_prefix_context: &'repo IdPrefixContext<'repo>, template_text: &str, aliases_map: &TemplateAliasesMap, ) -> TemplateParseResult + 'repo>> { - let language = CommitTemplateLanguage { repo, workspace_id }; + let language = CommitTemplateLanguage { + repo, + workspace_id, + id_prefix_context, + }; let node = template_parser::parse(template_text, aliases_map)?; template_builder::build(&language, &node) }