You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/03/04 16:07:17 UTC
svn commit: r919014 - in /subversion/trunk/subversion: libsvn_client/patch.c
tests/cmdline/patch_tests.py
Author: stsp
Date: Thu Mar 4 15:07:17 2010
New Revision: 919014
URL: http://svn.apache.org/viewvc?rev=919014&view=rev
Log:
Rework how svn patch handles EOL styles.
* subversion/libsvn_client/patch.c
(patch_target_t): Amend documentation of EOL_STYLE and EOL_STR fields.
Declare them next to each other.
They've changed their meanings slightly. The EOL_STR used to be the
first newline character found in the target. Now it is either a fixed
EOL character (in case the target has svn:eol-style set), or it is
the EOL character which terminated the line most recently read from
the target. EOL_STYLE is set to either 'none' (if no svn:eol-style is
set), or it is set to the EOL-style specified by svn:eol-style.
(init_patch_target): Initialise EOL_STYLE and EOL_STR according to the
above. Also repair newlines of the target's eol-style is native (we
used to repair newlines only if the style was fixed).
(read_line): Put the EOL character which terminated the line into
TARGET->EOL_STR is TARGET->EOL_STYLE is 'none'. Don't pass an EOL
argument to svn_subst_translate_cstring2(), the string we're translating
does not contain a newline.
(match_hunk): Don't pass an EOL arguement to svn_subst_translate_cstring2(),
same reason as above. Discard the newline read from the patch file, we do
not need it during matching.
(reject_hunk): Write the native EOL-character in the diff header of reject
files.
(apply_hunk): When writing out the patched result to a target which does
not have an svn:eol-style property set, use the EOL character read from
the patch for all lines copied from the patch (this includes context lines).
In case the target has a different EOL-style and no svn:eol-style set,
the target will end up with mixed EOLs. In this case users need to either
change the newlines in the patch file, or set an svn:eol-style property
on the target to get consistent EOLs in the patched result.
* subversion/tests/cmdline/patch_tests.py
(patch_no_svn_eol_style, patch_with_svn_eol_style,
patch_with_svn_eol_style_uncommitted): New regression tests which check
for the behaviour around EOLs implemented as of this commit. Based on
a patch submitted by dannas.
(test_list): Add new tests.
Modified:
subversion/trunk/subversion/libsvn_client/patch.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=919014&r1=919013&r2=919014&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Thu Mar 4 15:07:17 2010
@@ -107,7 +107,13 @@
/* The line last read from the target file. */
svn_linenum_t current_line;
- /* EOL-marker used by target file. */
+ /* The EOL-style of the target. Either 'none', 'fixed', or 'native'.
+ * See the documentation of svn_subst_eol_style_t. */
+ svn_subst_eol_style_t eol_style;
+
+ /* If the EOL_STYLE above is not 'none', this is the EOL string
+ * corresponding to the EOL-style. Else, it is the EOL string the
+ * last line read from the target file was using. */
const char *eol_str;
/* An array containing stream markers marking the beginning
@@ -150,9 +156,6 @@
/* The keywords of the target. */
apr_hash_t *keywords;
- /* The EOL-style of the target. */
- svn_subst_eol_style_t eol_style;
-
/* The pool the target is allocated in. */
apr_pool_t *pool;
} patch_target_t;
@@ -371,11 +374,12 @@
svn_string_t *keywords_val;
svn_string_t *eol_style_val;
const char *diff_header;
+ svn_boolean_t repair_eol;
apr_size_t len;
- target->eol_str = APR_EOL_STR;
target->keywords = NULL;
- target->eol_style = svn_subst_eol_style_unknown;
+ target->eol_style = svn_subst_eol_style_none;
+ target->eol_str = NULL;
if (target->kind == svn_node_file)
{
@@ -422,26 +426,10 @@
&target->eol_str,
eol_style_val->data);
}
- else
- {
- /* Just use the first EOL sequence we can find in the file. */
- SVN_ERR(svn_eol__detect_file_eol(&target->eol_str,
- target->file, scratch_pool));
- /* But don't enforce any particular EOL-style. */
- target->eol_style = svn_subst_eol_style_none;
- }
-
- if (target->eol_str == NULL)
- {
- /* We couldn't figure out the target files's EOL scheme,
- * just use native EOL makers. */
- target->eol_str = APR_EOL_STR;
- target->eol_style = svn_subst_eol_style_native;
- }
/* Create a stream to read from the target. */
target->stream = svn_stream_from_aprfile2(target->file,
- FALSE, result_pool);
+ FALSE, result_pool);
/* Also check the file for local mods and the Xbit. */
SVN_ERR(check_local_mods(&target->local_mods, wc_ctx,
@@ -451,17 +439,17 @@
scratch_pool));
}
- /* Create a temporary file to write the patched result to.
- * Expand keywords in the patched file. */
+ /* Create a temporary file to write the patched result to. */
SVN_ERR(svn_stream_open_unique(&target->patched_raw,
&target->patched_path, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, scratch_pool));
+ /* Expand keywords in the patched file.
+ * Repair newlines if svn:eol-style dictates a particular style. */
+ repair_eol = (target->eol_style == svn_subst_eol_style_fixed ||
+ target->eol_style == svn_subst_eol_style_native);
target->patched = svn_subst_stream_translated(
- target->patched_raw,
- target->eol_str,
- target->eol_style ==
- svn_subst_eol_style_fixed,
+ target->patched_raw, target->eol_str, repair_eol,
target->keywords, TRUE, result_pool);
/* We'll also need a stream to write rejected hunks to.
@@ -507,6 +495,7 @@
apr_pool_t *result_pool)
{
svn_stringbuf_t *line_raw;
+ const char *eol_str;
if (target->eof)
{
@@ -522,11 +511,15 @@
APR_ARRAY_PUSH(target->lines, svn_stream_mark_t *) = mark;
}
- SVN_ERR(svn_stream_readline(target->stream, &line_raw, target->eol_str,
- &target->eof, scratch_pool));
+ SVN_ERR(svn_stream_readline_detect_eol(target->stream, &line_raw,
+ &eol_str, &target->eof,
+ scratch_pool));
+ if (target->eol_style == svn_subst_eol_style_none)
+ target->eol_str = eol_str;
+
/* Contract keywords. */
SVN_ERR(svn_subst_translate_cstring2(line_raw->data, line,
- target->eol_str, FALSE,
+ NULL, FALSE,
target->keywords, FALSE,
result_pool));
if (! target->eof)
@@ -613,17 +606,16 @@
do
{
const char *hunk_line_translated;
- const char *eol_str;
svn_pool_clear(iterpool);
SVN_ERR(svn_stream_readline_detect_eol(hunk->original_text,
- &hunk_line, &eol_str,
+ &hunk_line, NULL,
&hunk_eof, iterpool));
/* Contract keywords, if any, before matching. */
SVN_ERR(svn_subst_translate_cstring2(hunk_line->data,
&hunk_line_translated,
- eol_str, FALSE,
+ NULL, FALSE,
target->keywords, FALSE,
iterpool));
lines_read++;
@@ -866,7 +858,7 @@
hi->hunk->original_length,
hi->hunk->modified_start,
hi->hunk->modified_length,
- target->eol_str);
+ APR_EOL_STR);
len = strlen(hunk_header);
SVN_ERR(svn_stream_write(target->reject, hunk_header, &len));
@@ -955,8 +947,15 @@
hunk_line->data, hunk_line->len,
iterpool));
if (eol_str)
- SVN_ERR(try_stream_write(target->patched, target->patched_path,
- eol_str, strlen(eol_str), iterpool));
+ {
+ /* Use the EOL as it was read from the patch file,
+ * unless the target's EOL style is set by svn:eol-style */
+ if (target->eol_style != svn_subst_eol_style_none)
+ eol_str = target->eol_str;
+
+ SVN_ERR(try_stream_write(target->patched, target->patched_path,
+ eol_str, strlen(eol_str), iterpool));
+ }
}
}
while (! eof);
Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=919014&r1=919013&r2=919014&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Thu Mar 4 15:07:17 2010
@@ -1352,6 +1352,332 @@
1, # dry-run
'--reverse-diff')
+def patch_no_svn_eol_style(sbox):
+ "patch target with no svn:eol-style"
+
+ 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')
+
+ if os.name == 'nt':
+ crlf = '\n'
+ else:
+ crlf = '\r\n'
+ eols = [crlf, '\015', '\n', '\012']
+
+ for target_eol in eols:
+ for patch_eol in eols:
+ mu_contents = [
+ "We wish to congratulate you over your email success in our computer",
+ target_eol,
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ target_eol,
+ "in which email addresses were used. All participants were selected",
+ target_eol,
+ "through a computer ballot system drawn from over 100,000 company",
+ target_eol,
+ "and 50,000,000 individual email addresses from all over the world.",
+ target_eol,
+ "It is a promotional program aimed at encouraging internet users;",
+ target_eol,
+ ]
+
+ # Set mu contents
+ svntest.main.file_write(mu_path, ''.join(mu_contents))
+
+ unidiff_patch = [
+ "Index: mu",
+ patch_eol,
+ "===================================================================",
+ patch_eol,
+ "--- A/mu\t(revision 0)",
+ patch_eol,
+ "+++ A/mu\t(revision 0)",
+ patch_eol,
+ "@@ -1,5 +1,6 @@",
+ patch_eol,
+ " We wish to congratulate you over your email success in our computer",
+ patch_eol,
+ " Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ patch_eol,
+ "+A new line here",
+ patch_eol,
+ " in which email addresses were used. All participants were selected",
+ patch_eol,
+ " through a computer ballot system drawn from over 100,000 company",
+ patch_eol,
+ " and 50,000,000 individual email addresses from all over the world.",
+ patch_eol,
+ ]
+
+ mu_contents = [
+ "We wish to congratulate you over your email success in our computer",
+ patch_eol,
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ patch_eol,
+ "A new line here",
+ patch_eol,
+ "in which email addresses were used. All participants were selected",
+ patch_eol,
+ "through a computer ballot system drawn from over 100,000 company",
+ patch_eol,
+ "and 50,000,000 individual email addresses from all over the world.",
+ patch_eol,
+ "It is a promotional program aimed at encouraging internet users;",
+ target_eol,
+ ]
+
+ svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+ expected_output = [
+ 'G %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=1)
+
+ 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
+
+ expected_output = ["Reverted '" + mu_path + "'\n"]
+ svntest.actions.run_and_verify_svn(None, expected_output, [], 'revert', '-R', wc_dir)
+
+def patch_with_svn_eol_style(sbox):
+ "patch target with svn:eol-style"
+
+ 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')
+
+
+ if os.name == 'nt':
+ crlf = '\n'
+ else:
+ crlf = '\r\n'
+
+ eols = [crlf, '\015', '\n', '\012']
+ eol_styles = ['CRLF', 'CR', 'native', 'LF']
+ rev = 1
+ for target_eol, target_eol_style in zip(eols, eol_styles):
+ for patch_eol in eols:
+ mu_contents = [
+ "We wish to congratulate you over your email success in our computer",
+ target_eol,
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ target_eol,
+ "in which email addresses were used. All participants were selected",
+ target_eol,
+ "through a computer ballot system drawn from over 100,000 company",
+ target_eol,
+ "and 50,000,000 individual email addresses from all over the world.",
+ target_eol,
+ "It is a promotional program aimed at encouraging internet users;",
+ target_eol,
+ ]
+
+ # Set mu contents
+ svntest.main.run_svn(None, 'rm', mu_path)
+ svntest.main.run_svn(None, 'commit', '-m', 'delete mu', mu_path)
+ svntest.main.file_write(mu_path, ''.join(mu_contents))
+ svntest.main.run_svn(None, 'add', mu_path)
+ svntest.main.run_svn(None, 'propset', 'svn:eol-style', target_eol_style,
+ mu_path)
+ svntest.main.run_svn(None, 'commit', '-m', 'set eol-style', mu_path)
+
+ unidiff_patch = [
+ "Index: mu",
+ patch_eol,
+ "===================================================================",
+ patch_eol,
+ "--- A/mu\t(revision 0)",
+ patch_eol,
+ "+++ A/mu\t(revision 0)",
+ patch_eol,
+ "@@ -1,5 +1,6 @@",
+ patch_eol,
+ " We wish to congratulate you over your email success in our computer",
+ patch_eol,
+ " Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ patch_eol,
+ "+A new line here",
+ patch_eol,
+ " in which email addresses were used. All participants were selected",
+ patch_eol,
+ " through a computer ballot system drawn from over 100,000 company",
+ patch_eol,
+ " and 50,000,000 individual email addresses from all over the world.",
+ patch_eol,
+ ]
+
+ mu_contents = [
+ "We wish to congratulate you over your email success in our computer",
+ target_eol,
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ target_eol,
+ "A new line here",
+ target_eol,
+ "in which email addresses were used. All participants were selected",
+ target_eol,
+ "through a computer ballot system drawn from over 100,000 company",
+ target_eol,
+ "and 50,000,000 individual email addresses from all over the world.",
+ target_eol,
+ "It is a promotional program aimed at encouraging internet users;",
+ target_eol,
+ ]
+
+ svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+ 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),
+ props={'svn:eol-style' : target_eol_style})
+
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ rev += 2
+ expected_status.tweak('A/mu', status='M ', wc_rev=rev)
+
+ 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
+
+ expected_output = ["Reverted '" + mu_path + "'\n"]
+ svntest.actions.run_and_verify_svn(None, expected_output, [], 'revert', '-R', wc_dir)
+
+def patch_with_svn_eol_style_uncommitted(sbox):
+ "patch target with uncommitted svn:eol-style"
+
+ 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')
+
+
+ if os.name == 'nt':
+ crlf = '\n'
+ else:
+ crlf = '\r\n'
+
+ eols = [crlf, '\015', '\n', '\012']
+ eol_styles = ['CRLF', 'CR', 'native', 'LF']
+ for target_eol, target_eol_style in zip(eols, eol_styles):
+ for patch_eol in eols:
+ mu_contents = [
+ "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',
+ "It is a promotional program aimed at encouraging internet users;",
+ '\n',
+ ]
+
+ # Set mu contents
+ svntest.main.file_write(mu_path, ''.join(mu_contents))
+ svntest.main.run_svn(None, 'propset', 'svn:eol-style', target_eol_style,
+ mu_path)
+
+ unidiff_patch = [
+ "Index: mu",
+ patch_eol,
+ "===================================================================",
+ patch_eol,
+ "--- A/mu\t(revision 0)",
+ patch_eol,
+ "+++ A/mu\t(revision 0)",
+ patch_eol,
+ "@@ -1,5 +1,6 @@",
+ patch_eol,
+ " We wish to congratulate you over your email success in our computer",
+ patch_eol,
+ " Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ patch_eol,
+ "+A new line here",
+ patch_eol,
+ " in which email addresses were used. All participants were selected",
+ patch_eol,
+ " through a computer ballot system drawn from over 100,000 company",
+ patch_eol,
+ " and 50,000,000 individual email addresses from all over the world.",
+ patch_eol,
+ ]
+
+ mu_contents = [
+ "We wish to congratulate you over your email success in our computer",
+ target_eol,
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw",
+ target_eol,
+ "A new line here",
+ target_eol,
+ "in which email addresses were used. All participants were selected",
+ target_eol,
+ "through a computer ballot system drawn from over 100,000 company",
+ target_eol,
+ "and 50,000,000 individual email addresses from all over the world.",
+ target_eol,
+ "It is a promotional program aimed at encouraging internet users;",
+ target_eol,
+ ]
+
+ svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+ expected_output = [
+ 'G %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),
+ props={'svn:eol-style' : target_eol_style})
+
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ expected_status.tweak('A/mu', status='MM', wc_rev=1)
+
+ 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
+
+ expected_output = ["Reverted '" + mu_path + "'\n"]
+ svntest.actions.run_and_verify_svn(None, expected_output, [], 'revert', '-R', wc_dir)
+
+
########################################################################
#Run the tests
@@ -1367,6 +1693,9 @@
patch_keywords,
patch_with_fuzz,
patch_reverse,
+ patch_no_svn_eol_style,
+ patch_with_svn_eol_style,
+ patch_with_svn_eol_style_uncommitted,
]
if __name__ == '__main__':