Adopt mature Git config and DNS codecs

This commit is contained in:
ms2k 2026-07-19 00:40:26 +09:00
commit bda342890f
2 changed files with 634 additions and 122 deletions

View file

@ -8,6 +8,7 @@ license.workspace = true
[dependencies]
bstr.workspace = true
fuser.workspace = true
gix-config.workspace = true
gix-ignore.workspace = true
libc.workspace = true
nix.workspace = true

View file

@ -330,8 +330,10 @@ impl RepositoryPolicy {
gix_ignore::search::Ignore::default(),
);
}
let ignore_case = read_external_optional(&metadata.config)?
.is_some_and(|bytes| config_boolean(&bytes, b"core", b"ignorecase"));
let ignore_case = match read_external_optional(&metadata.config)? {
Some(bytes) => config_boolean(&bytes, "core", "ignoreCase")?,
None => false,
};
let case = if ignore_case {
gix_ignore::glob::pattern::Case::Fold
} else {
@ -414,7 +416,7 @@ fn repository_metadata(
let value = bytes
.strip_prefix(b"gitdir:")
.ok_or_else(|| io::Error::other("worktree Git pointer has no gitdir prefix"))?;
let value = trim_ascii(value);
let value = value.trim_ascii();
let path = PathBuf::from(OsString::from_vec(value.to_vec()));
if path.is_absolute() {
path
@ -429,7 +431,7 @@ fn repository_metadata(
let git_root = BackingRoot::open(&git_dir)?;
let common_dir = match git_root.read_file(Path::new("commondir"), 64 * 1024) {
Ok(bytes) => {
let path = PathBuf::from(OsString::from_vec(trim_ascii(&bytes).to_vec()));
let path = PathBuf::from(OsString::from_vec(bytes.trim_ascii().to_vec()));
if path.is_absolute() {
path
} else {
@ -691,38 +693,18 @@ fn index_overflow() -> io::Error {
io::Error::other("Git index offset is out of range")
}
fn config_boolean(bytes: &[u8], section: &[u8], key: &[u8]) -> bool {
let mut current = Vec::new();
for line in bytes.split(|byte| *byte == b'\n') {
let line = trim_ascii(line);
if line.starts_with(b"[") && line.ends_with(b"]") {
current = line[1..line.len() - 1]
.split(u8::is_ascii_whitespace)
.next()
.unwrap_or_default()
.to_ascii_lowercase();
} else if current == section {
let mut fields = line.splitn(2, |byte| *byte == b'=');
let name = trim_ascii(fields.next().unwrap_or_default()).to_ascii_lowercase();
let value = trim_ascii(fields.next().unwrap_or(b"true"));
if name == key {
return value.eq_ignore_ascii_case(b"true")
|| value.eq_ignore_ascii_case(b"yes")
|| value == b"1";
}
}
}
false
}
fn trim_ascii(mut bytes: &[u8]) -> &[u8] {
while bytes.first().is_some_and(u8::is_ascii_whitespace) {
bytes = &bytes[1..];
}
while bytes.last().is_some_and(u8::is_ascii_whitespace) {
bytes = &bytes[..bytes.len() - 1];
}
bytes
fn config_boolean(bytes: &[u8], section: &str, key: &str) -> io::Result<bool> {
let configuration = gix_config::File::from_bytes_no_includes(
bytes,
gix_config::file::Metadata::api(),
gix_config::file::init::Options::default(),
)
.map_err(|error| io::Error::other(format!("Git configuration is malformed: {error}")))?;
configuration
.boolean_by(section, None, key)
.transpose()
.map(|value| value.unwrap_or(false))
.map_err(|error| io::Error::other(format!("Git configuration boolean is invalid: {error}")))
}
fn input_stamp(path: &Path) -> io::Result<Option<InputStamp>> {
@ -782,7 +764,7 @@ mod tests {
use std::fs;
use std::sync::atomic::{AtomicU64, Ordering};
use super::{Classification, GitClassifier, parse_index_paths};
use super::{Classification, GitClassifier, config_boolean, parse_index_paths};
static SEQUENCE: AtomicU64 = AtomicU64::new(0);
@ -802,6 +784,34 @@ mod tests {
assert!(parse_index_paths(&truncated).is_err());
}
#[test]
fn git_configuration_boolean_uses_git_casing_and_last_value_semantics() {
assert!(config_boolean(b"[CoRe]\nIgNoReCaSe = on\n", "core", "ignoreCase").unwrap());
assert!(
!config_boolean(
b"[core]\nignoreCase = true\nignoreCase = false\n",
"core",
"ignoreCase"
)
.unwrap()
);
assert!(config_boolean(b"[core]\nignoreCase = perhaps\n", "core", "ignoreCase").is_err());
assert!(!config_boolean(b"[core]\n", "core", "ignoreCase").unwrap());
}
#[test]
fn repository_with_invalid_ignore_case_is_refused() {
let fixture = Fixture::new("invalid-ignore-case");
repository(&fixture.workspace, &[]);
fs::write(
fixture.workspace.join(".git/config"),
b"[core]\nignoreCase = perhaps\n",
)
.unwrap();
assert!(GitClassifier::open(&fixture.workspace, None).is_err());
}
#[test]
fn repository_without_an_index_has_no_tracked_paths() {
let fixture = Fixture::new("absent-index");