Add world CLI compatibility for Git, xargs, devices and tool aliases

This commit is contained in:
2026-11-08 00:58:30 +00:00
parent 3134be3ebf
commit ffee43b33c
12 changed files with 475 additions and 32 deletions
+107
View File
@@ -618,6 +618,113 @@ fn unified(a: &str, b: &str, context: usize) -> String {
out
}
/// Git's default whitespace checks apply only to added lines.
pub fn check(
repo: &gix::Repository,
diffs: &[FileDiff],
revision: Option<&str>,
cached: bool,
) -> Result<i32> {
let snapshot = if let Some(rev) = revision {
Some(tree(repo, rev)?)
} else if cached {
Some(index(repo)?.0)
} else {
None
};
let mut failed = false;
for diff in diffs.iter().filter(|diff| !diff.binary) {
let mut number = 0;
let mut added = BTreeSet::new();
for line in diff.patch.split_terminator('\n') {
if line.starts_with("@@ ") {
number = line
.split_whitespace()
.nth(2)
.unwrap()
.trim_start_matches('+')
.split(',')
.next()
.unwrap()
.parse::<usize>()
.unwrap();
} else if number > 0 && line.starts_with('+') {
let text = &line[1..];
if text.trim_ascii().is_empty() {
added.insert(number);
}
let indent = text
.chars()
.take_while(|ch| *ch == ' ' || *ch == '\t')
.collect::<String>();
let mut errors = Vec::new();
if text.ends_with(|ch: char| ch.is_ascii_whitespace()) {
errors.push("trailing whitespace");
}
if !text.trim_ascii().is_empty() && indent.contains(" \t") {
errors.push("space before tab in indent");
}
if text.starts_with("<<<<<<< ") || text == "=======" || text.starts_with(">>>>>>> ")
{
println!("{}:{number}: leftover conflict marker", diff.path);
failed = true;
}
if !errors.is_empty() {
println!("{}:{number}: {}.", diff.path, errors.join(", "));
println!("{line}");
failed = true;
}
number += 1;
} else if number > 0 && line.starts_with(' ') {
number += 1;
}
}
if added.is_empty() {
continue;
}
let bytes = if let Some(ref snapshot) = snapshot {
blob(repo, snapshot.get(diff.path.as_bytes()))?
} else if diff.status == Kind::Deleted {
Vec::new()
} else {
let file = repo.workdir().unwrap().join(&diff.path);
if std::fs::symlink_metadata(&file)
.map_err(|e| error("diff check", e))?
.file_type()
.is_symlink()
{
gix::path::into_bstr(std::fs::read_link(&file).map_err(|e| error("diff check", e))?)
.map_err(|e| error("diff check", e))?
.into_owned()
.to_vec()
} else {
std::fs::read(file).map_err(|e| error("diff check", e))?
}
};
let text = String::from_utf8_lossy(&bytes);
let mut lines: Vec<_> = text.lines().collect();
if !text.ends_with('\n')
&& lines
.last()
.is_some_and(|line| line.trim_ascii().is_empty())
{
lines.pop();
}
let first = lines
.iter()
.enumerate()
.rev()
.take_while(|(_, line)| line.trim_ascii().is_empty())
.filter_map(|(i, _)| added.contains(&(i + 1)).then_some(i + 1))
.min();
if let Some(line) = first {
println!("{}:{line}: new blank line at EOF.", diff.path);
failed = true;
}
}
Ok(if failed { 2 } else { 0 })
}
#[cfg(test)]
mod tests {
use super::*;