Add world CLI compatibility for Git, xargs, devices and tool aliases
This commit is contained in:
@@ -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::*;
|
||||
|
||||
Reference in New Issue
Block a user