Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h	(revision 1931310)
+++ subversion/include/svn_ra.h	(working copy)
@@ -2288,7 +2288,17 @@ svn_ra_has_capability(svn_ra_session_t *session,
  */
 #define SVN_RA_CAPABILITY_LIST "list"
 
+/**
+ * The capability of a server to accept svn:author and svn:date
+ * revision properties in a commit without overriding them.
+ * This is useful for tools like svnsync that need to preserve
+ * the original author and date from the source repository.
+ *
+ * @since New in 1.15.
+ */
+#define SVN_RA_CAPABILITY_COMMIT_ALLOW_REV_PROPS "commit-allow-rev-props"
 
+
 /*       *** PLEASE READ THIS IF YOU ADD A NEW CAPABILITY ***
  *
  * RA layers generally fetch all capabilities when asked about any
Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h	(revision 1931310)
+++ subversion/include/svn_repos.h	(working copy)
@@ -1521,7 +1521,7 @@ svn_repos_replay(svn_fs_root_t *root,
                  apr_pool_t *pool);
 
 /* ---------------------------------------------------------------*/
-
+
 /* Making commits. */
 
 /**
@@ -1540,9 +1540,15 @@ svn_repos_replay(svn_fs_root_t *root,
  * of the commit transaction, including author and log message if
  * present.
  *
- * @note #SVN_PROP_REVISION_DATE may be present in @a revprop_table, but
- * it will be overwritten when the transaction is committed.
+ * @a txn_flags is passed to svn_repos_fs_begin_txn_for_commit3() when
+ * creating a new transaction (i.e., when @a txn is NULL). It may include
+ * #SVN_FS_TXN_CLIENT_DATE to allow the caller to set the final svn:date
+ * of the revision via @a revprop_table.
  *
+ * @note If #SVN_FS_TXN_CLIENT_DATE is included in @a txn_flags, the
+ * #SVN_PROP_REVISION_DATE in @a revprop_table will be preserved as
+ * the commit date; otherwise it will be overwritten with the commit time.
+ *
  * Iff @a authz_callback is provided, check read/write authorizations
  * on paths accessed by editor operations.  An operation which fails
  * due to authz will return SVN_ERR_AUTHZ_UNREADABLE or
@@ -1566,7 +1572,7 @@ svn_repos_replay(svn_fs_root_t *root,
  * NULL).  Callers who supply their own transactions are responsible
  * for cleaning them up (either by committing them, or aborting them).
  *
- * @since New in 1.5. Since 1.6, @a commit_callback can be @c NULL.
+ * @since New in 1.15.
  *
  * @note Yes, @a repos_url_decoded is a <em>decoded</em> URL.  We realize
  * that's sorta wonky.  Sorry about that.
@@ -1576,6 +1582,32 @@ svn_repos_replay(svn_fs_root_t *root,
  * methods is a full, URI-encoded URL, not a relative path.
  */
 svn_error_t *
+svn_repos_get_commit_editor6(const svn_delta_editor_t **editor,
+                             void **edit_baton,
+                             svn_repos_t *repos,
+                             svn_fs_txn_t *txn,
+                             const char *repos_url_decoded,
+                             const char *base_path,
+                             apr_hash_t *revprop_table,
+                             apr_uint32_t txn_flags,
+                             svn_commit_callback2_t commit_callback,
+                             void *commit_baton,
+                             svn_repos_authz_callback_t authz_callback,
+                             void *authz_baton,
+                             apr_pool_t *pool);
+
+/**
+ * Similar to svn_repos_get_commit_editor6(), but with @a txn_flags
+ * always set to 0.
+ *
+ * @note #SVN_PROP_REVISION_DATE may be present in @a revprop_table, but
+ * it will be overwritten when the transaction is committed.
+ *
+ * @since New in 1.5. Since 1.6, @a commit_callback can be @c NULL.
+ * @deprecated Provided for backward compatibility with the 1.14 API.
+ */
+SVN_DEPRECATED
+svn_error_t *
 svn_repos_get_commit_editor5(const svn_delta_editor_t **editor,
                              void **edit_baton,
                              svn_repos_t *repos,
@@ -2510,16 +2542,41 @@ svn_repos_fs_commit_txn(const char **conflict_p,
  * repository object which contains the filesystem.  @a rev, @a
  * *txn_p, and @a pool are as in svn_fs_begin_txn().
  *
+ * @a flags is passed to svn_fs_begin_txn2() and may include
+ * #SVN_FS_TXN_CLIENT_DATE to allow the caller to set the final
+ * svn:date of the revision.
+ *
  * Before a txn is created, the repository's start-commit hooks are
  * run; if any of them fail, no txn is created, @a *txn_p is unaffected,
  * and #SVN_ERR_REPOS_HOOK_FAILURE is returned.
  *
  * @note @a revprop_table may contain an #SVN_PROP_REVISION_DATE property,
+ * which will be set on the transaction. If #SVN_FS_TXN_CLIENT_DATE is
+ * included in @a flags, this date will be preserved when the transaction
+ * is committed; otherwise it will be overwritten with the commit time.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_repos_fs_begin_txn_for_commit3(svn_fs_txn_t **txn_p,
+                                   svn_repos_t *repos,
+                                   svn_revnum_t rev,
+                                   apr_hash_t *revprop_table,
+                                   apr_uint32_t flags,
+                                   apr_pool_t *pool);
+
+
+/** Like svn_repos_fs_begin_txn_for_commit3(), but with @a flags
+ * set to #SVN_FS_TXN_CHECK_LOCKS only.
+ *
+ * @note @a revprop_table may contain an #SVN_PROP_REVISION_DATE property,
  * which will be set on the transaction, but that will be overwritten
  * when the transaction is committed.
  *
  * @since New in 1.5.
+ * @deprecated Provided for backward compatibility with the 1.14 API.
  */
+SVN_DEPRECATED
 svn_error_t *
 svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
                                    svn_repos_t *repos,
Index: subversion/libsvn_ra_local/ra_plugin.c
===================================================================
--- subversion/libsvn_ra_local/ra_plugin.c	(revision 1931310)
+++ subversion/libsvn_ra_local/ra_plugin.c	(working copy)
@@ -874,6 +874,7 @@ svn_ra_local__get_commit_editor(svn_ra_session_t *
 {
   svn_ra_local__session_baton_t *sess = session->priv;
   struct deltify_etc_baton *deb = apr_palloc(pool, sizeof(*deb));
+  apr_uint32_t txn_flags = 0;
 
   /* Set repos_root_url in commit info */
   remap_commit_callback(&callback, &callback_baton, session,
@@ -896,10 +897,20 @@ svn_ra_local__get_commit_editor(svn_ra_session_t *
   SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens,
                             session->pool, pool));
 
-  /* Copy the revprops table so we can add the username. */
+  /* Copy the revprops table so we can add/modify properties. */
   revprop_table = apr_hash_copy(pool, revprop_table);
-  svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
-                svn_string_create(sess->username, pool));
+
+  /* If the caller hasn't provided an author, use the session username.
+     This allows tools like svnsync to specify the original author. */
+  if (! svn_hash_gets(revprop_table, SVN_PROP_REVISION_AUTHOR))
+    svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
+                  svn_string_create(sess->username, pool));
+
+  /* If the caller provided a date, tell the FS layer to use it.
+     This allows tools like svnsync to preserve the original date. */
+  if (svn_hash_gets(revprop_table, SVN_PROP_REVISION_DATE))
+    txn_flags |= SVN_FS_TXN_CLIENT_DATE;
+
   svn_hash_sets(revprop_table, SVN_PROP_TXN_CLIENT_COMPAT_VERSION,
                 svn_string_create(SVN_VER_NUMBER, pool));
   svn_hash_sets(revprop_table, SVN_PROP_TXN_USER_AGENT,
@@ -906,10 +917,10 @@ svn_ra_local__get_commit_editor(svn_ra_session_t *
                 svn_string_create(sess->useragent, pool));
 
   /* Get the repos commit-editor */
-  return svn_repos_get_commit_editor5
+  return svn_repos_get_commit_editor6
          (editor, edit_baton, sess->repos, NULL,
           svn_path_uri_decode(sess->repos_url, pool), sess->fs_path->data,
-          revprop_table, deltify_etc, deb, NULL, NULL, pool);
+          revprop_table, txn_flags, deltify_etc, deb, NULL, NULL, pool);
 }
 
 
@@ -1674,6 +1685,7 @@ svn_ra_local__has_capability(svn_ra_session_t *ses
       || strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0
       || strcmp(capability, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) == 0
       || strcmp(capability, SVN_RA_CAPABILITY_LIST) == 0
+      || strcmp(capability, SVN_RA_CAPABILITY_COMMIT_ALLOW_REV_PROPS) == 0
       )
     {
       *has = TRUE;
@@ -1803,11 +1815,15 @@ svn_ra_local__get_commit_ev2(svn_editor_t **editor
   SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens,
                             session->pool, scratch_pool));
 
-  /* Copy the REVPROPS and insert the author/username.  */
+  /* Copy the REVPROPS so we can add/modify properties.  */
   revprops = apr_hash_copy(scratch_pool, revprops);
-  svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR,
-                svn_string_create(sess->username, scratch_pool));
 
+  /* If the caller hasn't provided an author, use the session username.
+     This allows tools like svnsync to specify the original author. */
+  if (! svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR))
+    svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR,
+                  svn_string_create(sess->username, scratch_pool));
+
   return svn_error_trace(svn_repos__get_commit_ev2(
                            editor, sess->repos, NULL /* authz */,
                            NULL /* authz_repos_name */, NULL /* authz_user */,
Index: subversion/libsvn_repos/commit.c
===================================================================
--- subversion/libsvn_repos/commit.c	(revision 1931310)
+++ subversion/libsvn_repos/commit.c	(working copy)
@@ -88,6 +88,10 @@ struct edit_baton
   /* Does this set of interfaces 'own' the commit transaction? */
   svn_boolean_t txn_owner;
 
+  /* Flags to pass to svn_repos_fs_begin_txn_for_commit3() when
+     creating the transaction (e.g., SVN_FS_TXN_CLIENT_DATE). */
+  apr_uint32_t txn_flags;
+
   /* svn transaction associated with this edit (created in
      open_root, or supplied by the public API caller). */
   svn_fs_txn_t *txn;
@@ -434,10 +438,11 @@ open_root(void *edit_baton,
      make our own. */
   if (eb->txn_owner)
     {
-      SVN_ERR(svn_repos_fs_begin_txn_for_commit2(&(eb->txn),
+      SVN_ERR(svn_repos_fs_begin_txn_for_commit3(&(eb->txn),
                                                  eb->repos,
                                                  youngest,
                                                  eb->revprop_table,
+                                                 eb->txn_flags,
                                                  eb->pool));
     }
   else /* Even if we aren't the owner of the transaction, we might
@@ -992,7 +997,7 @@ fetch_base_func(const char **filename,
 /*** Public interfaces. ***/
 
 svn_error_t *
-svn_repos_get_commit_editor5(const svn_delta_editor_t **editor,
+svn_repos_get_commit_editor6(const svn_delta_editor_t **editor,
                              void **edit_baton,
                              svn_repos_t *repos,
                              svn_fs_txn_t *txn,
@@ -999,6 +1004,7 @@ svn_error_t *
                              const char *repos_url_decoded,
                              const char *base_path,
                              apr_hash_t *revprop_table,
+                             apr_uint32_t txn_flags,
                              svn_commit_callback2_t commit_callback,
                              void *commit_baton,
                              svn_repos_authz_callback_t authz_callback,
@@ -1058,6 +1064,7 @@ svn_error_t *
   eb->fs = svn_repos_fs(repos);
   eb->txn = txn;
   eb->txn_owner = txn == NULL;
+  eb->txn_flags = txn_flags;
 
   *edit_baton = eb;
   *editor = e;
@@ -1075,6 +1082,28 @@ svn_error_t *
 }
 
 
+svn_error_t *
+svn_repos_get_commit_editor5(const svn_delta_editor_t **editor,
+                             void **edit_baton,
+                             svn_repos_t *repos,
+                             svn_fs_txn_t *txn,
+                             const char *repos_url_decoded,
+                             const char *base_path,
+                             apr_hash_t *revprop_table,
+                             svn_commit_callback2_t commit_callback,
+                             void *commit_baton,
+                             svn_repos_authz_callback_t authz_callback,
+                             void *authz_baton,
+                             apr_pool_t *pool)
+{
+  return svn_repos_get_commit_editor6(editor, edit_baton, repos, txn,
+                                      repos_url_decoded, base_path,
+                                      revprop_table, 0,
+                                      commit_callback, commit_baton,
+                                      authz_callback, authz_baton, pool);
+}
+
+
 #if 0
 static svn_error_t *
 ev2_check_authz(const struct ev2_baton *eb,
Index: subversion/libsvn_repos/fs-wrap.c
===================================================================
--- subversion/libsvn_repos/fs-wrap.c	(revision 1931310)
+++ subversion/libsvn_repos/fs-wrap.c	(working copy)
@@ -129,10 +129,11 @@ svn_repos_fs_commit_txn(const char **conflict_p,
 
 
 svn_error_t *
-svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
+svn_repos_fs_begin_txn_for_commit3(svn_fs_txn_t **txn_p,
                                    svn_repos_t *repos,
                                    svn_revnum_t rev,
                                    apr_hash_t *revprop_table,
+                                   apr_uint32_t flags,
                                    apr_pool_t *pool)
 {
   apr_array_header_t *revprops;
@@ -149,7 +150,7 @@ svn_error_t *
   /* Begin the transaction, ask for the fs to do on-the-fly lock checks.
      We fetch its name, too, so the start-commit hook can use it.  */
   SVN_ERR(svn_fs_begin_txn2(&txn, repos->fs, rev,
-                            SVN_FS_TXN_CHECK_LOCKS, pool));
+                            SVN_FS_TXN_CHECK_LOCKS | flags, pool));
   err = svn_fs_txn_name(&txn_name, txn, pool);
   if (err)
     return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
@@ -177,6 +178,18 @@ svn_error_t *
 
 
 svn_error_t *
+svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
+                                   svn_repos_t *repos,
+                                   svn_revnum_t rev,
+                                   apr_hash_t *revprop_table,
+                                   apr_pool_t *pool)
+{
+  return svn_repos_fs_begin_txn_for_commit3(txn_p, repos, rev, revprop_table,
+                                            0, pool);
+}
+
+
+svn_error_t *
 svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p,
                                   svn_repos_t *repos,
                                   svn_revnum_t rev,
Index: subversion/svnsync/svnsync.c
===================================================================
--- subversion/svnsync/svnsync.c	(revision 1931310)
+++ subversion/svnsync/svnsync.c	(working copy)
@@ -1029,6 +1029,7 @@ typedef struct replay_baton_t {
   subcommand_baton_t *sb;
   svn_boolean_t has_commit_revprops_capability;
   svn_boolean_t has_atomic_revprops_capability;
+  svn_boolean_t has_commit_allow_rev_props_capability;
   int normalized_rev_props_count;
   int normalized_node_props_count;
   const char *to_root;
@@ -1081,6 +1082,20 @@ filter_exclude_date_author_sync(const char *key)
   return FALSE;
 }
 
+/* Return TRUE iff KEY is the name of any svnsync property.
+ * Implements filter_func_t. Use with filter_props() to filter out
+ * svnsync properties while keeping svn:author and svn:date.
+ */
+static svn_boolean_t
+filter_exclude_sync_only(const char *key)
+{
+  if (strncmp(key, SVNSYNC_PROP_PREFIX,
+              sizeof(SVNSYNC_PROP_PREFIX) - 1) == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
 /* Return FALSE iff KEY is the name of an svn:date or svn:author or any svnsync
  * property. Implements filter_func_t. Use with filter_props() to filter out
  * all properties except svn:date and svn:author and svnsync properties.
@@ -1285,14 +1300,24 @@ replay_rev_started(svn_revnum_t revision,
      all the revision properties from the source repositories, except
      'svn:author' and 'svn:date', those are not guaranteed to get
      through the editor anyway.
+     If we're syncing to a server that supports commit-allow-rev-props
+     (like file:// via ra_local), include author and date so they can
+     be set directly on the commit without a follow-up revprop change.
      If we're syncing to an non-commit-revprops capable server, filter
      out all revprops except svn:log and add them later in
-     revplay_rev_finished. */
-  filtered = filter_props(&filtered_count, rev_props,
-                          (rb->has_commit_revprops_capability
-                            ? filter_exclude_date_author_sync
-                            : filter_include_log),
-                          pool);
+     replay_rev_finished. */
+  if (rb->has_commit_allow_rev_props_capability)
+    filtered = filter_props(&filtered_count, rev_props,
+                            filter_exclude_sync_only,
+                            pool);
+  else if (rb->has_commit_revprops_capability)
+    filtered = filter_props(&filtered_count, rev_props,
+                            filter_exclude_date_author_sync,
+                            pool);
+  else
+    filtered = filter_props(&filtered_count, rev_props,
+                            filter_include_log,
+                            pool);
 
   /* svn_ra_get_commit_editor3 requires the log message to be
      set. It's possible that we didn't receive 'svn:log' here, so we
@@ -1373,13 +1398,27 @@ replay_rev_finished(svn_revnum_t revision,
 
   /* Ok, we're done with the data, now we just need to copy the remaining
      'svn:date' and 'svn:author' revprops and we're all set.
+     If the server supports commit-allow-rev-props (like file://), the
+     author and date were already set during the commit, so we only need
+     to handle any extra svnsync properties here.
      If the server doesn't support revprops-in-a-commit, we still have to
      set all revision properties except svn:log. */
-  filtered = filter_props(&filtered_count, rev_props,
-                          (rb->has_commit_revprops_capability
-                            ? filter_include_date_author_sync
-                            : filter_exclude_log),
-                          subpool);
+  if (rb->has_commit_allow_rev_props_capability)
+    {
+      /* Author and date were set during commit; we may still need to
+         handle any svnsync-specific properties if present (though they
+         should have been filtered out). */
+      filtered_count = 0;
+      filtered = apr_hash_make(subpool);
+    }
+  else if (rb->has_commit_revprops_capability)
+    filtered = filter_props(&filtered_count, rev_props,
+                            filter_include_date_author_sync,
+                            subpool);
+  else
+    filtered = filter_props(&filtered_count, rev_props,
+                            filter_exclude_log,
+                            subpool);
 
   /* If necessary, normalize encoding and line ending style, and add the number
      of EOL-normalized properties to the overall count in the replay baton. */
@@ -1387,8 +1426,9 @@ replay_rev_finished(svn_revnum_t revision,
                                      rb->sb->source_prop_encoding, pool));
   rb->normalized_rev_props_count += normalized_count;
 
-  SVN_ERR(write_revprops(&filtered_count, rb->to_session, revision, filtered,
-                         NULL, subpool));
+  if (apr_hash_count(filtered) > 0)
+    SVN_ERR(write_revprops(&filtered_count, rb->to_session, revision, filtered,
+                           NULL, subpool));
 
   /* Remove all extra properties in TARGET. */
   SVN_ERR(remove_props_not_in_source(rb->to_session, revision,
@@ -1415,8 +1455,11 @@ replay_rev_finished(svn_revnum_t revision,
                                     ? &rev_str : NULL,
                                   NULL, subpool));
 
-  /* Notify the user that we copied revision properties. */
-  if (! rb->sb->quiet)
+  /* Notify the user that we copied revision properties.
+     If we used commit-allow-rev-props, author and date were set during the
+     commit itself, so no additional "copied properties" message is needed
+     (the commit_callback already printed "Committed revision X."). */
+  if (! rb->sb->quiet && !rb->has_commit_allow_rev_props_capability)
     SVN_ERR(log_properties_copied(filtered_count > 0, revision, subpool));
 
   svn_pool_destroy(subpool);
@@ -1551,6 +1594,19 @@ do_synchronize(svn_ra_session_t *to_session,
                                 SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
                                 pool));
 
+  /* Check if the destination supports setting author/date directly in
+     the commit, avoiding the need to update them as a second step. */
+  SVN_ERR(svn_ra_has_capability(rb->to_session,
+                                &rb->has_commit_allow_rev_props_capability,
+                                SVN_RA_CAPABILITY_COMMIT_ALLOW_REV_PROPS,
+                                pool));
+
+  if (! baton->quiet && rb->has_commit_allow_rev_props_capability)
+    SVN_ERR(svn_cmdline_printf(pool,
+                               _("Destination supports commit-time author "
+                                 "and date; no post-commit revprop sync "
+                                 "needed.\n")));
+
   start_revision = last_merged + 1;
   end_revision = from_latest;
 
