pub struct ValidatedRepository<'index, 'repo, R: Repository + ?Sized> { /* private fields */ }Expand description
Validate and index a stable repository, including streaming-only providers.
Checks repository-format IDs, unique enumeration, links and target kinds, tree modes, names, Git ordering and duplicates, tag links/peeling, and exact reachability from the union of HEAD and refs. Commit parents are followed; gitlinks name external repositories and are not followed. Branches and resolved HEAD must name commits.
This is graph validation, not a replacement for all of Git’s fsck policies: identity semantics, platform-specific filename aliases, and special files such as .gitmodules are outside its scope.
The provider must remain stable after validation. Bodies are never retained or copied in full. Enumeration/IDs/metadata use the index; body reads and refs continue to use the provider.
Implementations§
Source§impl<R: Repository + ?Sized> ValidatedRepository<'_, '_, R>
impl<R: Repository + ?Sized> ValidatedRepository<'_, '_, R>
Sourcepub fn select_revision_fetch<'selected>(
&self,
roots: &[ObjectId],
history: (impl IntoIterator<Item = Result<ObjectId>>, impl IntoIterator<Item = Result<ObjectId>>, impl IntoIterator<Item = Result<ObjectId>>),
work: &mut [Option<ValidationMetadata>],
selected: &'selected mut [usize],
scratch: (&mut [u8], &mut [u8], &mut RevisionState<'_>),
options: (SelectionOptions, FetchOptions, RevisionOptions),
) -> Result<&'selected [usize]>
pub fn select_revision_fetch<'selected>( &self, roots: &[ObjectId], history: (impl IntoIterator<Item = Result<ObjectId>>, impl IntoIterator<Item = Result<ObjectId>>, impl IntoIterator<Item = Result<ObjectId>>), work: &mut [Option<ValidationMetadata>], selected: &'selected mut [usize], scratch: (&mut [u8], &mut [u8], &mut RevisionState<'_>), options: (SelectionOptions, FetchOptions, RevisionOptions), ) -> Result<&'selected [usize]>
Select revision-limited history with separate caller-owned boundary state.
history contains common commits, client boundaries, and resolved exclusion
endpoints. This additive path rejects an absolute depth, preserves blob
filtering/ascending indices, and shares structural bytes across every pass.
Exclusion and candidate passes visit commits only; payload traversal follows
trees/tags with the existing validated graph contract. No blob body is read.
Existing shallow commits are known, and unshallow transitions seed parents;
common haves outside candidate history cannot hide newly requested history.
Metadata scans are bounded by object capacity (quadratic worst case), and
exclusion count is capped independently. Total preparation CPU accounting
is a separate host concern, as with ordinary bounded selection.
Source§impl<'index, 'repo, R: Repository + ?Sized> ValidatedRepository<'index, 'repo, R>
impl<'index, 'repo, R: Repository + ?Sized> ValidatedRepository<'index, 'repo, R>
Sourcepub fn new(
repository: &'repo R,
object_storage: &'index mut [Option<ValidationMetadata>],
index_storage: &'index mut [usize],
read_scratch: &mut [u8],
parse_scratch: &mut [u8],
) -> Result<Self>
pub fn new( repository: &'repo R, object_storage: &'index mut [Option<ValidationMetadata>], index_storage: &'index mut [usize], read_scratch: &mut [u8], parse_scratch: &mut [u8], ) -> Result<Self>
Each metadata/index array needs object_count slots; extra slots are
untouched. Initialize metadata with [None; N]. Read scratch needs at
least one byte for nonempty bodies. Empty repositories need no scratch.
Parse scratch must fit the longest commit/tag header line
(excluding LF), and twice the longest tree name (excluding NUL).
Neither scratch buffer is retained. Their sizes do not limit body sizes.
Insufficient arrays/scratch return BufferTooSmall, absent objects/links ObjectNotFound, invalid refs InvalidReference, invalid names InvalidFileName, and ordering/duplicate names UnsortedFiles. Invalid IDs, kinds, framing, modes, duplicate IDs or unreachable objects return InvalidObject. Backend errors propagate. Discard partial arrays on error.
Hashing is linear in body bytes. Lookup uses binary search. Traversal scans pending metadata without recursion, using O(n^2) metadata probes in the worst case. Directory/file duplicate detection can reread earlier tree entries, trading worst-case quadratic tree reads for bounded memory.
pub fn metadata(&self, index: usize) -> Option<&ValidationMetadata>
Sourcepub fn select_objects<'selected>(
&self,
roots: &[ObjectId],
work: &mut [Option<ValidationMetadata>],
selected: &'selected mut [usize],
read_scratch: &mut [u8],
parse_scratch: &mut [u8],
graph_read_limit: u64,
) -> Result<&'selected [usize]>
pub fn select_objects<'selected>( &self, roots: &[ObjectId], work: &mut [Option<ValidationMetadata>], selected: &'selected mut [usize], read_scratch: &mut [u8], parse_scratch: &mut [u8], graph_read_limit: u64, ) -> Result<&'selected [usize]>
Select the exact reachable union of explicit object IDs without allocation.
Each work/index array needs object_count slots. Read/parse budgets have the
same requirements as validation; graph bodies are read again, not hashed.
The result borrows only selected, in the provider’s enumeration order.
Overlapping roots are deduplicated. Gitlinks remain external. A failed
selection invalidates the work arrays; every retry resets all traversal
state. graph_read_limit caps actual structural reads, including prefetch
and rereads for tree duplicate checks; exhaustion returns LimitExceeded.
Blob bodies and opaque commit/tag messages are not traversed. Metadata
probes are bounded by the object-array capacity (quadratic worst case).
Independent requests use independent caller storage. This primitive
accepts any present root; HTTP’s advertised-want policy is separate.
Sourcepub fn select_missing_objects<'selected>(
&self,
roots: &[ObjectId],
common: impl IntoIterator<Item = Result<ObjectId>>,
work: &mut [Option<ValidationMetadata>],
selected: &'selected mut [usize],
scratch: (&mut [u8], &mut [u8]),
graph_read_limit: u64,
) -> Result<&'selected [usize]>
pub fn select_missing_objects<'selected>( &self, roots: &[ObjectId], common: impl IntoIterator<Item = Result<ObjectId>>, work: &mut [Option<ValidationMetadata>], selected: &'selected mut [usize], scratch: (&mut [u8], &mut [u8]), graph_read_limit: u64, ) -> Result<&'selected [usize]>
Select the wanted closure minus the complete closure of common roots.
Common roots must be present, and are trusted to describe complete client
history (shallow clients must be rejected by the protocol layer). Both
traversals share one graph-read budget and the same caller-owned arrays.
Common-root iteration may fail (for example, a host metadata read); errors
propagate. Unknown roots fail; retries reset exclusions and traversal state.
Sourcepub fn select_fetch_objects<'selected>(
&self,
roots: &[ObjectId],
common: impl IntoIterator<Item = Result<ObjectId>>,
work: &mut [Option<ValidationMetadata>],
selected: &'selected mut [usize],
scratch: (&mut [u8], &mut [u8]),
options: SelectionOptions,
) -> Result<&'selected [usize]>
pub fn select_fetch_objects<'selected>( &self, roots: &[ObjectId], common: impl IntoIterator<Item = Result<ObjectId>>, work: &mut [Option<ValidationMetadata>], selected: &'selected mut [usize], scratch: (&mut [u8], &mut [u8]), options: SelectionOptions, ) -> Result<&'selected [usize]>
Select missing objects and optionally include advertised annotated tags whose final peeled targets are being sent. Tag traversal shares the graph-read budget with wanted and common history; retries reset all state.
Sourcepub fn select_bounded_fetch<'selected>(
&self,
roots: &[ObjectId],
history: (impl IntoIterator<Item = Result<ObjectId>>, impl IntoIterator<Item = Result<ObjectId>>),
work: &mut [Option<ValidationMetadata>],
selected: &'selected mut [usize],
scratch: (&mut [u8], &mut [u8]),
options: (SelectionOptions, FetchOptions),
) -> Result<&'selected [usize]>
pub fn select_bounded_fetch<'selected>( &self, roots: &[ObjectId], history: (impl IntoIterator<Item = Result<ObjectId>>, impl IntoIterator<Item = Result<ObjectId>>), work: &mut [Option<ValidationMetadata>], selected: &'selected mut [usize], scratch: (&mut [u8], &mut [u8]), options: (SelectionOptions, FetchOptions), ) -> Result<&'selected [usize]>
Select shallow/filtered history with caller-owned arrays. Client shallow IDs must name present commits. Common traversal stops at those commits; depth requests instead resend their closure without trusting common haves. Relative requests from full clients preserve ordinary common exclusion. Relative depth measures the nearest boundary from wanted history, then extends that absolute cut (Git 2.55). Both passes share the budget. Work metadata retains shallow/unshallow markers until the next selection. All structural reads, including revisits after a shorter merge path, consume one budget. Retries reset depths, exclusions and boundary markers.
history contains common roots followed by the client’s shallow IDs.
Each work/index slice needs one slot per repository object. Extra slots
remain untouched. The returned indices use provider enumeration order;
inspect only the first object_count() work slots for boundary updates.
A depth request may resend common objects, while an ordinary request
subtracts common history only as far as the client’s declared boundaries.
Empty wants, zero/oversized depth and absent or non-commit shallow IDs
return InvalidRequest. Missing wanted/common objects return
ObjectNotFound; short arrays or scratch return BufferTooSmall, and
exhausted structural-read budgets return LimitExceeded. No result is
published on failure; reuse the same arrays for a fresh attempt.
This example selects a depth-one snapshot without its blob. The root commit is a shallow boundary even though it has no parents, matching Git. The source, validation index and per-request work arrays are independent.
use git_zero_core::{
BlobFilter, FetchOptions, File, Repository, SelectionOptions, Snapshot,
ValidatedRepository,
};
let files = [File { name: "hello.txt", contents: b"hello\n" }];
let (mut tree, mut commit) = ([0; 128], [0; 512]);
let snapshot = Snapshot::new(&files, &mut tree, &mut commit)?;
let (mut metadata, mut by_id) = ([None; 3], [0; 3]);
let repository = ValidatedRepository::new(
&snapshot, &mut metadata, &mut by_id, &mut [0; 64], &mut [0; 128],
)?;
let (mut work, mut indices) = ([None; 3], [0; 3]);
let wanted = [repository.head()];
let selected = repository.select_bounded_fetch(
&wanted, (core::iter::empty(), core::iter::empty()),
&mut work, &mut indices, (&mut [0; 64], &mut [0; 128]),
(SelectionOptions { graph_read_limit: 4096, include_tags: false },
FetchOptions { depth: Some(1), filter: BlobFilter::None, ..FetchOptions::default() }),
)?;
assert_eq!(selected.len(), 2); // Commit and tree, with no blob.
let boundary = work.iter().flatten().find(|object| object.is_shallow()).unwrap();
assert_eq!(boundary.id, repository.head());Trait Implementations§
Source§impl<R: Repository + ?Sized> Repository for ValidatedRepository<'_, '_, R>
impl<R: Repository + ?Sized> Repository for ValidatedRepository<'_, '_, R>
Source§fn object_format(&self) -> ObjectFormat
fn object_format(&self) -> ObjectFormat
object_id.Source§fn head(&self) -> ObjectId
fn head(&self) -> ObjectId
head_state().id() when absence needs to be distinguished.fn head_state(&self) -> Head<'_>
fn object_count(&self) -> usize
Source§fn object(&self, index: usize) -> Option<Object<'_>>
fn object(&self, index: usize) -> Option<Object<'_>>
None and implement object_info, read_object, and object_id instead.fn object_info(&self, index: usize) -> Result<ObjectInfo>
Source§fn object_id(&self, index: usize) -> Option<ObjectId>
fn object_id(&self, index: usize) -> Option<ObjectId>
ObjectHasher computes them incrementally with caller-owned scratch.Source§fn read_object(
&self,
index: usize,
offset: u64,
storage: &mut [u8],
) -> Result<usize>
fn read_object( &self, index: usize, offset: u64, storage: &mut [u8], ) -> Result<usize>
storage.len() body bytes starting at a byte offset.
Short reads are allowed. Return zero only at EOF or for empty storage.
Calls must be independent of earlier reads, enabling interleaved readers
and retries. Errors may modify storage but must not change the object.