Add a --check-only flag

Fixes #386
pull/454/head 0.39.0
Wilfred Hughes 2022-12-18 23:55:22 +07:00
parent abd5e07654
commit e0fcf2b84b
3 changed files with 65 additions and 1 deletions

@ -17,6 +17,10 @@ interleaved output from different files.
### Command Line Interface ### Command Line Interface
Added a `--check-only` flag that reports if there are any syntactic
differences, but doesn't calculate or print them. This is much faster
than normal syntactic diffing.
Difftastic now sets the exit code if it finds changes and Difftastic now sets the exit code if it finds changes and
`--exit-code` is set. See [usage in the `--exit-code` is set. See [usage in the
manual](https://difftastic.wilfred.me.uk/usage.html) for the full list manual](https://difftastic.wilfred.me.uk/usage.html) for the full list

@ -162,6 +162,7 @@ fn main() {
display_options, display_options,
missing_as_empty, missing_as_empty,
set_exit_code, set_exit_code,
check_only,
language_override, language_override,
lhs_path, lhs_path,
rhs_path, rhs_path,
@ -211,6 +212,7 @@ fn main() {
&display_options, &display_options,
graph_limit, graph_limit,
byte_limit, byte_limit,
check_only,
language_override, language_override,
) )
.try_for_each_with(send, |s, diff_result| s.send(diff_result)) .try_for_each_with(send, |s, diff_result| s.send(diff_result))
@ -230,6 +232,7 @@ fn main() {
missing_as_empty, missing_as_empty,
graph_limit, graph_limit,
byte_limit, byte_limit,
check_only,
language_override, language_override,
); );
print_diff_result(&display_options, &diff_result); print_diff_result(&display_options, &diff_result);
@ -270,6 +273,7 @@ fn diff_file(
missing_as_empty: bool, missing_as_empty: bool,
graph_limit: usize, graph_limit: usize,
byte_limit: usize, byte_limit: usize,
check_only: bool,
language_override: Option<parse::guess_language::Language>, language_override: Option<parse::guess_language::Language>,
) -> DiffResult { ) -> DiffResult {
let (lhs_bytes, rhs_bytes) = read_files_or_die(lhs_path, rhs_path, missing_as_empty); let (lhs_bytes, rhs_bytes) = read_files_or_die(lhs_path, rhs_path, missing_as_empty);
@ -283,6 +287,7 @@ fn diff_file(
display_options, display_options,
graph_limit, graph_limit,
byte_limit, byte_limit,
check_only,
language_override, language_override,
) )
} }
@ -297,6 +302,7 @@ fn diff_file_content(
display_options: &DisplayOptions, display_options: &DisplayOptions,
graph_limit: usize, graph_limit: usize,
byte_limit: usize, byte_limit: usize,
check_only: bool,
language_override: Option<parse::guess_language::Language>, language_override: Option<parse::guess_language::Language>,
) -> DiffResult { ) -> DiffResult {
let (mut lhs_src, mut rhs_src) = match (guess_content(lhs_bytes), guess_content(rhs_bytes)) { let (mut lhs_src, mut rhs_src) = match (guess_content(lhs_bytes), guess_content(rhs_bytes)) {
@ -376,6 +382,25 @@ fn diff_file_content(
init_all_info(&lhs, &rhs); init_all_info(&lhs, &rhs);
if check_only {
let lang_name = language.map(|l| language_name(l).into());
let has_syntactic_changes = lhs != rhs;
return DiffResult {
lhs_display_path: lhs_display_path.into(),
rhs_display_path: rhs_display_path.into(),
language: lang_name,
detected_language: language,
lhs_src: FileContent::Text(lhs_src),
rhs_src: FileContent::Text(rhs_src),
lhs_positions: vec![],
rhs_positions: vec![],
hunks: vec![],
has_byte_changes: true,
has_syntactic_changes,
};
}
let mut change_map = ChangeMap::default(); let mut change_map = ChangeMap::default();
let possibly_changed = if env::var("DFT_DBG_KEEP_UNCHANGED").is_ok() { let possibly_changed = if env::var("DFT_DBG_KEEP_UNCHANGED").is_ok() {
vec![(lhs.clone(), rhs.clone())] vec![(lhs.clone(), rhs.clone())]
@ -475,6 +500,7 @@ fn diff_directories<'a>(
display_options: &DisplayOptions, display_options: &DisplayOptions,
graph_limit: usize, graph_limit: usize,
byte_limit: usize, byte_limit: usize,
check_only: bool,
language_override: Option<parse::guess_language::Language>, language_override: Option<parse::guess_language::Language>,
) -> impl ParallelIterator<Item = DiffResult> + 'a { ) -> impl ParallelIterator<Item = DiffResult> + 'a {
let display_options = display_options.clone(); let display_options = display_options.clone();
@ -499,6 +525,7 @@ fn diff_directories<'a>(
true, true,
graph_limit, graph_limit,
byte_limit, byte_limit,
check_only,
language_override, language_override,
) )
}) })
@ -510,7 +537,7 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
let hunks = &summary.hunks; let hunks = &summary.hunks;
let lang_name = summary.language.clone().unwrap_or_else(|| "Text".into()); let lang_name = summary.language.clone().unwrap_or_else(|| "Text".into());
if hunks.is_empty() { if !summary.has_syntactic_changes {
if display_options.print_unchanged { if display_options.print_unchanged {
println!( println!(
"{}", "{}",
@ -534,6 +561,29 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
return; return;
} }
if summary.has_syntactic_changes && hunks.is_empty() {
println!(
"{}",
display::style::header(
&summary.lhs_display_path,
&summary.rhs_display_path,
1,
1,
&lang_name,
display_options
)
);
if lang_name == "Text" {
// TODO: there are other Text names now, so
// they will hit the second case incorrectly.
println!("Has changes.\n");
} else {
println!("Has syntactic changes.\n");
}
return;
}
match display_options.display_mode { match display_options.display_mode {
DisplayMode::Inline => { DisplayMode::Inline => {
display::inline::print( display::inline::print(
@ -624,6 +674,7 @@ mod tests {
&DisplayOptions::default(), &DisplayOptions::default(),
DEFAULT_GRAPH_LIMIT, DEFAULT_GRAPH_LIMIT,
DEFAULT_BYTE_LIMIT, DEFAULT_BYTE_LIMIT,
false,
None, None,
); );

@ -157,6 +157,11 @@ fn app() -> clap::Command<'static> {
.env("DFT_EXIT_CODE") .env("DFT_EXIT_CODE")
.help("Set the exit code to 1 if there are syntactic changes in any text files, or byte changes in any binary files.") .help("Set the exit code to 1 if there are syntactic changes in any text files, or byte changes in any binary files.")
) )
.arg(
Arg::new("check-only").long("check-only")
.env("DFT_CHECK_ONLY")
.help("Report whether there are any syntactic changes, but don't calculate them. Much faster.")
)
.arg( .arg(
Arg::new("skip-unchanged").long("skip-unchanged") Arg::new("skip-unchanged").long("skip-unchanged")
.help("Don't display anything if a file is unchanged.") .help("Don't display anything if a file is unchanged.")
@ -265,6 +270,7 @@ pub enum Mode {
display_options: DisplayOptions, display_options: DisplayOptions,
missing_as_empty: bool, missing_as_empty: bool,
set_exit_code: bool, set_exit_code: bool,
check_only: bool,
language_override: Option<guess_language::Language>, language_override: Option<guess_language::Language>,
/// The path where we can read the LHS file. This is often a /// The path where we can read the LHS file. This is often a
/// temporary file generated by source control. /// temporary file generated by source control.
@ -459,6 +465,8 @@ pub fn parse_args() -> Mode {
let set_exit_code = matches.is_present("exit-code"); let set_exit_code = matches.is_present("exit-code");
let check_only = matches.is_present("check-only");
let display_options = DisplayOptions { let display_options = DisplayOptions {
background_color, background_color,
use_color, use_color,
@ -477,6 +485,7 @@ pub fn parse_args() -> Mode {
display_options, display_options,
missing_as_empty, missing_as_empty,
set_exit_code, set_exit_code,
check_only,
language_override, language_override,
lhs_path, lhs_path,
rhs_path, rhs_path,