Okay, I've touched up the @since and @deprecated lines of the patch to
point to 1.16 and 1.15, respectively.  I also took a crack at writing a
formal log message (which helped me also to more thoroughly review the
change).  I'm re-posting the patch (with inline log) here for something
like safekeeping.

-- Mike

On Mon, Feb 2, 2026 at 6:51 PM C. Michael Pilato <[email protected]>
wrote:

> Actually, I did spot something that I think needs correction.  The "@since
> New in 1.15." annotations should say "New in 1.16", right?
>
> -- Mike
>
> On Sun, Feb 1, 2026 at 10:19 PM C. Michael Pilato <[email protected]>
> wrote:
>
>> Today, I setup a Subversion development environment for the first time in
>> ... more years than I can recall.  I can confirm that the test suite passes
>> with this patch, and that the patch itself makes sense to me.  I also used
>> it for my own svnsync activity (I do nightly backups of personal
>> repositories), and it seems to work as advertised.
>>
>> Not gonna lie, I feel too "out of the game" to commit this patch
>> outright.  But, I *am* comfortable giving a +1 on it, for whatever that
>> nuance might mean.
>>
>> -- Mike
>>
>> On Sat, Jan 31, 2026 at 2:48 PM Timofei Zhakov <[email protected]> wrote:
>>
>>> On Thu, Jan 29, 2026 at 10:47 PM Jordan Peck via dev <
>>> [email protected]> wrote:
>>>
>>>> Hi all,
>>>>
>>>> I'd like to propose a patch that optimises svnsync when synchronizing to
>>>> local filesystem repositories (file:// URLs). Currently, svnsync
>>>> performs
>>>> a two-step process for each revision:
>>>>
>>>>   1. Commit the revision content (author/date are set to the current
>>>>      user and current time)
>>>>   2. Update svn:author and svn:date revision properties to match the
>>>>      source repository
>>>>
>>>> This works but is inefficient for file:// URLs where we have direct
>>>> repository access and can set these properties correctly during the
>>>> initial commit.
>>>>
>>>>
>>>> THE PROBLEM
>>>> -----------
>>>>
>>>> When svnsync replays a revision from a source repository, the commit
>>>> is created with the local user as the author and the current time as
>>>> the date. After the commit completes, svnsync then makes separate
>>>> svn_ra_change_rev_prop2() calls to update svn:author and svn:date to
>>>> match the source repository.
>>>>
>>>> For remote servers (svn://, http://), this two-step process is
>>>> necessary
>>>> because the server enforces its own author/date policies. However, for
>>>> file:// URLs via ra_local, we have direct access to the repository and
>>>> can bypass this limitation.
>>>>
>>>> We have an SVN server acting as the slave via the Apache module proxy,
>>>> the slave server is kept in sync with the master using svnsync. This
>>>> two-step approach has caused issues with various tools that monitor the
>>>> slave SVN server for new revisions, they will occasionally get the data
>>>> for a synced revision that hasn't yet had the revision properties
>>>> copied. Having the sync be a single atomic operation means this can
>>>> never happen.
>>>>
>>>>
>>>> THE SOLUTION
>>>> ------------
>>>>
>>>> This patch introduces a new RA capability "commit-allow-rev-props" that
>>>> ra_local advertises. When svnsync detects this capability on the
>>>> destination repository, it:
>>>>
>>>>   1. Includes svn:author and svn:date in the initial commit's revprop
>>>>      table (instead of filtering them out)
>>>>
>>>>   2. ra_local now conditionally preserves these properties:
>>>>      - svn:author is only set to the session user if not already
>>>> provided
>>>>      - If svn:date is provided, the SVN_FS_TXN_CLIENT_DATE flag is
>>>> passed
>>>>        to svn_fs_begin_txn2() so the filesystem uses that date
>>>>
>>>>   3. Skips the post-commit revprop update step since author/date are
>>>>      already correct
>>>>
>>>>
>>>> CHANGES OVERVIEW
>>>> ----------------
>>>>
>>>> subversion/include/svn_ra.h:
>>>>   - Added SVN_RA_CAPABILITY_COMMIT_ALLOW_REV_PROPS capability definition
>>>>
>>>> subversion/include/svn_repos.h:
>>>>   - Added svn_repos_fs_begin_txn_for_commit3() with flags parameter
>>>>   - Added svn_repos_get_commit_editor6() with txn_flags parameter
>>>>
>>>> subversion/libsvn_repos/fs-wrap.c:
>>>>   - Implemented svn_repos_fs_begin_txn_for_commit3() which passes flags
>>>>     (including SVN_FS_TXN_CLIENT_DATE) to svn_fs_begin_txn2()
>>>>
>>>> subversion/libsvn_repos/commit.c:
>>>>   - Added txn_flags to edit_baton structure
>>>>   - Implemented svn_repos_get_commit_editor6() to support txn_flags
>>>>   - Updated open_root() to use the new begin_txn function
>>>>
>>>> subversion/libsvn_ra_local/ra_plugin.c:
>>>>   - Advertises SVN_RA_CAPABILITY_COMMIT_ALLOW_REV_PROPS
>>>>   - Modified get_commit_editor to preserve caller-provided author/date
>>>>   - Sets SVN_FS_TXN_CLIENT_DATE when date is provided in revprops
>>>>
>>>> subversion/svnsync/svnsync.c:
>>>>   - Detects the new capability on the destination
>>>>   - When capability is present, includes author/date in commit revprops
>>>>   - Skips post-commit revprop sync when capability is present
>>>>   - Updated user-facing messages to reflect the optimization
>>>>
>>>>
>>>> USER-VISIBLE CHANGES
>>>> --------------------
>>>>
>>>> When synchronizing to a file:// URL, svnsync now displays:
>>>>
>>>>   Destination supports commit-time author and date; no post-commit
>>>>   revprop sync needed.
>>>>   Committed revision 1.
>>>>   Committed revision 2.
>>>>   ...
>>>>
>>>> Instead of the previous:
>>>>
>>>>   Committed revision 1.
>>>>   Copied properties for revision 1.
>>>>   Committed revision 2.
>>>>   Copied properties for revision 2.
>>>>   ...
>>>>
>>>>
>>>> BENEFITS
>>>> --------
>>>>
>>>> 1. Atomicity: Author and date are set in the same transaction as the
>>>>    commit, rather than being updated afterward.
>>>>
>>>> 2. No behavioral change for remote servers: The optimization only
>>>>    applies when the destination advertises the capability, so svn://
>>>>    and http:// synchronization continues to work as before.
>>>>
>>>> 3. Reduced I/O: Eliminates separate revprop change operations for each
>>>>    revision synced to a file:// destination.
>>>>
>>>> COMPATIBILITY
>>>> -------------
>>>>
>>>> - The new capability is only advertised by ra_local, so this is a
>>>>   client-side optimization with no server protocol changes.
>>>>
>>>> - Existing svnsync mirrors continue to work unchanged.
>>>>
>>>> - The new API functions (svn_repos_fs_begin_txn_for_commit3 and
>>>>   svn_repos_get_commit_editor6) maintain backward compatibility
>>>>   through wrapper functions.
>>>>
>>>>
>>>> TESTING
>>>> -------
>>>>
>>>> I have tested this with svnsync to file:// URLs and verified that:
>>>> - Revision properties (author, date, log) match the source repository
>>>> - The new optimization message appears at sync start
>>>> - No "Copied properties" messages appear
>>>> - Subsequent incremental syncs work correctly
>>>>
>>>>
>>>> Please review and let me know if you have any questions or suggestions.
>>>>
>>>> Thanks,
>>>> Jordan Peck
>>>>
>>>
>>> Hi,
>>>
>>> +1
>>>
>>> I didn't look into the patch in detail, but the idea sounds good to me.
>>>
>>> --
>>> Timofei Zhakov
>>>
>>
Introduce a new RA capability "commit-allow-rev-props" (currently used
only by ra-local) which indicates that the commit author and date can
be accepted via the commit editor as-is without needing to be
overridden at the end by the session user and current date.  Teach
svnsync to make use of this when syncronizing via ra-local.

* subversion/include/svn_ra.h
  (SVN_RA_CAPABILITY_COMMIT_ALLOW_REV_PROPS): New capability definition.

* subversion/include/svn_repos.h
  (svn_repos_fs_begin_txn_for_commit3): New version which adds 'flags'
     parameter and replaces...
  (svn_repos_fs_begin_txn_for_commit2): ...which is now deprecated.
  (svn_repos_get_commit_editor6): New version which adds 'txn_flags'
     parameter and replaces...
  (svn_repos_get_commit_editor5): ...which is now deprecated.

* subversion/libsvn_ra_local/ra_plugin.c:
  (svn_ra_local__get_commit_editor): Preserve caller-provided commit
    author and date, and now uses svn_repos_get_commit_editor6().
  (svn_ra_local__has_capability): Advertise new capability
    SVN_RA_CAPABILITY_COMMIT_ALLOW_REV_PROPS.
  (svn_ra_local__get_commit_ev2): Preserve the caller-provided commit
     author.

* subversion/libsvn_repos/commit.c
  (edit_baton): Add 'txn_flags'.
  (open_root): Use svn_repos_fs_begin_txn_for_commit3() now.
  (svn_repos_get_commit_editor6): New version which adds 'txn_flags'
     parameter and replaces...
  (svn_repos_get_commit_editor5): ...this, which is just a
     compatibility wrapper now.

* subversion/libsvn_repos/fs-wrap.c
  (svn_repos_fs_begin_txn_for_commit3): New version which adds 'flags'
     parameter (passed to svn_fs_begin_txn2()) and replaces...
  (svn_repos_fs_begin_txn_for_commit2): ...this, which is just a
     compatibility wrapper now.

* subversion/svnsync/svnsync.c
  (filter_exclude_sync_only): New.
  (replay_rev_started): Rework revprop filtering logic to preserve the
    commit author and date where the server supports the new
    commit-allow-rev-props capability.
  (replay_rev_finished): In situations where the server supports the
    new commit-allow-rev-props capability, assume the commit author
    and date have already been set (and don't set them again).  Also,
    avoid notifying about revprops being copied post facto when, in
    fact, they weren't.
  (do_synchronize): Check for the new commit-allow-rev-props
    capability, notifying the user if the post-commit revprop sync
    step will be skipped as unnecessary.

Patch by: Jordan Peck <[email protected]>
(tweaked by cmpilato.)

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.16.
+ */
+#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)
@@ -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
o+ * 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.16.
  *
  * @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.15 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.16.
+ */
+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.15 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;
 

Reply via email to