Hi!

This is not a finished patch.

I've added mark and seek handlers to svn_subst_stream_translated().
Those two handlers are just wrappers that calls the underlying
streams handlers. Now that works in my test but what happens if we set a
mark in front of what we've read so far from the file? We might end up
with an off-by-one caused by one dropped '\r' causing the hunk to be
applied at the wrong line. I guess we could forbid a mark from beeing
set in front of the last character read so far but that would be a
restraint that would look strange on the outside. What to do?

If we plan to add keyword expansion in the future, things would get even
more complex.

Right now I open a translated stream for all cases when there's a
svn:eol-style property. In reality it's only needed when the eol of the
file differs from the property. I will fix that if there's a solution to
'the seekable translated stream' problem.

[[[
Fix #3356 - svn patch ignores svn:eol-style. When the eol in the file to
be patched differed from the svn:eol-style prop the code now translates
to the prop eol. To do that we need to use svn_subst_stream_translated
on a stream that needs to be seekable.

* subversion/libsvn_subr/subst.c
  (translated_stream_mark): New.
  (translated_stream_seek): New.
  (svn_subst_straem_translated): Set seek and mark functions.

* subversion/libsvn_client/patch.c
  (init_patch_target): Check if there is a svn:eol-style prop set on the
    file to be patched. If it is we translate the eols of both the file
    and the patch to that value.

* subversion/tests/cmdline/patch_tests.py
  (patch_handle_eol): New. 
  (test_list): Add the new test.

Patch by: Daniel Näslund <daniel{_AT_}longitudo.com>
]]]

Index: subversion/libsvn_subr/subst.c
===================================================================
--- subversion/libsvn_subr/subst.c	(revision 900853)
+++ subversion/libsvn_subr/subst.c	(arbetskopia)
@@ -1136,7 +1136,25 @@
   return svn_error_return(err);
 }
 
+static svn_error_t *
+translated_stream_mark(void *baton, svn_stream_mark_t **mark, 
+                       apr_pool_t *pool)
+{
+  struct translated_stream_baton *b = baton;
+  SVN_ERR(svn_stream_mark(b->stream, mark, pool));
+  return SVN_NO_ERROR;
+}
 
+static svn_error_t *
+translated_stream_seek(void *baton, svn_stream_mark_t *mark)
+{
+  struct translated_stream_baton *b = baton;
+  SVN_ERR(svn_stream_seek(b->stream, mark));
+  return SVN_NO_ERROR;
+}
+
+
+
 svn_error_t *
 svn_subst_read_specialfile(svn_stream_t **stream,
                            const char *path,
@@ -1235,6 +1253,8 @@
   svn_stream_set_write(s, translated_stream_write);
   svn_stream_set_close(s, translated_stream_close);
   svn_stream_set_reset(s, translated_stream_reset);
+  svn_stream_set_mark(s, translated_stream_mark);
+  svn_stream_set_seek(s, translated_stream_seek);
 
   return s;
 }
Index: subversion/tests/cmdline/patch_tests.py
===================================================================
--- subversion/tests/cmdline/patch_tests.py	(revision 900853)
+++ subversion/tests/cmdline/patch_tests.py	(arbetskopia)
@@ -833,7 +833,73 @@
                                        1, # dry-run
                                        '-p1')
 
+def patch_handle_eol(sbox):
+  "patching should not change eol of target"
 
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  patch_file_path = tempfile.mkstemp(dir=os.path.abspath(svntest.main.temp_dir))[1]
+
+  if os.name == 'nt':
+    crlf = '\n'
+  else:
+    crlf = '\r\n'
+
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+
+  orig = "This is the file 'mu'.\n"
+
+  rev = 0
+  for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
+                          [crlf, '\015', '\n', '\012']):
+
+    expected  = ''.join(["Inserting one line at the top.", eolchar,
+                         "This is the file 'mu'.", eolchar,
+                         "Inserting one line at the bottom.", eolchar])
+
+    svntest.main.file_write(mu_path, orig, 'wb')
+    svntest.main.run_svn(None, 'commit', '-m', "''", wc_dir)
+    svntest.main.run_svn(None, 'update', wc_dir)
+    svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
+
+    # Create a patch
+    patch = [
+      "Index: A/mu\n",
+      "===================================================================\n",
+      "--- A/mu\t(revision 1)\n",
+      "+++ A/mu\t(working copy)\n",
+      "@@ -1 +1,3 @@\n",
+      "+Inserting one line at the top.\n",
+      " This is the file 'mu'.\n",
+      "+Inserting one line at the bottom.\n",
+    ]
+
+    svntest.main.file_write(patch_file_path, ''.join(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', props={u'svn:eol-style' : unicode(eol)})
+    expected_disk.tweak('A/mu', contents=expected)
+    rev += 1
+    expected_status = svntest.actions.get_virginal_state(wc_dir, rev)
+    #expected_status.tweak('A/mu', status='MM', wc_rev=rev)
+    expected_status.tweak('A/mu', status='MM', wc_rev=rev)
+    expected_skip = wc.State('', { })
+      
+    # apply the patch
+    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
+    
 ########################################################################
 #Run the tests
 
@@ -844,6 +910,7 @@
               patch_unidiff_offset,
               patch_chopped_leading_spaces,
               patch_unidiff_strip1,
+              patch_handle_eol,
             ]
 
 if __name__ == '__main__':
Index: subversion/libsvn_client/patch.c
===================================================================
--- subversion/libsvn_client/patch.c	(revision 900853)
+++ subversion/libsvn_client/patch.c	(arbetskopia)
@@ -34,6 +34,7 @@
 #include "svn_path.h"
 #include "svn_pools.h"
 #include "svn_subst.h"
+#include "svn_props.h"
 #include "svn_wc.h"
 
 #include "svn_private_config.h"
@@ -368,14 +369,45 @@
 
   if (new_target->kind == svn_node_file && ! new_target->skipped)
     {
+      apr_hash_t *props;
+      svn_string_t *eol_style;
+
       /* Try to open the target file */
       SVN_ERR(svn_io_file_open(&new_target->file, new_target->abs_path,
                                APR_READ | APR_BINARY | APR_BUFFERED,
                                APR_OS_DEFAULT, result_pool));
-      SVN_ERR(svn_eol__detect_file_eol(&new_target->eol_str, new_target->file,
-                                        scratch_pool));
-      new_target->stream = svn_stream_from_aprfile2(new_target->file, FALSE,
-                                                    result_pool);
+
+      /* Try to get the eol-style property. If that fails use the eol of the
+       * file. */
+      SVN_ERR(svn_wc_prop_list2(&props, ctx->wc_ctx, new_target->abs_path,
+                                result_pool, scratch_pool));
+      eol_style = apr_hash_get(props, SVN_PROP_EOL_STYLE,
+                               APR_HASH_KEY_STRING);
+      if (eol_style)
+        svn_subst_eol_style_from_value(NULL, &new_target->eol_str, 
+                                       eol_style->data);
+      if (new_target->eol_str == NULL)
+        {
+          SVN_ERR(svn_eol__detect_file_eol(&new_target->eol_str, 
+                                           new_target->file, scratch_pool));
+
+          new_target->stream = svn_stream_from_aprfile2(new_target->file, 
+                                                        FALSE, 
+                                                        result_pool);
+        }
+      else
+        {
+          svn_stream_t *stream;
+          stream = svn_stream_from_aprfile2(new_target->file, 
+                                            FALSE, 
+                                            result_pool);
+          
+          new_target->stream = svn_subst_stream_translated(stream,
+                                                           new_target->eol_str,
+                                                           TRUE,
+                                                           NULL, FALSE,
+                                                           result_pool);
+        }
     }
 
   if (new_target->eol_str == NULL)
@@ -403,10 +435,12 @@
                                      &new_target->patched_path, NULL,
                                      svn_io_file_del_on_pool_cleanup,
                                      result_pool, scratch_pool));
-      new_target->patched = svn_subst_stream_translated(new_target->patched_raw,
-                                                        "\n", TRUE,
-                                                        keywords, FALSE,
-                                                        result_pool);
+      new_target->patched = 
+        svn_subst_stream_translated(new_target->patched_raw,
+                                    new_target->eol_str,
+                                    TRUE,
+                                    keywords, FALSE,
+                                    result_pool);
 
       SVN_ERR(check_local_mods(&new_target->local_mods, ctx->wc_ctx,
                                new_target->abs_path, scratch_pool));

Reply via email to