Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/git.rs
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 7 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mod diff;
mod display;
mod exit_codes;
mod files;
mod git;
mod hash;
mod line_parser;
mod lines;
Expand Down Expand Up @@ -68,6 +69,7 @@ use crate::files::{
guess_content, read_file_or_die, read_files_or_die, read_or_die, relative_paths_in_either,
ProbableFileKind,
};
use crate::git::{check_attr, DiffAttribute};
use crate::parse::guess_language::language_globs;
use crate::parse::guess_language::{guess, language_name, Language, LanguageOverride};
use crate::parse::syntax;
Expand Down Expand Up @@ -405,8 +407,11 @@ fn diff_file(
let (mut lhs_src, mut rhs_src) = match (
guess_content(&lhs_bytes, lhs_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 {
None
} else {
Expand All @@ -425,7 +430,7 @@ fn diff_file(
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 {
Expand Down
Loading