threads / patch / 29791

patch, 4 parts"diff -p" patch header generation updates

Subject: [PATCH 0/4] "diff -p" patch header generation updates

## tl;dr

10 messages between Mar 1, 2012 and Mar 1, 2012. Diffs are folded; open one to read it.

replies: 9people: 3as markdown or json

Junio C Hamano· Mar 1, 2012, 02:14 UTC · lore

There has always been one thing fishy about the plumbing level "diff" used to generate patches with "-p" option: we produced a header-only diff when there is no patch necessary to be shown, when there is no contents change. We are calling into xdiff to produce a patch, and we should have all the necessary information to suppress the unnecessary header, but because historically we didn't, we never updated the code to do so.

It turns out that we already have the necessary machinery to notice such a situation and suppressing the useless header. We just did not realize that we could use it. There is a backward compatibility worry for scripts, but I would imagine that the scripts saw these header-only diff as a nuisance that needs to be filtered out, not a feature to learn about paths that are only stat-dirty. And more importantly, even if this _were_ a feature, it was not working correctly when "-w" option was in effect to begin with, as the second one in the series illustrates.

This series changes the world order that is almost 6 years old. No more phantom diff headers in patch output due to stat-dirtiness.

The first patch in this series replaces the one I posted earlier (one block that prepares the expected result was in a wrong test).

The second patch adds test vectors that illustrate the current behaviour that will be changed with this series.

The third patch is the real change, whose effect can be seen in the patch to the test the second one introduces.

The fourth one is unrelated from the rest of the series from the feature point of view, but its implementation depends on the updated semantics of header suppression.

The series is designed to apply on top of v1.7.4-rc0~147 and has trivial conflict in diff.h if applied/merged to 'master'. Just take the addition of DIFF_OPT* macros from both sides.

Junio C Hamano (4):
  t4011: modernise style
  t4011: illustrate "diff-index -p" on stat-dirty paths
  diff -p: squelch "diff --git" header for stat-dirty paths
  diff --ignore-mode-change
 diff.c                  |   31 +++++++-
 diff.h                  |    1 +
 t/t4011-diff-symlink.sh |  196 +++++++++++++++++++++++++++--------------------
 3 files changed, 141 insertions(+), 87 deletions(-)
-- 
1.7.9.2.350.g74d65
Junio C Hamano· Mar 1, 2012, 02:14 UTC · re: Junio C Hamano · lore

[PATCH 1/4] t4011: modernise style

Match the style to more modern test scripts, namely:
 - The first line of each test has prereq, title and opening sq for the
   script body.  This makes the test shorter while reducing the need for
   backquotes.
 - Be prepared for the case in which the previous test may have failed.
   If a test wants to start from not having "frotz" that the previous test
   may have created, write "rm -f frotz", not "rm frotz".
 - Prepare the expected output inside your own test.
 - The order of comparison to check the result is "diff expected actual",
   so that the output will show how the output from the git you just broke
   is different from what is expected.
 - Write no SP between redirection '>' (or '<' for that matter) and the
   filename.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 * replaces the one I posted earlier (one block that prepares the expected
   result was in a wrong test).
 t/t4011-diff-symlink.sh |  168 +++++++++++++++++++++++------------------------
 1 file changed, 82 insertions(+), 86 deletions(-)
Show changes to t/t4011-diff-symlink.sh +82 −82
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 408a19c..cb47ec1 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -9,85 +9,81 @@ test_description='Test diff of symlinks.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-cat > expected << EOF
-diff --git a/frotz b/frotz
-new file mode 120000
-index 0000000..7c465af
---- /dev/null
-+++ b/frotz
-@@ -0,0 +1 @@
-+xyzzy
-\ No newline at end of file
-EOF
-
-test_expect_success SYMLINKS \
-    'diff new symlink' \
-    'ln -s xyzzy frotz &&
-    git update-index &&
-    tree=$(git write-tree) &&
-    git update-index --add frotz &&
-    GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
-
-test_expect_success SYMLINKS \
-    'diff unchanged symlink' \
-    'tree=$(git write-tree) &&
-    git update-index frotz &&
-    test -z "$(git diff-index --name-only $tree)"'
-
-cat > expected << EOF
-diff --git a/frotz b/frotz
-deleted file mode 120000
-index 7c465af..0000000
---- a/frotz
-+++ /dev/null
-@@ -1 +0,0 @@
--xyzzy
-\ No newline at end of file
-EOF
-
-test_expect_success SYMLINKS \
-    'diff removed symlink' \
-    'mv frotz frotz2 &&
-    git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
+test_expect_success SYMLINKS 'diff new symlink' '
+	cat >expected <<-\EOF &&
+	diff --git a/frotz b/frotz
+	new file mode 120000
+	index 0000000..7c465af
+	--- /dev/null
+	+++ b/frotz
+	@@ -0,0 +1 @@
+	+xyzzy
+	\ No newline at end of file
+	EOF
+	ln -s xyzzy frotz &&
+	git update-index &&
+	tree=$(git write-tree) &&
+	git update-index --add frotz &&
+	GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current
+'
 
-cat > expected << EOF
-diff --git a/frotz b/frotz
-EOF
+test_expect_success SYMLINKS 'diff unchanged symlink'  '
+	tree=$(git write-tree) &&
+	git update-index frotz &&
+	test -z "$(git diff-index --name-only $tree)"
+'
 
-test_expect_success SYMLINKS \
-    'diff identical, but newly created symlink' \
-    'ln -s xyzzy frotz &&
-    git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
+test_expect_success SYMLINKS 'diff removed symlink' '
+	cat >expected <<-\EOF &&
+	diff --git a/frotz b/frotz
+	deleted file mode 120000
+	index 7c465af..0000000
+	--- a/frotz
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-xyzzy
+	\ No newline at end of file
+	EOF
+	mv frotz frotz2 &&
+	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current
+'
 
-cat > expected << EOF
-diff --git a/frotz b/frotz
-index 7c465af..df1db54 120000
---- a/frotz
-+++ b/frotz
-@@ -1 +1 @@
--xyzzy
-\ No newline at end of file
-+yxyyz
-\ No newline at end of file
-EOF
+test_expect_success SYMLINKS 'diff identical, but newly created symlink' '
+	cat >expected <<-\EOF &&
+	diff --git a/frotz b/frotz
+	EOF
+	ln -s xyzzy frotz &&
+	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current
+'
 
-test_expect_success SYMLINKS \
-    'diff different symlink' \
-    'rm frotz &&
-    ln -s yxyyz frotz &&
-    git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
+test_expect_success SYMLINKS 'diff different symlink' '
+	cat >expected <<-\EOF &&
+	diff --git a/frotz b/frotz
+	index 7c465af..df1db54 120000
+	--- a/frotz
+	+++ b/frotz
+	@@ -1 +1 @@
+	-xyzzy
+	\ No newline at end of file
+	+yxyyz
+	\ No newline at end of file
+	EOF
+	rm -f frotz &&
+	ln -s yxyyz frotz &&
+	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current
+'
 
-test_expect_success SYMLINKS \
-    'diff symlinks with non-existing targets' \
-    'ln -s narf pinky &&
-    ln -s take\ over brain &&
-    test_must_fail git diff --no-index pinky brain > output 2> output.err &&
-    grep narf output &&
-    ! grep error output.err'
+test_expect_success SYMLINKS 'diff symlinks with non-existing targets' '
+	ln -s narf pinky &&
+	ln -s take\ over brain &&
+	test_must_fail git diff --no-index pinky brain >output 2>output.err &&
+	grep narf output &&
+	! test -s output.err
+'
 
 test_expect_success SYMLINKS 'setup symlinks with attributes' '
 	echo "*.bin diff=bin" >>.gitattributes &&
@@ -96,19 +92,19 @@ test_expect_success SYMLINKS 'setup symlinks with attributes' '
 	git add -N file.bin link.bin
 '
 
-cat >expect <<'EOF'
-diff --git a/file.bin b/file.bin
-index e69de29..d95f3ad 100644
-Binary files a/file.bin and b/file.bin differ
-diff --git a/link.bin b/link.bin
-index e69de29..dce41ec 120000
---- a/link.bin
-+++ b/link.bin
-@@ -0,0 +1 @@
-+file.bin
-\ No newline at end of file
-EOF
 test_expect_success SYMLINKS 'symlinks do not respect userdiff config by path' '
+	cat >expect <<-\EOF &&
+	diff --git a/file.bin b/file.bin
+	index e69de29..d95f3ad 100644
+	Binary files a/file.bin and b/file.bin differ
+	diff --git a/link.bin b/link.bin
+	index e69de29..dce41ec 120000
+	--- a/link.bin
+	+++ b/link.bin
+	@@ -0,0 +1 @@
+	+file.bin
+	\ No newline at end of file
+	EOF
 	git config diff.bin.binary true &&
 	git diff file.bin link.bin >actual &&
 	test_cmp expect actual
-- 
1.7.9.2.350.g74d65
Junio C Hamano· Mar 1, 2012, 02:14 UTC · re: Junio C Hamano · lore

[PATCH 2/4] t4011: illustrate "diff-index -p" on stat-dirty paths

The plumbing that looks at the working tree, i.e. "diff-index" and "diff-files", always emit the "diff --git a/path b/path" header lines without anything else for paths that are only stat-dirty (i.e. different only because the cached stat information in the index no longer matches that of the working tree, but the real contents are the same), when these commands are run with "-p" option to produce patches.

Illustrate this current behaviour. The new part that uses "-w" option demonstrates that we do not show any "diff --git" header for blobs whose true contents are different but compares the same when whitespaces are ignored, which is inconsistent with the behaviour for stat-dirty paths.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t4011-diff-symlink.sh |   47 ++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 40 insertions(+), 7 deletions(-)
Show changes to t/t4011-diff-symlink.sh +40 −7
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index cb47ec1..6097e19 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -9,7 +9,7 @@ test_description='Test diff of symlinks.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-test_expect_success SYMLINKS 'diff new symlink' '
+test_expect_success SYMLINKS 'diff new symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	new file mode 120000
@@ -19,22 +19,30 @@ test_expect_success SYMLINKS 'diff new symlink' '
 	@@ -0,0 +1 @@
 	+xyzzy
 	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	new file mode 100644
+	index 0000000..7c465af
+	--- /dev/null
+	+++ b/nitfol
+	@@ -0,0 +1 @@
+	+xyzzy
 	EOF
 	ln -s xyzzy frotz &&
+	echo xyzzy >nitfol &&
 	git update-index &&
 	tree=$(git write-tree) &&
-	git update-index --add frotz &&
+	git update-index --add frotz nitfol &&
 	GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff unchanged symlink'  '
+test_expect_success SYMLINKS 'diff unchanged symlink and file'  '
 	tree=$(git write-tree) &&
-	git update-index frotz &&
+	git update-index frotz nitfol &&
 	test -z "$(git diff-index --name-only $tree)"
 '
 
-test_expect_success SYMLINKS 'diff removed symlink' '
+test_expect_success SYMLINKS 'diff removed symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	deleted file mode 120000
@@ -44,22 +52,39 @@ test_expect_success SYMLINKS 'diff removed symlink' '
 	@@ -1 +0,0 @@
 	-xyzzy
 	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	deleted file mode 100644
+	index 7c465af..0000000
+	--- a/nitfol
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-xyzzy
 	EOF
 	mv frotz frotz2 &&
+	mv nitfol nitfol2 &&
 	git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff identical, but newly created symlink' '
+test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
+	diff --git a/nitfol b/nitfol
 	EOF
+	sleep 3 &&
+	rm -f frotz &&
+	echo xyzzy >nitfol3 &&
+	mv nitfol3 nitfol &&
 	ln -s xyzzy frotz &&
 	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current &&
+
+	>expected &&
+	git diff-index -M -p -w $tree >current &&
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff different symlink' '
+test_expect_success SYMLINKS 'diff different symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	index 7c465af..df1db54 120000
@@ -70,9 +95,17 @@ test_expect_success SYMLINKS 'diff different symlink' '
 	\ No newline at end of file
 	+yxyyz
 	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	index 7c465af..df1db54 100644
+	--- a/nitfol
+	+++ b/nitfol
+	@@ -1 +1 @@
+	-xyzzy
+	+yxyyz
 	EOF
 	rm -f frotz &&
 	ln -s yxyyz frotz &&
+	echo yxyyz >nitfol &&
 	git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
 '
-- 
1.7.9.2.350.g74d65
Thomas Rast· Mar 1, 2012, 09:05 UTC · re: Junio C Hamano · lore

Re: [PATCH 2/4] t4011: illustrate "diff-index -p" on stat-dirty paths

Junio C Hamano <gitster@pobox.com> writes:
Show 11 quoted lines
> The plumbing that looks at the working tree, i.e. "diff-index" and
> "diff-files", always emit the "diff --git a/path b/path" header lines
> without anything else for paths that are only stat-dirty (i.e. different
> only because the cached stat information in the index no longer matches
> that of the working tree, but the real contents are the same), when
> these commands are run with "-p" option to produce patches.
>
> Illustrate this current behaviour.  The new part that uses "-w" option
> demonstrates that we do not show any "diff --git" header for blobs whose
> true contents are different but compares the same when whitespaces are
> ignored, which is inconsistent with the behaviour for stat-dirty paths.
[...]
Show 18 quoted lines
> -test_expect_success SYMLINKS 'diff identical, but newly created symlink' '
> +test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' '
>  	cat >expected <<-\EOF &&
>  	diff --git a/frotz b/frotz
> +	diff --git a/nitfol b/nitfol
>  	EOF
> +	sleep 3 &&
> +	rm -f frotz &&
> +	echo xyzzy >nitfol3 &&
> +	mv nitfol3 nitfol &&
>  	ln -s xyzzy frotz &&
>  	git diff-index -M -p $tree >current &&
> +	compare_diff_patch expected current &&
> +
> +	>expected &&
> +	git diff-index -M -p -w $tree >current &&
>  	compare_diff_patch expected current
>  '

I find the last bit of the commit message rather confusing. You appear to be using -w here to diff the stat-dirty worktree nitfol 'xyzzy\n' against the $tree:nitfol which is also 'xyzzy\n'.

If that analysis is correct, then
  we do not show any "diff --git" header for blobs whose true contents
  are different but compares the same when whitespaces are ignored

is not what is going on here; the blobs have exactly the same content. The difference is that

* without -w, the code "knows" from the lstat() data that the files are
  different, prints a header, and then fails to find any differences;
* with -w, the code correctly holds off on printing anything since it
  will invariably have to inspect the contents beforehand.
So perhaps you can say
  Illustrate this current behaviour.  Also demonstrate that with the
  "-w" option, we (correctly) hold off showing a "diff --git" header
  until actual differences have been found.  This also suppresses the
  header for merely stat-dirty files, which is inconsistent.
-- 
Thomas Rast
trast@{inf,student}.ethz.ch
Junio C Hamano· Mar 1, 2012, 16:18 UTC · re: Thomas Rast · lore

Re: [PATCH 2/4] t4011: illustrate "diff-index -p" on stat-dirty paths

Thomas Rast <trast@inf.ethz.ch> writes:
Show 6 quoted lines
> If that analysis is correct, then
>
>   we do not show any "diff --git" header for blobs whose true contents
>   are different but compares the same when whitespaces are ignored
>
> is not what is going on here; the blobs have exactly the same content.

True; an earlier draft actually had an 'echo " xyzzy" >nitfol' in there, but it meant that it needed another annoying 'sleep 3' (I could have used test-chmtime), so the test was updated but the above description stayed.

> The difference is that
>
> * without -w, the code "knows" from the lstat() data that the files are
>   different, prints a header, and then fails to find any differences;
Correct. Those quotes around "knows" are the most important ;-)

The code incorrectly thinks the stat dirtyness is all that it "knows"; even though it has a way to act on a better knowledge from the xdiff these days, it does not utilize it.

Show 9 quoted lines
> * with -w, the code correctly holds off on printing anything since it
>   will invariably have to inspect the contents beforehand.
>
> So perhaps you can say
>
>   Illustrate this current behaviour.  Also demonstrate that with the
>   "-w" option, we (correctly) hold off showing a "diff --git" header
>   until actual differences have been found.  This also suppresses the
>   header for merely stat-dirty files, which is inconsistent.
That is much better; thanks.
Junio C Hamano· Mar 1, 2012, 16:52 UTC · re: Thomas Rast · lore

[PATCH 2/4 v2] t4011: illustrate "diff-index -p" on stat-dirty paths

The plumbing that looks at the working tree, i.e. "diff-index" and "diff-files", always emit the "diff --git a/path b/path" header lines without anything else for paths that are only stat-dirty (i.e. different only because the cached stat information in the index no longer matches that of the working tree, but the real contents are the same), when these commands are run with "-p" option to produce patches.

Illustrate this current behaviour. Also demonstrate that with the "-w" option, we (correctly) hold off showing a "diff --git" header until actual differences have been found. This also suppresses the header for merely stat-dirty files, which is inconsistent.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 * This time with test-chmtime to get rid of the annoying 3 second delay
   to invalidate the cached stat information.
 t/t4011-diff-symlink.sh |   46 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 39 insertions(+), 7 deletions(-)
Show changes to t/t4011-diff-symlink.sh +39 −7
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index cb47ec1..164f153 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -9,7 +9,7 @@ test_description='Test diff of symlinks.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-test_expect_success SYMLINKS 'diff new symlink' '
+test_expect_success SYMLINKS 'diff new symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	new file mode 120000
@@ -19,22 +19,30 @@ test_expect_success SYMLINKS 'diff new symlink' '
 	@@ -0,0 +1 @@
 	+xyzzy
 	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	new file mode 100644
+	index 0000000..7c465af
+	--- /dev/null
+	+++ b/nitfol
+	@@ -0,0 +1 @@
+	+xyzzy
 	EOF
 	ln -s xyzzy frotz &&
+	echo xyzzy >nitfol &&
 	git update-index &&
 	tree=$(git write-tree) &&
-	git update-index --add frotz &&
+	git update-index --add frotz nitfol &&
 	GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff unchanged symlink'  '
+test_expect_success SYMLINKS 'diff unchanged symlink and file'  '
 	tree=$(git write-tree) &&
-	git update-index frotz &&
+	git update-index frotz nitfol &&
 	test -z "$(git diff-index --name-only $tree)"
 '
 
-test_expect_success SYMLINKS 'diff removed symlink' '
+test_expect_success SYMLINKS 'diff removed symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	deleted file mode 120000
@@ -44,22 +52,38 @@ test_expect_success SYMLINKS 'diff removed symlink' '
 	@@ -1 +0,0 @@
 	-xyzzy
 	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	deleted file mode 100644
+	index 7c465af..0000000
+	--- a/nitfol
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-xyzzy
 	EOF
 	mv frotz frotz2 &&
+	mv nitfol nitfol2 &&
 	git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff identical, but newly created symlink' '
+test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
+	diff --git a/nitfol b/nitfol
 	EOF
+	rm -f frotz nitfol &&
+	echo xyzzy >nitfol &&
+	test-chmtime +10 nitfol &&
 	ln -s xyzzy frotz &&
 	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current &&
+
+	>expected &&
+	git diff-index -M -p -w $tree >current &&
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff different symlink' '
+test_expect_success SYMLINKS 'diff different symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	index 7c465af..df1db54 120000
@@ -70,9 +94,17 @@ test_expect_success SYMLINKS 'diff different symlink' '
 	\ No newline at end of file
 	+yxyyz
 	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	index 7c465af..df1db54 100644
+	--- a/nitfol
+	+++ b/nitfol
+	@@ -1 +1 @@
+	-xyzzy
+	+yxyyz
 	EOF
 	rm -f frotz &&
 	ln -s yxyyz frotz &&
+	echo yxyyz >nitfol &&
 	git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
 '
-- 
1.7.9.2.350.g74d65
Zbigniew Jędrzejewski-Szmek· Mar 1, 2012, 10:05 UTC · re: Junio C Hamano · lore

Re: [PATCH 2/4] t4011: illustrate "diff-index -p" on stat-dirty paths

On 03/01/2012 03:14 AM, Junio C Hamano wrote:
Show 7 quoted lines
> -test_expect_success SYMLINKS 'diff unchanged symlink'  '
> +test_expect_success SYMLINKS 'diff unchanged symlink and file'  '
>   	tree=$(git write-tree)&&
> -	git update-index frotz&&
> +	git update-index frotz nitfol&&
>   	test -z "$(git diff-index --name-only $tree)"
>   '

Hi, why modify and extend an existing test instead of adding a new separate one? I think the test-suite should be getting more unittest-y, ie. checking minimal aspects of functionality, not less.

Zbyszek
Show 22 quoted lines
> -test_expect_success SYMLINKS 'diff removed symlink' '
> +test_expect_success SYMLINKS 'diff removed symlink and file' '
>   	cat>expected<<-\EOF&&
>   	diff --git a/frotz b/frotz
>   	deleted file mode 120000
> @@ -44,22 +52,39 @@ test_expect_success SYMLINKS 'diff removed symlink' '
>   	@@ -1 +0,0 @@
>   	-xyzzy
>   	\ No newline at end of file
> +	diff --git a/nitfol b/nitfol
> +	deleted file mode 100644
> +	index 7c465af..0000000
> +	--- a/nitfol
> +	+++ /dev/null
> +	@@ -1 +0,0 @@
> +	-xyzzy
>   	EOF
>   	mv frotz frotz2&&
> +	mv nitfol nitfol2&&
>   	git diff-index -M -p $tree>current&&
>   	compare_diff_patch expected current
>   '
Junio C Hamano· Mar 1, 2012, 16:10 UTC · re: Zbigniew Jędrzejewski-Szmek · lore

Re: [PATCH 2/4] t4011: illustrate "diff-index -p" on stat-dirty paths

Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> writes:
Show 10 quoted lines
> On 03/01/2012 03:14 AM, Junio C Hamano wrote:
>> -test_expect_success SYMLINKS 'diff unchanged symlink'  '
>> +test_expect_success SYMLINKS 'diff unchanged symlink and file'  '
>>   	tree=$(git write-tree)&&
>> -	git update-index frotz&&
>> +	git update-index frotz nitfol&&
>>   	test -z "$(git diff-index --name-only $tree)"
>>   '
> why modify and extend an existing test instead of adding a new
> separate one?

If you are talking about assigning a new test number for a new script, it is better to avoid it when you can. If on the other hand you meant to add a new "test_expect_success" block, yes, that is one of the right things to consider when adding tests, and I tried it both ways.

The result was easier to read when done the way it was posted.

For this update, you could keep the later test that originally checked symlink intact and add an identical check for regular file, but the thing is, the behaviour change brought by the update in this series affect both symlink and regular file comes from the same underlying mechanism, and demonstrating the change in a single hunk that makes two phantom output lines disappear was far easier to review than two hunks each of which shows one phantom output line disapper.

Junio C Hamano· Mar 1, 2012, 02:14 UTC · re: Junio C Hamano · lore

[PATCH 3/4] diff -p: squelch "diff --git" header for stat-dirty paths

The plumbing "diff" commands look at the working tree files without refreshing the index themselves for performance reasons (the calling script is expected to do that upfront just once, before calling one or more of them). In the early days of git, they showed the "diff --git" header before they actually ask the xdiff machinery to produce patches, and ended up showing only these headers if the real contents are the same and the difference they noticed was only because the stat info cached in the index did not match that of the working tree. It was too late for the implementation to take the header that it already emitted back.

But 3e97c7c (No diff -b/-w output for all-whitespace changes, 2009-11-19) introduced necessary logic to keep the meta-information headers in a strbuf and delay their output until the xdiff machinery noticed actual changes. This was primarily in order to generate patches that ignore whitespaces. When operating under "-w" mode, we wouldn't know if the header is needed until we actually look at the resulting patch, so it was a sensible thing to do, but we did not realize that the same reasoning applies to stat-dirty paths.

Later, 296c6bb2 generalized this machinery and introduced must_show_header toggle. This is turned on when the header must be shown even when there is no patch to be produced, e.g. only the mode was changed, or the path was renamed, without changing the contents. However, when it did so, it still kept the special case for the "-w" mode, which meant that the plumbing would keep showing these phantom changes.

This corrects this historical inconsistency by allowing the plumbing to omit paths that are only stat-dirty from its output in the same way as it handles whitespace only changes under "-w" option.

The change in the behaviour can be seen in the updated test.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 diff.c                  |    2 +-
 t/t4011-diff-symlink.sh |    5 +----
 2 files changed, 2 insertions(+), 5 deletions(-)
Show changes to 2 files +2 −5

diff.c, t/t4011-diff-symlink.sh

diff --git a/diff.c b/diff.c
index c8e4366..0ecbf32 100644
--- a/diff.c
+++ b/diff.c
@@ -1972,7 +1972,7 @@ static void builtin_diff(const char *name_a,
 		struct emit_callback ecbdata;
 		const struct userdiff_funcname *pe;
 
-		if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS) || must_show_header) {
+		if (must_show_header) {
 			fprintf(o->file, "%s", header.buf);
 			strbuf_reset(&header);
 		}
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 6097e19..7547c6d 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -67,10 +67,7 @@ test_expect_success SYMLINKS 'diff removed symlink and file' '
 '
 
 test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' '
-	cat >expected <<-\EOF &&
-	diff --git a/frotz b/frotz
-	diff --git a/nitfol b/nitfol
-	EOF
+	>expected &&
 	sleep 3 &&
 	rm -f frotz &&
 	echo xyzzy >nitfol3 &&
-- 
1.7.9.2.350.g74d65
Junio C Hamano· Mar 1, 2012, 02:14 UTC · re: Junio C Hamano · lore

[PATCH 4/4] diff --ignore-mode-change

It may be useful if you can view the diff while ignoring changes that only touch the executable bit without changing the contents (e.g. a careless imports of vendor drop from tarballs), and this teaches the diff machinery to ignore them. A change that touches both contents and the executable bit is not ignored.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 diff.c |   29 ++++++++++++++++++++++++++++-
 diff.h |    1 +
 2 files changed, 29 insertions(+), 1 deletion(-)
Show changes to 2 files +29 −1

diff.c, diff.h

diff --git a/diff.c b/diff.c
index 0ecbf32..62fefba 100644
--- a/diff.c
+++ b/diff.c
@@ -1920,7 +1920,8 @@ static void builtin_diff(const char *name_a,
 		if (one->mode != two->mode) {
 			strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, set, one->mode, reset);
 			strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, set, two->mode, reset);
-			must_show_header = 1;
+			if (!DIFF_OPT_TST(o, IGNORE_MODE_CHANGE))
+				must_show_header = 1;
 		}
 		if (xfrm_msg)
 			strbuf_addstr(&header, xfrm_msg);
@@ -3166,6 +3167,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 	}
 	else if (!strcmp(arg, "--no-renames"))
 		options->detect_rename = 0;
+	else if (!strcmp(arg, "--ignore-mode-change"))
+		DIFF_OPT_SET(options, IGNORE_MODE_CHANGE);
 	else if (!strcmp(arg, "--relative"))
 		DIFF_OPT_SET(options, RELATIVE_NAME);
 	else if (!prefixcmp(arg, "--relative=")) {
@@ -4191,10 +4194,34 @@ void diffcore_fix_diff_index(struct diff_options *options)
 	qsort(q->queue, q->nr, sizeof(q->queue[0]), diffnamecmp);
 }
 
+static void diffcore_ignore_mode_change(struct diff_options *diffopt)
+{
+	int i;
+	struct diff_queue_struct *q = &diff_queued_diff;
+	struct diff_queue_struct outq;
+	DIFF_QUEUE_CLEAR(&outq);
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+
+		if (DIFF_FILE_VALID(p->one) &&
+		    DIFF_FILE_VALID(p->two) &&
+		    (p->one->sha1_valid && p->two->sha1_valid) &&
+		    !hashcmp(p->one->sha1, p->two->sha1))
+			diff_free_filepair(p); /* skip this */
+		else
+			diff_q(&outq, p);
+	}
+	free(q->queue);
+	*q = outq;
+}
+
 void diffcore_std(struct diff_options *options)
 {
 	if (options->skip_stat_unmatch)
 		diffcore_skip_stat_unmatch(options);
+	if (DIFF_OPT_TST(options, IGNORE_MODE_CHANGE))
+		diffcore_ignore_mode_change(options);
 	if (!options->found_follow) {
 		/* See try_to_follow_renames() in tree-diff.c */
 		if (options->break_opt != -1)
diff --git a/diff.h b/diff.h
index 0083d92..9f10f72 100644
--- a/diff.h
+++ b/diff.h
@@ -78,6 +78,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
 #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
 #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
+#define DIFF_OPT_IGNORE_MODE_CHANGE  (1 << 30)
 
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
-- 
1.7.9.2.350.g74d65

← back to recent threads