This repository has no description
1//! The same way one would do this for uploading images onto a server,
2//! we stage pushes such that objects unpack into a separate bare repo
3//! under the incoming prefix, because we treat a push as real only once
4//! every object it referenced actually made it over.
5//!
6//! Hence aborting is as simple as removal of the directory that
7//! represents the push in flight.
8//!
9//! "Why not use `GIT_QUARANTINE_PATH`?" - because we never forked `receive-pack`.
10//!
11//! If you were wondering, this `sweep_incoming` is for a crash between a stage
12//! and migration that would otherwise leak a staging dir.
13
14use std::path::Path;
15
16use knot_git::{INCOMING_PREFIX, Repo, Staging};
17
18use crate::error::PackError;
19use crate::meter::PackLimits;
20use crate::objects;
21
22pub(crate) struct Quarantine {
23 staging: Staging,
24}
25
26impl Quarantine {
27 pub(crate) fn stage(
28 live: &Repo,
29 pack: Option<&gix_pack::data::File>,
30 limits: &PackLimits,
31 kind: gix::hash::Kind,
32 live_empty: bool,
33 ) -> Result<(Self, Option<objects::FreshClosure>), PackError> {
34 let staging = Staging::new(live)?;
35 let (unpack, closure) = crate::receive::ingest(
36 &staging.repo().objects_dir(),
37 pack,
38 limits,
39 kind,
40 live_empty,
41 );
42 unpack?;
43 Ok((Self { staging }, closure))
44 }
45
46 pub(crate) fn repo(&self) -> &Repo {
47 self.staging.repo()
48 }
49
50 pub(crate) fn migrate_into(&self, live: &Repo) -> Result<(), PackError> {
51 self.staging.migrate_into(live).map_err(PackError::from)
52 }
53}
54
55pub fn sweep_incoming(scan_path: &Path) -> usize {
56 walkdir::WalkDir::new(scan_path)
57 .into_iter()
58 .filter_entry(|entry| entry.file_name().to_str() != Some("objects"))
59 .filter_map(Result::ok)
60 .filter(|entry| entry.file_type().is_dir())
61 .filter(|entry| {
62 entry
63 .file_name()
64 .to_str()
65 .is_some_and(|name| name.starts_with(INCOMING_PREFIX))
66 })
67 .map(|entry| entry.into_path())
68 .collect::<Vec<_>>()
69 .into_iter()
70 .filter(|path| std::fs::remove_dir_all(path).is_ok())
71 .count()
72}