You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/12/16 14:57:16 UTC
svn commit: r1720359 - in /subversion/trunk/subversion:
libsvn_client/patch.c libsvn_diff/parse-diff.c tests/cmdline/patch_tests.py
Author: rhuijben
Date: Wed Dec 16 13:57:15 2015
New Revision: 1720359
URL: http://svn.apache.org/viewvc?rev=1720359&view=rev
Log:
Following up on r1720351, fix handling patch files that have more context
than the diff headers tell us in the same way as a shortage of context.
This change makes a bad patch in our testsuite visible, where we expected
bad behavior. This patch fixes that test and adds more testing.
* subversion/libsvn_client/patch.c
(hunk_info_t): Separate reported fuzz from matching fuzz as mixing them
causes errors.
(get_hunk_info): Store both fuzz values.
(apply_hunk): Use match fuzz for apply-magic.
(send_hunk_notification): Report fuzz with penalty.
* subversion/libsvn_diff/parse-diff.c
(parse_next_hunk): Parse extra context when that exists. Report this case
as fuzz penalty, like a fuzz shortage.
* subversion/tests/cmdline/patch_tests.py
(patch_empty_file): Fix patch to work as document. Expect the proper result.
Extract the original version as new test.
(missing_trailing_context): Extend test with reverse patching and too much
trailing context.
(patch_missed_trail): New test, with the original case of patch_empty_file.
Expect that it no longer ignores the 'replacement' line.
(test_list): Add patch_missed_trail.
Modified:
subversion/trunk/subversion/libsvn_client/patch.c
subversion/trunk/subversion/libsvn_diff/parse-diff.c
subversion/trunk/subversion/tests/cmdline/patch_tests.py
Modified: subversion/trunk/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=1720359&r1=1720358&r2=1720359&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Wed Dec 16 13:57:15 2015
@@ -67,7 +67,10 @@ typedef struct hunk_info_t {
/* The fuzz factor used when matching this hunk, i.e. how many
* lines of leading and trailing context to ignore during matching. */
- svn_linenum_t fuzz;
+ svn_linenum_t match_fuzz;
+
+ /* match_fuzz + the penalty caused by bad patch files */
+ svn_linenum_t report_fuzz;
} hunk_info_t;
/* A struct carrying information related to the patched and unpatched
@@ -2087,7 +2090,8 @@ get_hunk_info(hunk_info_t **hi, patch_ta
(*hi)->matched_line = matched_line;
(*hi)->rejected = (matched_line == 0);
(*hi)->already_applied = already_applied;
- (*hi)->fuzz = fuzz;
+ (*hi)->report_fuzz = fuzz;
+ (*hi)->match_fuzz = fuzz - svn_diff_hunk__get_fuzz_penalty(hunk);
return SVN_NO_ERROR;
}
@@ -2209,6 +2213,7 @@ apply_hunk(patch_target_t *target, targe
svn_linenum_t lines_read;
svn_boolean_t eof;
apr_pool_t *iterpool;
+ svn_linenum_t fuzz = hi->match_fuzz;
/* ### Is there a cleaner way to describe if we have an existing target?
*/
@@ -2220,13 +2225,13 @@ apply_hunk(patch_target_t *target, targe
* Also copy leading lines of context which matched with fuzz.
* The target has changed on the fuzzy-matched lines,
* so we should retain the target's version of those lines. */
- SVN_ERR(copy_lines_to_target(content, hi->matched_line + hi->fuzz,
+ SVN_ERR(copy_lines_to_target(content, hi->matched_line + fuzz,
pool));
/* Skip the target's version of the hunk.
* Don't skip trailing lines which matched with fuzz. */
line = content->current_line +
- svn_diff_hunk_get_original_length(hi->hunk) - (2 * hi->fuzz);
+ svn_diff_hunk_get_original_length(hi->hunk) - (2 * fuzz);
SVN_ERR(seek_to_line(content, line, pool));
if (content->current_line != line && ! content->eof)
{
@@ -2253,8 +2258,8 @@ apply_hunk(patch_target_t *target, targe
&eol_str, &eof,
iterpool, iterpool));
lines_read++;
- if (lines_read > hi->fuzz &&
- lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - hi->fuzz)
+ if (lines_read > fuzz &&
+ lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - fuzz)
{
apr_size_t len;
@@ -2323,7 +2328,7 @@ send_hunk_notification(const hunk_info_t
notify->hunk_modified_length =
svn_diff_hunk_get_modified_length(hi->hunk);
notify->hunk_matched_line = hi->matched_line;
- notify->hunk_fuzz = hi->fuzz;
+ notify->hunk_fuzz = hi->report_fuzz;
notify->prop_name = prop_name;
ctx->notify_func2(ctx->notify_baton2, notify, pool);
Modified: subversion/trunk/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_diff/parse-diff.c?rev=1720359&r1=1720358&r2=1720359&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/trunk/subversion/libsvn_diff/parse-diff.c Wed Dec 16 13:57:15 2015
@@ -1193,24 +1193,38 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
}
c = line->data[0];
- if (original_lines > 0 && modified_lines > 0 &&
- ((c == ' ')
+ if (c == ' '
+ || ((original_lines > 0 && modified_lines > 0)
+ && (
/* Tolerate chopped leading spaces on empty lines. */
- || (! eof && line->len == 0)
+ (! eof && line->len == 0)
/* Maybe tolerate chopped leading spaces on non-empty lines. */
- || (ignore_whitespace && c != del && c != add)))
+ || (ignore_whitespace && c != del && c != add))))
{
/* It's a "context" line in the hunk. */
hunk_seen = TRUE;
- original_lines--;
- modified_lines--;
+ if (original_lines > 0)
+ original_lines--;
+ else
+ {
+ (*hunk)->original_length++;
+ (*hunk)->original_fuzz++;
+ }
+ if (modified_lines > 0)
+ modified_lines--;
+ else
+ {
+ (*hunk)->modified_length++;
+ (*hunk)->modified_fuzz++;
+ }
if (changed_line_seen)
trailing_context++;
else
leading_context++;
last_line_type = context_line;
}
- else if (original_lines > 0 && c == del)
+ else if (c == del
+ && (original_lines > 0 || line->data[1] != del))
{
/* It's a "deleted" line in the hunk. */
hunk_seen = TRUE;
@@ -1221,10 +1235,17 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
if (trailing_context > 0)
trailing_context = 0;
- original_lines--;
+ if (original_lines > 0)
+ original_lines--;
+ else
+ {
+ (*hunk)->original_length++;
+ (*hunk)->original_fuzz++;
+ }
last_line_type = original_line;
}
- else if (modified_lines > 0 && c == add)
+ else if (c == add
+ && (modified_lines > 0 || line->data[1] != add))
{
/* It's an "added" line in the hunk. */
hunk_seen = TRUE;
@@ -1235,7 +1256,13 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
if (trailing_context > 0)
trailing_context = 0;
- modified_lines--;
+ if (modified_lines > 0)
+ modified_lines--;
+ else
+ {
+ (*hunk)->modified_length++;
+ (*hunk)->modified_fuzz++;
+ }
last_line_type = modified_line;
}
else
@@ -1251,7 +1278,6 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
* after the hunk text. */
end = last_line;
}
-
if (original_end == 0)
original_end = end;
if (modified_end == 0)
@@ -1335,12 +1361,12 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
if (original_lines)
{
(*hunk)->original_length -= original_lines;
- (*hunk)->original_fuzz = original_lines;
+ (*hunk)->original_fuzz += original_lines;
}
if (modified_lines)
{
(*hunk)->modified_length -= modified_lines;
- (*hunk)->modified_fuzz = modified_lines;
+ (*hunk)->modified_fuzz += modified_lines;
}
(*hunk)->patch = patch;
Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=1720359&r1=1720358&r2=1720359&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Wed Dec 16 13:57:15 2015
@@ -4518,7 +4518,7 @@ def patch_empty_file(sbox):
"--- lf.txt\t(revision 2)\n",
"+++ lf.txt\t(working copy)\n",
"@@ -1 +1 @@\n",
- "\n"
+ "-\n"
"+replacement\n",
# patch a new file 'new.txt\n'
@@ -4553,7 +4553,7 @@ def patch_empty_file(sbox):
# Current result: lf.txt patched ok, new created, empty succeeds with offset.
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
- 'lf.txt' : Item(contents="\n"),
+ 'lf.txt' : Item(contents="replacement\n"),
'new.txt' : Item(contents="new file\n"),
'empty.txt' : Item(contents="replacement\n"),
})
@@ -7582,6 +7582,100 @@ def missing_trailing_context(sbox):
expected_output, expected_disk,
expected_status, expected_skip)
+ # Try reverse patch
+ expected_disk.tweak('A/mu', contents =
+ 'a\n'
+ 'b\n'
+ 'c\n'
+ 'd\n'
+ 'e\n')
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], False, True, '--reverse-diff')
+
+ # The hunk is expected to have two lines of trailing context but
+ # only has one.
+ unidiff_patch = [
+ "Index: A/mu\n"
+ "===================================================================\n",
+ "--- A/mu\t(revision 2)\n",
+ "+++ A/mu\t(working copy)\n",
+ "@@ -1,4 +1,4 @@\n",
+ " a\n",
+ " b\n",
+ "-c\n",
+ "+cc\n",
+ " d\n",
+ " e\n",
+ ]
+ patch_file_path = sbox.get_tempname('my2.patch')
+ svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+ expected_output = [
+ 'U %s\n' % sbox.ospath('A/mu'),
+ '> applied hunk @@ -1,5 +1,5 @@ with fuzz 1\n',
+ ]
+ expected_disk.tweak('A/mu', contents =
+ 'a\n'
+ 'b\n'
+ 'cc\n'
+ 'd\n'
+ 'e\n')
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output, expected_disk,
+ expected_status, expected_skip)
+
+ # Try reverse patch
+ expected_disk.tweak('A/mu', contents =
+ 'a\n'
+ 'b\n'
+ 'c\n'
+ 'd\n'
+ 'e\n')
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], False, True, '--reverse-diff')
+
+def patch_missed_trail(sbox):
+ "apply a patch to an empty file"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ patch_file_path = sbox.get_tempname('my.patch')
+ svntest.main.file_write(patch_file_path, ''.join([
+ # Add a line to a file with just '\n' with bad header (should be +1,2)
+ "Index: lf.txt\n",
+ "===================================================================\n",
+ "--- lf.txt\t(revision 2)\n",
+ "+++ lf.txt\t(working copy)\n",
+ "@@ -1 +1 @@\n",
+ "\n"
+ "+replacement\n",
+ ]))
+
+ sbox.simple_add_text('\n', 'lf.txt')
+ sbox.simple_commit()
+
+ expected_output = [
+ 'U %s\n' % sbox.ospath('lf.txt'),
+ '> applied hunk @@ -1,1 +1,2 @@ with fuzz 1\n',
+ ]
+
+ # Current result: lf.txt patched ok, new created, empty succeeds with offset.
+ expected_disk = svntest.main.greek_state.copy()
+ expected_disk.add({
+ 'lf.txt' : Item(contents="\nreplacement\n"),
+ })
+ expected_skip = wc.State(wc_dir, {})
+ expected_status = None
+
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output, expected_disk,
+ expected_status, expected_skip)
+
########################################################################
#Run the tests
@@ -7663,6 +7757,7 @@ test_list = [ None,
patch_with_mergeinfo,
patch_move_and_change,
missing_trailing_context,
+ patch_missed_trail,
]
if __name__ == '__main__':