use crate::hooks_off::HOOK_EVENTS;
use crate::config_snapshot::ConfigSnapshot;
use crate::transaction_git::{GitContext, GitFailure, GitRequest, run_git_checked};
use std::path::Path;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RepositoryFormat {
pub version: String,
pub extensions: Vec<(String, String)>,
}
pub fn quote_git_config_value(value: &str) -> String {
let mut body: String = String::with_capacity(value.len());
for character in value.chars() {
match character {
'\\' => body.push_str(r"\\"),
'"' => body.push_str(r#"\""#),
'\n' => body.push_str(r"\n"),
'\t' => body.push_str(r"\t"),
other => body.push(other),
}
}
return format!("\"{body}\"");
}
pub fn read_repository_format(
context: &GitContext,
common_dir: &Path,
) -> Result<RepositoryFormat, GitFailure> {
let mut request: GitRequest = GitRequest::new(
common_dir,
&["config", "--file", "config", "--null", "--list"],
);
request.without_prefix = true;
let output = run_git_checked(context, &request)?;
let mut version: Option<String> = None;
let mut extensions: Vec<(String, String)> = Vec::new();
for entry in output.stdout.split(|byte| { return *byte == 0; }).filter(|entry| { return !entry.is_empty(); }) {
let (key, value): (&[u8], &[u8]) = match entry.iter().position(|byte| { return *byte == b'\n'; }) {
Some(newline) => (&entry[..newline], &entry[newline + 1..]),
None => (entry, &b"true"[..]),
};
note_format_entry(
String::from_utf8_lossy(key).as_ref(),
String::from_utf8_lossy(value).into_owned(),
&mut version,
&mut extensions,
);
}
return Ok(RepositoryFormat {
version: version.unwrap_or_else(|| { return String::from("0"); }),
extensions,
});
}
fn note_format_entry(
key_text: &str,
value_text: String,
version: &mut Option<String>,
extensions: &mut Vec<(String, String)>,
) {
if key_text.to_lowercase() == "core.repositoryformatversion" {
*version = Some(value_text.clone());
}
if key_text.is_char_boundary(11) && key_text[..11].to_lowercase() == "extensions." {
extensions.push((key_text[11..].to_string(), value_text));
}
}
pub fn repository_format_from_snapshot(snapshot: &ConfigSnapshot) -> Option<RepositoryFormat> {
if !snapshot.without_global_options() {
return None;
}
let includes: bool = snapshot.entries().iter().any(|entry| {
return entry.key == "include.path" || (entry.key.starts_with("includeif.") && entry.key.ends_with(".path"));
});
if includes {
return None;
}
let mut version: Option<String> = None;
let mut extensions: Vec<(String, String)> = Vec::new();
for entry in snapshot.entries().iter().filter(|candidate| { return candidate.scope == "local"; }) {
let value_text: String = match &entry.value {
Some(bytes) => String::from_utf8_lossy(bytes).into_owned(),
None => String::from("true"),
};
note_format_entry(entry.key.as_str(), value_text, &mut version, &mut extensions);
}
return Some(RepositoryFormat {
version: version.unwrap_or_else(|| { return String::from("0"); }),
extensions,
});
}
pub fn format_shadow_config(format: &RepositoryFormat, common_dir: &Path, worktree_root: &Path) -> String {
let common: String = common_dir.display().to_string();
let mut lines: Vec<String> = vec![
String::from("[core]"),
format!("\trepositoryformatversion = {}", quote_git_config_value(format.version.as_str())),
];
if !format.extensions.is_empty() {
lines.push(String::from("[extensions]"));
for (name, value) in &format.extensions {
lines.push(format!("\t{name} = {}", quote_git_config_value(value.as_str())));
}
}
lines.push(String::from("[include]"));
lines.push(format!(
"\tpath = {}",
quote_git_config_value(format!("{common}/config").as_str())
));
lines.push(String::from("[core]"));
lines.push(format!(
"\tworktree = {}",
quote_git_config_value(worktree_root.display().to_string().as_str())
));
lines.push(String::from("[gc]"));
lines.push(String::from("\tauto = 0"));
lines.push(String::from("[maintenance]"));
lines.push(String::from("\tauto = false"));
for event in HOOK_EVENTS {
lines.push(format!("[hook \"{event}\"]"));
lines.push(String::from("\tenabled = false"));
}
lines.push(String::new());
return lines.join("\n");
}
#[cfg(test)]
#[path = "shadow_config_tests.rs"]
mod tests;