use crate::merge_second_pass::{Conflict, conflicts_of_merge_tree};
use crate::child_environment::environment_with_overlay;
use crate::merge_second_pass::ContextRunner;
use crate::merge_second_pass_tree::{AttributeProbe, attribute_tree_variables};
use crate::shadow_git::shadow_request;
use crate::transaction_git::{GitContext, GitFailure, GitRequest, run_git};
use std::path::Path;
const MERGE_CONFLICT_EXIT: i32 = 1;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ReplayMerge {
Merged {
tree_oid: String,
},
Conflict {
paths: Vec<Vec<u8>>,
tree_oid: String,
conflicts: Vec<Conflict>,
},
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ReplayMergeError {
Git(GitFailure),
Rebuild(String),
}
pub fn merge_replay_tree(
context: &GitContext,
shadow: &Path,
worktree_root: &Path,
merge_base: &str,
current: &str,
prepared: &str,
) -> Result<ReplayMerge, ReplayMergeError> {
let merge_base_argument: String = format!("--merge-base={merge_base}");
let mut request: GitRequest = shadow_request(
shadow,
worktree_root,
&[
"merge-tree",
"--write-tree",
"-z",
merge_base_argument.as_str(),
current,
prepared,
],
);
let process_environment: Vec<(std::ffi::OsString, std::ffi::OsString)> = std::env::vars_os().collect();
let variables = environment_with_overlay(process_environment.as_slice(), context.overlay.as_slice());
let probing = ContextRunner { context, cwd: worktree_root, without_prefix: true, object_directory: None, environment: Vec::new() };
let probe = AttributeProbe { directory: worktree_root, global_prefix: &[], variables: variables.as_slice(), runner: &probing };
request.environment.extend(attribute_tree_variables(current, &probe));
let output = run_git(context, &request).map_err(|error| {
return ReplayMergeError::Git(GitFailure(format!("git merge-tree could not start: {error}")));
})?;
let code: Option<i32> = output.code;
if code != Some(0) && code != Some(MERGE_CONFLICT_EXIT) {
return Err(ReplayMergeError::Git(GitFailure(format!(
"git merge-tree could not replay {prepared} onto {current} (exit {code:?}): {}",
String::from_utf8_lossy(&output.stderr).trim()
))));
}
let (tree_oid, conflicts) = conflicts_of_merge_tree(output.stdout.as_slice());
if code == Some(0) {
return Ok(ReplayMerge::Merged { tree_oid });
}
let mut paths: Vec<Vec<u8>> = conflicts.iter().map(|conflict| { return conflict.path.clone(); }).collect();
paths.sort();
paths.dedup();
return Ok(ReplayMerge::Conflict { paths, tree_oid, conflicts });
}
#[cfg(test)]
#[path = "replay_merge_tests.rs"]
mod tests;