use crate::action::ENGINE_FAILURE_EXIT_CODE;
#[cfg(unix)]
use crate::auto_push_trigger::{TipWatch, hand_over_if_ahead, try_watch_tip};
use crate::push_trigger_log::{Decision, Origin, Outcome, record_decision};
use crate::forwarding::replace_process_with_real_git;
use crate::index_writer_forward::{CoordinatedForward, CoordinatedRun, run_coordinated_forward};
use crate::forwarded_command::ResolvedCommand;
use crate::worktree_identity::WorktreeIdentity;
use crate::worktree_copy_forward::{ForwardedRun, run_forwarded_git};
use std::io::Write;
use std::path::PathBuf;
pub(crate) fn finish_forwarded(run: ForwardedRun) -> i32 {
match run {
ForwardedRun::Ran(code) => return code,
ForwardedRun::Failed { stderr, code } => {
write_stream(&mut std::io::stderr(), stderr.as_str());
return code;
}
}
}
pub(crate) fn run_coordinated_action(forward: &CoordinatedForward) -> i32 {
let watch: Option<TipWatch> = if adds_commits(&forward.command) { watch_forward(forward) } else { None };
let code: i32 = match run_coordinated_forward(forward) {
CoordinatedRun::Ran(code) => code,
CoordinatedRun::Failed { stderr, code } => {
write_stream(&mut std::io::stderr(), stderr.as_str());
code
}
CoordinatedRun::NotCoordinated if watch.is_some() => {
finish_forwarded(run_forwarded_git(forward.real_git.as_path(), forward.arguments.as_slice(), forward.overlay.as_slice(), None))
}
CoordinatedRun::NotCoordinated => {
let error: std::io::Error = replace_process_with_real_git(
forward.real_git.as_path(),
forward.arguments.as_slice(),
forward.overlay.as_slice(),
);
write_stream(
&mut std::io::stderr(),
format!("monochromatic-git-policy-cli: cannot start {}: {error}\n", forward.real_git.display()).as_str(),
);
ENGINE_FAILURE_EXIT_CODE
}
};
if let Some(found) = watch.as_ref() {
if code == 0 {
hand_over_if_ahead(found, trigger_command(&forward.command).as_str(), &mut std::io::stderr());
} else {
record_decision(&Decision {
common_dir: Some(found.common_dir()),
origin: Some(found.origin()),
command: trigger_command(&forward.command).as_str(),
outcome: Outcome::Declined,
reason: "command-failed",
detail: format!("Git exited with status {code}; {} was at {} before the command", found.branch_ref(), found.tip_before()).as_str(),
branch: Some(found.branch_ref()),
});
}
}
return code;
}
#[cfg(not(unix))]
struct TipWatch;
#[cfg(not(unix))]
fn watch_forward(_forward: &CoordinatedForward) -> Option<TipWatch> {
return None;
}
#[cfg(not(unix))]
fn hand_over_if_ahead(_watch: &TipWatch, _command: &str, _stderr: &mut dyn Write) {}
fn adds_commits(command: &ResolvedCommand) -> bool {
return command.is_builtin() && matches!(command.word(), b"merge" | b"pull" | b"cherry-pick" | b"revert" | b"am");
}
fn trigger_command(command: &ResolvedCommand) -> String {
return String::from_utf8_lossy(command.word()).into_owned();
}
#[cfg(unix)]
fn watch_forward(forward: &CoordinatedForward) -> Option<TipWatch> {
let (root, common): (PathBuf, PathBuf) = match &forward.identity {
WorktreeIdentity::MainWorktree { worktree_root, common_dir, .. } | WorktreeIdentity::LinkedWorktree { worktree_root, common_dir, .. } => {
(worktree_root.clone(), common_dir.clone())
}
WorktreeIdentity::OutsideWorktree | WorktreeIdentity::BareRepository { .. } => return None,
};
return match try_watch_tip(root.as_path(), forward.real_git.as_path(), forward.overlay.as_slice(), forward.environment.as_slice()) {
Ok(watch) => Some(watch),
Err(declined) => {
let git_dir: PathBuf = match &forward.identity {
WorktreeIdentity::MainWorktree { git_dir, .. } | WorktreeIdentity::LinkedWorktree { git_dir, .. } => git_dir.clone(),
WorktreeIdentity::OutsideWorktree | WorktreeIdentity::BareRepository { .. } => common.clone(),
};
record_decision(&Decision {
common_dir: Some(common.as_path()),
origin: Some(Origin { worktree: root.as_path(), git_dir: git_dir.as_path() }),
command: trigger_command(&forward.command).as_str(),
outcome: Outcome::Declined,
reason: declined.reason,
detail: declined.detail.as_str(),
branch: None,
});
None
}
};
}
pub(crate) fn write_stream(stream: &mut dyn Write, text: &str) {
let _ = stream.write_all(text.as_bytes());
}