summaryrefslogtreecommitdiffstats
path: root/vendor/gix-index/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-index/src')
-rw-r--r--vendor/gix-index/src/access/mod.rs205
-rw-r--r--vendor/gix-index/src/decode/mod.rs12
-rw-r--r--vendor/gix-index/src/entry/mode.rs28
-rw-r--r--vendor/gix-index/src/entry/stat.rs4
-rw-r--r--vendor/gix-index/src/extension/link.rs2
-rw-r--r--vendor/gix-index/src/extension/tree/verify.rs1
-rw-r--r--vendor/gix-index/src/file/init.rs61
-rw-r--r--vendor/gix-index/src/file/verify.rs34
-rw-r--r--vendor/gix-index/src/file/write.rs27
-rw-r--r--vendor/gix-index/src/init.rs1
-rw-r--r--vendor/gix-index/src/verify.rs1
-rw-r--r--vendor/gix-index/src/write.rs18
12 files changed, 326 insertions, 68 deletions
diff --git a/vendor/gix-index/src/access/mod.rs b/vendor/gix-index/src/access/mod.rs
index d07a55bf0..08cb23020 100644
--- a/vendor/gix-index/src/access/mod.rs
+++ b/vendor/gix-index/src/access/mod.rs
@@ -1,4 +1,5 @@
use std::cmp::Ordering;
+use std::ops::Range;
use bstr::{BStr, ByteSlice, ByteVec};
use filetime::FileTime;
@@ -70,9 +71,67 @@ impl State {
///
/// Use the index for accessing multiple stages if they exists, but at least the single matching entry.
pub fn entry_index_by_path_and_stage(&self, path: &BStr, stage: entry::Stage) -> Option<usize> {
- self.entries
- .binary_search_by(|e| e.path(self).cmp(path).then_with(|| e.stage().cmp(&stage)))
- .ok()
+ let mut stage_cmp = Ordering::Equal;
+ let idx = self
+ .entries
+ .binary_search_by(|e| {
+ let res = e.path(self).cmp(path);
+ if res.is_eq() {
+ stage_cmp = e.stage().cmp(&stage);
+ }
+ res
+ })
+ .ok()?;
+ self.entry_index_by_idx_and_stage(path, idx, stage, stage_cmp)
+ }
+
+ /// Walk as far in `direction` as possible, with [`Ordering::Greater`] towards higher stages, and [`Ordering::Less`]
+ /// towards lower stages, and return the lowest or highest seen stage.
+ /// Return `None` if there is no greater or smaller stage.
+ fn walk_entry_stages(&self, path: &BStr, base: usize, direction: Ordering) -> Option<usize> {
+ match direction {
+ Ordering::Greater => self
+ .entries
+ .get(base + 1..)?
+ .iter()
+ .enumerate()
+ .take_while(|(_, e)| e.path(self) == path)
+ .last()
+ .map(|(idx, _)| base + 1 + idx),
+ Ordering::Equal => Some(base),
+ Ordering::Less => self.entries[..base]
+ .iter()
+ .enumerate()
+ .rev()
+ .take_while(|(_, e)| e.path(self) == path)
+ .last()
+ .map(|(idx, _)| idx),
+ }
+ }
+
+ fn entry_index_by_idx_and_stage(
+ &self,
+ path: &BStr,
+ idx: usize,
+ wanted_stage: entry::Stage,
+ stage_cmp: Ordering,
+ ) -> Option<usize> {
+ match stage_cmp {
+ Ordering::Greater => self.entries[..idx]
+ .iter()
+ .enumerate()
+ .rev()
+ .take_while(|(_, e)| e.path(self) == path)
+ .find_map(|(idx, e)| (e.stage() == wanted_stage).then_some(idx)),
+ Ordering::Equal => Some(idx),
+ Ordering::Less => self
+ .entries
+ .get(idx + 1..)?
+ .iter()
+ .enumerate()
+ .take_while(|(_, e)| e.path(self) == path)
+ .find_map(|(ofs, e)| (e.stage() == wanted_stage).then_some(idx + ofs + 1)),
+ }
}
/// Find the entry index in [`entries()[..upper_bound]`][State::entries()] matching the given repository-relative
@@ -101,6 +160,68 @@ impl State {
.map(|idx| &self.entries[idx])
}
+ /// Return the entry at `path` that is either at stage 0, or at stage 2 (ours) in case of a merge conflict.
+ ///
+ /// Using this method is more efficient in comparison to doing two searches, one for stage 0 and one for stage 2.
+ pub fn entry_by_path(&self, path: &BStr) -> Option<&Entry> {
+ let mut stage_at_index = 0;
+ let idx = self
+ .entries
+ .binary_search_by(|e| {
+ let res = e.path(self).cmp(path);
+ if res.is_eq() {
+ stage_at_index = e.stage();
+ }
+ res
+ })
+ .ok()?;
+ let idx = if stage_at_index == 0 || stage_at_index == 2 {
+ idx
+ } else {
+ self.entry_index_by_idx_and_stage(path, idx, 2, stage_at_index.cmp(&2))?
+ };
+ Some(&self.entries[idx])
+ }
+
+ /// Return the slice of entries which all share the same `prefix`, or `None` if there isn't a single such entry.
+ ///
+ /// If `prefix` is empty, all entries are returned.
+ pub fn prefixed_entries(&self, prefix: &BStr) -> Option<&[Entry]> {
+ self.prefixed_entries_range(prefix).map(|range| &self.entries[range])
+ }
+
+ /// Return the range of entries which all share the same `prefix`, or `None` if there isn't a single such entry.
+ ///
+ /// If `prefix` is empty, the range will include all entries.
+ pub fn prefixed_entries_range(&self, prefix: &BStr) -> Option<Range<usize>> {
+ if prefix.is_empty() {
+ return Some(0..self.entries.len());
+ }
+ let prefix_len = prefix.len();
+ let mut low = self.entries.partition_point(|e| {
+ e.path(self)
+ .get(..prefix_len)
+ .map_or_else(|| e.path(self) <= &prefix[..e.path.len()], |p| p < prefix)
+ });
+ let mut high = low
+ + self.entries[low..].partition_point(|e| e.path(self).get(..prefix_len).map_or(false, |p| p <= prefix));
+
+ let low_entry = &self.entries.get(low)?;
+ if low_entry.stage() != 0 {
+ low = self
+ .walk_entry_stages(low_entry.path(self), low, Ordering::Less)
+ .unwrap_or(low);
+ }
+ if let Some(high_entry) = self.entries.get(high) {
+ if high_entry.stage() != 0 {
+ high = self
+ .walk_entry_stages(high_entry.path(self), high, Ordering::Less)
+ .unwrap_or(high);
+ }
+ }
+ (low != high).then_some(low..high)
+ }
+
/// Return the entry at `idx` or _panic_ if the index is out of bounds.
///
/// The `idx` is typically returned by [`entry_by_path_and_stage()`][State::entry_by_path_and_stage()].
@@ -114,6 +235,30 @@ impl State {
pub fn is_sparse(&self) -> bool {
self.is_sparse
}
+
+ /// Return the range of entries that exactly match the given `path`, in all available stages, or `None` if no entry with such
+ /// path exists.
+ ///
+ /// The range can be used to access the respective entries via [`entries()`](Self::entries()) or [`entries_mut()](Self::entries_mut()).
+ pub fn entry_range(&self, path: &BStr) -> Option<Range<usize>> {
+ let mut stage_at_index = 0;
+ let idx = self
+ .entries
+ .binary_search_by(|e| {
+ let res = e.path(self).cmp(path);
+ if res.is_eq() {
+ stage_at_index = e.stage();
+ }
+ res
+ })
+ .ok()?;
+
+ let (start, end) = (
+ self.walk_entry_stages(path, idx, Ordering::Less).unwrap_or(idx),
+ self.walk_entry_stages(path, idx, Ordering::Greater).unwrap_or(idx) + 1,
+ );
+ Some(start..end)
+ }
}
/// Mutation
@@ -224,6 +369,25 @@ impl State {
.then_with(|| compare(a, b))
});
}
+
+ /// Physically remove all entries for which `should_remove(idx, path, entry)` returns `true`, traversing them from first to last.
+ ///
+ /// Note that the memory used for the removed entries paths is not freed, as it's append-only.
+ ///
+ /// ### Performance
+ ///
+ /// To implement this operation typically, one would rather add [entry::Flags::REMOVE] to each entry to remove
+ /// them when [writing the index](Self::write_to()).
+ pub fn remove_entries(&mut self, mut should_remove: impl FnMut(usize, &BStr, &mut Entry) -> bool) {
+ let mut index = 0;
+ let paths = &self.path_backing;
+ self.entries.retain_mut(|e| {
+ let path = e.path_in(paths);
+ let res = !should_remove(index, path, e);
+ index += 1;
+ res
+ });
+ }
}
/// Extensions
@@ -249,3 +413,38 @@ impl State {
self.fs_monitor.as_ref()
}
}
+
+#[cfg(test)]
+mod tests {
+ use std::path::{Path, PathBuf};
+
+ #[test]
+ fn entry_by_path_with_conflicting_file() {
+ let file = PathBuf::from("tests")
+ .join("fixtures")
+ .join(Path::new("loose_index").join("conflicting-file.git-index"));
+ let file = crate::File::at(file, gix_hash::Kind::Sha1, false, Default::default()).expect("valid file");
+ assert_eq!(
+ file.entries().len(),
+ 3,
+ "we have a set of conflict entries for a single file"
+ );
+ for idx in 0..3 {
+ for wanted_stage in 1..=3 {
+ let actual_idx = file
+ .entry_index_by_idx_and_stage(
+ "file".into(),
+ idx,
+ wanted_stage,
+ (idx + 1).cmp(&(wanted_stage as usize)),
+ )
+ .expect("found");
+ assert_eq!(
+ actual_idx + 1,
+ wanted_stage as usize,
+ "the index and stage have a relation, and that is upheld if we search correctly"
+ );
+ }
+ }
+ }
+}
diff --git a/vendor/gix-index/src/decode/mod.rs b/vendor/gix-index/src/decode/mod.rs
index f51a5d5e9..12c8c53e4 100644
--- a/vendor/gix-index/src/decode/mod.rs
+++ b/vendor/gix-index/src/decode/mod.rs
@@ -54,7 +54,7 @@ pub struct Options {
impl State {
/// Decode an index state from `data` and store `timestamp` in the resulting instance for pass-through, assuming `object_hash`
- /// to be used through the file.
+ /// to be used through the file. Also return the stored hash over all bytes in `data` or `None` if none was written due to `index.skipHash`.
pub fn from_bytes(
data: &[u8],
timestamp: FileTime,
@@ -64,7 +64,8 @@ impl State {
min_extension_block_in_bytes_for_threading,
expected_checksum,
}: Options,
- ) -> Result<(Self, gix_hash::ObjectId), Error> {
+ ) -> Result<(Self, Option<gix_hash::ObjectId>), Error> {
+ let _span = gix_features::trace::detail!("gix_index::State::from_bytes()");
let (version, num_entries, post_header_data) = header::decode(data, object_hash)?;
let start_of_extensions = extension::end_of_index_entry::decode(data, object_hash);
@@ -213,10 +214,11 @@ impl State {
}
let checksum = gix_hash::ObjectId::from(data);
- if let Some(expected_checksum) = expected_checksum {
- if checksum != expected_checksum {
+ let checksum = (!checksum.is_null()).then_some(checksum);
+ if let Some((expected_checksum, actual_checksum)) = expected_checksum.zip(checksum) {
+ if actual_checksum != expected_checksum {
return Err(Error::ChecksumMismatch {
- actual_checksum: checksum,
+ actual_checksum,
expected_checksum,
});
}
diff --git a/vendor/gix-index/src/entry/mode.rs b/vendor/gix-index/src/entry/mode.rs
index 7d3fdf506..0301df438 100644
--- a/vendor/gix-index/src/entry/mode.rs
+++ b/vendor/gix-index/src/entry/mode.rs
@@ -1,24 +1,16 @@
use crate::entry::Mode;
-#[cfg(unix)]
-/// Returns whether a a file has the executable permission set.
-fn is_executable(metadata: &std::fs::Metadata) -> bool {
- use std::os::unix::fs::MetadataExt;
- (metadata.mode() & 0o100) != 0
-}
-
-#[cfg(not(unix))]
-/// Returns whether a a file has the executable permission set.
-fn is_executable(_metadata: &std::fs::Metadata) -> bool {
- false
-}
-
impl Mode {
- /// Return true if this is a sparse entry, as it points to a directory which usually isn't what an 'unsparse' index tracks.
+ /// Return `true` if this is a sparse entry, as it points to a directory which usually isn't what an 'unsparse' index tracks.
pub fn is_sparse(&self) -> bool {
*self == Self::DIR
}
+ /// Return `true` if this is a submodule entry.
+ pub fn is_submodule(&self) -> bool {
+ *self == Self::DIR | Self::SYMLINK
+ }
+
/// Compares this mode to the file system version ([`std::fs::symlink_metadata`])
/// and returns the change needed to update this mode to match the file.
///
@@ -54,13 +46,15 @@ impl Mode {
Mode::SYMLINK if has_symlinks && !stat.is_symlink() => (),
Mode::SYMLINK if !has_symlinks && !stat.is_file() => (),
Mode::COMMIT | Mode::DIR if !stat.is_dir() => (),
- Mode::FILE if executable_bit && is_executable(stat) => return Some(Change::ExecutableBit),
- Mode::FILE_EXECUTABLE if executable_bit && !is_executable(stat) => return Some(Change::ExecutableBit),
+ Mode::FILE if executable_bit && gix_fs::is_executable(stat) => return Some(Change::ExecutableBit),
+ Mode::FILE_EXECUTABLE if executable_bit && !gix_fs::is_executable(stat) => {
+ return Some(Change::ExecutableBit)
+ }
_ => return None,
};
let new_mode = if stat.is_dir() {
Mode::COMMIT
- } else if executable_bit && is_executable(stat) {
+ } else if executable_bit && gix_fs::is_executable(stat) {
Mode::FILE_EXECUTABLE
} else {
Mode::FILE
diff --git a/vendor/gix-index/src/entry/stat.rs b/vendor/gix-index/src/entry/stat.rs
index 65063dc16..7bde71763 100644
--- a/vendor/gix-index/src/entry/stat.rs
+++ b/vendor/gix-index/src/entry/stat.rs
@@ -95,8 +95,8 @@ impl Stat {
use std::os::unix::fs::MetadataExt;
#[cfg(unix)]
let res = Stat {
- mtime: mtime.try_into()?,
- ctime: ctime.try_into()?,
+ mtime: mtime.try_into().unwrap_or_default(),
+ ctime: ctime.try_into().unwrap_or_default(),
// truncating to 32 bits is fine here because
// that's what the linux syscalls returns
// just rust upcasts to 64 bits for some reason?
diff --git a/vendor/gix-index/src/extension/link.rs b/vendor/gix-index/src/extension/link.rs
index 20ce9cb21..5fed2f960 100644
--- a/vendor/gix-index/src/extension/link.rs
+++ b/vendor/gix-index/src/extension/link.rs
@@ -72,6 +72,7 @@ impl Link {
self,
split_index: &mut crate::File,
object_hash: gix_hash::Kind,
+ skip_hash: bool,
options: crate::decode::Options,
) -> Result<(), crate::file::init::Error> {
let shared_index_path = split_index
@@ -82,6 +83,7 @@ impl Link {
let mut shared_index = crate::File::at(
&shared_index_path,
object_hash,
+ skip_hash,
crate::decode::Options {
expected_checksum: self.shared_index_checksum.into(),
..options
diff --git a/vendor/gix-index/src/extension/tree/verify.rs b/vendor/gix-index/src/extension/tree/verify.rs
index 6280cecf8..793f31325 100644
--- a/vendor/gix-index/src/extension/tree/verify.rs
+++ b/vendor/gix-index/src/extension/tree/verify.rs
@@ -111,6 +111,7 @@ impl Tree {
}
Ok(entries.into())
}
+ let _span = gix_features::trace::coarse!("gix_index::extension::Tree::verify()");
if !self.name.is_empty() {
return Err(Error::RootWithName {
diff --git a/vendor/gix-index/src/file/init.rs b/vendor/gix-index/src/file/init.rs
index 534f1f08b..99f4be258 100644
--- a/vendor/gix-index/src/file/init.rs
+++ b/vendor/gix-index/src/file/init.rs
@@ -26,16 +26,18 @@ pub use error::Error;
/// Initialization
impl File {
/// Try to open the index file at `path` with `options`, assuming `object_hash` is used throughout the file, or create a new
- /// index that merely exists in memory and is empty.
+ /// index that merely exists in memory and is empty. `skip_hash` will increase the performance by a factor of 2, at the cost of
+ /// possibly not detecting corruption.
///
/// Note that the `path` will not be written if it doesn't exist.
pub fn at_or_default(
path: impl Into<PathBuf>,
object_hash: gix_hash::Kind,
+ skip_hash: bool,
options: decode::Options,
) -> Result<Self, Error> {
let path = path.into();
- Ok(match Self::at(&path, object_hash, options) {
+ Ok(match Self::at(&path, object_hash, skip_hash, options) {
Ok(f) => f,
Err(Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
File::from_state(State::new(object_hash), path)
@@ -44,25 +46,60 @@ impl File {
})
}
- /// Open an index file at `path` with `options`, assuming `object_hash` is used throughout the file.
- pub fn at(path: impl Into<PathBuf>, object_hash: gix_hash::Kind, options: decode::Options) -> Result<Self, Error> {
+ /// Open an index file at `path` with `options`, assuming `object_hash` is used throughout the file. If `skip_hash` is `true`,
+ /// we will not get or compare the checksum of the index at all, which generally increases performance of this method by a factor
+ /// of 2 or more.
+ ///
+ /// Note that the verification of the file hash depends on `options`, and even then it's performed after the file was read and not
+ /// before it is read. That way, invalid files would see a more descriptive error message as we try to parse them.
+ pub fn at(
+ path: impl Into<PathBuf>,
+ object_hash: gix_hash::Kind,
+ skip_hash: bool,
+ options: decode::Options,
+ ) -> Result<Self, Error> {
+ let _span = gix_features::trace::detail!("gix_index::File::at()");
let path = path.into();
let (data, mtime) = {
+ let mut file = std::fs::File::open(&path)?;
// SAFETY: we have to take the risk of somebody changing the file underneath. Git never writes into the same file.
- let file = std::fs::File::open(&path)?;
#[allow(unsafe_code)]
let data = unsafe { Mmap::map(&file)? };
+
+ if !skip_hash {
+ // Note that even though it's trivial to offload this into a thread, which is worth it for all but the smallest
+ // index files, we choose more safety here just like git does and don't even try to decode the index if the hashes
+ // don't match.
+ // Thanks to `skip_hash`, we can get performance and it's under caller control, at the cost of some safety.
+ let expected = gix_hash::ObjectId::from(&data[data.len() - object_hash.len_in_bytes()..]);
+ if !expected.is_null() {
+ let _span = gix_features::trace::detail!("gix::open_index::hash_index", path = ?path);
+ let meta = file.metadata()?;
+ let num_bytes_to_hash = meta.len() - object_hash.len_in_bytes() as u64;
+ let actual_hash = gix_features::hash::bytes(
+ &mut file,
+ num_bytes_to_hash as usize,
+ object_hash,
+ &mut gix_features::progress::Discard,
+ &Default::default(),
+ )?;
+
+ if actual_hash != expected {
+ return Err(Error::Decode(decode::Error::ChecksumMismatch {
+ actual_checksum: actual_hash,
+ expected_checksum: expected,
+ }));
+ }
+ }
+ }
+
(data, filetime::FileTime::from_last_modification_time(&file.metadata()?))
};
let (state, checksum) = State::from_bytes(&data, mtime, object_hash, options)?;
- let mut file = File {
- state,
- path,
- checksum: Some(checksum),
- };
+ let mut file = File { state, path, checksum };
if let Some(mut link) = file.link.take() {
- link.dissolve_into(&mut file, object_hash, options)?;
+ link.dissolve_into(&mut file, object_hash, skip_hash, options)?;
}
Ok(file)
@@ -71,7 +108,7 @@ impl File {
/// Consume `state` and pretend it was read from `path`, setting our checksum to `null`.
///
/// `File` instances created like that should be written to disk to set the correct checksum via `[File::write()]`.
- pub fn from_state(state: crate::State, path: impl Into<PathBuf>) -> Self {
+ pub fn from_state(state: State, path: impl Into<PathBuf>) -> Self {
File {
state,
path: path.into(),
diff --git a/vendor/gix-index/src/file/verify.rs b/vendor/gix-index/src/file/verify.rs
index 6743b37a7..3890acd95 100644
--- a/vendor/gix-index/src/file/verify.rs
+++ b/vendor/gix-index/src/file/verify.rs
@@ -14,8 +14,6 @@ mod error {
actual: gix_hash::ObjectId,
expected: gix_hash::ObjectId,
},
- #[error("Checksum of in-memory index wasn't computed yet")]
- NoChecksum,
}
}
pub use error::Error;
@@ -23,19 +21,23 @@ pub use error::Error;
impl File {
/// Verify the integrity of the index to assure its consistency.
pub fn verify_integrity(&self) -> Result<(), Error> {
- let checksum = self.checksum.ok_or(Error::NoChecksum)?;
- let num_bytes_to_hash = self.path.metadata()?.len() - checksum.as_bytes().len() as u64;
- let should_interrupt = AtomicBool::new(false);
- let actual = gix_features::hash::bytes_of_file(
- &self.path,
- num_bytes_to_hash as usize,
- checksum.kind(),
- &mut gix_features::progress::Discard,
- &should_interrupt,
- )?;
- (actual == checksum).then_some(()).ok_or(Error::ChecksumMismatch {
- actual,
- expected: checksum,
- })
+ let _span = gix_features::trace::coarse!("gix_index::File::verify_integrity()");
+ if let Some(checksum) = self.checksum {
+ let num_bytes_to_hash = self.path.metadata()?.len() - checksum.as_bytes().len() as u64;
+ let should_interrupt = AtomicBool::new(false);
+ let actual = gix_features::hash::bytes_of_file(
+ &self.path,
+ num_bytes_to_hash as usize,
+ checksum.kind(),
+ &mut gix_features::progress::Discard,
+ &should_interrupt,
+ )?;
+ (actual == checksum).then_some(()).ok_or(Error::ChecksumMismatch {
+ actual,
+ expected: checksum,
+ })
+ } else {
+ Ok(())
+ }
}
}
diff --git a/vendor/gix-index/src/file/write.rs b/vendor/gix-index/src/file/write.rs
index 1e8afc07d..47a4cde96 100644
--- a/vendor/gix-index/src/file/write.rs
+++ b/vendor/gix-index/src/file/write.rs
@@ -22,23 +22,28 @@ impl File {
mut out: impl std::io::Write,
options: write::Options,
) -> std::io::Result<(Version, gix_hash::ObjectId)> {
- let mut hasher = hash::Write::new(&mut out, self.state.object_hash);
- let version = self.state.write_to(&mut hasher, options)?;
-
- let hash = hasher.hash.digest();
- out.write_all(&hash)?;
- Ok((version, gix_hash::ObjectId::from(hash)))
+ let (version, hash) = if options.skip_hash {
+ let out: &mut dyn std::io::Write = &mut out;
+ let version = self.state.write_to(out, options)?;
+ (version, self.state.object_hash.null())
+ } else {
+ let mut hasher = hash::Write::new(&mut out, self.state.object_hash);
+ let out: &mut dyn std::io::Write = &mut hasher;
+ let version = self.state.write_to(out, options)?;
+ (version, gix_hash::ObjectId::from(hasher.hash.digest()))
+ };
+ out.write_all(hash.as_slice())?;
+ Ok((version, hash))
}
/// Write ourselves to the path we were read from after acquiring a lock, using `options`.
///
/// Note that the hash produced will be stored which is why we need to be mutable.
pub fn write(&mut self, options: write::Options) -> Result<(), Error> {
- let mut lock = std::io::BufWriter::new(gix_lock::File::acquire_to_update_resource(
- &self.path,
- gix_lock::acquire::Fail::Immediately,
- None,
- )?);
+ let mut lock = std::io::BufWriter::with_capacity(
+ 64 * 1024,
+ gix_lock::File::acquire_to_update_resource(&self.path, gix_lock::acquire::Fail::Immediately, None)?,
+ );
let (version, digest) = self.write_to(&mut lock, options)?;
match lock.into_inner() {
Ok(lock) => lock.commit()?,
diff --git a/vendor/gix-index/src/init.rs b/vendor/gix-index/src/init.rs
index abd71ffdd..9fe0b8e27 100644
--- a/vendor/gix-index/src/init.rs
+++ b/vendor/gix-index/src/init.rs
@@ -39,6 +39,7 @@ mod from_tree {
where
Find: for<'a> FnMut(&gix_hash::oid, &'a mut Vec<u8>) -> Option<TreeRefIter<'a>>,
{
+ let _span = gix_features::trace::coarse!("gix_index::State::from_tree()");
let mut buf = Vec::new();
let root = find(tree, &mut buf).ok_or(breadthfirst::Error::NotFound { oid: tree.into() })?;
diff --git a/vendor/gix-index/src/verify.rs b/vendor/gix-index/src/verify.rs
index ba7ec3872..7782cccbc 100644
--- a/vendor/gix-index/src/verify.rs
+++ b/vendor/gix-index/src/verify.rs
@@ -42,6 +42,7 @@ pub mod extensions {
impl State {
/// Assure our entries are consistent.
pub fn verify_entries(&self) -> Result<(), entries::Error> {
+ let _span = gix_features::trace::coarse!("gix_index::File::verify_entries()");
let mut previous = None::<&crate::Entry>;
for (idx, entry) in self.entries.iter().enumerate() {
if let Some(prev) = previous {
diff --git a/vendor/gix-index/src/write.rs b/vendor/gix-index/src/write.rs
index 43f9b3255..2050ed809 100644
--- a/vendor/gix-index/src/write.rs
+++ b/vendor/gix-index/src/write.rs
@@ -48,13 +48,27 @@ impl Extensions {
/// Note that default options write either index V2 or V3 depending on the content of the entries.
#[derive(Debug, Default, Clone, Copy)]
pub struct Options {
- /// Configures which extensions to write
+ /// Configures which extensions to write.
pub extensions: Extensions,
+ /// Set the trailing hash of the produced index to all zeroes to save some time.
+ ///
+ /// This value is typically controlled by `index.skipHash` and is respected when the index is written
+ /// via [`File::write()`](crate::File::write()) and [`File::write_to()`](crate::File::write_to()).
+ /// Note that
+ pub skip_hash: bool,
}
impl State {
/// Serialize this instance to `out` with [`options`][Options].
- pub fn write_to(&self, out: impl std::io::Write, Options { extensions }: Options) -> std::io::Result<Version> {
+ pub fn write_to(
+ &self,
+ out: impl std::io::Write,
+ Options {
+ extensions,
+ skip_hash: _,
+ }: Options,
+ ) -> std::io::Result<Version> {
+ let _span = gix_features::trace::detail!("gix_index::State::write()");
let version = self.detect_required_version();
let mut write = CountBytes::new(out);