Re: [PATCH v3 0/3] diff: fix --exit-code with external diff

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi René

On 09/06/2024 08:35, René Scharfe wrote:
Changes since v2:
- rebase; config strings are no longer const
- document the handling of unexpected exit codes
- document that external diffs are skipped with --quiet and trustExitCode=off
- silence external diff output with --quiet
- check output in tests
- test diff runs without --exit-code and --quiet as well
- slightly untangle the exit code handling code to make it easier to read
- fix copy/paste error in documentation of diff.<driver>.trustExitCode

The changes in this version all look good. I've re-read the patches and didn't spot any other issues so this is ready as far as I'm concerned.

Thanks

Phillip

   t4020: test exit code with external diffs
   userdiff: add and use struct external_diff
   diff: let external diffs report that changes are uninteresting

  Documentation/config/diff.txt   | 18 +++++++++
  Documentation/diff-options.txt  |  5 +++
  Documentation/git.txt           | 10 +++++
  Documentation/gitattributes.txt |  5 +++
  diff.c                          | 68 +++++++++++++++++++++++++--------
  t/t4020-diff-external.sh        | 66 ++++++++++++++++++++++++++++++++
  userdiff.c                      |  8 +++-
  userdiff.h                      |  7 +++-
  8 files changed, 168 insertions(+), 19 deletions(-)

Range-Diff gegen v2:
1:  118aba667b < -:  ---------- t4020: test exit code with external diffs
-:  ---------- > 1:  d59f0c6fdf t4020: test exit code with external diffs
2:  0b4dabebe1 ! 2:  4ad160ab1f userdiff: add and use struct external_diff
     @@ diff.c
      @@ diff.c: static int diff_color_moved_ws_default;
       static int diff_context_default = 3;
       static int diff_interhunk_context_default;
     - static const char *diff_word_regex_cfg;
     --static const char *external_diff_cmd_cfg;
     + static char *diff_word_regex_cfg;
     +-static char *external_diff_cmd_cfg;
      +static struct external_diff external_diff_cfg;
     - static const char *diff_order_file_cfg;
     + static char *diff_order_file_cfg;
       int diff_auto_refresh_index = 1;
       static int diff_mnemonic_prefix;
      @@ diff.c: int git_diff_ui_config(const char *var, const char *value,
     @@ userdiff.h: struct userdiff_funcname {
       };

      +struct external_diff {
     -+	const char *cmd;
     ++	char *cmd;
      +};
      +
       struct userdiff_driver {
       	const char *name;
     --	const char *external;
     +-	char *external;
      +	struct external_diff external;
     - 	const char *algorithm;
     + 	char *algorithm;
       	int binary;
       	struct userdiff_funcname funcname;
3:  4d54ca8281 ! 3:  29c8d3b61a diff: let external diffs report that changes are uninteresting
     @@ Commit message
          diff is not able to report empty diffs.  We can only do that check after
          evaluating the file-specific attributes in run_external_diff().

     +    If we do run the external diff with --quiet, send its output to
     +    /dev/null.
     +
          I considered checking the output of the external diff to check whether
          its empty.  It was added as 11be65cfa4 (diff: fix --exit-code with
          external diff, 2024-05-05) and quickly reverted, as it does not work
     @@ Documentation/config/diff.txt: diff.external::
       	your files, you might want to use linkgit:gitattributes[5] instead.

      +diff.trustExitCode::
     -+	If this boolean value is set to true then the `diff.external`
     -+	command is expected to return exit code 1 if it finds
     -+	significant changes and 0 if it doesn't, like diff(1).  If it's
     -+	false then the `diff.external` command is expected to always
     -+	return exit code 0.  Defaults to false.
     ++	If this boolean value is set to true then the
     ++	`diff.external` command is expected to return exit code
     ++	0 if it considers the input files to be equal or 1 if it
     ++	considers them to be different, like `diff(1)`.
     ++	If it is set to false, which is the default, then the command
     ++	is expected to return exit code 0 regardless of equality.
     ++	Any other exit code causes Git to report a fatal error.
      +
       diff.ignoreSubmodules::
       	Sets the default value of --ignore-submodules. Note that this
     @@ Documentation/config/diff.txt: diff.<driver>.command::
      +diff.<driver>.trustExitCode::
      +	If this boolean value is set to true then the
      +	`diff.<driver>.command` command is expected to return exit code
     -+	1 if it finds significant changes and 0 if it doesn't, like
     -+	diff(1).  If it's false then the `diff.external` command is
     -+	expected to always return exit code 0.  Defaults to false.
     ++	0 if it considers the input files to be equal or 1 if it
     ++	considers them to be different, like `diff(1)`.
     ++	If it is set to false, which is the default, then the command
     ++	is expected to return exit code 0 regardless of equality.
     ++	Any other exit code causes Git to report a fatal error.
      +
       diff.<driver>.xfuncname::
       	The regular expression that the diff driver should use to
       	recognize the hunk header.  A built-in pattern may also be used.

     + ## Documentation/diff-options.txt ##
     +@@ Documentation/diff-options.txt: ifndef::git-log[]
     +
     + --quiet::
     + 	Disable all output of the program. Implies `--exit-code`.
     +-	Disables execution of external diff helpers.
     ++	Disables execution of external diff helpers whose exit code
     ++	is not trusted, i.e. their respective configuration option
     ++	`diff.trustExitCode` or `diff.<driver>.trustExitCode` or
     ++	environment variable `GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE` is
     ++	false.
     + endif::git-log[]
     + endif::git-format-patch[]
     +
     +
       ## Documentation/git.txt ##
      @@ Documentation/git.txt: parameter, <path>.
       For each path `GIT_EXTERNAL_DIFF` is called, two environment variables,
       `GIT_DIFF_PATH_COUNTER` and `GIT_DIFF_PATH_TOTAL` are set.

      +`GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE`::
     -+	Setting this environment variable indicates the the program
     -+	specified with `GIT_EXTERNAL_DIFF` returns exit code 1 if it
     -+	finds significant changes and 0 if it doesn't, like diff(1).
     -+	If it's not set, the program is expected to always return
     -+	exit code 0.
     ++	If this Boolean environment variable is set to true then the
     ++	`GIT_EXTERNAL_DIFF` command is expected to return exit code
     ++	0 if it considers the input files to be equal or 1 if it
     ++	considers them to be different, like `diff(1)`.
     ++	If it is set to false, which is the default, then the command
     ++	is expected to return exit code 0 regardless of equality.
     ++	Any other exit code causes Git to report a fatal error.
     ++
      +
       `GIT_DIFF_PATH_COUNTER`::
       	A 1-based counter incremented by one for every path.
     @@ diff.c: static void run_external_diff(const struct external_diff *pgm,
       {
       	struct child_process cmd = CHILD_PROCESS_INIT;
       	struct diff_queue_struct *q = &diff_queued_diff;
     ++	int quiet = !(o->output_format & DIFF_FORMAT_PATCH);
      +	int rc;
      +
      +	/*
     @@ diff.c: static void run_external_diff(const struct external_diff *pgm,
      +	 * external diff program lacks the ability to tell us whether
      +	 * it's empty then we consider it non-empty without even asking.
      +	 */
     -+	if (!(o->output_format & DIFF_FORMAT_PATCH) && !pgm->trust_exit_code) {
     ++	if (!pgm->trust_exit_code && quiet) {
      +		o->found_changes = 1;
      +		return;
      +	}
     @@ diff.c: static void run_external_diff(const struct external_diff *pgm,
       	diff_free_filespec_data(two);
       	cmd.use_shell = 1;
      -	if (run_command(&cmd))
     ++	cmd.no_stdout = quiet;
      +	rc = run_command(&cmd);
     -+	if ((!pgm->trust_exit_code && !rc) || (pgm->trust_exit_code && rc == 1))
     ++	if (!pgm->trust_exit_code && rc == 0)
     ++		o->found_changes = 1;
     ++	else if (pgm->trust_exit_code && rc == 0)
     ++		; /* nothing */
     ++	else if (pgm->trust_exit_code && rc == 1)
      +		o->found_changes = 1;
     -+	else if (!pgm->trust_exit_code || rc)
     ++	else
       		die(_("external diff died, stopping at %s"), name);

       	remove_tempfile();
     @@ diff.c: void diff_setup_done(struct diff_options *options)
       	if (options->flags.follow_renames)

       ## t/t4020-diff-external.sh ##
     -@@ t/t4020-diff-external.sh: test_expect_success 'no diff with -diff' '
     - check_external_exit_code () {
     - 	expect_code=$1
     - 	command_code=$2
     --	option=$3
     -+	trust_exit_code=$3
     -+	option=$4
     -
     - 	command="exit $command_code;"
     +@@ t/t4020-diff-external.sh: check_external_diff () {
     + 	expect_out=$2
     + 	expect_err=$3
     + 	command_code=$4
     +-	shift 4
     ++	trust_exit_code=$5
     ++	shift 5
     + 	options="$@"
     +
     + 	command="echo output; exit $command_code;"
      -	desc="external diff '$command'"
      +	desc="external diff '$command' with trustExitCode=$trust_exit_code"
     + 	with_options="${options:+ with }$options"

     - 	test_expect_success "$desc via attribute with $option" "
     + 	test_expect_success "$desc via attribute$with_options" "
       		test_config diff.foo.command \"$command\" &&
      +		test_config diff.foo.trustExitCode $trust_exit_code &&
       		echo \"file diff=foo\" >.gitattributes &&
     - 		test_expect_code $expect_code git diff $option
     - 	"
     + 		test_expect_code $expect_code git diff $options >out 2>err &&
     + 		test_cmp $expect_out out &&
     +@@ t/t4020-diff-external.sh: check_external_diff () {

     - 	test_expect_success "$desc via diff.external with $option" "
     + 	test_expect_success "$desc via diff.external$with_options" "
       		test_config diff.external \"$command\" &&
      +		test_config diff.trustExitCode $trust_exit_code &&
       		>.gitattributes &&
     - 		test_expect_code $expect_code git diff $option
     - 	"
     -@@ t/t4020-diff-external.sh: check_external_exit_code () {
     + 		test_expect_code $expect_code git diff $options >out 2>err &&
     + 		test_cmp $expect_out out &&
     +@@ t/t4020-diff-external.sh: check_external_diff () {
       		>.gitattributes &&
       		test_expect_code $expect_code env \
       			GIT_EXTERNAL_DIFF=\"$command\" \
      +			GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE=$trust_exit_code \
     - 			git diff $option
     - 	"
     - }
     -
     --check_external_exit_code   1 0 --exit-code
     --check_external_exit_code   1 0 --quiet
     --check_external_exit_code 128 1 --exit-code
     --check_external_exit_code   1 1 --quiet # we don't even call the program
     -+check_external_exit_code   1 0 off --exit-code
     -+check_external_exit_code   1 0 off --quiet
     -+check_external_exit_code 128 1 off --exit-code
     -+check_external_exit_code   1 1 off --quiet # we don't even call the program
     + 			git diff $options >out 2>err &&
     + 		test_cmp $expect_out out &&
     + 		test_cmp $expect_err err
     +@@ t/t4020-diff-external.sh: test_expect_success 'setup output files' '
     + 	echo "fatal: external diff died, stopping at file" >error
     + '
     +
     +-check_external_diff   0 output empty 0
     +-check_external_diff 128 output error 1
     +-
     +-check_external_diff   1 output empty 0 --exit-code
     +-check_external_diff 128 output error 1 --exit-code
     +-
     +-check_external_diff   1 empty  empty 0 --quiet
     +-check_external_diff   1 empty  empty 1 --quiet # we don't even call the program
     ++check_external_diff   0 output empty 0 off
     ++check_external_diff 128 output error 1 off
     ++check_external_diff   0 output empty 0 on
     ++check_external_diff   0 output empty 1 on
     ++check_external_diff 128 output error 2 on
     ++
     ++check_external_diff   1 output empty 0 off --exit-code
     ++check_external_diff 128 output error 1 off --exit-code
     ++check_external_diff   0 output empty 0 on  --exit-code
     ++check_external_diff   1 output empty 1 on  --exit-code
     ++check_external_diff 128 output error 2 on  --exit-code
      +
     -+check_external_exit_code   0 0 on --exit-code
     -+check_external_exit_code   0 0 on --quiet
     -+check_external_exit_code   1 1 on --exit-code
     -+check_external_exit_code   1 1 on --quiet
     -+check_external_exit_code 128 2 on --exit-code
     -+check_external_exit_code 128 2 on --quiet
     ++check_external_diff   1 empty  empty 0 off --quiet
     ++check_external_diff   1 empty  empty 1 off --quiet # we don't even call the program
     ++check_external_diff   0 empty  empty 0 on  --quiet
     ++check_external_diff   1 empty  empty 1 on  --quiet
     ++check_external_diff 128 empty  error 2 on  --quiet

       echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file

     @@ userdiff.h
      @@ userdiff.h: struct userdiff_funcname {

       struct external_diff {
     - 	const char *cmd;
     + 	char *cmd;
      +	unsigned trust_exit_code:1;
       };

--
2.45.2




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux