use std::ops::Deref; use git_next_config::BranchName; use secrecy::ExposeSecret; use tracing::{info, warn}; use super::{GitRef, RepoDetails, Repository}; #[derive(Debug)] pub enum Force { No, From(GitRef), } impl std::fmt::Display for Force { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::No => write!(f, "fast-foward"), Self::From(from) => write!(f, "force-if-from:{}", from), } } } // TODO: (#72) reimplement using `gix` #[tracing::instrument(skip_all, fields(branch = %branch_name, to = %to_commit, force = %force))] pub fn reset( repository: &Repository, repo_details: &RepoDetails, branch_name: BranchName, to_commit: GitRef, force: Force, ) -> Result { 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(repository.deref().git_dir().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) } } } #[derive(Debug, derive_more::From, derive_more::Display)] pub enum BranchResetError { Open(Box), Fetch(super::fetch::Error), Push, } impl std::error::Error for BranchResetError {} pub type Result = core::result::Result<(), BranchResetError>;