use crate::service_client::{Activation, ClientConfig, START_WAIT, manager_watch_for};
use crate::service_protocol::HEARTBEAT_SILENCE;
use crate::service_messages::{ClientFailure, render};
use crate::service_provision::platform_without_manager;
use std::path::PathBuf;
use crate::service_server::Bind;
use crate::service_provision::ProvisionHost;
use crate::service_route::{Route, RouteInputs, choose_route, non_empty_path_variable, remembered_own_executable, worktree_git_dir};
use crate::commit_timing::CommitTiming;
use crate::commit_transaction::{CommitInvocation, TransactionStop, failed};
use crate::diagnostic_log::warn;
use crate::service_amend::{AmendSource, amended_author, read_amend_source};
use crate::service_commit_args::ServiceCommitArgs;
use crate::service_message::{author_overrides, build_message, identity};
use crate::transaction_git::{GitRequest, run_git};
use std::ffi::OsString;
use std::io::{Read, Write};
pub(crate) fn arguments_after_subcommand(arguments: &[OsString], subcommand_index: usize) -> &[OsString] {
return arguments.get(subcommand_index + 1..).unwrap_or(&[]);
}
pub(crate) fn switched_branch_name(reference: &str) -> String {
return if reference == "HEAD" { String::from("a detached head") } else { reference.trim_start_matches("refs/heads/").to_string() };
}
pub(crate) fn standard_input_for(invocation: &CommitInvocation<'_>) -> Result<Option<Vec<u8>>, TransactionStop> {
let after: &[OsString] = arguments_after_subcommand(invocation.arguments, invocation.subcommand_index);
let Some(value) = invocation.region.pathspec_from_file else { return Ok(None) };
if crate::command_options_query::value_bytes(after, value) != b"-" {
return Ok(None);
}
let mut bytes: Vec<u8> = Vec::new();
if let Err(error) = std::io::stdin().read_to_end(&mut bytes) {
return Err(failed(format!("reading the pathspec file failed: {error}")));
}
return Ok(Some(bytes));
}
pub(crate) fn repository_not_found(invocation: &CommitInvocation<'_>) -> TransactionStop {
let asked = run_git(invocation.context, &GitRequest::new(invocation.cwd, &["rev-parse", "--git-dir"]));
let output = match asked {
Ok(found) => found,
Err(error) => return failed(format!("asking Git which repository this directory belongs to failed: {error}")),
};
if output.succeeded() {
return failed(String::from("this directory belongs to a repository whose Git directory could not be located"));
}
let _reported = std::io::stderr().write_all(output.stderr.as_slice());
return TransactionStop::NativeExit(output.code.unwrap_or(1));
}
pub(crate) fn message_of_source(found: &AmendSource) -> &str {
return found.message.as_str();
}
pub(crate) fn print_summary(line: &str) {
let mut out = std::io::stdout();
let wrote: std::io::Result<()> = writeln!(out, "{line}");
let written: std::io::Result<()> = match wrote {
Ok(()) => out.flush(),
Err(error) => Err(error),
};
let Err(error) = written else { return };
if error.kind() == std::io::ErrorKind::BrokenPipe {
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
libc::raise(libc::SIGPIPE);
}
}
let _reported = writeln!(std::io::stderr(), "fatal: write error: {error}");
std::process::exit(128);
}
pub(crate) fn print_note(line: &str) {
if let Err(error) = writeln!(std::io::stderr(), "{line}") {
warn("serviceCommit", format!("writing the note about the rebuilt commit failed: {error}").as_str());
}
}
pub(crate) struct Embedded {
pub(crate) message: String,
pub(crate) author: String,
pub(crate) committer: String,
pub(crate) source: Option<AmendSource>,
pub(crate) quiet: bool,
}
pub(crate) fn embedded_commit(invocation: &CommitInvocation<'_>, parsed: ServiceCommitArgs) -> Result<Option<Embedded>, TransactionStop> {
let Some(committer) = identity(invocation.context, invocation.cwd, "GIT_COMMITTER_IDENT", &[])? else { return Ok(None) };
let source: Option<AmendSource> = if parsed.amend {
let Some(read) = read_amend_source(invocation.context, invocation.cwd)? else { return Ok(None) };
Some(read)
} else {
None
};
let previous: Option<&str> = source.as_ref().map(message_of_source);
let Some(built) = build_message(invocation, &parsed, committer.as_str(), previous)? else { return Ok(None) };
let author: String = match built.reused_author {
Some(reused) => reused,
None => match source.as_ref() {
Some(replaced) => {
let Some(made) = amended_author(invocation.context, invocation.cwd, replaced, &parsed)? else { return Ok(None) };
made
}
None => {
let Some(made) = identity(invocation.context, invocation.cwd, "GIT_AUTHOR_IDENT", author_overrides(&parsed).as_slice())? else {
return Ok(None);
};
made
}
},
};
return Ok(Some(Embedded { message: built.message, author, committer, source, quiet: parsed.quiet }));
}
pub(crate) fn route_for_this_worktree(invocation: &CommitInvocation<'_>, timing: &mut CommitTiming) -> Result<(Bind, Option<ProvisionHost>), TransactionStop> {
let runtime: Option<PathBuf> = non_empty_path_variable("XDG_RUNTIME_DIR");
let config_home: Option<PathBuf> = non_empty_path_variable("XDG_CONFIG_HOME");
let home: Option<PathBuf> = non_empty_path_variable("HOME");
let bus_address: Option<String> = std::env::var("DBUS_SESSION_BUS_ADDRESS").ok();
let Some(git_dir) = worktree_git_dir(invocation.context, invocation.cwd) else { return Err(repository_not_found(invocation)) };
let route: Option<Route> = choose_route(&RouteInputs {
runtime_dir: runtime.as_deref(),
bus_address: bus_address.as_deref(),
config_home: config_home.as_deref(),
home: home.as_deref(),
own_executable: remembered_own_executable(),
real_git: invocation.context.real_git.as_path(),
git_dir: git_dir.as_path(),
});
let Some(Route { address, provision: provisioning }) = route else {
return Err(TransactionStop::Message(render(&ClientFailure::NoActivation(platform_without_manager()))));
};
timing.place_at(git_dir.as_path());
return Ok((address, provisioning));
}
pub(crate) fn managed_client_config(address: Bind, provisioning: Option<&ProvisionHost>) -> ClientConfig {
return ClientConfig {
address,
activation: Activation::Managed,
start_wait: START_WAIT,
heartbeat_silence: HEARTBEAT_SILENCE,
start_watch: provisioning.map(manager_watch_for),
keep_fresh: None, answer_ceiling: crate::wait_ceilings::ANSWER_CEILING, withdraw_ceiling: crate::wait_ceilings::WITHDRAW_CEILING, extra_wait: crate::wait_ceilings::SHORT_EXTRA_WAIT, start_ceiling: crate::wait_ceilings::START_CEILING,
};
}