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__':