pub struct ManageBranch<'a> { /* private fields */ }Expand description
Operations on a single branch within a repository.
Implementations§
Source§impl<'a> ManageBranch<'a>
impl<'a> ManageBranch<'a>
Sourcepub async fn open(
store: Arc<dyn ObjectStore>,
prefix: impl Into<String>,
branch: impl Into<String>,
prompter: &'a dyn Prompter,
) -> Result<Self, ManageError>
pub async fn open( store: Arc<dyn ObjectStore>, prefix: impl Into<String>, branch: impl Into<String>, prompter: &'a dyn Prompter, ) -> Result<Self, ManageError>
Open a branch handle, verifying it exists by listing
<prefix>/refs/heads/<branch>/ (or refs/heads/<branch>/ when
prefix is empty).
§Errors
Returns ManageError::InvalidBranch if branch fails
gix-validate’s strict ref-name check. Returns
ManageError::BranchNotFound when no objects exist under the
branch prefix. Returns ManageError::Store for object-store
failures.
Sourcepub async fn delete(&self) -> Result<(), ManageError>
pub async fn delete(&self) -> Result<(), ManageError>
Delete every object under the branch’s prefix after a yes/no
confirmation. Aborts (returns Ok(())) if the user answers no;
the Cancelled variant is reserved for prompt I/O failures.
Refuses outright when a PROTECTED# marker is present under the
branch prefix — the operator must run unprotect first. This
mirrors the refusal the helper-protocol delete path
(delete_remote_ref_under_lock) emits, so a git push :branch
against a protected ref and a management-CLI delete-branch of
the same ref fail the same way.
§Per-ref lock (#158)
After the operator confirms the prompt, delete-branch acquires
the same <prefix>/<ref>/LOCK#.lock the helper-protocol push
and delete paths take. The lock is held across the fresh re-list,
the baseline tombstone write (#143), and the synchronous sweep.
Without it a concurrent git push could land a new bundle after
the post-prompt re-list, the sweep would delete only the stale
snapshot, and the ref would survive with the just-pushed bundle
even though delete-branch reported success.
Lock acquisition runs AFTER the prompt — the prompt is
interactive and could block indefinitely, and holding the lock
across user input would make every other writer wait on the
operator’s keyboard. If the lock is contended at acquisition
time the function returns ManageError::LockContended and
makes no changes. Release failures are downgraded to a warn!
because the lock’s TTL guarantees a stale lock is recovered by
the next acquirer; matches the protocol-push pattern.
The prompt-display and protection-marker check use a first listing
for accuracy of the displayed object count, then a second
listing is taken under the lock immediately before the deletion
loop. The fresh listing drives the sweep so that any concurrent
push landing under the branch prefix during the prompt window —
before the lock window opens — is caught and deleted rather than
left as a zombie object (#139). The protection-marker check is
re-evaluated on the fresh listing so a protect racing with the
prompt is honoured (#131) — the post-prompt re-check is what
closes the TOCTOU window between the initial marker check and
the deletion loop. If the fresh listing is empty (a concurrent
delete won the race) the function reports it and returns
Ok(()) rather than silently claiming success.
NotFound errors observed during the sweep are tolerated — they
mean a concurrent deleter swept the key first, which still
satisfies the operator’s intent. Other per-key delete errors
(Network, AccessDenied, …) are collected: the loop does NOT
short-circuit, every remaining key is still attempted, and the
function returns ManageError::PartialDelete with the exact
list of keys that survived so a retry can converge (#122). A
list-call failure still propagates immediately because there is
nothing to recover — without a listing the sweep cannot proceed.
Packchain refs with a parseable chain.json skip immediate
deletion of the baseline bundle (<full_at>.bundle): a
baseline tombstone is written first and the bundle is left for
gc sweep to reclaim after the grace window (#143). The
synchronous sweep still removes chain.json,
path-index.json, and any other residue. The deferral protects
an in-flight fetcher that already read the prior chain.json
from a BaselineMissing range-GET failure; a fresh reader
sees the missing chain and the ref is gone from its
perspective. Bundle-engine refs, refs with an unparseable
chain, and any tombstone PUT failure fall through to immediate
bundle deletion so the operator’s “ref is gone” intent is
never blocked on the tombstone path.
§Errors
Returns ManageError::Protected if the branch carries a
PROTECTED# marker (checked on both listings),
ManageError::LockContended if another writer holds the
per-ref lock at acquisition time,
ManageError::Cancelled if the user cancels the prompt,
ManageError::Io for prompt or write I/O failures,
ManageError::Store if a list operation fails, or
ManageError::PartialDelete when one or more per-key deletes
fail with a non-NotFound error after every key in the fresh
listing has been attempted.
Sourcepub async fn protect(&self) -> Result<(), ManageError>
pub async fn protect(&self) -> Result<(), ManageError>
Mark the branch as protected by writing the PROTECTED# sentinel.
Idempotent — overwrites any existing marker.
§Per-ref lock (#159)
protect acquires the same <prefix>/<ref>/LOCK#.lock the
helper-protocol push, helper-protocol delete, and delete-branch
take. Pre-#159, the push path’s pre-bundle is_protected check
could race a concurrent protect: a force-push that observed no
marker would still overwrite the bundle even if protect landed
between the under-lock is_protected and the bundle upload —
because protect was a lockless put_bytes. Taking the same
lock serialises protection state changes against the writers
that consult it, closing the entire write window rather than
narrowing it to a second sample.
If the lock is contended (a push, delete, or compact holds it),
protect returns ManageError::LockContended and makes no
changes. Operators can retry. Stale-lock recovery is inherited
from acquire_lock (a previous holder that crashed without
releasing).
Re-lists the branch prefix under the lock so a concurrent
delete-branch (or last-bundle removal) that landed between
ManageBranch::open and the lock window is caught and the
marker is NOT written for a non-existent branch (#137). Without
this re-check the orphaned PROTECTED# would persist with no
automated cleanup and would silently block a future recreation
of the same branch from being force-pushed or deleted. The
re-listing filters out stale lock keys and any pre-existing
PROTECTED# marker so a branch whose only residue is operational
metadata is treated as gone.
§Errors
Returns ManageError::BranchNotFound if the under-lock listing
shows the branch was deleted concurrently. Returns
ManageError::LockContended if another writer holds the
per-ref lock at acquisition time. Returns ManageError::Store
if a list or put operation fails.
Sourcepub async fn unprotect(&self) -> Result<(), ManageError>
pub async fn unprotect(&self) -> Result<(), ManageError>
Remove the PROTECTED# sentinel. A missing marker is treated as
already-unprotected rather than an error.
§Per-ref lock (#159)
unprotect acquires the same per-ref lock as Self::protect
so ALL protection state changes serialise against pushes,
deletes, and compactions. Without taking the lock here a
concurrent push observing is_protected() == true could
otherwise commit to the protected refusal path just as
unprotect landed, leaving the writer’s behaviour out of step
with operator intent. Symmetry with protect keeps the lock the
single point of serialisation for protection state.
§Errors
Returns ManageError::LockContended if another writer holds
the per-ref lock at acquisition time. Returns
ManageError::Store for object-store failures other than
NotFound.
Auto Trait Implementations§
impl<'a> Freeze for ManageBranch<'a>
impl<'a> !RefUnwindSafe for ManageBranch<'a>
impl<'a> Send for ManageBranch<'a>
impl<'a> Sync for ManageBranch<'a>
impl<'a> Unpin for ManageBranch<'a>
impl<'a> UnsafeUnpin for ManageBranch<'a>
impl<'a> !UnwindSafe for ManageBranch<'a>
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