apply: make update_pre_post_images() sanity check the given postlen
authorJunio C Hamano <gitster@pobox.com>
Fri, 16 Jan 2015 19:54:47 +0000 (11:54 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 22 Jan 2015 20:57:24 +0000 (12:57 -0800)
"git apply --whitespace=fix" used to be able to assume that fixing
errors will always reduce the size by e.g. stripping whitespaces at
the end of lines or collapsing runs of spaces into tabs at the
beginning of lines. An update to accomodate fixes that lengthens
the result by e.g. expanding leading tabs into spaces were made long
time ago but the logic miscounted the necessary space after such
whitespace fixes, leading to either under-allocation or over-usage
of already allocated space.

Illustrate this with a runtime sanity-check to protect us from
future breakage. The test was stolen from Kyle McKay who helped
to identify the problem.

Helped-by: "Kyle J. McKay" <mackyle@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/apply.c
t/t4138-apply-ws-expansion.sh [new file with mode: 0755]
index 31f873318c3f9ff020688d3ac1aab2d8b54e2757..da6fb355453c1a77d16a3ede38d295f5af8569d9 100644 (file)
@@ -2171,6 +2171,12 @@ static void update_pre_post_images(struct image *preimage,
                ctx++;
        }
 
                ctx++;
        }
 
+       if (postlen
+           ? postlen < new - postimage->buf
+           : postimage->len < new - postimage->buf)
+               die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d",
+                   (int)postlen, (int) postimage->len, (int)(new - postimage->buf));
+
        /* Fix the length of the whole thing */
        postimage->len = new - postimage->buf;
        postimage->nr -= reduced;
        /* Fix the length of the whole thing */
        postimage->len = new - postimage->buf;
        postimage->nr -= reduced;
diff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh
new file mode 100755 (executable)
index 0000000..0ffe33f
--- /dev/null
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (C) 2015 Kyle J. McKay
+#
+
+test_description='git apply test patches with whitespace expansion.'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       #
+       ## create test-N, patchN.patch, expect-N files
+       #
+
+       # test 1
+       printf "\t%s\n" 1 2 3 4 5 6 >before &&
+       printf "\t%s\n" 1 2 3 >after &&
+       printf "%64s\n" a b c >>after &&
+       printf "\t%s\n" 4 5 6 >>after &&
+       git diff --no-index before after |
+               sed -e "s/before/test-1/" -e "s/after/test-1/" >patch1.patch &&
+       printf "%64s\n" 1 2 3 4 5 6 >test-1 &&
+       printf "%64s\n" 1 2 3 a b c 4 5 6 >expect-1 &&
+
+       # test 2
+       printf "\t%s\n" a b c d e f >before &&
+       printf "\t%s\n" a b c >after &&
+       n=10 &&
+       x=1 &&
+       while test $x -lt $n
+       do
+               printf "%63s%d\n" "" $x >>after
+               x=$(( $x + 1 ))
+       done &&
+       printf "\t%s\n" d e f >>after &&
+       git diff --no-index before after |
+               sed -e "s/before/test-2/" -e "s/after/test-2/" >patch2.patch &&
+       printf "%64s\n" a b c d e f >test-2 &&
+       printf "%64s\n" a b c >expect-2 &&
+       x=1 &&
+       while test $x -lt $n
+       do
+               printf "%63s%d\n" "" $x >>expect-2
+               x=$(( $x + 1 ))
+       done &&
+       printf "%64s\n" d e f >>expect-2 &&
+
+       # test 3
+       printf "\t%s\n" a b c d e f >before &&
+       printf "\t%s\n" a b c >after &&
+       n=100 &&
+       x=0 &&
+       while test $x -lt $n
+       do
+               printf "%63s%02d\n" "" $x >>after
+               x=$(( $x + 1 ))
+       done &&
+       printf "\t%s\n" d e f >>after &&
+       git diff --no-index before after |
+       sed -e "s/before/test-3/" -e "s/after/test-3/" >patch3.patch &&
+       printf "%64s\n" a b c d e f >test-3 &&
+       printf "%64s\n" a b c >expect-3 &&
+       x=0 &&
+       while test $x -lt $n
+       do
+               printf "%63s%02d\n" "" $x >>expect-3
+               x=$(( $x + 1 ))
+       done &&
+       printf "%64s\n" d e f >>expect-3 &&
+
+       # test 4
+       >before &&
+       x=0 &&
+       while test $x -lt 50
+       do
+               printf "\t%02d\n" $x >>before
+               x=$(( $x + 1 ))
+       done &&
+       cat before >after &&
+       printf "%64s\n" a b c >>after &&
+       while test $x -lt 100
+       do
+               printf "\t%02d\n" $x >>before
+               printf "\t%02d\n" $x >>after
+               x=$(( $x + 1 ))
+       done &&
+       git diff --no-index before after |
+       sed -e "s/before/test-4/" -e "s/after/test-4/" >patch4.patch &&
+       >test-4 &&
+       x=0 &&
+       while test $x -lt 50
+       do
+               printf "%63s%02d\n" "" $x >>test-4
+               x=$(( $x + 1 ))
+       done &&
+       cat test-4 >expect-4 &&
+       printf "%64s\n" a b c >>expect-4 &&
+       while test $x -lt 100
+       do
+               printf "%63s%02d\n" "" $x >>test-4
+               printf "%63s%02d\n" "" $x >>expect-4
+               x=$(( $x + 1 ))
+       done &&
+
+       git config core.whitespace tab-in-indent,tabwidth=63 &&
+       git config apply.whitespace fix
+
+'
+
+# Note that `patch` can successfully apply all patches when run
+# with the --ignore-whitespace option.
+
+for t in 1 2 3 4
+do
+       test_expect_success 'apply with ws expansion (t=$t)' '
+               git apply patch$t.patch &&
+               test_cmp test-$t expect-$t
+       '
+done
+
+test_done