##// END OF EJS Templates
rust: add Vfs trait...
rust: add Vfs trait This will allow for the use of multiple vfs like in the Python implementation, as well as hiding the details of the upcoming Python vfs wrapper to hg-core.

File last commit:

r52761:db7dbe6f default
r52761:db7dbe6f default
Show More
filelog.rs
245 lines | 8.4 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 use crate::errors::HgError;
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 use crate::exit_codes;
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 use crate::repo::Repo;
use crate::revlog::path_encode::path_encode;
use crate::revlog::NodePrefix;
use crate::revlog::Revision;
Raphaël Gomès
rust-clippy: merge "revlog" module definition and struct implementation...
r50832 use crate::revlog::RevlogEntry;
use crate::revlog::{Revlog, RevlogError};
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 use crate::utils::files::get_path_from_bytes;
use crate::utils::hg_path::HgPath;
use crate::utils::SliceExt;
Raphaël Gomès
rust: implement the `Graph` trait for all revlogs...
r51871 use crate::Graph;
use crate::GraphError;
Raphaël Gomès
rust-revlog: teach the revlog opening code to read the repo options...
r52084 use crate::RevlogOpenOptions;
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 use crate::UncheckedRevision;
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 use std::path::PathBuf;
/// A specialized `Revlog` to work with file data logs.
pub struct Filelog {
/// The generic `revlog` format.
revlog: Revlog,
}
Raphaël Gomès
rust: implement the `Graph` trait for all revlogs...
r51871 impl Graph for Filelog {
fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
self.revlog.parents(rev)
}
}
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 impl Filelog {
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 pub fn open_vfs(
Raphaël Gomès
rust: add Vfs trait...
r52761 store_vfs: &crate::vfs::VfsImpl,
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 file_path: &HgPath,
Raphaël Gomès
rust-revlog: teach the revlog opening code to read the repo options...
r52084 options: RevlogOpenOptions,
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 ) -> Result<Self, HgError> {
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 let index_path = store_path(file_path, b".i");
let data_path = store_path(file_path, b".d");
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 let revlog =
Raphaël Gomès
rust-revlog: teach the revlog opening code to read the repo options...
r52084 Revlog::open(store_vfs, index_path, Some(&data_path), options)?;
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 Ok(Self { revlog })
}
Raphaël Gomès
rust-revlog: teach the revlog opening code to read the repo options...
r52084 pub fn open(
repo: &Repo,
file_path: &HgPath,
options: RevlogOpenOptions,
) -> Result<Self, HgError> {
Self::open_vfs(&repo.store_vfs(), file_path, options)
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 }
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 /// The given node ID is that of the file as found in a filelog, not of a
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 /// changeset.
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 pub fn data_for_node(
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 &self,
file_node: impl Into<NodePrefix>,
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 ) -> Result<FilelogRevisionData, RevlogError> {
Simon Sapin
rust: Rename the `Revlog::get_node_rev` method to `rev_from_node`...
r48782 let file_rev = self.revlog.rev_from_node(file_node.into())?;
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 self.data_for_rev(file_rev.into())
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 }
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 /// The given revision is that of the file as found in a filelog, not of a
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 /// changeset.
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 pub fn data_for_rev(
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 &self,
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 file_rev: UncheckedRevision,
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 ) -> Result<FilelogRevisionData, RevlogError> {
Simon Sapin
rhg: Add RevlogEntry::data that does delta resolution...
r49373 let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?.into_owned();
Raphaël Gomès
rust-clippy: fix most warnings in `hg-core`...
r50825 Ok(FilelogRevisionData(data))
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 }
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374
/// The given node ID is that of the file as found in a filelog, not of a
/// changeset.
pub fn entry_for_node(
&self,
file_node: impl Into<NodePrefix>,
) -> Result<FilelogEntry, RevlogError> {
let file_rev = self.revlog.rev_from_node(file_node.into())?;
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 self.entry_for_checked_rev(file_rev)
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 }
/// The given revision is that of the file as found in a filelog, not of a
/// changeset.
pub fn entry_for_rev(
&self,
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 file_rev: UncheckedRevision,
) -> Result<FilelogEntry, RevlogError> {
Ok(FilelogEntry(self.revlog.get_entry(file_rev)?))
}
fn entry_for_checked_rev(
&self,
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 file_rev: Revision,
) -> Result<FilelogEntry, RevlogError> {
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 Ok(FilelogEntry(
self.revlog.get_entry_for_checked_rev(file_rev)?,
))
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 }
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 }
fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
let encoded_bytes =
path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
get_path_from_bytes(&encoded_bytes).into()
}
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 pub struct FilelogEntry<'a>(RevlogEntry<'a>);
impl FilelogEntry<'_> {
Simon Sapin
rhg: desambiguate status without decompressing filelog if possible...
r49378 /// `self.data()` can be expensive, with decompression and delta
/// resolution.
///
/// *Without* paying this cost, based on revlog index information
/// including `RevlogEntry::uncompressed_len`:
///
/// * Returns `true` if the length that `self.data().file_data().len()`
/// would return is definitely **not equal** to `other_len`.
/// * Returns `false` if available information is inconclusive.
pub fn file_data_len_not_equal_to(&self, other_len: u64) -> bool {
// Relevant code that implement this behavior in Python code:
// basefilectx.cmp, filelog.size, storageutil.filerevisioncopied,
// revlog.size, revlog.rawsize
// Let’s call `file_data_len` what would be returned by
// `self.data().file_data().len()`.
Arseniy Alekseyev
censor: make rhg fall back to python when encountering a censored node...
r50069 if self.0.is_censored() {
Simon Sapin
rhg: desambiguate status without decompressing filelog if possible...
r49378 let file_data_len = 0;
return other_len != file_data_len;
}
if self.0.has_length_affecting_flag_processor() {
// We can’t conclude anything about `file_data_len`.
return false;
}
// Revlog revisions (usually) have metadata for the size of
// their data after decompression and delta resolution
// as would be returned by `Revlog::get_rev_data`.
//
// For filelogs this is the file’s contents preceded by an optional
// metadata block.
let uncompressed_len = if let Some(l) = self.0.uncompressed_len() {
l as u64
} else {
// The field was set to -1, the actual uncompressed len is unknown.
// We need to decompress to say more.
return false;
};
// `uncompressed_len = file_data_len + optional_metadata_len`,
// so `file_data_len <= uncompressed_len`.
if uncompressed_len < other_len {
// Transitively, `file_data_len < other_len`.
// So `other_len != file_data_len` definitely.
return true;
}
if uncompressed_len == other_len + 4 {
// It’s possible that `file_data_len == other_len` with an empty
// metadata block (2 start marker bytes + 2 end marker bytes).
// This happens when there wouldn’t otherwise be metadata, but
// the first 2 bytes of file data happen to match a start marker
// and would be ambiguous.
return false;
}
if !self.0.has_p1() {
// There may or may not be copy metadata, so we can’t deduce more
// about `file_data_len` without computing file data.
return false;
}
// Filelog ancestry is not meaningful in the way changelog ancestry is.
// It only provides hints to delta generation.
// p1 and p2 are set to null when making a copy or rename since
// contents are likely unrelatedto what might have previously existed
// at the destination path.
//
// Conversely, since here p1 is non-null, there is no copy metadata.
// Note that this reasoning may be invalidated in the presence of
// merges made by some previous versions of Mercurial that
// swapped p1 and p2. See <https://bz.mercurial-scm.org/show_bug.cgi?id=6528>
// and `tests/test-issue6528.t`.
//
// Since copy metadata is currently the only kind of metadata
// kept in revlog data of filelogs,
// this `FilelogEntry` does not have such metadata:
let file_data_len = uncompressed_len;
Raphaël Gomès
rust-clippy: fix most warnings in `hg-core`...
r50825 file_data_len != other_len
Simon Sapin
rhg: desambiguate status without decompressing filelog if possible...
r49378 }
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 pub fn data(&self) -> Result<FilelogRevisionData, HgError> {
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 let data = self.0.data();
match data {
Ok(data) => Ok(FilelogRevisionData(data.into_owned())),
// Errors other than `HgError` should not happen at this point
Err(e) => match e {
RevlogError::Other(hg_error) => Err(hg_error),
revlog_error => Err(HgError::abort(
revlog_error.to_string(),
exit_codes::ABORT,
None,
)),
},
}
Simon Sapin
rhg: Expose FilelogEntry that wraps RevlogEntry...
r49374 }
}
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 /// The data for one revision in a filelog, uncompressed and delta-resolved.
pub struct FilelogRevisionData(Vec<u8>);
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 impl FilelogRevisionData {
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 /// Split into metadata and data
Arseniy Alekseyev
rhg: simplify split_metadata...
r49064 pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> {
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n'];
if let Some(rest) = self.0.drop_prefix(DELIMITER) {
if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) {
Arseniy Alekseyev
rhg: simplify split_metadata...
r49064 Ok((Some(metadata), data))
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 } else {
Err(HgError::corrupted(
"Missing metadata end delimiter in filelog entry",
))
}
} else {
Ok((None, &self.0))
}
}
/// Returns the file contents at this revision, stripped of any metadata
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 pub fn file_data(&self) -> Result<&[u8], HgError> {
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 let (_metadata, data) = self.split()?;
Ok(data)
}
Arseniy Alekseyev
rhg: internally, return a structured representation from hg cat...
r49051
/// Consume the entry, and convert it into data, discarding any metadata,
/// if present.
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 pub fn into_file_data(self) -> Result<Vec<u8>, HgError> {
Arseniy Alekseyev
rhg: simplify split_metadata...
r49064 if let (Some(_metadata), data) = self.split()? {
Arseniy Alekseyev
rhg: internally, return a structured representation from hg cat...
r49051 Ok(data.to_owned())
} else {
Ok(self.0)
}
}
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 }