Code

apply: Remove the quick rejection test
authorBjörn Gustavsson <bgustavsson@gmail.com>
Sat, 6 Mar 2010 14:30:29 +0000 (15:30 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 7 Mar 2010 07:53:00 +0000 (23:53 -0800)
In the next commit, we will make it possible for blank context
lines to match beyond the end of the file. That means that a hunk
with a preimage that has more lines than present in the file may
be possible to successfully apply. Therefore, we must remove
the quick rejection test in find_pos().

find_pos() will already work correctly without the quick
rejection test, but that might not be obvious. Therefore,
comment the test for handling out-of-range line numbers in
find_pos() and cast the "line" variable to the same (unsigned)
type as img->nr.

What are performance implications of removing the quick
rejection test?

It can only help "git apply" to reject a patch faster. For example,
if I have a file with one million lines and a patch that removes
slightly more than 50 percent of the lines and try to apply that
patch twice, the second attempt will fail slightly faster
with the test than without (based on actual measurements).

However, there is the pathological case of a patch with many
more context lines than the default three, and applying that patch
using "git apply -C1". Without the rejection test, the running
time will be roughly proportional to the number of context lines
times the size of the file. That could be handled by writing
a more complicated rejection test (it would have to count the
number of blanks at the end of the preimage), but I don't find
that worth doing until there is a real-world use case that
would benfit from it.

It would be possible to keep the quick rejection test if
--whitespace=fix is not given, but I don't like that from
a testing point of view.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-apply.c
t/t4104-apply-boundary.sh

index fc6c7083f76043168bf8024a9fa50b7f767b99bc..9641a6479a5325d5d1680d5e313a23e5d536f822 100644 (file)
@@ -1997,11 +1997,8 @@ static int find_pos(struct image *img,
        unsigned long backwards, forwards, try;
        int backwards_lno, forwards_lno, try_lno;
 
-       if (preimage->nr > img->nr)
-               return -1;
-
        /*
-        * If match_begining or match_end is specified, there is no
+        * If match_beginning or match_end is specified, there is no
         * point starting from a wrong line that will never match and
         * wander around and wait for a match at the specified end.
         */
@@ -2010,7 +2007,12 @@ static int find_pos(struct image *img,
        else if (match_end)
                line = img->nr - preimage->nr;
 
-       if (line > img->nr)
+       /*
+        * Because the comparison is unsigned, the following test
+        * will also take care of a negative line number that can
+        * result when match_end and preimage is larger than the target.
+        */
+       if ((size_t) line > img->nr)
                line = img->nr;
 
        try = 0;
index 0e3ce3611d9e83ab290ce034f2439961864ce30a..c617c2a33d8e8ac1dc7e049f9056ca6025fbf852 100755 (executable)
@@ -134,4 +134,13 @@ test_expect_success 'two lines' '
 
 '
 
+test_expect_success 'apply patch with 3 context lines matching at end' '
+       { echo a; echo b; echo c; echo d; } >file &&
+       git add file &&
+       echo e >>file &&
+       git diff >patch &&
+       >file &&
+       test_must_fail git apply patch
+'
+
 test_done