Skip to content

Commit f928350

Browse files
committed
First stab at tree verification (#293)
1 parent 1ac2c21 commit f928350

File tree

5 files changed

+87
-2
lines changed

5 files changed

+87
-2
lines changed

git-index/src/extension/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub(crate) mod fs_monitor;
5757

5858
pub(crate) mod decode;
5959

60-
pub(crate) mod tree;
60+
pub mod tree;
6161

6262
pub(crate) mod end_of_index_entry;
6363

git-index/src/extension/tree.rs

+60
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use bstr::ByteSlice;
12
use git_hash::ObjectId;
23

34
use crate::util::split_at_pos;
@@ -8,6 +9,65 @@ use crate::{
89

910
pub const SIGNATURE: Signature = *b"TREE";
1011

12+
pub mod verify {
13+
use bstr::BString;
14+
use quick_error::quick_error;
15+
16+
quick_error! {
17+
#[derive(Debug)]
18+
pub enum Error {
19+
RootWithName { name: BString } {
20+
display("The root tree was named '{}', even though it should be empty", name)
21+
}
22+
EntriesCount {actual: u32, expected: u32 } {
23+
display("Expected not more than {} entries to be reachable from the top-level, but actual count was {}", expected, actual)
24+
}
25+
}
26+
}
27+
}
28+
29+
impl Tree {
30+
pub fn verify(&self) -> Result<(), verify::Error> {
31+
fn verify_recursive(children: &[Tree]) -> Result<Option<u32>, verify::Error> {
32+
if children.is_empty() {
33+
return Ok(None);
34+
}
35+
let mut entries = 0;
36+
for child in children {
37+
let actual_num_entries = verify_recursive(&child.children)?;
38+
if let Some(actual) = actual_num_entries {
39+
if actual > child.num_entries {
40+
return Err(verify::Error::EntriesCount {
41+
actual,
42+
expected: child.num_entries,
43+
});
44+
}
45+
}
46+
entries += child.num_entries;
47+
}
48+
Ok(entries.into())
49+
}
50+
51+
if !self.name.is_empty() {
52+
return Err(verify::Error::RootWithName {
53+
name: self.name.as_bstr().into(),
54+
});
55+
}
56+
57+
let declared_entries = verify_recursive(&self.children)?;
58+
if let Some(actual) = declared_entries {
59+
if actual > self.num_entries {
60+
return Err(verify::Error::EntriesCount {
61+
actual,
62+
expected: self.num_entries,
63+
});
64+
}
65+
}
66+
67+
Ok(())
68+
}
69+
}
70+
1171
/// A recursive data structure
1272
pub fn decode(data: &[u8], object_hash: git_hash::Kind) -> Option<Tree> {
1373
let (tree, data) = one_recursive(data, object_hash.len_in_bytes())?;

git-index/src/verify.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,23 @@ pub mod entries {
99
#[derive(Debug)]
1010
pub enum Error {
1111
OutOfOrder { current_index: usize, current_path: BString, current_stage: u8, previous_path: BString, previous_stage: u8 } {
12-
display("todo")
12+
display("Entry '{}' (stage = {}) at index {} should order after prior entry '{}' (stage = {})", current_path, current_stage, current_index, previous_path, previous_stage)
13+
}
14+
}
15+
}
16+
}
17+
18+
pub mod extensions {
19+
use crate::extension;
20+
use quick_error::quick_error;
21+
22+
quick_error! {
23+
#[derive(Debug)]
24+
pub enum Error {
25+
Tree(err: extension::tree::verify::Error) {
26+
display("The tree extension wasn't valid")
27+
source(err)
28+
from()
1329
}
1430
}
1531
}
@@ -34,4 +50,11 @@ impl State {
3450
}
3551
Ok(())
3652
}
53+
54+
pub fn verify_extensions(&self) -> Result<(), extensions::Error> {
55+
self.tree().map(|t| t.verify()).transpose()?;
56+
// TODO: verify links by running the whole set of tests on the index
57+
// - do that once we load it as well, or maybe that's lazy loaded? Too many questions for now.
58+
Ok(())
59+
}
3760
}

git-index/tests/index/file/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod init {
88
fn verify(index: git_index::File) -> git_index::File {
99
index.verify_integrity().unwrap();
1010
index.verify_entries().unwrap();
11+
index.verify_extensions().unwrap();
1112
index
1213
}
1314

gitoxide-core/src/index/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub fn verify(
1919
let file = parse_file(index_path, object_hash)?;
2020
file.verify_integrity()?;
2121
file.verify_entries()?;
22+
file.verify_extensions()?;
2223
#[cfg_attr(not(feature = "serde1"), allow(irrefutable_let_patterns))]
2324
if let crate::OutputFormat::Human = format {
2425
writeln!(out, "OK").ok();

0 commit comments

Comments
 (0)