blob: 59666fc5f10a3127d1ffc6657cdb8e4491551509 [file] [log] [blame]
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<gix_index::File, worktree::open_index::Error> {
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<worktree::Index, worktree::open_index::Error> {
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<worktree::Index, worktree::open_index::Error> {
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<Option<worktree::Index>, 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<IndexPersistedOrInMemory, crate::repository::index_or_load_from_head::Error> {
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<gix_index::File, gix_traverse::tree::breadthfirst::Error> {
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,
}
}
}