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 @@ -1,5 +1,6 @@ use crate::errors::HgError; use crate::repo::Repo; +use crate::revlog::node::NULL_NODE; use crate::revlog::revlog::{Revlog, RevlogError}; use crate::revlog::Revision; use crate::revlog::{Node, NodePrefix}; @@ -58,10 +59,9 @@ /// Return the node id of the `manifest` referenced by this `changelog` /// entry. pub fn manifest_node(&self) -> Result { - Node::from_hex_for_repo( - self.lines() - .next() - .ok_or_else(|| HgError::corrupted("empty changelog entry"))?, - ) + match self.lines().next() { + None => Ok(NULL_NODE), + Some(x) => Node::from_hex_for_repo(x), + } } } diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs --- a/rust/hg-core/src/revlog/index.rs +++ b/rust/hg-core/src/revlog/index.rs @@ -208,6 +208,9 @@ /// Value of the inline flag. pub fn is_inline(index_bytes: &[u8]) -> bool { + if index_bytes.len() < 4 { + return true; + } match &index_bytes[0..=1] { [0, 0] | [0, 2] => false, _ => true, 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 @@ -72,7 +72,7 @@ let index_path = index_path.as_ref(); let index_mmap = repo.store_vfs().mmap_open(&index_path)?; - let version = get_version(&index_mmap); + let version = get_version(&index_mmap)?; if version != 1 { // A proper new version should have had a repo/store requirement. return Err(HgError::corrupted("corrupted revlog")); @@ -179,6 +179,9 @@ /// snapshot to rebuild the final data. #[timed] pub fn get_rev_data(&self, rev: Revision) -> Result, RevlogError> { + if rev == NULL_REVISION { + return Ok(vec![]); + }; // Todo return -> Cow let mut entry = self.get_entry(rev)?; let mut delta_chain = vec![]; @@ -371,8 +374,16 @@ } /// Format version of the revlog. -pub fn get_version(index_bytes: &[u8]) -> u16 { - BigEndian::read_u16(&index_bytes[2..=3]) +pub fn get_version(index_bytes: &[u8]) -> Result { + if index_bytes.len() == 0 { + return Ok(1); + }; + if index_bytes.len() < 4 { + return Err(HgError::corrupted( + "corrupted revlog: can't read the index format header", + )); + }; + Ok(BigEndian::read_u16(&index_bytes[2..=3])) } /// Calculate the hash of a revision given its data and its parents. diff --git a/tests/test-empty-manifest-index.t b/tests/test-empty-manifest-index.t new file mode 100644 --- /dev/null +++ b/tests/test-empty-manifest-index.t @@ -0,0 +1,23 @@ +Create a repo such that the changelog entry refers to a null manifest node: + + $ hg init a + $ cd a + $ hg log + $ touch x + $ hg add x + $ hg commit -m "init" + $ hg rm x + $ hg commit -q --amend + + $ wc -c < .hg/store/00manifest.i + 0 + +Make sure that the manifest can be read (and is empty): + + $ hg --config rhg.on-unsupported=abort files -r . + [1] + +Test a null changelog rev, too: + + $ hg --config rhg.on-unsupported=abort files -r 0000000000000000000000000000000000000000 + [1]