##// END OF EJS Templates
hg-cpython: fallback when encountering an unknown matcher...
hg-cpython: fallback when encountering an unknown matcher At this point in the process, nothing user-visible has happened, it is still safe to fallback. This can happen now that we're going to be using "container matchers" like unionmatcher and intersectionmatcher. This is easier and less error-prone than recursive checking beforehand since only the presence of a transformation case will allow the process to continue.

File last commit:

r49987:07ec9f4f default
r50240:44319aa4 default
Show More
changelog.rs
271 lines | 8.5 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 use crate::errors::HgError;
Martin von Zweigbergk
rust-revlog: add methods for getting parent revs and entries...
r49939 use crate::revlog::revlog::{Revlog, RevlogEntry, RevlogError};
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 use crate::revlog::Revision;
Simon Sapin
rhg: `cat` command: print error messages for missing files...
r47478 use crate::revlog::{Node, NodePrefix};
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 use crate::utils::hg_path::HgPath;
Martin von Zweigbergk
rust-revlog: make `Changelog` and `ManifestLog` unaware of `Repo`...
r49981 use crate::vfs::Vfs;
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 use itertools::Itertools;
use std::ascii::escape_default;
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 use std::borrow::Cow;
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 use std::fmt::{Debug, Formatter};
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103
/// A specialized `Revlog` to work with `changelog` data format.
pub struct Changelog {
/// The generic `revlog` format.
Simon Sapin
rhg: centralize parsing of `--rev` CLI arguments...
r47162 pub(crate) revlog: Revlog,
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
impl Changelog {
/// Open the `changelog` of a repository given by its root.
Martin von Zweigbergk
rust-revlog: make `Changelog` and `ManifestLog` unaware of `Repo`...
r49981 pub fn open(store_vfs: &Vfs, use_nodemap: bool) -> Result<Self, HgError> {
let revlog =
Revlog::open(store_vfs, "00changelog.i", None, use_nodemap)?;
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 Ok(Self { revlog })
}
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 /// Return the `ChangelogEntry` for the given node ID.
pub fn data_for_node(
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 &self,
Simon Sapin
rust: Make NodePrefix allocation-free and Copy, remove NodePrefixRef...
r47160 node: NodePrefix,
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 ) -> Result<ChangelogRevisionData, RevlogError> {
Simon Sapin
rust: Rename the `Revlog::get_node_rev` method to `rev_from_node`...
r48782 let rev = self.revlog.rev_from_node(node)?;
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 self.data_for_rev(rev)
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
Martin von Zweigbergk
rust-revlog: add methods for getting parent revs and entries...
r49939 /// Return the `RevlogEntry` of the given revision number.
pub fn entry_for_rev(
&self,
rev: Revision,
) -> Result<RevlogEntry, RevlogError> {
self.revlog.get_entry(rev)
}
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 /// Return the `ChangelogEntry` of the given revision number.
pub fn data_for_rev(
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 &self,
rev: Revision,
Simon Sapin
rhg: Rename some revlog-related types and methods...
r49372 ) -> Result<ChangelogRevisionData, RevlogError> {
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 let bytes = self.revlog.get_rev_data(rev)?;
Martin von Zweigbergk
rust-changelog: remove special parsing of empty changelog data for null rev...
r49937 if bytes.is_empty() {
Ok(ChangelogRevisionData::null())
} else {
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 Ok(ChangelogRevisionData::new(bytes).map_err(|err| {
RevlogError::Other(HgError::CorruptedRepository(format!(
"Invalid changelog data for revision {}: {:?}",
rev, err
)))
})?)
Martin von Zweigbergk
rust-changelog: remove special parsing of empty changelog data for null rev...
r49937 }
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
Simon Sapin
rhg: `cat` command: print error messages for missing files...
r47478
pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
Simon Sapin
rust: Make private the `index` field of the `Revlog` struct...
r48781 self.revlog.node_from_rev(rev)
Simon Sapin
rhg: `cat` command: print error messages for missing files...
r47478 }
Martin von Zweigbergk
rust-revlog: add methods for getting parent revs and entries...
r49939
pub fn rev_from_node(
&self,
node: NodePrefix,
) -> Result<Revision, RevlogError> {
self.revlog.rev_from_node(node)
}
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
/// `Changelog` entry which knows how to interpret the `changelog` data bytes.
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 #[derive(PartialEq)]
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 pub struct ChangelogRevisionData<'changelog> {
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 /// The data bytes of the `changelog` entry.
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 bytes: Cow<'changelog, [u8]>,
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 /// The end offset for the hex manifest (not including the newline)
manifest_end: usize,
/// The end offset for the user+email (not including the newline)
user_end: usize,
/// The end offset for the timestamp+timezone+extras (not including the
/// newline)
timestamp_end: usize,
/// The end offset for the file list (not including the newline)
files_end: usize,
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 impl<'changelog> ChangelogRevisionData<'changelog> {
fn new(bytes: Cow<'changelog, [u8]>) -> Result<Self, HgError> {
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 let mut line_iter = bytes.split(|b| b == &b'\n');
let manifest_end = line_iter
.next()
.expect("Empty iterator from split()?")
.len();
let user_slice = line_iter.next().ok_or_else(|| {
HgError::corrupted("Changeset data truncated after manifest line")
})?;
let user_end = manifest_end + 1 + user_slice.len();
let timestamp_slice = line_iter.next().ok_or_else(|| {
HgError::corrupted("Changeset data truncated after user line")
})?;
let timestamp_end = user_end + 1 + timestamp_slice.len();
let mut files_end = timestamp_end + 1;
loop {
let line = line_iter.next().ok_or_else(|| {
HgError::corrupted("Changeset data truncated in files list")
})?;
if line.is_empty() {
if files_end == bytes.len() {
// The list of files ended with a single newline (there
// should be two)
return Err(HgError::corrupted(
"Changeset data truncated after files list",
));
}
files_end -= 1;
break;
}
files_end += line.len() + 1;
}
Ok(Self {
bytes,
manifest_end,
user_end,
timestamp_end,
files_end,
})
Martin von Zweigbergk
rust-changelog: remove special parsing of empty changelog data for null rev...
r49937 }
fn null() -> Self {
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 Self::new(Cow::Borrowed(
b"0000000000000000000000000000000000000000\n\n0 0\n\n",
))
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 .unwrap()
Martin von Zweigbergk
rust-changelog: remove special parsing of empty changelog data for null rev...
r49937 }
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 /// Return an iterator over the lines of the entry.
pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
Martin von Zweigbergk
rust-changelog: don't skip empty lines when iterating over changeset lines...
r49936 self.bytes.split(|b| b == &b'\n')
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
/// Return the node id of the `manifest` referenced by this `changelog`
/// entry.
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 pub fn manifest_node(&self) -> Result<Node, HgError> {
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 let manifest_node_hex = &self.bytes[..self.manifest_end];
Martin von Zweigbergk
rust-changelog: remove special parsing of empty changelog data for null rev...
r49937 Node::from_hex_for_repo(manifest_node_hex)
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938
/// The full user string (usually a name followed by an email enclosed in
/// angle brackets)
pub fn user(&self) -> &[u8] {
&self.bytes[self.manifest_end + 1..self.user_end]
}
/// The full timestamp line (timestamp in seconds, offset in seconds, and
/// possibly extras)
// TODO: We should expose this in a more useful way
pub fn timestamp_line(&self) -> &[u8] {
&self.bytes[self.user_end + 1..self.timestamp_end]
}
/// The files changed in this revision.
pub fn files(&self) -> impl Iterator<Item = &HgPath> {
self.bytes[self.timestamp_end + 1..self.files_end]
.split(|b| b == &b'\n')
.map(|path| HgPath::new(path))
}
/// The change description.
pub fn description(&self) -> &[u8] {
&self.bytes[self.files_end + 2..]
}
Antoine Cezar
hg-core: add `Changlog` a specialized `Revlog`...
r46103 }
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 impl Debug for ChangelogRevisionData<'_> {
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChangelogRevisionData")
.field("bytes", &debug_bytes(&self.bytes))
.field("manifest", &debug_bytes(&self.bytes[..self.manifest_end]))
.field(
"user",
&debug_bytes(
&self.bytes[self.manifest_end + 1..self.user_end],
),
)
.field(
"timestamp",
&debug_bytes(
&self.bytes[self.user_end + 1..self.timestamp_end],
),
)
.field(
"files",
&debug_bytes(
&self.bytes[self.timestamp_end + 1..self.files_end],
),
)
.field(
"description",
&debug_bytes(&self.bytes[self.files_end + 2..]),
)
.finish()
}
}
fn debug_bytes(bytes: &[u8]) -> String {
String::from_utf8_lossy(
&bytes.iter().flat_map(|b| escape_default(*b)).collect_vec(),
)
.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_create_changelogrevisiondata_invalid() {
// Completely empty
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 assert!(ChangelogRevisionData::new(Cow::Borrowed(b"abcd")).is_err());
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 // No newline after manifest
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 assert!(ChangelogRevisionData::new(Cow::Borrowed(b"abcd")).is_err());
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 // No newline after user
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 assert!(ChangelogRevisionData::new(Cow::Borrowed(b"abcd\n")).is_err());
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 // No newline after timestamp
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 assert!(
ChangelogRevisionData::new(Cow::Borrowed(b"abcd\n\n0 0")).is_err()
);
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 // Missing newline after files
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 assert!(ChangelogRevisionData::new(Cow::Borrowed(
b"abcd\n\n0 0\nfile1\nfile2"
))
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 .is_err(),);
// Only one newline after files
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 assert!(ChangelogRevisionData::new(Cow::Borrowed(
b"abcd\n\n0 0\nfile1\nfile2\n"
))
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 .is_err(),);
}
#[test]
fn test_create_changelogrevisiondata() {
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 let data = ChangelogRevisionData::new(Cow::Borrowed(
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 b"0123456789abcdef0123456789abcdef01234567
Some One <someone@example.com>
0 0
file1
file2
some
commit
Martin von Zweigbergk
changelog: avoid copying changeset data into `ChangesetRevisionData`...
r49987 message",
))
Martin von Zweigbergk
rust-changelog: start parsing changeset data...
r49938 .unwrap();
assert_eq!(
data.manifest_node().unwrap(),
Node::from_hex("0123456789abcdef0123456789abcdef01234567")
.unwrap()
);
assert_eq!(data.user(), b"Some One <someone@example.com>");
assert_eq!(data.timestamp_line(), b"0 0");
assert_eq!(
data.files().collect_vec(),
vec![HgPath::new("file1"), HgPath::new("file2")]
);
assert_eq!(data.description(), b"some\ncommit\nmessage");
}
}