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::*;
|
||||
|
||||
@@ -207,7 +207,7 @@ enum Command {
|
||||
diff_merges: Option<String>,
|
||||
#[arg(long)]
|
||||
oneline: bool,
|
||||
#[arg(short = 'n')]
|
||||
#[arg(short = 'n', long = "max-count")]
|
||||
limit: Option<usize>,
|
||||
#[arg(long)]
|
||||
format: Option<String>,
|
||||
@@ -222,6 +222,8 @@ enum Command {
|
||||
paths: Vec<String>,
|
||||
},
|
||||
Diff {
|
||||
#[arg(long)]
|
||||
check: bool,
|
||||
#[arg(long)]
|
||||
numstat: bool,
|
||||
#[arg(long)]
|
||||
@@ -892,6 +894,7 @@ pub fn run(cli: Cli) -> Result<i32> {
|
||||
}
|
||||
}
|
||||
Command::Diff {
|
||||
check,
|
||||
numstat,
|
||||
no_renames,
|
||||
diff_filter,
|
||||
@@ -919,13 +922,16 @@ pub fn run(cli: Cli) -> Result<i32> {
|
||||
changes::DiffParams {
|
||||
repo: path,
|
||||
from,
|
||||
to,
|
||||
to: to.clone(),
|
||||
staged: Some(cached),
|
||||
path: paths.first().cloned(),
|
||||
context: None,
|
||||
},
|
||||
!no_renames,
|
||||
)?;
|
||||
if check {
|
||||
return changes::check(&repository, &diffs, to.as_deref(), cached);
|
||||
}
|
||||
if name_only || name_status {
|
||||
for diff in diffs {
|
||||
if name_status {
|
||||
|
||||
@@ -1,11 +1,38 @@
|
||||
//! Parse Git CLI arguments and return Git-compatible usage and operation exit codes.
|
||||
use clap::{Parser, error::ErrorKind};
|
||||
fn main() {
|
||||
if matches!(std::env::args().nth(1).as_deref(), Some("--version" | "version")) {
|
||||
if matches!(
|
||||
std::env::args().nth(1).as_deref(),
|
||||
Some("--version" | "version")
|
||||
) {
|
||||
println!("git version {}.sausage", env!("CARGO_PKG_VERSION"));
|
||||
exit(0);
|
||||
}
|
||||
let cli = match git::Cli::try_parse() {
|
||||
let mut args: Vec<_> = std::env::args_os().collect();
|
||||
let mut command = 1;
|
||||
while command < args.len() && args[command].to_string_lossy().starts_with('-') {
|
||||
command += if args[command] == "-C" || args[command] == "-c" {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
}
|
||||
if args.get(command).is_some_and(|arg| arg == "log") {
|
||||
let mut i = command + 1;
|
||||
while i < args.len() && args[i] != "--" {
|
||||
let arg = args[i].to_string_lossy();
|
||||
if let Some(count) = arg
|
||||
.strip_prefix('-')
|
||||
.filter(|s| !s.is_empty() && s.bytes().all(|b| b.is_ascii_digit()))
|
||||
{
|
||||
let count = count.to_owned();
|
||||
args.splice(i..=i, ["-n".into(), count.into()]);
|
||||
i += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
let cli = match git::Cli::try_parse_from(args) {
|
||||
Ok(cli) => cli,
|
||||
Err(error) => {
|
||||
let code = if !error.use_stderr() {
|
||||
|
||||
Reference in New Issue
Block a user