128 lines
4.3 KiB
Rust
128 lines
4.3 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
bare::GitBare,
|
|
cmd::{
|
|
commit::CommitSignature, merge::MergeOptions, oid::ObjectId,
|
|
parse::format_git_timestamp,
|
|
},
|
|
errors::{GitError, GitResult},
|
|
};
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct MergeCommitParams {
|
|
pub their_commit: ObjectId,
|
|
pub author: CommitSignature,
|
|
pub committer: CommitSignature,
|
|
pub message: String,
|
|
pub update_ref: Option<String>,
|
|
pub options: Option<MergeOptions>,
|
|
}
|
|
|
|
impl GitBare {
|
|
pub fn merge_commit(
|
|
&self,
|
|
params: MergeCommitParams,
|
|
) -> GitResult<ObjectId> {
|
|
let repo = self.gix_repo()?;
|
|
let head_id =
|
|
repo.head_id().map_err(|e| GitError::Gix(e.to_string()))?;
|
|
let head_oid = ObjectId::new(head_id.detach().to_hex().to_string());
|
|
let mut merge_tree_args =
|
|
vec!["merge-tree".to_string(), "--write-tree".to_string()];
|
|
if let Some(opts) = ¶ms.options {
|
|
if opts.find_renames {
|
|
merge_tree_args.push("--find-renames".to_string());
|
|
if opts.rename_threshold > 0 {
|
|
merge_tree_args.push(format!(
|
|
"--rename-threshold={}",
|
|
opts.rename_threshold
|
|
));
|
|
}
|
|
}
|
|
if opts.fail_on_conflict {
|
|
merge_tree_args.push("--fail-on-conflict".to_string());
|
|
}
|
|
}
|
|
merge_tree_args.push(head_oid.as_str().to_string());
|
|
merge_tree_args.push(params.their_commit.as_str().to_string());
|
|
|
|
let merge_tree_output = self.git_command_stdout(merge_tree_args)?;
|
|
let tree_oid_str =
|
|
merge_tree_output.lines().next().unwrap_or("").trim();
|
|
if tree_oid_str.is_empty() {
|
|
return Err(GitError::CommandFailed {
|
|
status_code: None,
|
|
stderr: "merge-tree produced no output".to_string(),
|
|
});
|
|
}
|
|
|
|
let tree_oid = ObjectId::new(tree_oid_str);
|
|
let commit_tree_args = vec![
|
|
"commit-tree".to_string(),
|
|
tree_oid.as_str().to_string(),
|
|
"-p".to_string(),
|
|
head_oid.as_str().to_string(),
|
|
"-p".to_string(),
|
|
params.their_commit.as_str().to_string(),
|
|
"-F".to_string(),
|
|
"-".to_string(), // read message from stdin
|
|
];
|
|
|
|
let author_timestamp = format_git_timestamp(
|
|
params.author.time_secs,
|
|
params.author.offset_minutes,
|
|
);
|
|
let committer_timestamp = format_git_timestamp(
|
|
params.committer.time_secs,
|
|
params.committer.offset_minutes,
|
|
);
|
|
|
|
let commit_output = self.git_command_with(
|
|
crate::cmd::command::GitCommandParams::new(commit_tree_args)
|
|
.with_stdin(params.message.as_bytes().to_vec())
|
|
.with_env(
|
|
"GIT_AUTHOR_NAME".to_string(),
|
|
params.author.name.clone(),
|
|
)
|
|
.with_env(
|
|
"GIT_AUTHOR_EMAIL".to_string(),
|
|
params.author.email.clone(),
|
|
)
|
|
.with_env("GIT_AUTHOR_DATE".to_string(), author_timestamp)
|
|
.with_env(
|
|
"GIT_COMMITTER_NAME".to_string(),
|
|
params.committer.name.clone(),
|
|
)
|
|
.with_env(
|
|
"GIT_COMMITTER_EMAIL".to_string(),
|
|
params.committer.email.clone(),
|
|
)
|
|
.with_env(
|
|
"GIT_COMMITTER_DATE".to_string(),
|
|
committer_timestamp,
|
|
),
|
|
)?;
|
|
|
|
let stdout_str = commit_output.stdout_lossy();
|
|
let commit_oid_str = stdout_str.trim();
|
|
if commit_oid_str.is_empty() {
|
|
return Err(GitError::CommandFailed {
|
|
status_code: commit_output.status_code,
|
|
stderr: commit_output.stderr_lossy(),
|
|
});
|
|
}
|
|
|
|
let result_oid = ObjectId::new(commit_oid_str);
|
|
if let Some(ref_name) = ¶ms.update_ref {
|
|
self.git_command_trusted_stdout(vec![
|
|
"update-ref".to_string(),
|
|
ref_name.clone(),
|
|
result_oid.as_str().to_string(),
|
|
])?;
|
|
}
|
|
|
|
Ok(result_oid)
|
|
}
|
|
}
|