diff --git a/rust/hg-core/src/lib.rs b/rust/hg-core/src/lib.rs --- a/rust/hg-core/src/lib.rs +++ b/rust/hg-core/src/lib.rs @@ -28,6 +28,7 @@ pub use revlog::*; pub mod config; pub mod operations; +pub mod revset; pub mod utils; use crate::utils::hg_path::{HgPathBuf, HgPathError}; diff --git a/rust/hg-core/src/operations/cat.rs b/rust/hg-core/src/operations/cat.rs --- a/rust/hg-core/src/operations/cat.rs +++ b/rust/hg-core/src/operations/cat.rs @@ -15,8 +15,6 @@ use crate::revlog::revlog::Revlog; use crate::revlog::revlog::RevlogError; use crate::revlog::Node; -use crate::revlog::NodePrefix; -use crate::revlog::Revision; use crate::utils::files::get_path_from_bytes; use crate::utils::hg_path::{HgPath, HgPathBuf}; @@ -77,23 +75,15 @@ /// * `files`: The files to output. pub fn cat( repo: &Repo, - rev: &str, + revset: &str, files: &[HgPathBuf], ) -> Result, CatRevError> { + let rev = crate::revset::resolve_single(revset, repo)?; let changelog = Changelog::open(repo)?; let manifest = Manifest::open(repo)?; - - let changelog_entry = match rev.parse::() { - Ok(rev) => changelog.get_rev(rev)?, - _ => { - let changelog_node = NodePrefix::from_hex(&rev) - .map_err(|_| CatRevErrorKind::InvalidRevision)?; - changelog.get_node(changelog_node)? - } - }; + let changelog_entry = changelog.get_rev(rev)?; let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; - let manifest_entry = manifest.get_node(manifest_node.into())?; let mut bytes = vec![]; diff --git a/rust/hg-core/src/operations/debugdata.rs b/rust/hg-core/src/operations/debugdata.rs --- a/rust/hg-core/src/operations/debugdata.rs +++ b/rust/hg-core/src/operations/debugdata.rs @@ -7,8 +7,6 @@ use crate::repo::Repo; use crate::revlog::revlog::{Revlog, RevlogError}; -use crate::revlog::NodePrefix; -use crate::revlog::Revision; /// Kind of data to debug #[derive(Debug, Copy, Clone)] @@ -79,7 +77,7 @@ /// Dump the contents data of a revision. pub fn debug_data( repo: &Repo, - rev: &str, + revset: &str, kind: DebugDataKind, ) -> Result, DebugDataError> { let index_file = match kind { @@ -87,16 +85,8 @@ DebugDataKind::Manifest => "00manifest.i", }; let revlog = Revlog::open(repo, index_file, None)?; - - let data = match rev.parse::() { - Ok(rev) => revlog.get_rev_data(rev)?, - _ => { - let node = NodePrefix::from_hex(&rev) - .map_err(|_| DebugDataErrorKind::InvalidRevision)?; - let rev = revlog.get_node_rev(node)?; - revlog.get_rev_data(rev)? - } - }; - + let rev = + crate::revset::resolve_rev_number_or_hex_prefix(revset, &revlog)?; + let data = revlog.get_rev_data(rev)?; Ok(data) } diff --git a/rust/hg-core/src/operations/list_tracked_files.rs b/rust/hg-core/src/operations/list_tracked_files.rs --- a/rust/hg-core/src/operations/list_tracked_files.rs +++ b/rust/hg-core/src/operations/list_tracked_files.rs @@ -9,9 +9,8 @@ use crate::repo::Repo; use crate::revlog::changelog::Changelog; use crate::revlog::manifest::{Manifest, ManifestEntry}; -use crate::revlog::node::{Node, NodePrefix}; +use crate::revlog::node::Node; use crate::revlog::revlog::RevlogError; -use crate::revlog::Revision; use crate::utils::hg_path::HgPath; use crate::{DirstateParseError, EntryState}; use rayon::prelude::*; @@ -137,19 +136,12 @@ /// List files under Mercurial control at a given revision. pub fn list_rev_tracked_files( repo: &Repo, - rev: &str, + revset: &str, ) -> Result { + let rev = crate::revset::resolve_single(revset, repo)?; let changelog = Changelog::open(repo)?; let manifest = Manifest::open(repo)?; - - let changelog_entry = match rev.parse::() { - Ok(rev) => changelog.get_rev(rev)?, - _ => { - let changelog_node = NodePrefix::from_hex(&rev) - .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?; - changelog.get_node(changelog_node)? - } - }; + let changelog_entry = changelog.get_rev(rev)?; let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?; let manifest_entry = manifest.get_node(manifest_node.into())?; diff --git a/rust/hg-core/src/revlog/changelog.rs b/rust/hg-core/src/revlog/changelog.rs --- a/rust/hg-core/src/revlog/changelog.rs +++ b/rust/hg-core/src/revlog/changelog.rs @@ -6,7 +6,7 @@ /// A specialized `Revlog` to work with `changelog` data format. pub struct Changelog { /// The generic `revlog` format. - revlog: Revlog, + pub(crate) revlog: Revlog, } impl Changelog { diff --git a/rust/hg-core/src/revlog/revlog.rs b/rust/hg-core/src/revlog/revlog.rs --- a/rust/hg-core/src/revlog/revlog.rs +++ b/rust/hg-core/src/revlog/revlog.rs @@ -144,6 +144,11 @@ found_by_prefix.ok_or(RevlogError::InvalidRevision) } + /// Returns whether the given revision exists in this revlog. + pub fn has_rev(&self, rev: Revision) -> bool { + self.index.get_entry(rev).is_some() + } + /// Return the full data associated to a revision. /// /// All entries required to build the final data out of deltas will be diff --git a/rust/hg-core/src/revset.rs b/rust/hg-core/src/revset.rs new file mode 100644 --- /dev/null +++ b/rust/hg-core/src/revset.rs @@ -0,0 +1,53 @@ +//! The revset query language +//! +//! + +use crate::repo::Repo; +use crate::revlog::changelog::Changelog; +use crate::revlog::revlog::{Revlog, RevlogError}; +use crate::revlog::NodePrefix; +use crate::revlog::{Revision, NULL_REVISION}; + +/// Resolve a query string into a single revision. +/// +/// Only some of the revset language is implemented yet. +pub fn resolve_single( + input: &str, + repo: &Repo, +) -> Result { + let changelog = Changelog::open(repo)?; + + let result = resolve_rev_number_or_hex_prefix(input, &changelog.revlog); + if !matches!(result, Err(RevlogError::InvalidRevision)) { + return result; + } + + if input == "null" { + return Ok(NULL_REVISION); + } + + // TODO: support for the rest of the language here. + + Err(RevlogError::InvalidRevision) +} + +/// Resolve the small subset of the language suitable for revlogs other than +/// the changelog, such as in `hg debugdata --manifest` CLI argument. +/// +/// * A non-negative decimal integer for a revision number, or +/// * An hexadecimal string, for the unique node ID that starts with this +/// prefix +pub fn resolve_rev_number_or_hex_prefix( + input: &str, + revlog: &Revlog, +) -> Result { + if let Ok(integer) = input.parse::() { + if integer >= 0 && revlog.has_rev(integer) { + return Ok(integer); + } + } + if let Ok(prefix) = NodePrefix::from_hex(input) { + return revlog.get_node_rev(prefix); + } + Err(RevlogError::InvalidRevision) +}