t6044: recursive can silently incorporate dirty changes in a merge
[gitweb.git] / t / t4015-diff-whitespace.sh
index 289806d0c7eb02e0acc5244df5e3999d45b4e085..6c9a93b734542cc17fa1591525dc3c49019b77fe 100755 (executable)
@@ -155,7 +155,7 @@ test_expect_success 'ignore-blank-lines: only new lines' '
 " >x &&
        git diff --ignore-blank-lines >out &&
        >expect &&
-       test_cmp out expect
+       test_cmp expect out
 '
 
 test_expect_success 'ignore-blank-lines: only new lines with space' '
@@ -165,7 +165,7 @@ test_expect_success 'ignore-blank-lines: only new lines with space' '
  " >x &&
        git diff -w --ignore-blank-lines >out &&
        >expect &&
-       test_cmp out expect
+       test_cmp expect out
 '
 
 test_expect_success 'ignore-blank-lines: after change' '
@@ -802,7 +802,6 @@ test_expect_success 'combined diff with autocrlf conversion' '
 # Start testing the colored format for whitespace checks
 
 test_expect_success 'setup diff colors' '
-       git config color.diff always &&
        git config color.diff.plain normal &&
        git config color.diff.meta bold &&
        git config color.diff.frag cyan &&
@@ -821,7 +820,7 @@ test_expect_success 'diff that introduces a line with only tabs' '
        echo "test" >x &&
        git commit -m "initial" x &&
        echo "{NTN}" | tr "NT" "\n\t" >>x &&
-       git -c color.diff=always diff | test_decode_color >current &&
+       git diff --color | test_decode_color >current &&
 
        cat >expected <<-\EOF &&
        <BOLD>diff --git a/x b/x<RESET>
@@ -851,7 +850,7 @@ test_expect_success 'diff that introduces and removes ws breakages' '
                echo "2. and a new line "
        } >x &&
 
-       git -c color.diff=always diff |
+       git diff --color |
        test_decode_color >current &&
 
        cat >expected <<-\EOF &&
@@ -923,15 +922,15 @@ test_expect_success 'ws-error-highlight test setup' '
 
 test_expect_success 'test --ws-error-highlight option' '
 
-       git -c color.diff=always diff --ws-error-highlight=default,old |
+       git diff --color --ws-error-highlight=default,old |
        test_decode_color >current &&
        test_cmp expect.default-old current &&
 
-       git -c color.diff=always diff --ws-error-highlight=all |
+       git diff --color --ws-error-highlight=all |
        test_decode_color >current &&
        test_cmp expect.all current &&
 
-       git -c color.diff=always diff --ws-error-highlight=none |
+       git diff --color --ws-error-highlight=none |
        test_decode_color >current &&
        test_cmp expect.none current
 
@@ -939,15 +938,15 @@ test_expect_success 'test --ws-error-highlight option' '
 
 test_expect_success 'test diff.wsErrorHighlight config' '
 
-       git -c color.diff=always -c diff.wsErrorHighlight=default,old diff |
+       git -c diff.wsErrorHighlight=default,old diff --color |
        test_decode_color >current &&
        test_cmp expect.default-old current &&
 
-       git -c color.diff=always -c diff.wsErrorHighlight=all diff |
+       git -c diff.wsErrorHighlight=all diff --color |
        test_decode_color >current &&
        test_cmp expect.all current &&
 
-       git -c color.diff=always -c diff.wsErrorHighlight=none diff |
+       git -c diff.wsErrorHighlight=none diff --color |
        test_decode_color >current &&
        test_cmp expect.none current
 
@@ -955,21 +954,730 @@ test_expect_success 'test diff.wsErrorHighlight config' '
 
 test_expect_success 'option overrides diff.wsErrorHighlight' '
 
-       git -c color.diff=always -c diff.wsErrorHighlight=none \
-               diff --ws-error-highlight=default,old |
+       git -c diff.wsErrorHighlight=none \
+               diff --color --ws-error-highlight=default,old |
        test_decode_color >current &&
        test_cmp expect.default-old current &&
 
-       git -c color.diff=always -c diff.wsErrorHighlight=default \
-               diff --ws-error-highlight=all |
+       git -c diff.wsErrorHighlight=default \
+               diff --color --ws-error-highlight=all |
        test_decode_color >current &&
        test_cmp expect.all current &&
 
-       git -c color.diff=always -c diff.wsErrorHighlight=all \
-               diff --ws-error-highlight=none |
+       git -c diff.wsErrorHighlight=all \
+               diff --color --ws-error-highlight=none |
        test_decode_color >current &&
        test_cmp expect.none current
 
 '
 
+test_expect_success 'detect moved code, complete file' '
+       git reset --hard &&
+       cat <<-\EOF >test.c &&
+       #include<stdio.h>
+       main()
+       {
+       printf("Hello World");
+       }
+       EOF
+       git add test.c &&
+       git commit -m "add main function" &&
+       git mv test.c main.c &&
+       test_config color.diff.oldMoved "normal red" &&
+       test_config color.diff.newMoved "normal green" &&
+       git diff HEAD --color-moved=zebra --color --no-renames | test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/main.c b/main.c<RESET>
+       <BOLD>new file mode 100644<RESET>
+       <BOLD>index 0000000..a986c57<RESET>
+       <BOLD>--- /dev/null<RESET>
+       <BOLD>+++ b/main.c<RESET>
+       <CYAN>@@ -0,0 +1,5 @@<RESET>
+       <BGREEN>+<RESET><BGREEN>#include<stdio.h><RESET>
+       <BGREEN>+<RESET><BGREEN>main()<RESET>
+       <BGREEN>+<RESET><BGREEN>{<RESET>
+       <BGREEN>+<RESET><BGREEN>printf("Hello World");<RESET>
+       <BGREEN>+<RESET><BGREEN>}<RESET>
+       <BOLD>diff --git a/test.c b/test.c<RESET>
+       <BOLD>deleted file mode 100644<RESET>
+       <BOLD>index a986c57..0000000<RESET>
+       <BOLD>--- a/test.c<RESET>
+       <BOLD>+++ /dev/null<RESET>
+       <CYAN>@@ -1,5 +0,0 @@<RESET>
+       <BRED>-#include<stdio.h><RESET>
+       <BRED>-main()<RESET>
+       <BRED>-{<RESET>
+       <BRED>-printf("Hello World");<RESET>
+       <BRED>-}<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'detect malicious moved code, inside file' '
+       test_config color.diff.oldMoved "normal red" &&
+       test_config color.diff.newMoved "normal green" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       git reset --hard &&
+       cat <<-\EOF >main.c &&
+               #include<stdio.h>
+               int stuff()
+               {
+                       printf("Hello ");
+                       printf("World\n");
+               }
+
+               int secure_foo(struct user *u)
+               {
+                       if (!u->is_allowed_foo)
+                               return;
+                       foo(u);
+               }
+
+               int main()
+               {
+                       foo();
+               }
+       EOF
+       cat <<-\EOF >test.c &&
+               #include<stdio.h>
+               int bar()
+               {
+                       printf("Hello World, but different\n");
+               }
+
+               int another_function()
+               {
+                       bar();
+               }
+       EOF
+       git add main.c test.c &&
+       git commit -m "add main and test file" &&
+       cat <<-\EOF >main.c &&
+               #include<stdio.h>
+               int stuff()
+               {
+                       printf("Hello ");
+                       printf("World\n");
+               }
+
+               int main()
+               {
+                       foo();
+               }
+       EOF
+       cat <<-\EOF >test.c &&
+               #include<stdio.h>
+               int bar()
+               {
+                       printf("Hello World, but different\n");
+               }
+
+               int secure_foo(struct user *u)
+               {
+                       foo(u);
+                       if (!u->is_allowed_foo)
+                               return;
+               }
+
+               int another_function()
+               {
+                       bar();
+               }
+       EOF
+       git diff HEAD --no-renames --color-moved=zebra --color | test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/main.c b/main.c<RESET>
+       <BOLD>index 27a619c..7cf9336 100644<RESET>
+       <BOLD>--- a/main.c<RESET>
+       <BOLD>+++ b/main.c<RESET>
+       <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+        printf("World\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BRED>-int secure_foo(struct user *u)<RESET>
+       <BRED>-{<RESET>
+       <BLUE>-if (!u->is_allowed_foo)<RESET>
+       <BLUE>-return;<RESET>
+       <RED>-foo(u);<RESET>
+       <RED>-}<RESET>
+       <RED>-<RESET>
+        int main()<RESET>
+        {<RESET>
+        foo();<RESET>
+       <BOLD>diff --git a/test.c b/test.c<RESET>
+       <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+       <BOLD>--- a/test.c<RESET>
+       <BOLD>+++ b/test.c<RESET>
+       <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+        printf("Hello World, but different\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+       <BGREEN>+<RESET><BGREEN>{<RESET>
+       <GREEN>+<RESET><GREEN>foo(u);<RESET>
+       <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+       <BGREEN>+<RESET><BGREEN>return;<RESET>
+       <GREEN>+<RESET><GREEN>}<RESET>
+       <GREEN>+<RESET>
+        int another_function()<RESET>
+        {<RESET>
+        bar();<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'plain moved code, inside file' '
+       test_config color.diff.oldMoved "normal red" &&
+       test_config color.diff.newMoved "normal green" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       # needs previous test as setup
+       git diff HEAD --no-renames --color-moved=plain --color | test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/main.c b/main.c<RESET>
+       <BOLD>index 27a619c..7cf9336 100644<RESET>
+       <BOLD>--- a/main.c<RESET>
+       <BOLD>+++ b/main.c<RESET>
+       <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+        printf("World\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BRED>-int secure_foo(struct user *u)<RESET>
+       <BRED>-{<RESET>
+       <BRED>-if (!u->is_allowed_foo)<RESET>
+       <BRED>-return;<RESET>
+       <BRED>-foo(u);<RESET>
+       <BRED>-}<RESET>
+       <BRED>-<RESET>
+        int main()<RESET>
+        {<RESET>
+        foo();<RESET>
+       <BOLD>diff --git a/test.c b/test.c<RESET>
+       <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+       <BOLD>--- a/test.c<RESET>
+       <BOLD>+++ b/test.c<RESET>
+       <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+        printf("Hello World, but different\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+       <BGREEN>+<RESET><BGREEN>{<RESET>
+       <BGREEN>+<RESET><BGREEN>foo(u);<RESET>
+       <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+       <BGREEN>+<RESET><BGREEN>return;<RESET>
+       <BGREEN>+<RESET><BGREEN>}<RESET>
+       <BGREEN>+<RESET>
+        int another_function()<RESET>
+        {<RESET>
+        bar();<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
+       git reset --hard &&
+       cat <<-\EOF >lines.txt &&
+               long line 1
+               long line 2
+               long line 3
+               line 4
+               line 5
+               line 6
+               line 7
+               line 8
+               line 9
+               line 10
+               line 11
+               line 12
+               line 13
+               long line 14
+               long line 15
+               long line 16
+       EOF
+       git add lines.txt &&
+       git commit -m "add poetry" &&
+       cat <<-\EOF >lines.txt &&
+               line 4
+               line 5
+               line 6
+               line 7
+               line 8
+               line 9
+               long line 1
+               long line 2
+               long line 3
+               long line 14
+               long line 15
+               long line 16
+               line 10
+               line 11
+               line 12
+               line 13
+       EOF
+       test_config color.diff.oldMoved "magenta" &&
+       test_config color.diff.newMoved "cyan" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       test_config color.diff.oldMovedDimmed "normal magenta" &&
+       test_config color.diff.newMovedDimmed "normal cyan" &&
+       test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+       test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+       git diff HEAD --no-renames --color-moved=dimmed_zebra --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,16 +1,16 @@<RESET>
+       <BMAGENTA>-long line 1<RESET>
+       <BMAGENTA>-long line 2<RESET>
+       <BMAGENTA>-long line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+        line 6<RESET>
+        line 7<RESET>
+        line 8<RESET>
+        line 9<RESET>
+       <BCYAN>+<RESET><BCYAN>long line 1<RESET>
+       <BCYAN>+<RESET><BCYAN>long line 2<RESET>
+       <CYAN>+<RESET><CYAN>long line 3<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+       <BYELLOW>+<RESET><BYELLOW>long line 15<RESET>
+       <BYELLOW>+<RESET><BYELLOW>long line 16<RESET>
+        line 10<RESET>
+        line 11<RESET>
+        line 12<RESET>
+        line 13<RESET>
+       <BMAGENTA>-long line 14<RESET>
+       <BMAGENTA>-long line 15<RESET>
+       <BMAGENTA>-long line 16<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'cmd option assumes configured colored-moved' '
+       test_config color.diff.oldMoved "magenta" &&
+       test_config color.diff.newMoved "cyan" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       test_config color.diff.oldMovedDimmed "normal magenta" &&
+       test_config color.diff.newMovedDimmed "normal cyan" &&
+       test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+       test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+       test_config diff.colorMoved zebra &&
+       git diff HEAD --no-renames --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,16 +1,16 @@<RESET>
+       <MAGENTA>-long line 1<RESET>
+       <MAGENTA>-long line 2<RESET>
+       <MAGENTA>-long line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+        line 6<RESET>
+        line 7<RESET>
+        line 8<RESET>
+        line 9<RESET>
+       <CYAN>+<RESET><CYAN>long line 1<RESET>
+       <CYAN>+<RESET><CYAN>long line 2<RESET>
+       <CYAN>+<RESET><CYAN>long line 3<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 15<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 16<RESET>
+        line 10<RESET>
+        line 11<RESET>
+        line 12<RESET>
+        line 13<RESET>
+       <MAGENTA>-long line 14<RESET>
+       <MAGENTA>-long line 15<RESET>
+       <MAGENTA>-long line 16<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'no effect from --color-moved with --word-diff' '
+       cat <<-\EOF >text.txt &&
+       Lorem Ipsum is simply dummy text of the printing and typesetting industry.
+       EOF
+       git add text.txt &&
+       git commit -a -m "clean state" &&
+       cat <<-\EOF >text.txt &&
+       simply Lorem Ipsum dummy is text of the typesetting and printing industry.
+       EOF
+       git diff --color-moved --word-diff >actual &&
+       git diff --word-diff >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'set up whitespace tests' '
+       git reset --hard &&
+       # Note that these lines have no leading or trailing whitespace.
+       cat <<-\EOF >lines.txt &&
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
+       long line 6
+       long line 7
+       long line 8
+       long line 9
+       EOF
+       git add lines.txt &&
+       git commit -m "add poetry" &&
+       git config color.diff.oldMoved "magenta" &&
+       git config color.diff.newMoved "cyan"
+'
+
+test_expect_success 'move detection ignoring whitespace ' '
+       q_to_tab <<-\EOF >lines.txt &&
+       Qlong line 6
+       Qlong line 7
+       Qlong line 8
+       Qchanged long line 9
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
+       EOF
+       git diff HEAD --no-renames --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <GREEN>+<RESET> <GREEN>long line 6<RESET>
+       <GREEN>+<RESET> <GREEN>long line 7<RESET>
+       <GREEN>+<RESET> <GREEN>long line 8<RESET>
+       <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <RED>-long line 6<RESET>
+       <RED>-long line 7<RESET>
+       <RED>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual &&
+
+       git diff HEAD --no-renames -w --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <CYAN>+<RESET>  <CYAN>long line 6<RESET>
+       <CYAN>+<RESET>  <CYAN>long line 7<RESET>
+       <CYAN>+<RESET>  <CYAN>long line 8<RESET>
+       <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <MAGENTA>-long line 6<RESET>
+       <MAGENTA>-long line 7<RESET>
+       <MAGENTA>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'move detection ignoring whitespace changes' '
+       git reset --hard &&
+       # Lines 6-8 have a space change, but 9 is new whitespace
+       q_to_tab <<-\EOF >lines.txt &&
+       longQline 6
+       longQline 7
+       longQline 8
+       long liQne 9
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
+       EOF
+
+       git diff HEAD --no-renames --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <GREEN>+<RESET><GREEN>long      line 6<RESET>
+       <GREEN>+<RESET><GREEN>long      line 7<RESET>
+       <GREEN>+<RESET><GREEN>long      line 8<RESET>
+       <GREEN>+<RESET><GREEN>long li   ne 9<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <RED>-long line 6<RESET>
+       <RED>-long line 7<RESET>
+       <RED>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual &&
+
+       git diff HEAD --no-renames -b --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <CYAN>+<RESET><CYAN>long        line 6<RESET>
+       <CYAN>+<RESET><CYAN>long        line 7<RESET>
+       <CYAN>+<RESET><CYAN>long        line 8<RESET>
+       <GREEN>+<RESET><GREEN>long li   ne 9<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <MAGENTA>-long line 6<RESET>
+       <MAGENTA>-long line 7<RESET>
+       <MAGENTA>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'move detection ignoring whitespace at eol' '
+       git reset --hard &&
+       # Lines 6-9 have new eol whitespace, but 9 also has it in the middle
+       q_to_tab <<-\EOF >lines.txt &&
+       long line 6Q
+       long line 7Q
+       long line 8Q
+       longQline 9Q
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
+       EOF
+
+       # avoid cluttering the output with complaints about our eol whitespace
+       test_config core.whitespace -blank-at-eol &&
+
+       git diff HEAD --no-renames --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <GREEN>+<RESET><GREEN>long line 6       <RESET>
+       <GREEN>+<RESET><GREEN>long line 7       <RESET>
+       <GREEN>+<RESET><GREEN>long line 8       <RESET>
+       <GREEN>+<RESET><GREEN>long      line 9  <RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <RED>-long line 6<RESET>
+       <RED>-long line 7<RESET>
+       <RED>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual &&
+
+       git diff HEAD --no-renames --ignore-space-at-eol --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <CYAN>+<RESET><CYAN>long line 6 <RESET>
+       <CYAN>+<RESET><CYAN>long line 7 <RESET>
+       <CYAN>+<RESET><CYAN>long line 8 <RESET>
+       <GREEN>+<RESET><GREEN>long      line 9  <RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <MAGENTA>-long line 6<RESET>
+       <MAGENTA>-long line 7<RESET>
+       <MAGENTA>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'clean up whitespace-test colors' '
+       git config --unset color.diff.oldMoved &&
+       git config --unset color.diff.newMoved
+'
+
+test_expect_success '--color-moved block at end of diff output respects MIN_ALNUM_COUNT' '
+       git reset --hard &&
+       >bar &&
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       line1
+       EOF
+       git add foo bar &&
+       git commit -m x &&
+
+       cat <<-\EOF >bar &&
+       line1
+       EOF
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       EOF
+
+       git diff HEAD --color-moved=zebra --color --no-renames |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/bar b/bar<RESET>
+       <BOLD>--- a/bar<RESET>
+       <BOLD>+++ b/bar<RESET>
+       <CYAN>@@ -0,0 +1 @@<RESET>
+       <GREEN>+<RESET><GREEN>line1<RESET>
+       <BOLD>diff --git a/foo b/foo<RESET>
+       <BOLD>--- a/foo<RESET>
+       <BOLD>+++ b/foo<RESET>
+       <CYAN>@@ -1,2 +1 @@<RESET>
+        irrelevant_line<RESET>
+       <RED>-line1<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success '--color-moved respects MIN_ALNUM_COUNT' '
+       git reset --hard &&
+       cat <<-\EOF >foo &&
+       nineteen chars 456789
+       irrelevant_line
+       twenty chars 234567890
+       EOF
+       >bar &&
+       git add foo bar &&
+       git commit -m x &&
+
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       EOF
+       cat <<-\EOF >bar &&
+       twenty chars 234567890
+       nineteen chars 456789
+       EOF
+
+       git diff HEAD --color-moved=zebra --color --no-renames |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/bar b/bar<RESET>
+       <BOLD>--- a/bar<RESET>
+       <BOLD>+++ b/bar<RESET>
+       <CYAN>@@ -0,0 +1,2 @@<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>twenty chars 234567890<RESET>
+       <GREEN>+<RESET><GREEN>nineteen chars 456789<RESET>
+       <BOLD>diff --git a/foo b/foo<RESET>
+       <BOLD>--- a/foo<RESET>
+       <BOLD>+++ b/foo<RESET>
+       <CYAN>@@ -1,3 +1 @@<RESET>
+       <RED>-nineteen chars 456789<RESET>
+        irrelevant_line<RESET>
+       <BOLD;MAGENTA>-twenty chars 234567890<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success '--color-moved treats adjacent blocks as separate for MIN_ALNUM_COUNT' '
+       git reset --hard &&
+       cat <<-\EOF >foo &&
+       7charsA
+       irrelevant_line
+       7charsB
+       7charsC
+       EOF
+       >bar &&
+       git add foo bar &&
+       git commit -m x &&
+
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       EOF
+       cat <<-\EOF >bar &&
+       7charsB
+       7charsC
+       7charsA
+       EOF
+
+       git diff HEAD --color-moved=zebra --color --no-renames | grep -v "index" | test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/bar b/bar<RESET>
+       <BOLD>--- a/bar<RESET>
+       <BOLD>+++ b/bar<RESET>
+       <CYAN>@@ -0,0 +1,3 @@<RESET>
+       <GREEN>+<RESET><GREEN>7charsB<RESET>
+       <GREEN>+<RESET><GREEN>7charsC<RESET>
+       <GREEN>+<RESET><GREEN>7charsA<RESET>
+       <BOLD>diff --git a/foo b/foo<RESET>
+       <BOLD>--- a/foo<RESET>
+       <BOLD>+++ b/foo<RESET>
+       <CYAN>@@ -1,4 +1 @@<RESET>
+       <RED>-7charsA<RESET>
+        irrelevant_line<RESET>
+       <RED>-7charsB<RESET>
+       <RED>-7charsC<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'move detection with submodules' '
+       test_create_repo bananas &&
+       echo ripe >bananas/recipe &&
+       git -C bananas add recipe &&
+       test_commit fruit &&
+       test_commit -C bananas recipe &&
+       git submodule add ./bananas &&
+       git add bananas &&
+       git commit -a -m "bananas are like a heavy library?" &&
+       echo foul >bananas/recipe &&
+       echo ripe >fruit.t &&
+
+       git diff --submodule=diff --color-moved --color >actual &&
+
+       # no move detection as the moved line is across repository boundaries.
+       test_decode_color <actual >decoded_actual &&
+       ! grep BGREEN decoded_actual &&
+       ! grep BRED decoded_actual &&
+
+       # nor did we mess with it another way
+       git diff --submodule=diff --color | test_decode_color >expect &&
+       test_cmp expect decoded_actual
+'
+
 test_done