pub struct CandidateStore { /* private fields */ }Expand description
What:
Everything one invocation needs to list and read candidates.
Option<ObjectReader> is “a running reader or nothing”;
u64 is an unsigned
64-bit counter (siblings u32,
usize).
Why:
The reader is started only when something needs it,
so an invocation that
reads no object starts no reader.
u64 cannot run out within a process.
In TS you’d write (pseudocode):
class CandidateStore { #reader?: ObjectReader; #generation = 0n; #versions: [CandidateSource, CandidateVersion][] = [] }Implementations§
Source§impl CandidateStore
What:
impl CandidateStore { ... } attaches the store’s operations,
like class methods.
Why:
The store is the only place that starts Git for candidates,
which is what
keeps the process count bounded.
impl CandidateStore
What:
impl CandidateStore { ... } attaches the store’s operations,
like class methods.
Why:
The store is the only place that starts Git for candidates,
which is what
keeps the process count bounded.
In TS you’d write (pseudocode):
class CandidateStore { version(source) {} bytes(candidate) {} invalidate() {} }Sourcepub fn new(
real_git: &Path,
global_prefix: &[OsString],
overlay: &[(OsString, OsString)],
) -> CandidateStore
pub fn new( real_git: &Path, global_prefix: &[OsString], overlay: &[(OsString, OsString)], ) -> CandidateStore
What:
Create a store without starting any process.
&[OsString] borrows the global arguments;
&[(OsString, OsString)] borrows
environment pairs;
.to_vec() and .to_path_buf() copy them into owned storage.
Why:
The store outlives the caller’s borrowed arguments,
and commands that
never ask for candidates pay nothing.
In TS you’d write (pseudocode):
constructor(realGit: string, globalPrefix: string[], overlay: [string, string][])Sourcepub fn with_baseline(self, baseline: Option<ObjectId>) -> CandidateStore
pub fn with_baseline(self, baseline: Option<ObjectId>) -> CandidateStore
What:
Compare every “against HEAD” listing with a recorded base instead of the live HEAD.
Why:
A commit transaction reads its candidates against the base it recorded at invocation,
because another landing may have moved HEAD since.
In TS you’d write (pseudocode):
listChangedIndexPaths({ baseRevision }) // never live HEADSourcepub fn version(
&mut self,
source: &CandidateSource,
) -> Result<Rc<CandidateVersion>, CandidateError>
pub fn version( &mut self, source: &CandidateSource, ) -> Result<Rc<CandidateVersion>, CandidateError>
What:
The version of one source,
listed once per generation and shared afterwards.
Rc::clone(..) makes another handle to the same version without copying it.
Why:
Every policy of a pass sees the same paths,
and asking again costs no process.
In TS you’d write (pseudocode):
async version(source: CandidateSource): Promise<CandidateVersion>Sourcepub fn stage_records(
&mut self,
pathspecs: &[OsString],
) -> Result<Vec<StageRecord>, CandidateError>
pub fn stage_records( &mut self, pathspecs: &[OsString], ) -> Result<Vec<StageRecord>, CandidateError>
What:
The index entries pathspecs select,
every conflict stage included,
with
repository-relative pathnames.
Why:
ls-files --stage reads only the index,
so comparing two of these listings
shows exactly which entries an operation on that index changed.
--full-name
keeps pathnames repository-relative from any directory.
In TS you’d write (pseudocode):
async stageRecords(pathspecs: string[]): Promise<StageRecord[]>Sourcepub fn head_records(
&mut self,
pathspecs: &[OsString],
) -> Result<Vec<CandidateRecord>, CandidateError>
pub fn head_records( &mut self, pathspecs: &[OsString], ) -> Result<Vec<CandidateRecord>, CandidateError>
What:
The changed-path records of the index against HEAD,
limited to pathspecs;
against the empty tree when no commit exists yet.
Why:
Which selected paths HEAD lacks makes them additions,
and which removed
paths it has makes them deletions.
The limit keeps a conflict elsewhere in the
index out of the listing.
In TS you’d write (pseudocode):
async headRecords(pathspecs: string[]): Promise<CandidateRecord[]>Sourcepub fn head_tree_records(&mut self) -> Result<Vec<TreeRecord>, CandidateError>
pub fn head_tree_records(&mut self) -> Result<Vec<TreeRecord>, CandidateError>
What: Every file of the HEAD commit with its mode and object; nothing when the
branch has no commit yet.
Why: A policy that compares the candidate state with HEAD reads HEAD’s copy of
a file without naming the path in an object request, and learns its mode.
--full-tree lists from the top level whatever directory Git runs in.
In TS you’d write (pseudocode):
async headTreeRecords(): Promise<TreeRecord[]>Sourcepub fn derived_version(
&mut self,
records: Vec<CandidateRecord>,
) -> Rc<CandidateVersion>
pub fn derived_version( &mut self, records: Vec<CandidateRecord>, ) -> Rc<CandidateVersion>
What: A version of the current generation built from records the caller derived.
Vec<CandidateRecord> is taken by value: the records move into the version.
Why: A predicted staging operation has no single listing command; its candidates
come from comparing index states. The version is not remembered by source,
because no source names it.
In TS you’d write (pseudocode):
derivedVersion(records: CandidateRecord[]): CandidateVersionSourcepub fn committed_records(
&mut self,
commit: &ObjectId,
) -> Result<Vec<CandidateRecord>, CandidateError>
pub fn committed_records( &mut self, commit: &ObjectId, ) -> Result<Vec<CandidateRecord>, CandidateError>
What: The records of one commit’s own delta, each against every parent (or the empty tree for a root commit). Why: A pushed commit’s changes are the content a manual-push scan reads; the listing is the one the landed-delta source already uses.
In TS you’d write (pseudocode):
committedRecords(commit: ObjectId): CandidateRecord[]Sourcepub fn tree_records(
&mut self,
tree: &ObjectId,
) -> Result<Vec<CandidateRecord>, CandidateError>
pub fn tree_records( &mut self, tree: &ObjectId, ) -> Result<Vec<CandidateRecord>, CandidateError>
What: Every blob entry of one tree, as records that add each path. Why: A push with no known published base scans the pushed tip’s whole tree. Submodule entries are not listed: their content is a commit name, which no content policy reads.
In TS you’d write (pseudocode):
treeRecords(tree: ObjectId): CandidateRecord[] // ls-tree -r, every blob as addedSourcepub fn bytes(
&mut self,
candidate: &Candidate,
) -> Result<Rc<[u8]>, CandidateError>
pub fn bytes( &mut self, candidate: &Candidate, ) -> Result<Rc<[u8]>, CandidateError>
What:
The exact bytes a candidate holds in its version.
Rc<[u8]> is a shared read-only byte list.
Why:
Bytes come from the object the listing named,
so an edit to the worktree
file after staging is invisible here.
A deleted path has no bytes.
A
gitlink has no blob in this repository,
so its bytes are the submodule
commit’s name,
as in the TypeScript wrapper.
A candidate of a retired
version is refused instead of answered with bytes that may be outdated.
In TS you’d write (pseudocode):
async bytes(candidate: Candidate): Promise<Uint8Array>Sourcepub fn invalidate(&mut self)
pub fn invalidate(&mut self)
What:
Retire every version listed so far.
+= 1 advances the generation.
Why:
A fix or replay rewrites index entries,
so earlier listings no longer
describe what would be committed.
Blob bytes stay remembered in the
reader:
an object name always denotes the same bytes.
In TS you’d write (pseudocode):
invalidate(): void { this.#versions = []; this.#generation += 1n; }Trait Implementations§
Source§impl CandidateBytes for CandidateStore
What:
impl CandidateBytes for CandidateStore reads through the store itself.
Why:
Without corrections the store’s objects are the bytes to scan.
impl CandidateBytes for CandidateStore
What:
impl CandidateBytes for CandidateStore reads through the store itself.
Why:
Without corrections the store’s objects are the bytes to scan.
In TS you’d write (pseudocode):
class CandidateStore implements CandidateBytes {}Source§fn candidate_bytes(
&mut self,
candidate: &Candidate,
) -> Result<Rc<[u8]>, CandidateError>
fn candidate_bytes( &mut self, candidate: &Candidate, ) -> Result<Rc<[u8]>, CandidateError>
What:
The store’s bytes for the candidate.
Why:
CandidateStore::bytes is the read.
In TS you’d write (pseudocode):
bytes(candidate) { return this.read(candidate); }Auto Trait Implementations§
impl !Send for CandidateStore
impl !Sync for CandidateStore
impl Freeze for CandidateStore
impl RefUnwindSafe for CandidateStore
impl Unpin for CandidateStore
impl UnsafeUnpin for CandidateStore
impl UnwindSafe for CandidateStore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more