use super::conclusion_cleanup::{
CONCLUSION_COPY_DIRECTORY, CONCLUSION_STATE_FILES, SEQUENCER_DIRECTORY, STORE_RECORD_FILENAME,
is_store_held, resolve_pseudoref,
};
use super::private_storage::{create_private_directory, write_private_file};
use super::recovery_files::{path_present, read_optional};
use super::json_record::ObjectWriter;
use super::shadow_git::shadow_request;
use super::transaction_git::{GitContext, GitFailure, GitRequest, run_git_checked};
use super::transaction_journal::RefFormat;
use std::path::{Path, PathBuf};
fn copy_tree(source: &Path, destination: &Path) -> std::io::Result<()> {
create_private_directory(destination)?;
for entry_result in std::fs::read_dir(source)? {
let entry: std::fs::DirEntry = entry_result?;
let target: PathBuf = destination.join(entry.file_name());
let kind: std::fs::FileType = entry.file_type()?;
if kind.is_dir() {
copy_tree(entry.path().as_path(), target.as_path())?;
} else if kind.is_symlink() {
#[cfg(unix)]
std::os::unix::fs::symlink(std::fs::read_link(entry.path())?, &target)?;
#[cfg(not(unix))]
std::fs::copy(entry.path(), &target).map(|_| ())?;
} else {
std::fs::copy(entry.path(), &target)?;
}
}
return Ok(());
}
fn owning_pseudoref(context: &GitContext, cwd: &Path, name: &str) -> Option<String> {
let mut request: GitRequest = GitRequest::new(cwd, &["rev-parse", "--verify", "--quiet", name]);
request.without_prefix = true;
return resolve_pseudoref(context, request);
}
pub fn copy_conclusion_state(
context: &GitContext,
cwd: &Path,
git_dir: &Path,
shadow: &Path,
transaction_directory: &Path,
ref_format: RefFormat,
) -> Result<(), GitFailure> {
let io = |step: &str, error: std::io::Error| { return GitFailure(format!("{step} failed: {error}")); };
let copies: PathBuf = transaction_directory.join(CONCLUSION_COPY_DIRECTORY);
create_private_directory(copies.as_path()).map_err(|error| { return io("creating the conclusion copies", error); })?;
let mut store_held: ObjectWriter = ObjectWriter::new();
for name in CONCLUSION_STATE_FILES {
if is_store_held(ref_format, name) {
if let Some(value) = owning_pseudoref(context, cwd, name) {
run_git_checked(
context,
&shadow_request(shadow, shadow, &["update-ref", name, value.as_str()]),
)?;
store_held.string(name, value.as_str());
}
continue;
}
let Some(bytes) = read_optional(git_dir.join(name).as_path())
.map_err(|error| { return GitFailure(error.0); })?
else {
continue;
};
write_private_file(shadow.join(name).as_path(), bytes.as_slice())
.map_err(|error| { return io("copying a conclusion entry into the shadow", error); })?;
write_private_file(copies.join(name).as_path(), bytes.as_slice())
.map_err(|error| { return io("copying a conclusion entry for comparison", error); })?;
}
let record: String = store_held.finish();
write_private_file(
copies.join(STORE_RECORD_FILENAME).as_path(),
format!("{record}\n").as_bytes(),
)
.map_err(|error| { return io("writing the store-held record", error); })?;
let sequencer: PathBuf = git_dir.join(SEQUENCER_DIRECTORY);
if path_present(sequencer.as_path()).map_err(|error| { return GitFailure(error.0); })? {
copy_tree(sequencer.as_path(), shadow.join(SEQUENCER_DIRECTORY).as_path())
.map_err(|error| { return io("copying the sequencer into the shadow", error); })?;
copy_tree(sequencer.as_path(), copies.join(SEQUENCER_DIRECTORY).as_path())
.map_err(|error| { return io("copying the sequencer for comparison", error); })?;
}
return Ok(());
}
#[cfg(test)]
#[path = "conclusion_copy_tests.rs"]
mod tests;