use std::ops::Deref; use secrecy::ExposeSecret; use tracing::{info, warn}; use crate::server::{ config::{BranchName, RepoDetails}, gitforge::{BranchResetError, BranchResetResult, Force}, types::GitRef, }; // TODO: (#72) reimplement using `gix` #[tracing::instrument(skip_all, fields(branch = %branch_name, to = %to_commit, force = %force))] pub fn reset( repo_details: &RepoDetails, branch_name: BranchName, to_commit: GitRef, force: Force, ) -> BranchResetResult { let repository = gix::ThreadSafeRepository::open(repo_details.gitdir.deref()) .map_err(Box::new)? .to_thread_local(); let gitdir = repository.git_dir(); let origin = repo_details.origin(); let force = match force { Force::No => "".to_string(), Force::From(old_ref) => format!("--force-with-lease={branch_name}:{old_ref}"), }; // INFO: never log the command as it contains the API token within the 'origin' let command: secrecy::Secret = format!( "/usr/bin/git push {} {to_commit}:{branch_name} {force}", origin.expose_secret() ) .into(); let ctx = gix::diff::command::Context { git_dir: Some(gitdir.to_path_buf()), ..Default::default() }; match gix::command::prepare(command.expose_secret()) .with_context(ctx) .with_shell_allow_argument_splitting() .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) .spawn() { Ok(mut child) => match child.wait() { Ok(_) => { info!("Branch updated"); Ok(()) } Err(err) => { warn!(?err, "Failed (wait)"); Err(BranchResetError::Push) } }, Err(err) => { warn!(?err, "Failed (spawn)"); Err(BranchResetError::Push) } } }