Hi again Stefan and Julian :-)!
On Thu, Jan 28, 2010 at 12:53:08PM +0000, Julian Foad wrote:
> > +/* Indicate in *MATCHED whether the original text of HUNK matches the patch
> > + * TARGET at its current line. Lines at FUZZ offset from start or end of
> > the
>
> "Lines within FUZZ lines of the start or end ..."
Fixed! For all linguistic issues I trust everyone but me.
> > + * hunk will always match. When this function returns, neither
> > + * TARGET->CURRENT_LINE nor the file offset in the target file will have
> > + * changed. HUNK->ORIGINAL_TEXT will be reset. Do temporary allocations in
> > + * POOL. */
> > static svn_error_t *
> > match_hunk(svn_boolean_t *matched, patch_target_t *target,
> > - const svn_hunk_t *hunk, apr_pool_t *pool)
> > + const svn_hunk_t *hunk, int fuzz, apr_pool_t *pool)
>
>
> > /* Scan lines of TARGET for a match of the original text of HUNK,
> > * up to but not including the specified UPPER_LINE.
> > + * Use fuzz factor FUZZ everywhere if possible.
>
> I would drop the "if possible". It implies that this function decides
> not to use the fuzz factor in some cases.
Fixed!
> > /* Copy HUNK_TEXT into TARGET, line by line, such that the line filter
> > * and transformation callbacks set on HUNK_TEXT by the diff parsing
> > * code in libsvn_diff will trigger. ABSPATH is the absolute path to the
> > - * file underlying TARGET. */
> > + * file underlying TARGET. Do not copy the lines that is within FUZZ offset
> > + * from the beginning or end of hunk unless NR_OF_LINES is set to 0. If
> > + * NR_OF_LINES is non-zero, it represents the number of lines in
> > HUNK_TEXT. */
> > static svn_error_t *
> > copy_hunk_text(svn_stream_t *hunk_text, svn_stream_t *target,
> > - const char *abspath, apr_pool_t *scratch_pool)
> > + const char *abspath, int fuzz, int nr_of_lines,
> > + apr_pool_t *scratch_pool)
>
> This looks odd. Before now, the function worked without needing to be
> told the number of lines in the hunk, and now it needs to be told, but
> that means it now has two ways of knowing. Looking at the
> implementation, I can see why you want this, but from an interface point
> of view it would be better if it could still work without being told. (I
> can envisage a way of doing that - by keeping the last FUZZ lines that
> have been read in a circular buffer before writing them out, so that at
> the end of the input you can discard the FUZZ lines that are in the
> buffer.)
That's a nice solution. Maybe I'll do it later but for now I'll do it as
simple as possible.
> But let's say you want to keep the simple implementation that needs to
> know NR_OF_LINES in advance. Why have two ways to request non-fuzzy
> operation: the obvious way (fuzz=0) or the special way (nr_of_lines=0)?
> I don't think you need that special exception "unless NR_OF_LINES is 0".
> Can you keep the rules simple and delete that special case?
So obvious... If no fuzz, then we can copy all lines. Fixed!
> > Index: subversion/libsvn_diff/parse-diff.c
> > ===================================================================
> > --- subversion/libsvn_diff/parse-diff.c (revision 903978)
> > +++ subversion/libsvn_diff/parse-diff.c (arbetskopia)
> > @@ -278,20 +281,45 @@
> > }
> >
> > c = line->data[0];
> > - if (original_lines > 0 && (c == ' ' || c == '-'))
> > + if (original_lines > 0 && c == ' ' )
> > {
> > hunk_seen = TRUE;
> > original_lines--;
> > + if (changed_line_seen)
> > + trailing_context++;
> > + else
> > + leading_context++;
> > }
> > + else if (original_lines > 0 && c == '-')
> > + {
> > + hunk_seen = TRUE;
> > + original_lines--;
> > + changed_line_seen = TRUE;
> > +
> > + /* A hunk may have context in the middle. We only want the
> > + last lines of context. */
> > + if (trailing_context > 0)
> > + trailing_context = 0;
> > + }
> > else if (c == '+')
> > {
> > hunk_seen = TRUE;
> > + changed_line_seen = TRUE;
> > +
> > + /* A hunk may have context in the middle. We only want the
> > + last lines of context. */
> > + if (trailing_context > 0)
> > + trailing_context = 0;
> > }
> > /* Tolerate chopped leading spaces on empty lines. */
> > else if (original_lines > 0 && ! eof && line->len == 0)
> > {
> > hunk_seen = TRUE;
> > original_lines--;
> > + if (changed_line_seen)
> > + trailing_context++;
> > + else
> > + leading_context++;
> > }
>
> This fourth block would neatly combine with the first block:
>
> if (original_lines > 0 && (c == ' ' || (! eof && line->len == 0))
> { ... }
Yeah, that piece of code had some duplicate code parts. I grouped the
'-' and '+' cases together too. That increased readability a bit.
And now Stefans suggestions.
On Thu, Jan 28, 2010 at 01:46:56PM +0100, Stefan Sperling wrote:
> On Thu, Jan 28, 2010 at 01:55:08PM +0100, Daniel Näslund wrote:
> > Index: subversion/libsvn_diff/parse-diff.c
> > svn_boolean_t lines_matched;
> > @@ -630,9 +635,21 @@
> > eol_str, FALSE,
> > target->keywords, FALSE,
> > iterpool));
> > + lines_read++;
> > +
> > +
> You're adding 2 empty lines here on purpose?
Oups, fixed.
> > @@ -833,13 +853,17 @@
> > /* Copy HUNK_TEXT into TARGET, line by line, such that the line filter
> > * and transformation callbacks set on HUNK_TEXT by the diff parsing
> > * code in libsvn_diff will trigger. ABSPATH is the absolute path to the
> > - * file underlying TARGET. */
> > + * file underlying TARGET. Do not copy the lines that is within FUZZ offset
> > + * from the beginning or end of hunk unless NR_OF_LINES is set to 0. If
> > + * NR_OF_LINES is non-zero, it represents the number of lines in
> > HUNK_TEXT. */
>
> Maybe rename the function to copy_hunk_lines and call 'nr_of_lines'
> just 'n'?
Fixed! We're copying lines so saying that increases readability, I
think and N for number of is well established.
> > @@ -895,15 +936,20 @@
> > +apply_one_hunk(patch_target_t *target, hunk_info_t *hi,
> > + apr_pool_t *pool)
> > {
> > + svn_linenum_t end_line_nr;
>
> Call it last_line ?
Fixed!
Thanks for your feedback!
A new log msg:
[[[
Fix #3460 - svn patch is not fuzzy when applying unidiffs. Refactor some
parts of the hunk parsing while at it.
* subversion/include/private/svn_diff_private.h
(svn_hunk_t): Add fields leading_context and trailing_context. They are
used for determining if there is enough context to apply a patch
with fuzz.
* subversion/libsvn_diff/parse-diff.c
(parse_next_hunk): Count number of lines of context at start and end
of hunk and save the information in hunk. Refactored some if
statements for increased readability.
* subversion/tests/cmdline/patch_tests.py
(patch_with_fuzz): New.
(test_list): Add new test.
* subversion/libsvn_client/patch.c
(hunk_info_t): Add field fuzz.
(match_hunk): Add new parameter fuzz. Record nr of lines read and
ignore the ones at the beginning and end of the hunk that is fuzzy.
Use leading_context and trailing_context to ignore the cases when there
isn't enough context to do fuzzy patching.
(scan_for_match): Add new parameter fuzz. Call match_hunk() with fuzz.
(get_hunk_info): Add new parameter fuzz. Call scan_for_match() with
fuzz. Save the used fuzz in hi to be used when the hunk should be
copied to the target.
(copy_hunk_text): Rename this to ..
(copy_hunk_lines): .. And add n and fuzz parameter. Record
read_lines and only copy those who are not within the fuzz limit. They
have already been or will be copied from the target. If fuzz is zero
we ignore n and copy all lines.
(apply_one_hunk): Adjust lines copied to the target to include the
context lines at the beginning of the hunk who are fuzzy. Adjust the
current_line of the target to point to the last line of the hunk
minus fuzz.
(apply_one_patch): Try to call get_hunk_info() with no fuzz. If we get
no matching line try with fuzz 1 and if that fails try with fuzz 2.
Patch by: Daniel Näslund <daniel{_AT_}longitudo.com>
Review by: julianfoad
stsp
]]]
Index: subversion/libsvn_diff/parse-diff.c
===================================================================
--- subversion/libsvn_diff/parse-diff.c (revision 904086)
+++ subversion/libsvn_diff/parse-diff.c (arbetskopia)
@@ -224,6 +224,9 @@
svn_stream_t *original_text;
svn_stream_t *modified_text;
svn_linenum_t original_lines;
+ svn_linenum_t leading_context = 0;
+ svn_linenum_t trailing_context = 0;
+ svn_boolean_t changed_line_seen = FALSE;
apr_pool_t *iterpool;
if (apr_file_eof(patch->patch_file) == APR_EOF)
@@ -278,21 +281,29 @@
}
c = line->data[0];
- if (original_lines > 0 && (c == ' ' || c == '-'))
+ /* Tolerate chopped leading spaces on empty lines. */
+ if (original_lines > 0 && (c == ' ' || (! eof && line->len == 0)))
{
hunk_seen = TRUE;
original_lines--;
+ if (changed_line_seen)
+ trailing_context++;
+ else
+ leading_context++;
}
- else if (c == '+')
+ else if (c == '+' || c == '-')
{
hunk_seen = TRUE;
+ changed_line_seen = TRUE;
+
+ /* A hunk may have context in the middle. We only want the
+ last lines of context. */
+ if (trailing_context > 0)
+ trailing_context = 0;
+
+ if (original_lines > 0 && c == '-')
+ original_lines--;
}
- /* Tolerate chopped leading spaces on empty lines. */
- else if (original_lines > 0 && ! eof && line->len == 0)
- {
- hunk_seen = TRUE;
- original_lines--;
- }
else
{
in_hunk = FALSE;
@@ -363,6 +374,8 @@
(*hunk)->diff_text = diff_text;
(*hunk)->original_text = original_text;
(*hunk)->modified_text = modified_text;
+ (*hunk)->leading_context = leading_context;
+ (*hunk)->trailing_context = trailing_context;
}
else
/* Something went wrong, just discard the result. */
Index: subversion/tests/cmdline/patch_tests.py
===================================================================
--- subversion/tests/cmdline/patch_tests.py (revision 904086)
+++ subversion/tests/cmdline/patch_tests.py (arbetskopia)
@@ -967,7 +967,137 @@
None, # expected err
1, # check-props
1) # dry-run
+def patch_with_fuzz(sbox):
+ "apply a patch with fuzz"
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ patch_file_path = tempfile.mkstemp(dir=os.path.abspath(svntest.main.temp_dir))[1]
+
+ mu_path = os.path.join(wc_dir, 'A', 'mu')
+
+ # We have replaced a couple of lines to cause fuzz. Those lines contains
+ # the word fuzz
+ mu_contents = [
+ "Line replaced for fuzz = 1\n",
+ "\n",
+ "We wish to congratulate you over your email success in our computer\n",
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+ "in which email addresses were used. All participants were selected\n",
+ "through a computer ballot system drawn from over 100,000 company\n",
+ "and 50,000,000 individual email addresses from all over the world.\n",
+ "Line replaced for fuzz = 2 with only the second context line changed\n",
+ "Your email address drew and have won the sum of 750,000 Euros\n",
+ "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+ "file with\n",
+ " REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+ " WINNING NUMBER : 14-17-24-34-37-45-16\n",
+ " BATCH NUMBERS :\n",
+ " EULO/1007/444/606/08;\n",
+ " SERIAL NUMBER: 45327\n",
+ "and PROMOTION DATE: 13th June. 2009\n",
+ "\n",
+ "To claim your winning prize, you are to contact the appointed\n",
+ "agent below as soon as possible for the immediate release of your\n",
+ "winnings with the below details.\n",
+ "\n",
+ "Line replaced for fuzz = 2\n",
+ "Line replaced for fuzz = 2\n",
+ ]
+
+ # Set mu contents
+ svntest.main.file_write(mu_path, ''.join(mu_contents))
+ expected_output = svntest.wc.State(wc_dir, {
+ 'A/mu' : Item(verb='Sending'),
+ })
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ expected_status.tweak('A/mu', wc_rev=2)
+ svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+ expected_status, None, wc_dir)
+
+ unidiff_patch = [
+ "Index: mu\n",
+ "===================================================================\n",
+ "--- A/mu\t(revision 0)\n",
+ "+++ A/mu\t(revision 0)\n",
+ "@@ -1,6 +1,7 @@\n",
+ " Dear internet user,\n",
+ " \n",
+ " We wish to congratulate you over your email success in our computer\n",
+ "+A new line here\n",
+ " Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+ " in which email addresses were used. All participants were selected\n",
+ " through a computer ballot system drawn from over 100,000 company\n",
+ "@@ -7,6 +8,7 @@\n",
+ " and 50,000,000 individual email addresses from all over the world.\n",
+ " \n",
+ " Your email address drew and have won the sum of 750,000 Euros\n",
+ "+Another new line\n",
+ " ( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+ " file with\n",
+ " REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+ "@@ -19,6 +20,7 @@\n",
+ " To claim your winning prize, you are to contact the appointed\n",
+ " agent below as soon as possible for the immediate release of your\n",
+ " winnings with the below details.\n",
+ "+A third new line\n",
+ " \n",
+ " Again, we wish to congratulate you over your email success in our\n"
+ " computer Balloting.\n"
+ ]
+
+ svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+ mu_contents = [
+ "Line replaced for fuzz = 1\n",
+ "\n",
+ "We wish to congratulate you over your email success in our computer\n",
+ "A new line here\n",
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+ "in which email addresses were used. All participants were selected\n",
+ "through a computer ballot system drawn from over 100,000 company\n",
+ "and 50,000,000 individual email addresses from all over the world.\n",
+ "Line replaced for fuzz = 2 with only the second context line changed\n",
+ "Your email address drew and have won the sum of 750,000 Euros\n",
+ "Another new line\n",
+ "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+ "file with\n",
+ " REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+ " WINNING NUMBER : 14-17-24-34-37-45-16\n",
+ " BATCH NUMBERS :\n",
+ " EULO/1007/444/606/08;\n",
+ " SERIAL NUMBER: 45327\n",
+ "and PROMOTION DATE: 13th June. 2009\n",
+ "\n",
+ "To claim your winning prize, you are to contact the appointed\n",
+ "agent below as soon as possible for the immediate release of your\n",
+ "winnings with the below details.\n",
+ "A third new line\n",
+ "\n",
+ "Line replaced for fuzz = 2\n",
+ "Line replaced for fuzz = 2\n",
+ ]
+
+ expected_output = [
+ 'U %s\n' % os.path.join(wc_dir, 'A', 'mu'),
+ ]
+ expected_disk = svntest.main.greek_state.copy()
+ expected_disk.tweak('A/mu', contents=''.join(mu_contents))
+
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ expected_status.tweak('A/mu', status='M ', wc_rev=2)
+
+ expected_skip = wc.State('', { })
+
+ svntest.actions.run_and_verify_patch(wc_dir, os.path.abspath(patch_file_path),
+ expected_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, # expected err
+ 1, # check-props
+ 1) # dry-run
+
def patch_keywords(sbox):
"apply a patch containing keywords"
@@ -1042,6 +1172,7 @@
patch_add_new_dir,
patch_reject,
patch_keywords,
+ patch_with_fuzz,
]
if __name__ == '__main__':
Index: subversion/include/private/svn_diff_private.h
===================================================================
--- subversion/include/private/svn_diff_private.h (revision 904086)
+++ subversion/include/private/svn_diff_private.h (arbetskopia)
@@ -81,6 +81,12 @@
svn_linenum_t original_length;
svn_linenum_t modified_start;
svn_linenum_t modified_length;
+
+ /* Number of lines starting with ' ' before first '+' or '-'. */
+ svn_linenum_t leading_context;
+
+ /* Number of lines starting with ' ' after last '+' or '-'. */
+ svn_linenum_t trailing_context;
} svn_hunk_t;
/* Data type to manage parsing of patches. */
Index: subversion/libsvn_client/patch.c
===================================================================
--- subversion/libsvn_client/patch.c (revision 904086)
+++ subversion/libsvn_client/patch.c (arbetskopia)
@@ -52,6 +52,10 @@
/* Whether this hunk has been rejected. */
svn_boolean_t rejected;
+
+ /* The fuzz factor used when matching this hunk, i.e. how many
+ * lines of leading and trailing context to ignore during matching. */
+ int fuzz;
} hunk_info_t;
typedef struct {
@@ -588,18 +592,19 @@
return SVN_NO_ERROR;
}
-/* Indicate in *MATCHED whether the original text of HUNK
- * matches the patch TARGET at its current line.
- * When this function returns, neither TARGET->CURRENT_LINE nor the
- * file offset in the target file will have changed.
- * HUNK->ORIGINAL_TEXT will be reset.
- * Do temporary allocations in POOL. */
+/* Indicate in *MATCHED whether the original text of HUNK matches the patch
+ * TARGET at its current line. Lines within FUZZ lines of the start or end
+ * of hunk will always match. When this function returns, neither
+ * TARGET->CURRENT_LINE nor the file offset in the target file will have
+ * changed. HUNK->ORIGINAL_TEXT will be reset. Do temporary allocations in
+ * POOL. */
static svn_error_t *
match_hunk(svn_boolean_t *matched, patch_target_t *target,
- const svn_hunk_t *hunk, apr_pool_t *pool)
+ const svn_hunk_t *hunk, int fuzz, apr_pool_t *pool)
{
svn_stringbuf_t *hunk_line;
const char *target_line;
+ svn_linenum_t lines_read = 0;
svn_linenum_t saved_line;
svn_boolean_t hunk_eof;
svn_boolean_t lines_matched;
@@ -630,9 +635,20 @@
eol_str, FALSE,
target->keywords, FALSE,
iterpool));
+ lines_read++;
+
SVN_ERR(read_line(target, &target_line, iterpool, iterpool));
+
if (! hunk_eof)
- lines_matched = ! strcmp(hunk_line_translated, target_line);
+ {
+ if (lines_read <= fuzz && hunk->leading_context > fuzz)
+ lines_matched = TRUE;
+ else if (lines_read > hunk->original_length - fuzz &&
+ hunk->trailing_context > fuzz)
+ lines_matched = TRUE;
+ else
+ lines_matched = ! strcmp(hunk_line_translated, target_line);
+ }
}
while (lines_matched && ! (hunk_eof || target->eof));
@@ -658,7 +674,7 @@
}
/* Scan lines of TARGET for a match of the original text of HUNK,
- * up to but not including the specified UPPER_LINE.
+ * up to but not including the specified UPPER_LINE. Use fuzz factor FUZZ.
* If UPPER_LINE is zero scan until EOF occurs when reading from TARGET.
* Return the line at which HUNK was matched in *MATCHED_LINE.
* If the hunk did not match at all, set *MATCHED_LINE to zero.
@@ -670,7 +686,7 @@
static svn_error_t *
scan_for_match(svn_linenum_t *matched_line, patch_target_t *target,
const svn_hunk_t *hunk, svn_boolean_t match_first,
- svn_linenum_t upper_line, apr_pool_t *pool)
+ svn_linenum_t upper_line, int fuzz, apr_pool_t *pool)
{
apr_pool_t *iterpool;
@@ -684,7 +700,7 @@
svn_pool_clear(iterpool);
- SVN_ERR(match_hunk(&matched, target, hunk, iterpool));
+ SVN_ERR(match_hunk(&matched, target, hunk, fuzz, iterpool));
if (matched)
{
svn_boolean_t taken = FALSE;
@@ -720,14 +736,15 @@
/* Determine the line at which a HUNK applies to the TARGET file,
* and return an appropriate hunk_info object in *HI, allocated from
- * RESULT_POOL. If no correct line can be determined,
- * set HI->MATCHED_LINE to zero.
+ * RESULT_POOL. Always set lines at start and end of HUNK text that is within
+ * FUZZ offset to be matching. Set HI->FUZZ to FUZZ. If no correct line can
+ * be determined, set HI->MATCHED_LINE to zero.
* When this function returns, neither TARGET->CURRENT_LINE nor the
* file offset in the target file will have changed.
* Do temporary allocations in POOL. */
static svn_error_t *
get_hunk_info(hunk_info_t **hi, patch_target_t *target,
- const svn_hunk_t *hunk, apr_pool_t *result_pool,
+ const svn_hunk_t *hunk, int fuzz, apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_linenum_t matched_line;
@@ -748,7 +765,7 @@
* should be going. */
SVN_ERR(seek_to_line(target, hunk->original_start, scratch_pool));
SVN_ERR(scan_for_match(&matched_line, target, hunk, TRUE,
- hunk->original_start + 1, scratch_pool));
+ hunk->original_start + 1, fuzz, scratch_pool));
if (matched_line != hunk->original_start)
{
/* Scan the whole file again from the start. */
@@ -757,7 +774,7 @@
/* Scan forward towards the hunk's line and look for a line
* where the hunk matches. */
SVN_ERR(scan_for_match(&matched_line, target, hunk, FALSE,
- hunk->original_start, scratch_pool));
+ hunk->original_start, fuzz, scratch_pool));
/* In tie-break situations, we arbitrarily prefer early matches
* to save us from scanning the rest of the file. */
@@ -766,7 +783,7 @@
/* Scan forward towards the end of the file and look
* for a line where the hunk matches. */
SVN_ERR(scan_for_match(&matched_line, target, hunk, TRUE, 0,
- scratch_pool));
+ fuzz, scratch_pool));
}
}
@@ -778,6 +795,7 @@
(*hi)->hunk = hunk;
(*hi)->matched_line = matched_line;
(*hi)->rejected = FALSE;
+ (*hi)->fuzz = fuzz;
return SVN_NO_ERROR;
}
@@ -833,13 +851,17 @@
/* Copy HUNK_TEXT into TARGET, line by line, such that the line filter
* and transformation callbacks set on HUNK_TEXT by the diff parsing
* code in libsvn_diff will trigger. ABSPATH is the absolute path to the
- * file underlying TARGET. */
+ * file underlying TARGET. Do not copy the lines that is within FUZZ offset
+ * from the beginning or end of hunk. N represents the number of lines in
+ * HUNK_TEXT. It is ignored if FUZZ is zero. */
static svn_error_t *
-copy_hunk_text(svn_stream_t *hunk_text, svn_stream_t *target,
- const char *abspath, apr_pool_t *scratch_pool)
+copy_hunk_lines(svn_stream_t *hunk_text, svn_stream_t *target,
+ const char *abspath, int fuzz, int n,
+ apr_pool_t *scratch_pool)
{
svn_boolean_t eof;
apr_pool_t *iterpool;
+ int read_lines = 0;
iterpool = svn_pool_create(scratch_pool);
do
@@ -851,7 +873,9 @@
SVN_ERR(svn_stream_readline_detect_eol(hunk_text, &line, &eol_str,
&eof, iterpool));
- if (! eof)
+ read_lines++;
+
+ if (! eof && fuzz == 0)
{
if (line->len >= 1)
SVN_ERR(try_stream_write(target, abspath, line->data, line->len,
@@ -860,6 +884,20 @@
SVN_ERR(try_stream_write(target, abspath, eol_str, strlen(eol_str),
iterpool));
}
+
+ /* We only want to copy the lines without fuzz. Fuzz means that the
+ * target is changed on those lines. We should use the targets
+ * version then. */
+ else if (! eof && (((read_lines > fuzz) && (read_lines <= n - fuzz))
+ || n == 0))
+ {
+ if (line->len >= 1)
+ SVN_ERR(try_stream_write(target, abspath, line->data, line->len,
+ iterpool));
+ if (eol_str)
+ SVN_ERR(try_stream_write(target, abspath, eol_str, strlen(eol_str),
+ iterpool));
+ }
}
while (! eof);
svn_pool_destroy(iterpool);
@@ -886,8 +924,10 @@
len = strlen(hunk_header);
SVN_ERR(svn_stream_write(target->reject, hunk_header, &len));
- SVN_ERR(copy_hunk_text(hi->hunk->diff_text, target->reject,
- target->reject_path, pool));
+ SVN_ERR(copy_hunk_lines(hi->hunk->diff_text, target->reject,
+ target->reject_path, 0,
+ 0, /* only needed if fuzz > 0 */
+ pool));
target->had_rejects = TRUE;
hi->rejected = TRUE;
@@ -895,15 +935,20 @@
return SVN_NO_ERROR;
}
-/* Apply a hunk described by hunk info HI to a patch TARGET.
+/* Apply a hunk described by hunk info HI to a patch TARGET. If we have FUZZ
+ * use the lines from the target for those lines instead of the hunk lines.
* Do all allocations in POOL. */
static svn_error_t *
-apply_one_hunk(patch_target_t *target, hunk_info_t *hi, apr_pool_t *pool)
+apply_one_hunk(patch_target_t *target, hunk_info_t *hi,
+ apr_pool_t *pool)
{
+ svn_linenum_t last_line;
+
if (target->kind == svn_node_file)
{
- /* Move forward to the hunk's line, copying data as we go. */
- SVN_ERR(copy_lines_to_target(target, hi->matched_line, pool));
+ /* Move forward to the hunk's line, copying data as we go. We want to
+ * use the targets version for the fuzzy lines of the hunk. */
+ SVN_ERR(copy_lines_to_target(target, hi->matched_line + hi->fuzz, pool));
if (target->eof)
{
/* File is shorter than it should be. */
@@ -912,14 +957,17 @@
}
/* Skip the target's version of the hunk. */
+ last_line = target->current_line +
+ hi->hunk->original_length - (2 * hi->fuzz);
SVN_ERR(seek_to_line(target,
- target->current_line + hi->hunk->original_length,
+ last_line,
pool));
}
/* Copy the patched hunk text into the patched stream. */
- SVN_ERR(copy_hunk_text(hi->hunk->modified_text, target->patched,
- target->patched_path, pool));
+ SVN_ERR(copy_hunk_lines(hi->hunk->modified_text, target->patched,
+ target->patched_path, hi->fuzz,
+ hi->hunk->modified_length, pool));
return SVN_NO_ERROR;
}
@@ -1026,6 +1074,7 @@
apr_finfo_t working_file;
apr_finfo_t patched_file;
int i;
+ int MAX_FUZZ = 2;
SVN_ERR(init_patch_target(&target, patch, abs_wc_path, ctx, strip_count,
pool, pool));
@@ -1044,13 +1093,21 @@
{
svn_hunk_t *hunk;
hunk_info_t *hi;
+ int fuzz = 0;
svn_pool_clear(iterpool);
hunk = APR_ARRAY_IDX(patch->hunks, i, svn_hunk_t *);
- /* Determine the line the hunk should be applied at. */
- SVN_ERR(get_hunk_info(&hi, target, hunk, pool, iterpool));
+ /* Determine the line the hunk should be applied at. If no match is
+ * found initially, try with fuzz. */
+ do
+ {
+ SVN_ERR(get_hunk_info(&hi, target, hunk, fuzz, pool, iterpool));
+ fuzz++;
+ }
+ while (hi->matched_line == 0 && fuzz <= MAX_FUZZ);
+
if (hi->matched_line == 0)
{
/* The hunk does not apply, reject it. */