diff --git a/lib/src/default_submodule_store.rs b/lib/src/default_submodule_store.rs new file mode 100644 index 000000000..243c22308 --- /dev/null +++ b/lib/src/default_submodule_store.rs @@ -0,0 +1,48 @@ +// 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 std::path::{Path, PathBuf}; + +use crate::submodule_store::SubmoduleStore; + +#[derive(Debug)] +pub struct DefaultSubmoduleStore { + #[allow(dead_code)] + path: PathBuf, +} + +impl DefaultSubmoduleStore { + /// Load an existing SubmoduleStore + pub fn load(store_path: &Path) -> Self { + DefaultSubmoduleStore { + path: store_path.to_path_buf(), + } + } + + pub fn init(store_path: &Path) -> Self { + DefaultSubmoduleStore { + path: store_path.to_path_buf(), + } + } + + pub fn name() -> &'static str { + "default" + } +} + +impl SubmoduleStore for DefaultSubmoduleStore { + fn name(&self) -> &str { + DefaultSubmoduleStore::name() + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 31ba31ee3..70984f38c 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -25,6 +25,7 @@ pub mod dag_walk; pub mod default_index_store; pub mod default_revset_engine; pub mod default_revset_graph_iterator; +pub mod default_submodule_store; pub mod diff; pub mod file_util; pub mod files; @@ -53,6 +54,7 @@ pub mod simple_op_heads_store; pub mod simple_op_store; pub mod stacked_table; pub mod store; +pub mod submodule_store; pub mod transaction; pub mod tree; pub mod tree_builder; diff --git a/lib/src/repo.rs b/lib/src/repo.rs index b0c1aab5d..ad8fcad4a 100644 --- a/lib/src/repo.rs +++ b/lib/src/repo.rs @@ -30,6 +30,7 @@ use crate::backend::{Backend, BackendError, BackendResult, ChangeId, CommitId, O use crate::commit::Commit; use crate::commit_builder::CommitBuilder; use crate::default_index_store::DefaultIndexStore; +use crate::default_submodule_store::DefaultSubmoduleStore; use crate::git_backend::GitBackend; use crate::index::{HexPrefix, Index, IndexStore, MutableIndex, PrefixResolution, ReadonlyIndex}; use crate::local_backend::LocalBackend; @@ -43,6 +44,7 @@ use crate::settings::{RepoSettings, UserSettings}; use crate::simple_op_heads_store::SimpleOpHeadsStore; use crate::simple_op_store::SimpleOpStore; use crate::store::Store; +use crate::submodule_store::SubmoduleStore; use crate::transaction::Transaction; use crate::view::{RefName, View}; use crate::{backend, dag_walk, op_store}; @@ -56,6 +58,8 @@ pub trait Repo { fn view(&self) -> &View; + fn submodule_store(&self) -> &Arc; + fn resolve_change_id(&self, change_id: &ChangeId) -> Option> { // Replace this if we added more efficient lookup method. let prefix = HexPrefix::from_bytes(change_id.as_bytes()); @@ -79,6 +83,7 @@ pub struct ReadonlyRepo { operation: Operation, settings: RepoSettings, index_store: Arc, + submodule_store: Arc, index: OnceCell>>, // Declared after `change_id_index` since it must outlive it on drop. change_id_index: OnceCell>, @@ -111,6 +116,10 @@ impl ReadonlyRepo { |store_path| Box::new(DefaultIndexStore::init(store_path)) } + pub fn default_submodule_store_factory() -> impl FnOnce(&Path) -> Box { + |store_path| Box::new(DefaultSubmoduleStore::init(store_path)) + } + pub fn init( user_settings: &UserSettings, repo_path: &Path, @@ -118,6 +127,7 @@ impl ReadonlyRepo { op_store_factory: impl FnOnce(&Path) -> Box, op_heads_store_factory: impl FnOnce(&Path) -> Box, index_store_factory: impl FnOnce(&Path) -> Box, + submodule_store_factory: impl FnOnce(&Path) -> Box, ) -> Result, PathError> { let repo_path = repo_path.canonicalize().context(repo_path)?; @@ -167,6 +177,14 @@ impl ReadonlyRepo { fs::write(&index_type_path, index_store.name()).context(&index_type_path)?; let index_store = Arc::from(index_store); + let submodule_store_path = repo_path.join("submodule_store"); + fs::create_dir(&submodule_store_path).context(&submodule_store_path)?; + let submodule_store = submodule_store_factory(&submodule_store_path); + let submodule_store_type_path = submodule_store_path.join("type"); + fs::write(&submodule_store_type_path, submodule_store.name()) + .context(&submodule_store_type_path)?; + let submodule_store = Arc::from(submodule_store); + let view = View::new(root_view); Ok(Arc::new(ReadonlyRepo { repo_path, @@ -179,6 +197,7 @@ impl ReadonlyRepo { index: OnceCell::new(), change_id_index: OnceCell::new(), view, + submodule_store, })) } @@ -190,6 +209,7 @@ impl ReadonlyRepo { op_store: self.op_store.clone(), op_heads_store: self.op_heads_store.clone(), index_store: self.index_store.clone(), + submodule_store: self.submodule_store.clone(), } } @@ -289,6 +309,10 @@ impl Repo for ReadonlyRepo { &self.view } + fn submodule_store(&self) -> &Arc { + &self.submodule_store + } + fn resolve_change_id_prefix(&self, prefix: &HexPrefix) -> PrefixResolution> { self.change_id_index().resolve_prefix(prefix) } @@ -302,12 +326,14 @@ type BackendFactory = Box Box>; type OpStoreFactory = Box Box>; type OpHeadsStoreFactory = Box Box>; type IndexStoreFactory = Box Box>; +type SubmoduleStoreFactory = Box Box>; pub struct StoreFactories { backend_factories: HashMap, op_store_factories: HashMap, op_heads_store_factories: HashMap, index_store_factories: HashMap, + submodule_store_factories: HashMap, } impl Default for StoreFactories { @@ -342,6 +368,12 @@ impl Default for StoreFactories { Box::new(|store_path| Box::new(DefaultIndexStore::load(store_path))), ); + // SubmoduleStores + factories.add_submodule_store( + DefaultSubmoduleStore::name(), + Box::new(|store_path| Box::new(DefaultSubmoduleStore::load(store_path))), + ); + factories } } @@ -367,6 +399,7 @@ impl StoreFactories { op_store_factories: HashMap::new(), op_heads_store_factories: HashMap::new(), index_store_factories: HashMap::new(), + submodule_store_factories: HashMap::new(), } } @@ -503,6 +536,43 @@ impl StoreFactories { })?; Ok(index_store_factory(store_path)) } + + pub fn add_submodule_store(&mut self, name: &str, factory: SubmoduleStoreFactory) { + self.submodule_store_factories + .insert(name.to_string(), factory); + } + + pub fn load_submodule_store( + &self, + store_path: &Path, + ) -> Result, StoreLoadError> { + let submodule_store_type = match fs::read_to_string(store_path.join("type")) { + Ok(content) => content, + Err(err) if err.kind() == ErrorKind::NotFound => { + // For compatibility with repos without repo/submodule_store. + // TODO Delete in TBD version + let default_type = DefaultSubmoduleStore::name().to_string(); + fs::create_dir(store_path).unwrap(); + fs::write(store_path.join("type"), &default_type).unwrap(); + default_type + } + Err(err) => { + return Err(StoreLoadError::ReadError { + store: "submodule_store", + source: err, + }); + } + }; + let submodule_store_factory = self + .submodule_store_factories + .get(&submodule_store_type) + .ok_or_else(|| StoreLoadError::UnsupportedType { + store: "submodule_store", + store_type: submodule_store_type.to_string(), + })?; + + Ok(submodule_store_factory(store_path)) + } } #[derive(Clone)] @@ -513,6 +583,7 @@ pub struct RepoLoader { op_store: Arc, op_heads_store: Arc, index_store: Arc, + submodule_store: Arc, } impl RepoLoader { @@ -527,6 +598,8 @@ impl RepoLoader { let op_heads_store = Arc::from(store_factories.load_op_heads_store(&repo_path.join("op_heads"))?); let index_store = Arc::from(store_factories.load_index_store(&repo_path.join("index"))?); + let submodule_store = + Arc::from(store_factories.load_submodule_store(&repo_path.join("submodule_store"))?); Ok(Self { repo_path: repo_path.to_path_buf(), repo_settings, @@ -534,6 +607,7 @@ impl RepoLoader { op_store, op_heads_store, index_store, + submodule_store, }) } @@ -589,6 +663,7 @@ impl RepoLoader { operation, settings: self.repo_settings.clone(), index_store: self.index_store.clone(), + submodule_store: self.submodule_store.clone(), index: OnceCell::with_value(Box::into_pin(index)), change_id_index: OnceCell::new(), view, @@ -620,6 +695,7 @@ impl RepoLoader { operation, settings: self.repo_settings.clone(), index_store: self.index_store.clone(), + submodule_store: self.submodule_store.clone(), index: OnceCell::new(), change_id_index: OnceCell::new(), view, @@ -1168,6 +1244,10 @@ impl Repo for MutableRepo { self.index.as_index() } + fn submodule_store(&self) -> &Arc { + self.base_repo.submodule_store() + } + fn view(&self) -> &View { self.view .get_or_ensure_clean(|v| self.enforce_view_invariants(v)) diff --git a/lib/src/submodule_store.rs b/lib/src/submodule_store.rs new file mode 100644 index 000000000..6b0359cbc --- /dev/null +++ b/lib/src/submodule_store.rs @@ -0,0 +1,19 @@ +// 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 std::fmt::Debug; + +pub trait SubmoduleStore: Send + Sync + Debug { + fn name(&self) -> &str; +} diff --git a/lib/src/workspace.rs b/lib/src/workspace.rs index fdcca87e7..289612691 100644 --- a/lib/src/workspace.rs +++ b/lib/src/workspace.rs @@ -30,6 +30,7 @@ use crate::repo::{ StoreLoadError, }; use crate::settings::UserSettings; +use crate::submodule_store::SubmoduleStore; use crate::working_copy::WorkingCopy; #[derive(Error, Debug)] @@ -163,6 +164,7 @@ impl Workspace { op_store_factory: impl FnOnce(&Path) -> Box, op_heads_store_factory: impl FnOnce(&Path) -> Box, index_store_factory: impl FnOnce(&Path) -> Box, + submodule_store_factory: impl FnOnce(&Path) -> Box, ) -> Result<(Self, Arc), WorkspaceInitError> { let jj_dir = create_jj_dir(workspace_root)?; let repo_dir = jj_dir.join("repo"); @@ -174,6 +176,7 @@ impl Workspace { op_store_factory, op_heads_store_factory, index_store_factory, + submodule_store_factory, )?; let (working_copy, repo) = init_working_copy( user_settings, @@ -199,6 +202,7 @@ impl Workspace { ReadonlyRepo::default_op_store_factory(), ReadonlyRepo::default_op_heads_store_factory(), ReadonlyRepo::default_index_store_factory(), + ReadonlyRepo::default_submodule_store_factory(), ) } diff --git a/lib/tests/test_git.rs b/lib/tests/test_git.rs index c40cf7afb..44e3af15d 100644 --- a/lib/tests/test_git.rs +++ b/lib/tests/test_git.rs @@ -854,6 +854,7 @@ impl GitRepoData { ReadonlyRepo::default_op_store_factory(), ReadonlyRepo::default_op_heads_store_factory(), ReadonlyRepo::default_index_store_factory(), + ReadonlyRepo::default_submodule_store_factory(), ) .unwrap(); Self { @@ -1420,6 +1421,7 @@ fn test_init() { ReadonlyRepo::default_op_store_factory(), ReadonlyRepo::default_op_heads_store_factory(), ReadonlyRepo::default_index_store_factory(), + ReadonlyRepo::default_submodule_store_factory(), ) .unwrap(); // The refs were *not* imported -- it's the caller's responsibility to import @@ -1679,6 +1681,7 @@ fn set_up_push_repos(settings: &UserSettings, temp_dir: &TempDir) -> PushTestSet ReadonlyRepo::default_op_store_factory(), ReadonlyRepo::default_op_heads_store_factory(), ReadonlyRepo::default_index_store_factory(), + ReadonlyRepo::default_submodule_store_factory(), ) .unwrap(); let mut tx = jj_repo.start_transaction(settings, "test"); diff --git a/lib/testutils/src/lib.rs b/lib/testutils/src/lib.rs index 7b9986989..d5e4b6cfa 100644 --- a/lib/testutils/src/lib.rs +++ b/lib/testutils/src/lib.rs @@ -99,6 +99,7 @@ impl TestRepo { ReadonlyRepo::default_op_store_factory(), ReadonlyRepo::default_op_heads_store_factory(), ReadonlyRepo::default_index_store_factory(), + ReadonlyRepo::default_submodule_store_factory(), ) .unwrap() } else { @@ -109,6 +110,7 @@ impl TestRepo { ReadonlyRepo::default_op_store_factory(), ReadonlyRepo::default_op_heads_store_factory(), ReadonlyRepo::default_index_store_factory(), + ReadonlyRepo::default_submodule_store_factory(), ) .unwrap() };