use crate::recovery_files::read_optional;
use crate::shadow_git::shadow_request;
use crate::service_protocol::Cleanup;
use crate::transaction_git::{GitContext, GitRequest, run_git};
use crate::diagnostic_log::{debug, warn};
use std::path::{Path, PathBuf};
const AUTOSTASH_FILE: &str = "MERGE_AUTOSTASH";
const STASH_MESSAGE: &str = "autostash";
fn conflicted_autostash(git: &GitContext, cleanup: &Cleanup) -> Option<String> {
let copy: Vec<u8> = read_optional(cleanup.scratch.join("conclusion").join(AUTOSTASH_FILE).as_path()).ok().flatten()?;
let recorded: String = String::from_utf8_lossy(copy.as_slice()).trim().to_owned();
let shadow: PathBuf = cleanup.scratch.join("shadow");
let stored = run_git(git, &shadow_request(shadow.as_path(), shadow.as_path(), &["rev-parse", "--verify", "--quiet", "refs/stash"])).ok()?;
if !stored.succeeded() {
return None;
}
let kept: String = String::from_utf8_lossy(stored.stdout.as_slice()).trim().to_owned();
if recorded.is_empty() || kept != recorded {
return None;
}
return Some(recorded);
}
pub fn carry_stash(git: &GitContext, repository: &Path, cleanup: &Cleanup) -> bool {
let Some(stash) = conflicted_autostash(git, cleanup) else { return true };
let listed = run_git(git, &GitRequest::new(repository, &["stash", "list", "--format=%H"]));
if let Ok(output) = &listed
&& output.succeeded()
&& String::from_utf8_lossy(output.stdout.as_slice()).lines().any(|line| { return line == stash; })
{
return true;
}
return match run_git(git, &GitRequest::new(repository, &["stash", "store", "-m", STASH_MESSAGE, stash.as_str()])) {
Ok(output) if output.succeeded() => {
debug("autostashCarry", format!("the stash {stash} the apply conflicted on was stored in the repository").as_str());
true
}
other => {
warn("autostashCarry", format!("the stash {stash} could not be stored in the repository: {other:?}").as_str());
false
}
};
}
pub fn carry_stages(git: &GitContext, repository: &Path, cleanup: &Cleanup, built: &Path) -> bool {
if conflicted_autostash(git, cleanup).is_none() || !built.exists() {
return true;
}
let mut listing: GitRequest = GitRequest::new(repository, &["ls-files", "--unmerged", "-z"]);
listing.index = Some(cleanup.scratch.join("commit.index"));
let Ok(stages) = run_git(git, &listing) else { return false };
if !stages.succeeded() {
return false;
}
if stages.stdout.is_empty() {
return true;
}
let entries: Vec<&[u8]> = stages.stdout.split(|byte| { return *byte == 0; }).filter(|entry| { return !entry.is_empty(); }).collect();
let length: usize = entries[0].split(|byte| { return *byte == b' '; }).nth(1).map_or(0, <[u8]>::len);
let mut paths: Vec<&[u8]> = entries.iter().filter_map(|entry| { return entry.split(|byte| { return *byte == b'\t'; }).nth(1); }).collect();
paths.dedup();
let mut input: Vec<u8> = Vec::new();
for path in &paths {
input.extend_from_slice(format!("0 {}\t", "0".repeat(length)).as_bytes());
input.extend_from_slice(path);
input.push(0);
}
for entry in &entries {
input.extend_from_slice(entry);
input.push(0);
}
let mut placing: GitRequest = GitRequest::new(repository, &["update-index", "-z", "--index-info"]);
placing.index = Some(built.to_path_buf());
placing.input = Some(input);
return match run_git(git, &placing) {
Ok(output) if output.succeeded() => true,
other => {
warn("autostashCarry", format!("the conflict stages of the stash could not be put into the index: {other:?}").as_str());
false
}
};
}