hg-core: add a `ListRevTrackedFiles` operation
List files tracked at a given revision.
Differential Revision: https://phab.mercurial-scm.org/D9014
--- a/rust/hg-core/src/operations/list_tracked_files.rs Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/operations/list_tracked_files.rs Fri Sep 18 16:52:16 2020 +0200
@@ -6,14 +6,16 @@
// GNU General Public License version 2 or any later version.
use crate::dirstate::parsers::parse_dirstate;
+use crate::revlog::changelog::Changelog;
+use crate::revlog::manifest::{Manifest, ManifestEntry};
+use crate::revlog::revlog::RevlogError;
+use crate::revlog::Revision;
use crate::utils::hg_path::HgPath;
use crate::{DirstateParseError, EntryState};
use rayon::prelude::*;
use std::convert::From;
-use std::fmt;
use std::fs;
-use std::ops::Deref;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
/// Kind of error encountered by `ListDirstateTrackedFiles`
#[derive(Debug)]
@@ -31,14 +33,6 @@
pub kind: ListDirstateTrackedFilesErrorKind,
}
-impl std::error::Error for ListDirstateTrackedFilesError {}
-
-impl fmt::Display for ListDirstateTrackedFilesError {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
impl From<ListDirstateTrackedFilesErrorKind>
for ListDirstateTrackedFilesError
{
@@ -84,3 +78,111 @@
Ok(files)
}
}
+
+/// Kind of error encountered by `ListRevTrackedFiles`
+#[derive(Debug)]
+pub enum ListRevTrackedFilesErrorKind {
+ /// Error when reading a `revlog` file.
+ IoError(std::io::Error),
+ /// The revision has not been found.
+ InvalidRevision,
+ /// A `revlog` file is corrupted.
+ CorruptedRevlog,
+ /// The `revlog` format version is not supported.
+ UnsuportedRevlogVersion(u16),
+ /// The `revlog` data format is not supported.
+ UnknowRevlogDataFormat(u8),
+}
+
+/// A `ListRevTrackedFiles` error
+#[derive(Debug)]
+pub struct ListRevTrackedFilesError {
+ /// Kind of error encountered by `ListRevTrackedFiles`
+ pub kind: ListRevTrackedFilesErrorKind,
+}
+
+impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
+ fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
+ ListRevTrackedFilesError { kind }
+ }
+}
+
+impl From<RevlogError> for ListRevTrackedFilesError {
+ fn from(err: RevlogError) -> Self {
+ match err {
+ RevlogError::IoError(err) => {
+ ListRevTrackedFilesErrorKind::IoError(err)
+ }
+ RevlogError::UnsuportedVersion(version) => {
+ ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
+ }
+ RevlogError::InvalidRevision => {
+ ListRevTrackedFilesErrorKind::InvalidRevision
+ }
+ RevlogError::Corrupted => {
+ ListRevTrackedFilesErrorKind::CorruptedRevlog
+ }
+ RevlogError::UnknowDataFormat(format) => {
+ ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
+ }
+ }
+ .into()
+ }
+}
+
+/// List files under Mercurial control at a given revision.
+pub struct ListRevTrackedFiles<'a> {
+ /// The revision to list the files from.
+ rev: &'a str,
+ /// The changelog file
+ changelog: Changelog,
+ /// The manifest file
+ manifest: Manifest,
+ /// The manifest entry corresponding to the revision.
+ ///
+ /// Used to hold the owner of the returned references.
+ manifest_entry: Option<ManifestEntry>,
+}
+
+impl<'a> ListRevTrackedFiles<'a> {
+ pub fn new(
+ root: &PathBuf,
+ rev: &'a str,
+ ) -> Result<Self, ListRevTrackedFilesError> {
+ let changelog = Changelog::open(&root)?;
+ let manifest = Manifest::open(&root)?;
+
+ Ok(Self {
+ rev,
+ changelog,
+ manifest,
+ manifest_entry: None,
+ })
+ }
+
+ pub fn run(
+ &mut self,
+ ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
+ let changelog_entry = match self.rev.parse::<Revision>() {
+ Ok(rev) => self.changelog.get_rev(rev)?,
+ _ => {
+ let changelog_node = hex::decode(&self.rev).map_err(|_| {
+ ListRevTrackedFilesErrorKind::InvalidRevision
+ })?;
+ self.changelog.get_node(&changelog_node)?
+ }
+ };
+ let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
+ .map_err(|_| ListRevTrackedFilesErrorKind::CorruptedRevlog)?;
+
+ self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
+
+ if let Some(ref manifest_entry) = self.manifest_entry {
+ Ok(manifest_entry.files())
+ } else {
+ panic!(
+ "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
+ )
+ }
+ }
+}
--- a/rust/hg-core/src/operations/mod.rs Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/operations/mod.rs Fri Sep 18 16:52:16 2020 +0200
@@ -14,6 +14,10 @@
ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
ListDirstateTrackedFilesErrorKind,
};
+pub use list_tracked_files::{
+ ListRevTrackedFiles, ListRevTrackedFilesError,
+ ListRevTrackedFilesErrorKind,
+};
// TODO add an `Operation` trait when GAT have landed (rust #44265):
// there is no way to currently define a trait which can both return
--- a/rust/hg-core/src/revlog/index.rs Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/revlog/index.rs Fri Sep 18 16:52:16 2020 +0200
@@ -44,6 +44,20 @@
}
}
+ /// Return number of entries of the revlog index.
+ pub fn len(&self) -> usize {
+ if let Some(offsets) = &self.offsets {
+ offsets.len()
+ } else {
+ self.bytes.len() / INDEX_ENTRY_SIZE
+ }
+ }
+
+ /// Returns `true` if the `Index` has zero `entries`.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
/// Return the index entry corresponding to the given revision if it
/// exists.
pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
--- a/rust/hg-core/src/revlog/revlog.rs Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/revlog/revlog.rs Fri Sep 18 16:52:16 2020 +0200
@@ -188,7 +188,7 @@
}
/// Return the revlog index.
- fn index(&self) -> Index {
+ pub fn index(&self) -> Index {
let is_inline = self.data_bytes.is_none();
Index::new(&self.index_bytes, is_inline)
}