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, pub options: Option, } impl GitBare { pub fn merge_commit( &self, params: MergeCommitParams, ) -> GitResult { 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) } }