storage/checkpoint/
base.rs1use std::io::Write;
9
10use kernel::{Payload, TimeMs};
11
12use crate::error::StorageError;
13
14use super::commit::{Checkpoint, checkpoint_ref, committed, git_err, subject_of};
15use super::provenance::Provenance;
16use super::scan::CommitPlan;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum BaseProgress {
21 Staged { files: usize },
23 Packed { bytes: usize },
25}
26
27impl Checkpoint {
28 pub fn base_checkpoint(
43 mut self,
44 scopes: &[String],
45 t: TimeMs,
46 of: &Provenance,
47 progress: &mut dyn FnMut(BaseProgress),
48 ) -> Result<Payload, StorageError> {
49 let objects = self.repo.path().join("objects");
50 let objects = objects.to_str().ok_or_else(|| StorageError::Checkpoint {
51 op: "open the object store",
52 detail: format!("{} is not valid UTF-8", objects.display()),
53 })?;
54 let odb = git2::Odb::new().map_err(git_err("open an in-memory object store"))?;
57 let held = odb
58 .add_new_mempack_backend(1)
59 .map_err(git_err("open an in-memory object store"))?;
60 odb.add_disk_alternate(objects)
61 .map_err(git_err("read the object store"))?;
62 self.repo
63 .set_odb(&odb)
64 .map_err(git_err("hold the base checkpoint in memory"))?;
65
66 let files = self.stage_scopes_held(scopes)?;
67 progress(BaseProgress::Staged { files: files.len() });
68 self.scan_staged()?;
69 let onto_head = self.repo.head().is_err();
72 let oid = self.commit(&CommitPlan {
73 t,
74 of,
75 subject: &subject_of(scopes),
76 onto_head: false,
77 })?;
78
79 let mut pack = git2::Buf::new();
80 held.dump(&self.repo, &mut pack)
81 .map_err(git_err("dump the base checkpoint"))?;
82 let disk =
83 git2::Repository::open(self.repo.path()).map_err(git_err("open the object store"))?;
84 let disk_odb = disk.odb().map_err(git_err("open the object store"))?;
85 let mut writer = disk_odb
86 .packwriter()
87 .map_err(git_err("write the base checkpoint pack"))?;
88 writer
89 .write_all(&pack)
90 .map_err(|err| StorageError::Checkpoint {
91 op: "write the base checkpoint pack",
92 detail: err.to_string(),
93 })?;
94 writer
95 .commit()
96 .map_err(git_err("write the base checkpoint pack"))?;
97 progress(BaseProgress::Packed { bytes: pack.len() });
98
99 super::scan::write_index(&mut self.repo.index().map_err(git_err("read index"))?)?;
102 let name = if onto_head {
103 self.repo
104 .find_reference("HEAD")
105 .map_err(git_err("read HEAD"))?
106 .symbolic_target()
107 .map_err(|err| StorageError::Checkpoint {
108 op: "read HEAD",
109 detail: err.to_string(),
110 })?
111 .map(str::to_owned)
112 .ok_or_else(|| StorageError::Checkpoint {
113 op: "move HEAD to the base checkpoint",
114 detail: "HEAD names no branch".to_owned(),
115 })?
116 } else {
117 checkpoint_ref(of, oid)
118 };
119 self.repo
120 .reference(&name, oid, true, "sprawling: a base checkpoint")
121 .map_err(git_err("file a base checkpoint"))?;
122 held.reset()
123 .map_err(git_err("release the base checkpoint"))?;
124 committed(oid, of, scopes, files)
125 }
126}
127
128#[cfg(test)]
129#[allow(
130 clippy::unwrap_used,
131 clippy::expect_used,
132 clippy::panic,
133 clippy::indexing_slicing,
134 reason = "test code"
135)]
136mod tests;