diff --git a/subversion/include/svn_string.h b/subversion/include/svn_string.h
index 2154ef7..c71fc58 100644
--- a/subversion/include/svn_string.h
+++ b/subversion/include/svn_string.h
@@ -290,6 +290,23 @@ svn_stringbuf_first_non_whitespace(const svn_stringbuf_t *str);
 void
 svn_stringbuf_strip_whitespace(svn_stringbuf_t *str);
 
+/** Return a svn_string_t with contents of @a str with linebreaks normalized.
+ *
+ * @a str contains zero or more linebreaks of the form \n or \r\n.
+ * The returned svn_string_t will have the same number of linebreaks
+ * as @a str but will contain no \r characters.
+ *
+ * The returned svn_string_t may be identical (the same memory) to @a
+ * str, when @a str is already fully eol-normalized.
+ *
+ * We make no attempt to handle lone \r characters (as used in Classic
+ * Mac OS) correctly as this is not a supported platform. (This
+ * simplifies the implementation.)
+ */
+svn_string_t *
+svn_string_with_normalized_eol_style(const svn_string_t *str, apr_pool_t *pool);
+
+
 /** Return position of last occurrence of @a ch in @a str, or return
  * @a str->len if no occurrence.
  */
diff --git a/subversion/libsvn_subr/svn_string.c b/subversion/libsvn_subr/svn_string.c
index c64ebdc..feb78e7 100644
--- a/subversion/libsvn_subr/svn_string.c
+++ b/subversion/libsvn_subr/svn_string.c
@@ -460,6 +460,46 @@ svn_stringbuf_strip_whitespace(svn_stringbuf_t *str)
 }
 
 
+static void
+svn_stringbuf_normalize_eol_style(svn_stringbuf_t *str)
+{
+  char *r, *w; /* read and write pointer, w <= r */
+  char *z;     /* end of buf pointer, r < z   */
+
+  /* We walk r and w along the buffer, copying from r to w. Initially
+     they are equal, though w will lag behind r as we encounter '\r'
+     chars. */
+
+  z = &(str->data[str->len]);
+  w = r = &(str->data[0]);
+
+  while (r < z)
+    {
+      if (*r != '\r') /* Copy char and advance the writer when not \r */
+        {             /* Copying from r to w is a no-op when w and r are */
+          *w++ = *r;  /* equal. Advancing w, however always takes place */
+        }
+      ++r;            /* always advance the reader */
+    }
+
+  /* We must now re-establish the invariants of str.
+     w points to the correct place for the null terminator:
+     one beyond the last copied character. */
+  *w = '\0';
+  str->len = w - &(str->data[0]);
+}
+
+/* Current implementation always returns a new svn_string_t, though
+   API does not promise this. */
+svn_string_t *
+svn_string_with_normalized_eol_style(const svn_string_t *str, apr_pool_t *pool)
+{
+  svn_stringbuf_t *buf;
+  buf = svn_stringbuf_create_from_string(str, pool);
+  svn_stringbuf_normalize_eol_style(buf);
+  return svn_string_create_from_buf(buf, pool);
+}
+
 apr_size_t
 svn_stringbuf_find_char_backward(const svn_stringbuf_t *str, char ch)
 {
diff --git a/subversion/svnsync/main.c b/subversion/svnsync/main.c
index 3772041..987397a 100644
--- a/subversion/svnsync/main.c
+++ b/subversion/svnsync/main.c
@@ -222,7 +222,6 @@ check_lib_versions(void)
   return svn_ver_check_list(&my_version, checklist);
 }
 
-
 /* Acquire a lock (of sorts) on the repository associated with the
  * given RA SESSION.
  */
@@ -754,6 +753,7 @@ 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? */
+  svn_boolean_t normalize_prop_eol_style; /* strip \r from svn: props? */
 } edit_baton_t;
 
 
@@ -1003,6 +1003,14 @@ change_file_prop(void *file_baton,
       eb->svnmerge_blocked = TRUE;
     }
 
+  if (eb->normalize_prop_eol_style && value && svn_prop_is_svn_prop(name))
+    {
+      svn_string_t *real_value;
+      real_value = svn_string_with_normalized_eol_style(value, pool);
+      return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton,
+                                                  name, real_value, pool);
+    }
+
   return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton,
                                               name, value, pool);
 }
@@ -1084,6 +1092,10 @@ change_dir_prop(void *dir_baton,
       name = SVN_PROP_MERGEINFO;
       eb->svnmerge_migrated = TRUE;
     }
+  else if (eb->normalize_prop_eol_style && value && svn_prop_is_svn_prop(name))
+    {
+      real_value = svn_string_with_normalized_eol_style(value, pool);
+    }
 
   /* Remember if we see any svnmerge-blocked properties. */
   if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0))
@@ -1198,6 +1210,10 @@ get_sync_editor(const svn_delta_editor_t *wrapped_editor,
       eb->migrate_svnmerge = TRUE;
       eb->strip_mergeinfo = TRUE;
     }
+  if (getenv("SVNSYNC_UNSUPPORTED_NORMALIZE_PROP_EOL_STYLE"))
+    {
+      eb->normalize_prop_eol_style = TRUE;
+    }
 
   *editor = tree_editor;
   *edit_baton = eb;
@@ -1389,6 +1405,18 @@ replay_rev_started(svn_revnum_t revision,
     apr_hash_set(filtered, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING,
                  svn_string_create("", pool));
 
+  /* Now that we have assured that there will be an svn:log message,
+     normalize line breaks in log message.*/
+  if (getenv("SVNSYNC_UNSUPPORTED_NORMALIZE_PROP_EOL_STYLE"))
+    {
+      svn_string_t *svn_log;
+      svn_log = apr_hash_get(filtered, SVN_PROP_REVISION_LOG,
+                             APR_HASH_KEY_STRING);
+      svn_log = svn_string_with_normalized_eol_style(svn_log, pool);
+      apr_hash_set(filtered, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING,
+                   svn_log);
+    }
+
   SVN_ERR(svn_ra_get_commit_editor3(rb->to_session, &commit_editor,
                                     &commit_baton,
                                     filtered,
