Mike Crowe [off-list ref] writes:
If "git diff --quiet" finds it necessary to compare actual file contents,
and a file requires CRLF conversion, then it incorrectly exits with an exit
code of 1 even if there have been no changes.
The patch below adds a test file that shows the problem.
If "git diff" does not show any output and "git diff --exit-code" or
"git diff --quiet" says there are differences, then it is a bug.
I would however have expected that any culprit would involve a code
that says "under QUICK option, we do not have to bother doing
this". The part you quoted:
if (!DIFF_FILE_VALID(p->one) || /* (1) */
!DIFF_FILE_VALID(p->two) ||
(p->one->oid_valid && p->two->oid_valid) ||
(p->one->mode != p->two->mode) ||
diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
(p->one->size != p->two->size) ||
!diff_filespec_is_identical(p->one, p->two)) /* (2) */
p->skip_stat_unmatch_result = 1;
is used by "git diff" with and without "--quiet", afacr, so I
suspect that the bug lies somewhere else.
On Friday 17 February 2017 at 14:05:17 -0800, Junio C Hamano wrote:
Mike Crowe [off-list ref] writes:
quoted
If "git diff --quiet" finds it necessary to compare actual file contents,
and a file requires CRLF conversion, then it incorrectly exits with an exit
code of 1 even if there have been no changes.
The patch below adds a test file that shows the problem.
If "git diff" does not show any output and "git diff --exit-code" or
"git diff --quiet" says there are differences, then it is a bug.
I would however have expected that any culprit would involve a code
that says "under QUICK option, we do not have to bother doing
this". The part you quoted:
quoted
if (!DIFF_FILE_VALID(p->one) || /* (1) */
!DIFF_FILE_VALID(p->two) ||
(p->one->oid_valid && p->two->oid_valid) ||
(p->one->mode != p->two->mode) ||
diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
(p->one->size != p->two->size) ||
!diff_filespec_is_identical(p->one, p->two)) /* (2) */
p->skip_stat_unmatch_result = 1;
is used by "git diff" with and without "--quiet", afacr, so I
suspect that the bug lies somewhere else.
I can't say that I really understand the code fully, but it appears that
the first pass generates a queue of files that contain differences. The
result of the quiet diff comes from the size of that queue,
diff_queued_diff.nr, being non-zero in diffcore_std. I'm assuming that the
result of the noisy diff comes from actually comparing the files.
But, I've only spent a short while looking so I may have got the wrong end
of the stick.
Thanks.
Mike.
On Friday 17 February 2017 at 22:19:58 +0000, Mike Crowe wrote:
On Friday 17 February 2017 at 14:05:17 -0800, Junio C Hamano wrote:
quoted
Mike Crowe [off-list ref] writes:
quoted
If "git diff --quiet" finds it necessary to compare actual file contents,
and a file requires CRLF conversion, then it incorrectly exits with an exit
code of 1 even if there have been no changes.
The patch below adds a test file that shows the problem.
If "git diff" does not show any output and "git diff --exit-code" or
"git diff --quiet" says there are differences, then it is a bug.
I would however have expected that any culprit would involve a code
that says "under QUICK option, we do not have to bother doing
this". The part you quoted:
quoted
if (!DIFF_FILE_VALID(p->one) || /* (1) */
!DIFF_FILE_VALID(p->two) ||
(p->one->oid_valid && p->two->oid_valid) ||
(p->one->mode != p->two->mode) ||
diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
(p->one->size != p->two->size) ||
!diff_filespec_is_identical(p->one, p->two)) /* (2) */
p->skip_stat_unmatch_result = 1;
is used by "git diff" with and without "--quiet", afacr, so I
suspect that the bug lies somewhere else.
I can't say that I really understand the code fully, but it appears that
the first pass generates a queue of files that contain differences. The
result of the quiet diff comes from the size of that queue,
diff_queued_diff.nr, being non-zero in diffcore_std. I'm assuming that the
result of the noisy diff comes from actually comparing the files.
But, I've only spent a short while looking so I may have got the wrong end
of the stick.
Tricking Git into checking the actual file contents (by passing
--ignore-space-change for example) is sufficient to ensure that the exit
status is never 1 when it should be zero. (Of course that option has other
unwanted effects in this case.)
I think that if there's a risk that file contents will undergo conversion
then this should force the diff to check the file contents. Something like:
diff --git a/diff.c b/diff.c
index 051761b..bee1662 100644
--- a/diff.c
+++ b/diff.c
@@ -3413,13 +3413,14 @@ void diff_setup_done(struct diff_options *options)
/*
* Most of the time we can say "there are changes"
* only by checking if there are changed paths, but
- * --ignore-whitespace* options force us to look
- * inside contents.
+ * --ignore-whitespace* options or text conversion
+ * force us to look inside contents.
*/
if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
- DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
+ DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL) ||
+ DIFF_OPT_TST(options, ALLOW_TEXTCONV))
DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
else
DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);
This solves the problem for me and my test case now passes. Unfortunately
it breaks the 'removing and adding subproject' test case in
t3040-subprojects-basic at the line:
test_expect_code 1 git diff -M --name-status --exit-code HEAD^ HEAD
presumably because after the rename has been detected the file contents are
identical. :( A rename of a single file appears to still be handled
correctly.
Mike.