Attached are version 3 of the patch and corresponding log message.
Also attached is a TGZ archive of two Subversion repository dumps that
are used by a new test case.
Index: subversion/svnsync/sync.h
===================================================================
--- subversion/svnsync/sync.h   (revision 1052903)
+++ subversion/svnsync/sync.h   (working copy)
@@ -33,15 +33,20 @@ extern "C" {
 #include "svn_delta.h"
 
 
-/* Normalize the line ending style of the values of properties in REV_PROPS
- * that "need translation" (according to svn_prop_needs_translation(),
- * currently all svn:* props) so that they contain only LF (\n) line endings.
- * The number of properties that needed normalization is returned in
- * *NORMALIZED_COUNT.
+/* Normalize the encoding and line ending style of the values of properties
+ * in REV_PROPS that "need translation" (according to
+ * svn_prop_needs_translation(), which is currently all svn:* props) so that
+ * they are encoded in UTF-8 and contain only LF (\n) line endings.
+ *
+ * The number of properties that needed line ending normalization is returned 
in
+ * *NORMALIZED_LE_COUNT.
+ *
+ * No re-encoding is performed if ENCODING is NULL.
  */
 svn_error_t *
 svnsync_normalize_revprops(apr_hash_t *rev_props,
-                           int *normalized_count,
+                           int *normalized_le_count,
+                           const char *encoding,
                            apr_pool_t *pool);
 
 
@@ -51,19 +56,25 @@ svnsync_normalize_revprops(apr_hash_t *rev_props,
  * the commit.  TO_URL is the URL of the root of the repository into
  * which the commit is being made.
  *
+ * If PROP_ENCODING is NULL, then property values are presumed to be encoded
+ * in UTF-8 and are not re-encoded. Otherwise, the property values are
+ * presumed to be encoded in PROP_ENCODING, and are normalized to UTF-8.
+ *
  * As the sync editor encounters property values, it might see the need to
- * normalize them (to LF line endings). Each carried out normalization adds 1
- * to the *NORMALIZED_NODE_PROPS_COUNTER (for notification).
+ * normalize them (re-encode and/or change to LF line endings). Each 
carried-out
+ * line ending normalization adds 1 to the *LE_NORMALIZED_NODE_PROPS_COUNTER
+ * (for notification).
  */
 svn_error_t *
 svnsync_get_sync_editor(const svn_delta_editor_t *wrapped_editor,
                         void *wrapped_edit_baton,
                         svn_revnum_t base_revision,
                         const char *to_url,
+                        const char *prop_encoding,
                         svn_boolean_t quiet,
                         const svn_delta_editor_t **editor,
                         void **edit_baton,
-                        int *normalized_node_props_counter,
+                        int *le_normalized_node_props_counter,
                         apr_pool_t *pool);
 
 
Index: subversion/svnsync/main.c
===================================================================
--- subversion/svnsync/main.c   (revision 1052903)
+++ subversion/svnsync/main.c   (working copy)
@@ -61,6 +61,7 @@ enum svnsync__opt {
   svnsync_opt_sync_password,
   svnsync_opt_config_dir,
   svnsync_opt_config_options,
+  svnsync_opt_source_encoding,
   svnsync_opt_disable_locking,
   svnsync_opt_version,
   svnsync_opt_trust_server_cert,
@@ -105,8 +106,9 @@ static const svn_opt_subcommand_desc2_t svnsync_cm
          "the destination repository by any method other than 'svnsync'.\n"
          "In other words, the destination repository should be a read-only\n"
          "mirror of the source repository.\n"),
-      { SVNSYNC_OPTS_DEFAULT, 'q', svnsync_opt_allow_non_empty,
-        svnsync_opt_disable_locking, svnsync_opt_steal_lock } },
+      { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_encoding, 'q',
+        svnsync_opt_allow_non_empty, svnsync_opt_disable_locking,
+        svnsync_opt_steal_lock } },
     { "synchronize", synchronize_cmd, { "sync" },
       N_("usage: svnsync synchronize DEST_URL [SOURCE_URL]\n"
          "\n"
@@ -118,8 +120,8 @@ static const svn_opt_subcommand_desc2_t svnsync_cm
          "source URL.  Specifying SOURCE_URL is recommended in particular\n"
          "if untrusted users/administrators may have write access to the\n"
          "DEST_URL repository.\n"),
-      { SVNSYNC_OPTS_DEFAULT, 'q', svnsync_opt_disable_locking,
-        svnsync_opt_steal_lock } },
+      { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_encoding, 'q',
+        svnsync_opt_disable_locking, svnsync_opt_steal_lock } },
     { "copy-revprops", copy_revprops_cmd, { 0 },
       N_("usage:\n"
          "\n"
@@ -139,8 +141,8 @@ static const svn_opt_subcommand_desc2_t svnsync_cm
          "DEST_URL repository.\n"
          "\n"
          "Form 2 is deprecated syntax, equivalent to specifying 
\"-rREV[:REV2]\".\n"),
-      { SVNSYNC_OPTS_DEFAULT, 'q', 'r', svnsync_opt_disable_locking,
-        svnsync_opt_steal_lock } },
+      { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_encoding, 'q', 'r',
+        svnsync_opt_disable_locking, svnsync_opt_steal_lock } },
     { "info", info_cmd, { 0 },
       N_("usage: svnsync info DEST_URL\n"
          "\n"
@@ -159,7 +161,7 @@ static const apr_getopt_option_t svnsync_options[]
   {
     {"quiet",          'q', 0,
                        N_("print as little as possible") },
-    {"revision",       'r', 1, 
+    {"revision",       'r', 1,
                        N_("operate on revision ARG (or range ARG1:ARG2)\n"
                           "                             "
                           "A revision argument can be one of:\n"
@@ -203,6 +205,12 @@ static const apr_getopt_option_t svnsync_options[]
                           "For example:\n"
                           "                             "
                           "    servers:global:http-library=serf")},
+    {"source-encoding", svnsync_opt_source_encoding, 1,
+                       N_("convert translatable properties from encoding ARG\n"
+                          "                             "
+                          "to UTF-8. If not specified, then properties are\n"
+                          "                             "
+                          "presumed to be encoded in UTF-8.")},
     {"disable-locking",  svnsync_opt_disable_locking, 0,
                        N_("Disable built-in locking.  Use of this option can\n"
                           "                             "
@@ -226,7 +234,7 @@ static const apr_getopt_option_t svnsync_options[]
     { 0, 0, 0, 0 }
   };
 
-typedef struct {
+typedef struct { /* opt_baton_t */
   svn_boolean_t non_interactive;
   svn_boolean_t trust_server_cert;
   svn_boolean_t no_auth_cache;
@@ -238,6 +246,7 @@ static const apr_getopt_option_t svnsync_options[]
   const char *sync_password;
   const char *config_dir;
   apr_hash_t *config;
+  const char *source_encoding;
   svn_boolean_t disable_locking;
   svn_boolean_t steal_lock;
   svn_boolean_t quiet;
@@ -355,7 +364,7 @@ get_lock(const svn_string_t **lock_string_p,
 
 
 /* Baton for the various subcommands to share. */
-typedef struct {
+typedef struct { /* subcommand_baton_t */
   /* common to all subcommands */
   apr_hash_t *config;
   svn_ra_callbacks2_t source_callbacks;
@@ -364,6 +373,9 @@ get_lock(const svn_string_t **lock_string_p,
   svn_boolean_t allow_non_empty;
   const char *to_url;
 
+  /* initialize, synchronize, and copy-revprops only */
+  const char *source_encoding;
+
   /* initialize only */
   const char *from_url;
 
@@ -441,7 +453,7 @@ check_if_session_is_at_repos_root(svn_ra_session_t
  * revision REV of the repository associated with RA session SESSION.
  *
  * For REV zero, don't remove properties with the "svn:sync-" prefix.
- * 
+ *
  * All allocations will be done in a subpool of POOL.
  */
 static svn_error_t *
@@ -585,22 +597,22 @@ log_properties_copied(svn_boolean_t syncprops_foun
   return SVN_NO_ERROR;
 }
 
-/* Print a notification that NORMALIZED_REV_PROPS_COUNT rev-props and
- * NORMALIZED_NODE_PROPS_COUNT node-props were normalized to LF line
+/* Print a notification that LE_NORMALIZED_REV_PROPS_COUNT rev-props and
+ * LE_NORMALIZED_NODE_PROPS_COUNT node-props were normalized to LF line
  * endings, if either of those numbers is non-zero. */
 static svn_error_t *
-log_properties_normalized(int normalized_rev_props_count,
-                          int normalized_node_props_count,
+log_properties_normalized(int le_normalized_rev_props_count,
+                          int le_normalized_node_props_count,
                           apr_pool_t *pool)
 {
-  if (normalized_rev_props_count > 0 || normalized_node_props_count > 0)
+  if (le_normalized_rev_props_count > 0 || le_normalized_node_props_count > 0)
     SVN_ERR(svn_cmdline_printf(pool,
                                _("NOTE: Normalized %s* properties "
                                  "to LF line endings (%d rev-props, "
                                  "%d node-props).\n"),
                                SVN_PROP_PREFIX,
-                               normalized_rev_props_count,
-                               normalized_node_props_count));
+                               le_normalized_rev_props_count,
+                               le_normalized_node_props_count));
   return SVN_NO_ERROR;
 }
 
@@ -613,9 +625,12 @@ static svn_error_t *
  * If SYNC is TRUE, then properties on the destination revision that
  * do not exist on the source revision will be removed.
  *
+ * If QUIET is FALSE, then log_properties_copied() is called to log that
+ * properties were copied for revision REV.
+ *
  * Make sure the values of svn:* revision properties use only LF (\n)
- * lineending style, correcting their values as necessary. The number
- * of properties that were normalized is returned in *NORMALIZED_COUNT.
+ * line ending style, correcting their values as necessary. The number
+ * of properties that were normalized is returned in *NORMALIZED_LE_COUNT.
  */
 static svn_error_t *
 copy_revprops(svn_ra_session_t *from_session,
@@ -623,7 +638,8 @@ copy_revprops(svn_ra_session_t *from_session,
               svn_revnum_t rev,
               svn_boolean_t sync,
               svn_boolean_t quiet,
-              int *normalized_count,
+              const char *prop_encoding,
+              int *normalized_le_count,
               apr_pool_t *pool)
 {
   apr_pool_t *subpool = svn_pool_create(pool);
@@ -638,9 +654,10 @@ copy_revprops(svn_ra_session_t *from_session,
   /* Get the list of revision properties on REV of SOURCE. */
   SVN_ERR(svn_ra_rev_proplist(from_session, rev, &rev_props, subpool));
 
-  /* If necessary, normalize line ending style, and return the count
-     of changes in int *NORMALIZED_COUNT. */
-  SVN_ERR(svnsync_normalize_revprops(rev_props, normalized_count, pool));
+  /* If necessary, normalize encoding and line ending style and return the 
count
+     of changes in int *NORMALIZED_LE_COUNT. */
+  SVN_ERR(svnsync_normalize_revprops(rev_props, normalized_le_count,
+                                     prop_encoding, pool));
 
   /* Copy all but the svn:svnsync properties. */
   SVN_ERR(write_revprops(&filtered_count, to_session, rev, rev_props, pool));
@@ -681,6 +698,7 @@ make_subcommand_baton(opt_baton_t *opt_baton,
   b->quiet = opt_baton->quiet;
   b->allow_non_empty = opt_baton->allow_non_empty;
   b->to_url = to_url;
+  b->source_encoding = opt_baton->source_encoding;
   b->from_url = from_url;
   b->start_rev = start_rev;
   b->end_rev = end_rev;
@@ -708,7 +726,7 @@ do_initialize(svn_ra_session_t *to_session,
   svn_string_t *from_url;
   svn_revnum_t latest, from_latest;
   const char *uuid, *root_url;
-  int normalized_rev_props_count;
+  int le_normalized_rev_props_count;
 
   /* First, sanity check to see that we're copying into a brand new
      repos.  If we aren't, and we aren't being asked to forcibly
@@ -785,10 +803,11 @@ do_initialize(svn_ra_session_t *to_session,
      LATEST is not 0, this really serves merely aesthetic and
      informational purposes, keeping the output of this command
      consistent while allowing folks to see what the latest revision is.  */
-  SVN_ERR(copy_revprops(from_session, to_session, latest, FALSE,
-                        baton->quiet, &normalized_rev_props_count, pool));
+  SVN_ERR(copy_revprops(from_session, to_session, latest, FALSE, baton->quiet,
+                        baton->source_encoding, &le_normalized_rev_props_count,
+                        pool));
 
-  SVN_ERR(log_properties_normalized(normalized_rev_props_count, 0, pool));
+  SVN_ERR(log_properties_normalized(le_normalized_rev_props_count, 0, pool));
 
   /* TODO: It would be nice if we could set the dest repos UUID to be
      equal to the UUID of the source repos, at least optionally.  That
@@ -930,13 +949,13 @@ open_target_session(svn_ra_session_t **target_sess
 }
 
 /* Replay baton, used during sychnronization. */
-typedef struct {
+typedef struct { /* replay_baton_t */
   svn_ra_session_t *from_session;
   svn_ra_session_t *to_session;
   subcommand_baton_t *sb;
   svn_boolean_t has_commit_revprops_capability;
-  int normalized_rev_props_count;
-  int normalized_node_props_count;
+  int le_normalized_rev_props_count;
+  int le_normalized_node_props_count;
 } replay_baton_t;
 
 /* Return a replay baton allocated from POOL and populated with
@@ -1024,7 +1043,7 @@ replay_rev_started(svn_revnum_t revision,
   replay_baton_t *rb = replay_baton;
   apr_hash_t *filtered;
   int filtered_count;
-  int normalized_count;
+  int normalized_le_count;
 
   /* We set this property so that if we error out for some reason
      we can later determine where we were in the process of
@@ -1063,10 +1082,12 @@ replay_rev_started(svn_revnum_t revision,
     apr_hash_set(filtered, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING,
                  svn_string_create("", pool));
 
-  /* If necessary, normalize line ending style, and add the number
-     of changes to the overall count in the replay baton. */
-  SVN_ERR(svnsync_normalize_revprops(filtered, &normalized_count, pool));
-  rb->normalized_rev_props_count += normalized_count;
+  /* If necessary, normalize encoding and line ending style. Add the number
+     of properties that required line ending normalization to the overall count
+     in the replay baton. */
+  SVN_ERR(svnsync_normalize_revprops(filtered, &normalized_le_count,
+                                     rb->sb->source_encoding, pool));
+  rb->le_normalized_rev_props_count += normalized_le_count;
 
   SVN_ERR(svn_ra_get_commit_editor3(rb->to_session, &commit_editor,
                                     &commit_baton,
@@ -1078,9 +1099,9 @@ replay_rev_started(svn_revnum_t revision,
      over the RA interface, so we need an editor that's smart enough
      to filter those out for us.  */
   SVN_ERR(svnsync_get_sync_editor(commit_editor, commit_baton, revision - 1,
-                                  rb->sb->to_url, rb->sb->quiet,
-                                  &sync_editor, &sync_baton,
-                                  &(rb->normalized_node_props_count), pool));
+                                  rb->sb->to_url, rb->sb->source_encoding,
+                                  rb->sb->quiet, &sync_editor, &sync_baton,
+                                  &(rb->le_normalized_node_props_count), 
pool));
 
   SVN_ERR(svn_delta_get_cancellation_editor(check_cancel, NULL,
                                             sync_editor, sync_baton,
@@ -1108,7 +1129,7 @@ replay_rev_finished(svn_revnum_t revision,
   replay_baton_t *rb = replay_baton;
   apr_hash_t *filtered, *existing_props;
   int filtered_count;
-  int normalized_count;
+  int normalized_le_count;
 
   SVN_ERR(editor->close_edit(edit_baton, pool));
 
@@ -1133,10 +1154,11 @@ replay_rev_finished(svn_revnum_t revision,
                             : filter_exclude_log),
                           subpool);
 
-  /* If necessary, normalize line ending style, and add the number
+  /* If necessary, normalize encoding and line ending style, and add the number
      of changes to the overall count in the replay baton. */
-  SVN_ERR(svnsync_normalize_revprops(filtered, &normalized_count, pool));
-  rb->normalized_rev_props_count += normalized_count;
+  SVN_ERR(svnsync_normalize_revprops(filtered, &normalized_le_count,
+                                     rb->sb->source_encoding, pool));
+  rb->le_normalized_rev_props_count += normalized_le_count;
 
   SVN_ERR(write_revprops(&filtered_count, rb->to_session, revision, filtered,
                          subpool));
@@ -1187,7 +1209,7 @@ do_synchronize(svn_ra_session_t *to_session,
   svn_revnum_t to_latest, copying, last_merged;
   svn_revnum_t start_revision, end_revision;
   replay_baton_t *rb;
-  int normalized_rev_props_count = 0;
+  int le_normalized_rev_props_count = 0;
 
   SVN_ERR(open_source_session(&from_session, &last_merged_rev,
                               baton->from_url, to_session,
@@ -1239,10 +1261,9 @@ do_synchronize(svn_ra_session_t *to_session,
         {
           if (copying > last_merged)
             {
-              SVN_ERR(copy_revprops(from_session, to_session,
-                                    to_latest, TRUE, baton->quiet,
-                                    &normalized_rev_props_count,
-                                    pool));
+              SVN_ERR(copy_revprops(from_session, to_session, to_latest, TRUE,
+                                    baton->quiet, baton->source_encoding,
+                                    &le_normalized_rev_props_count, pool));
               last_merged = copying;
               last_merged_rev = svn_string_create
                 (apr_psprintf(pool, "%ld", last_merged), pool);
@@ -1301,9 +1322,9 @@ do_synchronize(svn_ra_session_t *to_session,
                               0, TRUE, replay_rev_started,
                               replay_rev_finished, rb, pool));
 
-  SVN_ERR(log_properties_normalized(rb->normalized_rev_props_count
-                                      + normalized_rev_props_count,
-                                    rb->normalized_node_props_count,
+  SVN_ERR(log_properties_normalized(rb->le_normalized_rev_props_count
+                                      + le_normalized_rev_props_count,
+                                    rb->le_normalized_node_props_count,
                                     pool));
 
 
@@ -1374,7 +1395,7 @@ do_copy_revprops(svn_ra_session_t *to_session,
   svn_string_t *last_merged_rev;
   svn_revnum_t i;
   svn_revnum_t step = 1;
-  int normalized_rev_props_count = 0;
+  int le_normalized_rev_props_count = 0;
 
   SVN_ERR(open_source_session(&from_session, &last_merged_rev,
                               baton->from_url, to_session,
@@ -1403,15 +1424,16 @@ do_copy_revprops(svn_ra_session_t *to_session,
   step = (baton->start_rev > baton->end_rev) ? -1 : 1;
   for (i = baton->start_rev; i != baton->end_rev + step; i = i + step)
     {
-      int normalized_count;
+      int normalized_le_count;
       SVN_ERR(check_cancel(NULL));
-      SVN_ERR(copy_revprops(from_session, to_session, i, TRUE,
-                            baton->quiet, &normalized_count, pool));
-      normalized_rev_props_count += normalized_count;
+      SVN_ERR(copy_revprops(from_session, to_session, i, TRUE, baton->quiet,
+                            baton->source_encoding, &normalized_le_count,
+                            pool));
+      le_normalized_rev_props_count += normalized_le_count;
     }
 
   /* Notify about normalized props, if any. */
-  SVN_ERR(log_properties_normalized(normalized_rev_props_count, 0, pool));
+  SVN_ERR(log_properties_normalized(le_normalized_rev_props_count, 0, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1482,7 +1504,7 @@ resolve_revnums(svn_revnum_t *start_revnum,
                                  _("Invalid revision number (%ld)"),
                                  end_rev);
     }
-  
+
   *start_revnum = start_rev;
   *end_revnum = end_rev;
   return SVN_NO_ERROR;
@@ -1552,7 +1574,7 @@ copy_revprops_cmd(apr_getopt_t *os, void *b, apr_p
           from_url = NULL;
         }
     }
-  
+
   if (! to_url)
     {
       /* This is the "... TO_URL SOURCE_URL" syntax.  Revisions
@@ -1580,7 +1602,7 @@ copy_revprops_cmd(apr_getopt_t *os, void *b, apr_p
   if (from_url && (! svn_path_is_url(from_url)))
     return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                              _("Path '%s' is not a URL"), from_url);
-      
+
   baton = make_subcommand_baton(opt_baton, to_url, from_url,
                                 start_rev, end_rev, pool);
   SVN_ERR(open_target_session(&to_session, baton, pool));
@@ -1707,6 +1729,7 @@ main(int argc, const char *argv[])
   const char *username = NULL, *source_username = NULL, *sync_username = NULL;
   const char *password = NULL, *source_password = NULL, *sync_password = NULL;
   apr_array_header_t *config_options = NULL;
+  opt_baton.source_encoding = NULL;
   apr_allocator_t *allocator;
 
   if (svn_cmdline_init("svnsync", stderr) != EXIT_SUCCESS)
@@ -1831,6 +1854,10 @@ main(int argc, const char *argv[])
               return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
             break;
 
+          case svnsync_opt_source_encoding:
+            opt_baton.source_encoding = apr_pstrdup(pool, opt_arg);
+            break;
+
           case svnsync_opt_disable_locking:
             opt_baton.disable_locking = TRUE;
             break;
Index: subversion/svnsync/sync.c
===================================================================
--- subversion/svnsync/sync.c   (revision 1052903)
+++ subversion/svnsync/sync.c   (working copy)
@@ -45,56 +45,72 @@
 #include <apr_uuid.h>
 
 
-/* Normalize the line ending style of *STR, so that it contains only
- * LF (\n) line endings. After return, *STR may point at a new
- * svn_string_t* allocated from POOL.
+/* Normalize the encoding and line ending style of *STR, so that it contains
+ * only LF (\n) line endings and is encoded in UTF-8. After return, *STR may
+ * point at a new svn_string_t* allocated in RESULT_POOL.
  *
- * *WAS_NORMALIZED is set to TRUE when *STR needed to be normalized,
- * and to FALSE if *STR remains unchanged.
+ * If ENCODING is NULL, then *STR is presumed to be encoded in UTF-8.
+ *
+ * *WAS_LE_NORMALIZED is set to TRUE when *STR needed line ending 
normalization.
+ *
+ * SCRATCH_POOL is used for temporary allocations.
  */
 static svn_error_t *
 normalize_string(const svn_string_t **str,
-                 svn_boolean_t *was_normalized,
-                 apr_pool_t *pool)
+                 svn_boolean_t *was_le_normalized,
+                 const char *encoding,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
 {
-  *was_normalized = FALSE;
+  *was_le_normalized = FALSE;
 
   if (*str == NULL)
     return SVN_NO_ERROR;
 
   SVN_ERR_ASSERT((*str)->data != NULL);
 
-  /* Detect inconsistent line ending style simply by looking
-     for carriage return (\r) characters. */
-  if (strchr((*str)->data, '\r') != NULL)
+  if (encoding)
     {
+      svn_string_t *new_str = NULL;
+      SVN_ERR(svn_subst_translate_string2(&new_str, NULL, was_le_normalized,
+                                          *str, encoding, result_pool,
+                                          scratch_pool));
+      *str = new_str;
+    }
+
+  /* Only detect inconsistent line ending style. This is accomplished by simply
+     looking for carriage return (\r) characters. */
+  else if (memchr((*str)->data, (unsigned char) '\r', (*str)->len) != NULL)
+    {
       /* Found some. Normalize. */
       const char* cstring = NULL;
       SVN_ERR(svn_subst_translate_cstring2((*str)->data, &cstring,
                                            "\n", TRUE,
                                            NULL, FALSE,
-                                           pool));
-      *str = svn_string_create(cstring, pool);
-      *was_normalized = TRUE;
+                                           scratch_pool));
+      *str = svn_string_create(cstring, result_pool);
+      *was_le_normalized = TRUE;
     }
 
   return SVN_NO_ERROR;
 }
 
 
-/* Normalize the line ending style of the values of properties in REV_PROPS
- * that "need translation" (according to svn_prop_needs_translation(),
- * currently all svn:* props) so that they contain only LF (\n) line endings.
- * The number of properties that needed normalization is returned in
- * *NORMALIZED_COUNT.
+/* Normalize the encoding and line ending style of the values of properties
+ * in REV_PROPS that "need translation" (according to
+ * svn_prop_needs_translation(), which is currently all svn:* props) so that
+ * they are encoded in UTF-8 and contain only LF (\n) line endings.
+ * The number of properties that needed line ending normalization is returned 
in
+ * *NORMALIZED_LE_COUNT. No re-encoding is performed if ENCODING is NULL.
  */
 svn_error_t *
 svnsync_normalize_revprops(apr_hash_t *rev_props,
-                           int *normalized_count,
+                           int *normalized_le_count,
+                           const char *encoding,
                            apr_pool_t *pool)
 {
   apr_hash_index_t *hi;
-  *normalized_count = 0;
+  *normalized_le_count = 0;
 
   for (hi = apr_hash_first(pool, rev_props);
        hi;
@@ -105,15 +121,15 @@ svnsync_normalize_revprops(apr_hash_t *rev_props,
 
       if (svn_prop_needs_translation(propname))
         {
-          svn_boolean_t was_normalized;
-          SVN_ERR(normalize_string(&propval, &was_normalized, pool));
-          if (was_normalized)
-            {
-              /* Replace the existing prop value. */
-              apr_hash_set(rev_props, propname, APR_HASH_KEY_STRING, propval);
-              /* And count this. */
-              (*normalized_count)++;
-            }
+          svn_boolean_t was_le_normalized;
+          SVN_ERR(normalize_string(&propval, &was_le_normalized, encoding, 
pool,
+                  pool));
+
+          /* Replace the existing prop value. */
+          apr_hash_set(rev_props, propname, APR_HASH_KEY_STRING, propval);
+
+          if (was_le_normalized)
+            (*normalized_le_count)++; /* Count it. */
         }
     }
   return SVN_NO_ERROR;
@@ -141,6 +157,7 @@ typedef struct {
   const svn_delta_editor_t *wrapped_editor;
   void *wrapped_edit_baton;
   const char *to_url;  /* URL we're copying into, for correct copyfrom URLs */
+  const char *prop_encoding;
   svn_boolean_t called_open_root;
   svn_boolean_t got_textdeltas;
   svn_revnum_t base_revision;
@@ -150,7 +167,8 @@ typedef struct {
   svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */
   svn_boolean_t svnmerge_migrated;  /* Did we convert svnmerge.py data? */
   svn_boolean_t svnmerge_blocked;   /* Was there any blocked svnmerge data? */
-  int *normalized_node_props_counter;  /* Where to count normalizations? */
+  int *le_normalized_node_props_counter;  /* Where to count line ending
+                                             normalizations? */
 } edit_baton_t;
 
 
@@ -407,9 +425,10 @@ change_file_prop(void *file_baton,
   if (svn_prop_needs_translation(name))
     {
       svn_boolean_t was_normalized;
-      SVN_ERR(normalize_string(&value, &was_normalized, pool));
+      SVN_ERR(normalize_string(&value, &was_normalized, eb->prop_encoding,
+                               pool, pool));
       if (was_normalized)
-        (*(eb->normalized_node_props_counter))++;
+        (*(eb->le_normalized_node_props_counter))++;
     }
 
   return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton,
@@ -505,9 +524,10 @@ change_dir_prop(void *dir_baton,
   if (svn_prop_needs_translation(name))
     {
       svn_boolean_t was_normalized;
-      SVN_ERR(normalize_string(&value, &was_normalized, pool));
+      SVN_ERR(normalize_string(&value, &was_normalized, eb->prop_encoding,
+                               pool, pool));
       if (was_normalized)
-        (*(eb->normalized_node_props_counter))++;
+        (*(eb->le_normalized_node_props_counter))++;
     }
 
   return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton,
@@ -572,10 +592,11 @@ svnsync_get_sync_editor(const svn_delta_editor_t *
                         void *wrapped_edit_baton,
                         svn_revnum_t base_revision,
                         const char *to_url,
+                        const char *prop_encoding,
                         svn_boolean_t quiet,
                         const svn_delta_editor_t **editor,
                         void **edit_baton,
-                        int *normalized_node_props_counter,
+                        int *le_normalized_node_props_counter,
                         apr_pool_t *pool)
 {
   svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
@@ -602,8 +623,9 @@ svnsync_get_sync_editor(const svn_delta_editor_t *
   eb->wrapped_edit_baton = wrapped_edit_baton;
   eb->base_revision = base_revision;
   eb->to_url = to_url;
+  eb->prop_encoding = prop_encoding;
   eb->quiet = quiet;
-  eb->normalized_node_props_counter = normalized_node_props_counter;
+  eb->le_normalized_node_props_counter = le_normalized_node_props_counter;
 
   if (getenv("SVNSYNC_UNSUPPORTED_STRIP_MERGEINFO"))
     {
@@ -629,4 +651,3 @@ svnsync_get_sync_editor(const svn_delta_editor_t *
 
   return SVN_NO_ERROR;
 }
-
Index: subversion/tests/cmdline/svnsync_tests.py
===================================================================
--- subversion/tests/cmdline/svnsync_tests.py   (revision 1052903)
+++ subversion/tests/cmdline/svnsync_tests.py   (working copy)
@@ -186,7 +186,7 @@ def verify_mirror(dest_sbox, exp_dump_file_content
 
   svntest.verify.compare_and_display_lines(
     "Dump files", "DUMP", exp_dump_file_contents, dest_dump)
-  
+
 def run_test(sbox, dump_file_name, subdir=None, exp_dump_file_name=None,
              bypass_prop_validation=False):
   """Load a dump file, sync repositories, and compare contents with the 
original
@@ -214,9 +214,9 @@ or another dump file."""
     exp_master_dumpfile_contents = master_dumpfile_contents
 
   verify_mirror(dest_sbox, exp_master_dumpfile_contents)
-  
 
 
+
 ######################################################################
 # Tests
 
@@ -780,11 +780,17 @@ def info_not_synchronized(sbox):
 #----------------------------------------------------------------------
 
 def copy_bad_line_endings(sbox):
-  "copy with inconsistent lineendings in svn:props"
+  "copy with inconsistent line endings in svn:* props"
   run_test(sbox, "copy-bad-line-endings.dump",
            exp_dump_file_name="copy-bad-line-endings.expected.dump",
            bypass_prop_validation=True)
 
+def copy_bad_line_endings2(sbox):
+  "copy with non-LF line endings in svn:* props"
+  run_test(sbox, "copy-bad-line-endings2.dump",
+           exp_dump_file_name="copy-bad-line-endings2.expected.dump",
+           bypass_prop_validation=True)
+
 #----------------------------------------------------------------------
 
 def delete_svn_props(sbox):
@@ -818,8 +824,8 @@ def commit_a_copy_of_root(sbox):
 #   svnsync: File not found: revision 4, path '/trunk/H/Z/B/lambda'
 #
 # See also http://svn.haxx.se/dev/archive-2010-11/0411.shtml and
-# 
 #
+#
 # Note: For those who may poke around this test in the future, r3 of
 # delete-revprops.dump was created with the following svnmucc command:
 #
@@ -904,6 +910,7 @@ test_list = [ None,
               info_synchronized,
               info_not_synchronized,
               copy_bad_line_endings,
+              copy_bad_line_endings2,
               delete_svn_props,
               commit_a_copy_of_root,
               XFail(descend_into_replace),
[[[
Add a command line option (--source-encoding) to the svnsync init, sync, and
copy-revprops subcommands that allows the user to specify the character
encoding of translatable properties from the source repository. This is needed
to allow svnsync to sync some older Subversion repositories that have
properties that were not encoded in UTF-8.

As discussed at:
  http://thread.gmane.org/gmane.comp.version-control.subversion.user/100020
  http://thread.gmane.org/gmane.comp.version-control.subversion.devel/122518
  http://thread.gmane.org/gmane.comp.version-control.subversion.devel/122550

Around half of the changes are to rename the "normalized count" variables so
that it is more clear that the counters only count line ending normalizations
and not re-encoding normalizations. The other half of the changes are cosmetic
(adding comments or trimming trailing whitespace in lines) or exist to pass the
argument to --source-encoding through to the functions that need it (mainly
normalize_string() in subversion/svnsync/sync.c).

* subversion/svnsync/main.c
  (svnsync__opt) Add svnsync_opt_source_encoding.
  (svnsync_cmd_table): Add svnsync_opt_source_encoding to the list of
    acceptable options for the init, sync, and copy-revprops subcommands.
  (svnsync_options): Add a description of the --source-encoding option.
  (opt_baton_t, subcommand_baton_t): Add the SOURCE_ENCODING field.
  (log_properties_normalized): Rename the NORMALIZED_REV_PROPS_COUNT parameter
    to LE_NORMALIZED_REV_PROPS_COUNT and the NORMALIZED_NODE_PROPS_COUNT
    parameter to LE_NORMALIZED_NODE_PROPS_COUNT.
  (copy_revprops): Add the PROP_ENCODING parameter. Rename the NORMALIZED_COUNT
    parameter to LE_NORMALIZED_COUNT.
  (make_subcommand_baton): Set the SOURCE_ENCODING field of the resulting
    subcommand_baton_t object to the value of SOURCE_ENCODING from the
    opt_baton_t object.
  (do_initialize, do_synchronize, do_copy_revprops, replay_rev_started,
   replay_rev_finished): Pass SOURCE_ENCODING to svnsync_* functions and
    copy_revprops().
  (replay_baton_t): Rename the NORMALIZED_REV_PROPS_COUNT field to
    LE_NORMALIZED_REV_PROPS_COUNT and the NORMALIZED_NODE_PROPS_COUNT field to
    LE_NORMALIZED_NODE_PROPS_COUNT.
  (main): Handle the case when the command line option is --source-encoding.
    Set the SOURCE_ENCODING field of the opt_baton_t object to either OPT_ARG
    or NULL.

* subversion/svnsync/sync.c
  (normalize_string): Rename WAS_NORMALIZED to WAS_LE_NORMALIZED to make clear
    that the counter only counts line ending normalizations. Add the ENCODING
    parameter. Handle the case when ENCODING is not NULL by calling
    svn_subst_translate_string2(). Switch to the "two pools" (result & scratch
    pools) pattern. Use memchr() instead of strchr() because the length of
    (*STR)->data is known.
  (svnsync_normalize_revprops): Rename NORMALIZED_COUNT to
    NORMALIZED_LE_COUNT. Add the ENCODING parameter. Move the call to
    apr_hash_set() outside of the if block (if (le_normalized)), as the
    property value may have been changed because of re-encoding even when no
    line ending was normalized.
  (edit_baton_t): Add the PROP_ENCODING field. Rename the
    NORMALIZED_NODE_PROPS_COUNTER field to NORMALIZED_LE_NODE_PROPS_COUNTER.
  (change_file_prop, change_dir_prop): Pass in PROP_ENCODING to
    normalize_string().
  (svnsync_get_sync_editor): Add the PROP_ENCODING parameter. Rename the
    NORMALIZED_NODE_PROPS_COUNTER parameter to NORMALIZED_LE_NODE_PROPS_COUNTER.

* subversion/svnsync/sync.h
  (svnsync_normalize_revprops): Add the ENCODING parameter. Rename the
    NORMALIZED_COUNT parameter to NORMALIZED_LE_COUNT.
  (svnsync_get_sync_editor): Add the PROP_ENCODING parameter. Rename the
    NORMALIZED_NODE_PROPS_COUNTER parameter to LE_NORMALIZED_NODE_PROPS_COUNTER.

* subversion/tests/cmdline/svnsync_tests.py
  (copy_bad_line_endings2): New test case.
  (test_list): Add copy_bad_line_endings2.

* subversion/tests/cmdline/svnsync_tests_data/copy-bad-line-endings2.dump
  A dump of a repository with the following features:
  1. The log message (`svn:log` revision property) of revision 1 has CRLF line
     endings.
  2. The log message of revision 2 has CR line endings.
  3. Revision 3 introduces an `svn:ignore` node property with CRLF line endings.
  4. Revision 4 introduces a custom node property, `x:related-to`, with CRLF
     line endings.

* 
subversion/tests/cmdline/svnsync_tests_data/copy-bad-line-endings2.expected.dump
  A dump of the expected result of using svnsync to sync
  copy-bad-line-endings2.dump.
]]]

Attachment: copy-bad-line-endings2_dumps.tar.gz
Description: GNU Zip compressed data

Reply via email to