use gix_odb::FindExt; use crate::{config::cache::util::ApplyLeniencyDefault, repository::IndexPersistedOrInMemory, worktree}; /// Index access impl crate::Repository { /// Open a new copy of the index file and decode it entirely. /// /// It will use the `index.threads` configuration key to learn how many threads to use. /// Note that it may fail if there is no index. pub fn open_index(&self) -> Result { let thread_limit = self .config .resolved .string("index", None, "threads") .map(|value| crate::config::tree::Index::THREADS.try_into_index_threads(value)) .transpose() .with_lenient_default(self.config.lenient_config)?; let skip_hash = self .config .resolved .boolean("index", None, "skipHash") .map(|res| crate::config::tree::Index::SKIP_HASH.enrich_error(res)) .transpose() .with_lenient_default(self.config.lenient_config)? .unwrap_or_default(); let index = gix_index::File::at( self.index_path(), self.object_hash(), skip_hash, gix_index::decode::Options { thread_limit, min_extension_block_in_bytes_for_threading: 0, expected_checksum: None, }, )?; Ok(index) } /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file /// on disk has changed. /// /// The index file is shared across all clones of this repository. pub fn index(&self) -> Result { self.try_index().and_then(|opt| match opt { Some(index) => Ok(index), None => Err(worktree::open_index::Error::IndexFile( gix_index::file::init::Error::Io(std::io::Error::new( std::io::ErrorKind::NotFound, format!("Could not find index file at {:?} for opening.", self.index_path()), )), )), }) } /// Return the shared worktree index if present, or return a new empty one which has an association to the place where the index would be. pub fn index_or_empty(&self) -> Result { Ok(self.try_index()?.unwrap_or_else(|| { worktree::Index::new(gix_fs::FileSnapshot::new(gix_index::File::from_state( gix_index::State::new(self.object_hash()), self.index_path(), ))) })) } /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file /// on disk has changed, or `None` if no such file exists. /// /// The index file is shared across all clones of this repository. pub fn try_index(&self) -> Result, worktree::open_index::Error> { self.index.recent_snapshot( || self.index_path().metadata().and_then(|m| m.modified()).ok(), || { self.open_index().map(Some).or_else(|err| match err { worktree::open_index::Error::IndexFile(gix_index::file::init::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => { Ok(None) } err => Err(err), }) }, ) } /// Open the persisted worktree index or generate it from the current `HEAD^{tree}` to live in-memory only. /// /// Use this method to get an index in any repository, even bare ones that don't have one naturally. /// /// ### Note /// /// The locally stored index is not guaranteed to represent `HEAD^{tree}` if this repository is bare - bare repos /// don't naturally have an index and if an index is present it must have been generated by hand. pub fn index_or_load_from_head( &self, ) -> Result { Ok(match self.try_index()? { Some(index) => IndexPersistedOrInMemory::Persisted(index), None => { let tree = self.head_commit()?.tree_id()?; IndexPersistedOrInMemory::InMemory(self.index_from_tree(&tree)?) } }) } /// Create new index-file, which would live at the correct location, in memory from the given `tree`. /// /// Note that this is an expensive operation as it requires recursively traversing the entire tree to unpack it into the index. pub fn index_from_tree( &self, tree: &gix_hash::oid, ) -> Result { Ok(gix_index::File::from_state( gix_index::State::from_tree(tree, |oid, buf| self.objects.find_tree_iter(oid, buf).ok())?, self.git_dir().join("index"), )) } } impl std::ops::Deref for IndexPersistedOrInMemory { type Target = gix_index::File; fn deref(&self) -> &Self::Target { match self { IndexPersistedOrInMemory::Persisted(i) => i, IndexPersistedOrInMemory::InMemory(i) => i, } } } impl IndexPersistedOrInMemory { /// Consume this instance and turn it into an owned index file. /// /// Note that this will cause the persisted index to be cloned, which would happen whenever the repository has a worktree. pub fn into_owned(self) -> gix_index::File { match self { IndexPersistedOrInMemory::Persisted(i) => gix_index::File::clone(&i), IndexPersistedOrInMemory::InMemory(i) => i, } } }