Adopt mature Git config and DNS codecs
This commit is contained in:
parent
d08d931b5f
commit
bda342890f
2 changed files with 634 additions and 122 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Reference in a new issue