use crate::candidate_object::{ObjectId, parse_object_id};
use crate::candidate_prediction::CandidateRequest;
use crate::capture_invocation::InvocationCapture;
use crate::command_commit::CommitRegion;
use crate::commit_facts::PrivateIndexFacts;
use crate::config_schema::CliGitConfig;
use crate::diagnostics::EngineFailureCode;
use crate::path_bytes::os_from_path_bytes;
use crate::markdown_linter::embedded_linter;
use crate::policy_checks::{ShippedChecks, shipped_checks};
use crate::policy_forbidden_strings::default_scanner_settings;
use crate::policy_markdown::markdown_state;
use crate::policy_content::LifecycleContent;
use crate::policy_convergence::Convergence;
use crate::policy_events::PolicyEvent;
use crate::repository_facts::RepositoryFacts;
use crate::transaction_git::{GitContext, GitFailure};
use crate::transaction_journal::{Base, Conclusion, TransactionMode};
use crate::transaction_phase::PhaseMarkerError;
use crate::wrapper_controls::Controls;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
pub struct CommitInvocation<'a> {
pub context: &'a GitContext,
pub environment: &'a [(OsString, OsString)],
pub cwd: &'a Path,
pub arguments: &'a [OsString],
pub subcommand_index: usize,
pub region: &'a CommitRegion,
pub config: &'a CliGitConfig,
pub controls: &'a Controls,
}
#[derive(Debug)]
pub enum TransactionStop {
NativeExit(i32),
Message(String),
Failed {
code: EngineFailureCode,
message: String,
},
}
impl From<GitFailure> for TransactionStop {
fn from(failure: GitFailure) -> Self {
return TransactionStop::Message(failure.0);
}
}
impl From<PhaseMarkerError> for TransactionStop {
fn from(failure: PhaseMarkerError) -> Self {
return failed(failure.to_string());
}
}
pub fn failed(message: String) -> TransactionStop {
return TransactionStop::Failed {
code: EngineFailureCode::TransactionFailed,
message,
};
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TransactionResult {
pub events: Vec<PolicyEvent>,
pub exit_code: i32,
pub landed_oid: Option<String>,
pub uses_lfs: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Selection {
pub concludes_sequencer: bool,
pub read_only: bool,
pub selects_paths: bool,
pub staged_all: bool,
pub mode: TransactionMode,
}
pub fn decide_selection(region: &CommitRegion, conclusion: Conclusion) -> Selection {
let read_only: bool = region.include || region.interactive || region.patch;
let selects_paths: bool = !region.pathspecs.is_empty() || region.pathspec_from_file.is_some();
let paths_only: bool = selects_paths && !read_only && !region.all && region.only != Some(false);
let refused_by_git: bool = matches!(conclusion, Conclusion::Merge | Conclusion::CherryPick);
let concludes_sequencer: bool = conclusion != Conclusion::None && conclusion != Conclusion::Amend && !(paths_only && refused_by_git);
let staged_all: bool = region.all && !selects_paths && !read_only && region.only != Some(true);
let index_mode: bool = region.only == Some(false)
|| concludes_sequencer
|| read_only
|| region.all
|| !selects_paths;
return Selection {
concludes_sequencer,
read_only,
selects_paths,
staged_all,
mode: if index_mode { TransactionMode::Index } else { TransactionMode::ExplicitPath },
};
}
pub fn private_checks<'a, F: RepositoryFacts>(
checks: &'a mut ShippedChecks<F>,
index: &Path,
object_directory: &Path,
candidates: CandidateRequest,
) -> ShippedChecks<PrivateIndexFacts<&'a mut F>> {
let scanner_settings = std::mem::replace(&mut checks.scanner_settings, default_scanner_settings());
let markdown = std::mem::replace(&mut checks.markdown, markdown_state(Box::new(embedded_linter())));
let arguments: Vec<OsString> = checks.arguments.clone();
let allowed_worktree_dirs: Vec<PathBuf> = checks.allowed_worktree_dirs.clone();
let mut rebuilt: ShippedChecks<PrivateIndexFacts<&'a mut F>> = shipped_checks(
PrivateIndexFacts::new(&mut checks.facts, index, object_directory),
arguments,
LifecycleContent::Requested(candidates),
allowed_worktree_dirs,
);
rebuilt.scanner_settings = scanner_settings;
rebuilt.markdown = markdown;
return rebuilt;
}
pub fn settled_passes(convergence: &Convergence) -> u64 {
return match convergence {
Convergence::Settled { changed_passes } => *changed_passes as u64,
_ => 0,
};
}
pub fn staged_at_base(base: Option<ObjectId>, paths: &[Vec<u8>]) -> CandidateRequest {
return CandidateRequest::StagedAtBase {
base,
pathspecs: paths
.iter()
.map(|path| {
let mut pathspec: Vec<u8> = b":(top,literal)".to_vec();
pathspec.extend_from_slice(path.as_slice());
return os_from_path_bytes(pathspec.as_slice());
})
.collect(),
};
}
pub fn base_object(capture: &InvocationCapture) -> Result<Option<ObjectId>, TransactionStop> {
return match &capture.base {
Base::Unborn => Ok(None),
Base::Commit(oid) => match parse_object_id(oid.as_bytes()) {
Some(found) => Ok(Some(found)),
None => Err(failed(format!("the preparation base {oid} is not an object name"))),
},
};
}
#[cfg(test)]
#[path = "commit_transaction_tests.rs"]
mod tests;