pull/874/merge
WGH 2025-12-05 21:54:52 +07:00 committed by GitHub
commit 0a709b06c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 70 additions and 2 deletions

@ -0,0 +1,63 @@
use std::path::Path;
use std::process::Command;
/// Corresponds to the diff attribute. See man gitattribute.
pub(crate) enum DiffAttribute {
Set,
Unset,
Unspecified,
Other,
}
impl From<&str> for DiffAttribute {
fn from(s: &str) -> Self {
match s {
"set" => Self::Set,
"unset" => Self::Unset,
"unspecified" => Self::Unspecified,
_ => Self::Other,
}
}
}
/// Runs `git check-attr diff` to get the diff attribute of the path. Returns
/// [`Option::None`] when either `git` is not available, file is not inside git
/// directory, or something else went wrong.
pub(crate) fn check_attr(path: &Path) -> Option<DiffAttribute> {
let res = Command::new("git")
.args(["check-attr", "diff", "-z", "--"])
.arg(path)
.output();
match res {
Ok(output) => {
// Either git is not available, or file is outside git directory.
if !output.status.success() {
debug!("git check-attr exited with status {}", output.status);
return None;
}
// The output format is "path" "attribute name" "value". We
// specified both path and attribute name explicitly,
// so we only need value here.
let stdout = &output.stdout;
let value = stdout.split(|&b| b == b'\0').nth(2);
match value {
None => {
warn!("malformed git check-attr output {stdout:#?}");
}
Some(value) => match std::str::from_utf8(value) {
Ok(s) => return Some(s.into()),
Err(err) => {
warn!("invalid diff attribute value: {err}");
}
},
}
}
Err(err) => {
warn!("failed to execute git: {err}");
}
}
None
}

@ -47,6 +47,7 @@ mod diff;
mod display; mod display;
mod exit_codes; mod exit_codes;
mod files; mod files;
mod git;
mod hash; mod hash;
mod line_parser; mod line_parser;
mod lines; mod lines;
@ -78,6 +79,7 @@ use crate::files::{
guess_content, read_file_or_die, read_files_or_die, read_or_die, relative_paths_in_either, guess_content, read_file_or_die, read_files_or_die, read_or_die, relative_paths_in_either,
ProbableFileKind, ProbableFileKind,
}; };
use crate::git::{check_attr, DiffAttribute};
use crate::parse::guess_language::language_globs; use crate::parse::guess_language::language_globs;
use crate::parse::guess_language::{guess, language_name, Language, LanguageOverride}; use crate::parse::guess_language::{guess, language_name, Language, LanguageOverride};
use crate::parse::syntax; use crate::parse::syntax;
@ -413,8 +415,11 @@ fn diff_file(
let (mut lhs_src, mut rhs_src) = match ( let (mut lhs_src, mut rhs_src) = match (
guess_content(&lhs_bytes, lhs_path, binary_overrides), guess_content(&lhs_bytes, lhs_path, binary_overrides),
guess_content(&rhs_bytes, rhs_path, binary_overrides), guess_content(&rhs_bytes, rhs_path, binary_overrides),
check_attr(Path::new(display_path)),
) { ) {
(ProbableFileKind::Binary, _) | (_, ProbableFileKind::Binary) => { (ProbableFileKind::Binary, _, _)
| (_, ProbableFileKind::Binary, _)
| (_, _, Some(DiffAttribute::Unset)) => {
let has_byte_changes = if lhs_bytes == rhs_bytes { let has_byte_changes = if lhs_bytes == rhs_bytes {
None None
} else { } else {
@ -433,7 +438,7 @@ fn diff_file(
has_syntactic_changes: false, has_syntactic_changes: false,
}; };
} }
(ProbableFileKind::Text(lhs_src), ProbableFileKind::Text(rhs_src)) => (lhs_src, rhs_src), (ProbableFileKind::Text(lhs_src), ProbableFileKind::Text(rhs_src), _) => (lhs_src, rhs_src),
}; };
if diff_options.strip_cr { if diff_options.strip_cr {