Merge branch 'jc/apply-ws-fix-expands' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 25 Feb 2015 06:10:41 +0000 (22:10 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 25 Feb 2015 06:10:41 +0000 (22:10 -0800)
"git apply --whitespace=fix" used to under-allocate the memory
when the fix resulted in a longer text than the original patch.

* jc/apply-ws-fix-expands:
  apply: count the size of postimage correctly
  apply: make update_pre_post_images() sanity check the given postlen
  apply.c: typofix

builtin/apply.c
t/t4138-apply-ws-expansion.sh [new file with mode: 0755]

index dfd7a34..c484b53 100644 (file)
@@ -2230,6 +2230,12 @@ static void update_pre_post_images(struct image *preimage,
                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;
@@ -2385,10 +2391,27 @@ static int match_fragment(struct image *img,
 
        /*
         * The hunk does not apply byte-by-byte, but the hash says
-        * it might with whitespace fuzz. We haven't been asked to
+        * it might with whitespace fuzz. We weren't asked to
         * ignore whitespace, we were asked to correct whitespace
         * errors, so let's try matching after whitespace correction.
         *
+        * While checking the preimage against the target, whitespace
+        * errors in both fixed, we count how large the corresponding
+        * postimage needs to be.  The postimage prepared by
+        * apply_one_fragment() has whitespace errors fixed on added
+        * lines already, but the common lines were propagated as-is,
+        * which may become longer when their whitespace errors are
+        * fixed.
+        */
+
+       /* First count added lines in postimage */
+       postlen = 0;
+       for (i = 0; i < postimage->nr; i++) {
+               if (!(postimage->line[i].flag & LINE_COMMON))
+                       postlen += postimage->line[i].len;
+       }
+
+       /*
         * The preimage may extend beyond the end of the file,
         * but in this loop we will only handle the part of the
         * preimage that falls within the file.
@@ -2396,7 +2419,6 @@ static int match_fragment(struct image *img,
        strbuf_init(&fixed, preimage->len + 1);
        orig = preimage->buf;
        target = img->buf + try;
-       postlen = 0;
        for (i = 0; i < preimage_limit; i++) {
                size_t oldlen = preimage->line[i].len;
                size_t tgtlen = img->line[try_lno + i].len;
@@ -2424,7 +2446,10 @@ static int match_fragment(struct image *img,
                match = (tgtfix.len == fixed.len - fixstart &&
                         !memcmp(tgtfix.buf, fixed.buf + fixstart,
                                             fixed.len - fixstart));
-               postlen += tgtfix.len;
+
+               /* Add the length if this is common with the postimage */
+               if (preimage->line[i].flag & LINE_COMMON)
+                       postlen += tgtfix.len;
 
                strbuf_release(&tgtfix);
                if (!match)
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