[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] (3rd try) Fix issue #1976: Set arbitrary revision properties (revprops) during commit.

From: Gerco Ballintijn <gerco_at_ballintijn.com>
Date: 2007-03-11 12:24:32 CET

(Apologies if this email appears multiple times :-( )

Hi,

After Malcolm Rowe and Peter Lundblad concluded that my second approach
to setting revision properties during commits was too complicated, I went
back to my first approach and applied all comments from them (and those
from David Glasser) to that patch. Below is the end result. Apart from
their comments, I also noticed that the svn protocol code I wrote was the
same as existing code, so I removed and restructured that. I moved the
check against using standard Subversion properties deeper in the code to
keep it more centralized. This check is now done in the repos and dav
libraries. This patch doesn't support the serf-ra library. I'm looking
into that. I hope it's sufficiently similar to the dav stuff.

With regards,
Gerco.

[[[
Fix issue #1976: Set arbitrary revision properties (revprops) during
commit.

This patch adds a "--with-revprop" option to the various URL-based
modification operations (e.g., commit, mkdir, delete). This option
can be used multiple times to set multiple revision properties. The
argument of the --with-revprop option is of the form "name=value".
The fix also extends the svn protocol to transfer the revision
properties.

The revision properties are internally passed around in a hash table.
This commit thus consists of six types of changes:

1. Code added to set the revision properties in an FS transaction.
2. Code added to pass the revision properties table through various
     layers, either as parameter or field (client and server side).
3. Code added to marshall and unmarshall the revision properties table
     for the svn protocol.
4. Code added to marshall the revision properties table for the dav
     protocol (but exisiting code is reused for unmarshalling).
5. Code added to extract revision properties from the command line and
     start passing them down.
6. Code added to test the new functionality.

* subversion/include/svn_client.h
    (svn_client_ctx_t): Added the revprop_table field to hold revision
    properties that need to be set. It is not allowed to contain
    standard Subversion properties.

* subversion/include/svn_props.h (svn_prop_has_svn_props): New function
    to determine if a table with properties contains a standard Subversion
    property.

* subversion/include/svn_ra.h
    (svn_ra_get_commit_editor3): New prototype, obsoletes
    svn_ra_get_commit_editor2.
    (svn_ra_get_commit_editor2): Deprecate.

* subversion/include/svn_ra_svn.h
    (svn_ra_svn_write_proplist,
    svn_ra_svn_parse_proplist): Existing functions, moved and made public.

* subversion/include/svn_repos.h
    (svn_repos_fs_begin_txn_for_commit2): New prototype, obsoletes
    svn_repos_fs_begin_txn_for_commit.
    (svn_repos_fs_begin_txn_for_commit): Deprecate.
    (svn_repos_get_commit_editor5): New prototype, obsoletes
    svn_repos_get_commit_editor4.
    (svn_repos_get_commit_editor4): Deprecate.

* subversion/libsvn_repos/repos.h (svn_repos__change_txn_props):
    New function to change a number of transaction properties using
    the properties stored in a hash table.

* subversion/libsvn_repos/commit.c
    (edit_baton): Added revprop_table field to store revision properties.
    (open_root): Added code to pass revprop_table parameter and to set
    the properties in revprop_table directly on a transaction.
    (revprop_table_dup): New function to make a deep copy of a
revprop_table.
    (svn_repos_get_commit_editor5): Check for standard revision properties
    and pass on a copy of revprop_table in edit_baton.
    (svn_repos_get_commit_editor4): Added backward-compatability call
    to svn_repos_get_commit_editor5.

* subversion/libsvn_repos/fs-wrap.c
    (svn_repos__change_txn_props): New function to change a number of
    properties of a transaction using the properties stored in a hash table.
    (svn_repos_fs_begin_txn_for_commit2): Added code to set the revision
    properties in the new transaction by calling
svn_repos__change_txn_props.
    (svn_repos_fs_begin_txn_for_commit): Added backward-compatability
    call to svn_repos_fs_begin_txn_for_commit2.

* subversion/libsvn_ra/ra_loader.h
    (svn_ra__vtable_t): Added revprop_table parameter to the
    get_commit_editor function-pointer field.

* subversion/libsvn_ra/wrapper_template.h
    (compat_get_commit_editor): Added backward-compatability call.

* subversion/libsvn_ra/ra_loader.c
    (svn_ra_get_commit_editor3): Pass revprop_table as parameter.
    (svn_ra_get_commit_editor2): Added backward-compatability call
    to svn_ra_get_commit_editor3.

* subversion/libsvn_ra_local/ra_plugin.c
    (svn_ra_local__get_commit_editor): Pass revprop_table as parameter
    to new commit editor.

* subversion/libsvn_ra_svn/protocol (3.1.1. Main Command Set):
    Added the revision properties to the protocol description.

* subversion/libsvn_ra_svn/client.c
    (parse_proplist): Moved to marshal.c and made public.
    (ra_svn_commit): Added code to send the revprop_table to the server.
    (ra_svn_rev_proplist, ra_svn_get_file, ra_svn_get_dir,
    ra_svn_get_file_revs): Use public version of parse_proplist.

* subversion/libsvn_ra_svn/marshal.c
    (svn_ra_svn_write_proplist): Extracted from svnserve/serve.c and
    made public.
    (svn_ra_svn_parse_proplist): Extracted from client.c and made public.

* subversion/libsvn_ra_dav/ra_dav.h
    (svn_ra_dav__get_commit_editor): Added revprop_table parameter to
    prototype.

* subversion/libsvn_ra_dav/commit.c
    (apply_log_message): Renamed ...
    (apply_revprops): ... into this, and added revprop_table marshalling.
    (svn_ra_dav__get_commit_editor): Added check for standard revision
    properties and a call to apply_revprops to handle the log message and
    revprop_table.

* subversion/libsvn_client/add.c
    (mkdir_urls): Pass revprop_table as parameter to
    new commit editor.

* subversion/libsvn_client/commit.c
    (get_ra_editor): Pass revprop_table as parameter to
    new commit editor.

* subversion/libsvn_client/copy.c
    (repos_to_repos_copy): Pass revprop_table as parameter to
    new commit editor.
    (wc_to_repos_copy): Idem.

* subversion/libsvn_client/delete.c
    (delete_urls): Pass revprop_table as parameter to
    new commit editor.

* subversion/libsvn_client/prop_commands.c
    (propset_on_url): Pass revprop_table as parameter to
    new commit editor.

* subversion/libsvn_subr/properties.c
    (svn_prop_has_svn_props): New function to determine if a table with
    properties contains a standard Subversion property.

* subversion/svn/cl.h
    (svn_cl__longopt_t): Added svn_cl__with_revprop_opt enum value.
    (svn_cl__opt_state_t): Added revprop_table field.

* subversion/svn/main.c
    (svn_cl__options): Added --with-revprop option.
    (SVN_CL__LOG_MSG_OPTIONS): Added --with-revprop to other commit
    related options.
    (add_revprop_from_string): New function to extract revision
    property from command line argument.
    (main): Added call to add_revprop_from_string when svn_cl__options
    is found.

* subversion/svn/commit-cmd.c
    (svn_cl__commit): Pass on the revprop_table field.

* subversion/svn/copy-cmd.c
    (svn_cl__copy): Check whether the revprop_table field is set only when
    actually commiting, and pass on the revprop_table field.

* subversion/svn/delete-cmd.c
    (svn_cl__delete): Check whether the revprop_table field is set only when
    actually commiting, and pass on the revprop_table field.

* subversion/svn/import-cmd.c
    (svn_cl__import): Pass on the revprop_table field.

* subversion/svn/mkdir-cmd.c
    (svn_cl__mkdir): Check whether the revprop_table field is set only when
    actually commiting, and pass on the revprop_table field.

* subversion/svn/move-cmd.c
    (svn_cl__move): Check whether the revprop_table field is set only when
    actually commiting, and pass on the revprop_table field.

* subversion/svn/propedit-cmd.c
    (svn_cl__propedit): Check whether the revprop_table field is setonly
when
    actually commiting, and pass on the revprop_table field.

* subversion/svnserve/serve.c
    (write_proplist): Moved to libsvn_ra_svn/marshal.c and made public.
    (rev_proplist, get_file, get_dir, file_rev_handler): Use public version
    of write_proplist.
    (commit): Call unmarshalling code when needed, check for standard
    revision properties, and pass on the revprop_table as parameter.

* subversion/tests/cmdline/commit_tests.py
    (mkdir_with_revprop, delete_with_revprop, commit_with_revprop,
     import_with_revprop, copy_R2R_with_revprop, copy_WC2R_with_revprop,
     move_R2R_with_revprop, propedit_with_revprop,
     set_multiple_props_with_revprop, set_std_props_with_revprop,
     use_empty_string_as_revprop_pair, use_empty_value_in_revprop_pair,
     no_equals_in_revprop_pair): New test functions.

* subversion/tests/cmdline/svneditor.py (append_foo): Added test code
    to extend (change) a file.

* subversion/tests/cmdline/svntest/actions.py
    (UnorderedOutput): Added class to store unordered lines of output.
    (run_and_verify_svn): Added code to deal with unordered output.
    (display_lines): Added code to print unordered output.
    (compare_unordered_and_display_lines): Added call to
    main.compare_unordered_output to do actual unordered comparison.
]]]

Index: subversion/libsvn_ra/wrapper_template.h
===================================================================
--- subversion/libsvn_ra/wrapper_template.h (revision 23757)
+++ subversion/libsvn_ra/wrapper_template.h (working copy)
@@ -136,7 +136,7 @@
                                   callback, callback_baton,
                                   pool);
   return VTBL.get_commit_editor(session_baton, editor, edit_baton, log_msg,
- callback2, callback2_baton,
+ NULL, callback2, callback2_baton,
                                 NULL, TRUE, pool);
 }
 
Index: subversion/libsvn_ra/ra_loader.c
===================================================================
--- subversion/libsvn_ra/ra_loader.c (revision 23757)
+++ subversion/libsvn_ra/ra_loader.c (working copy)
@@ -380,10 +380,11 @@
   return session->vtable->rev_prop(session, rev, name, value, pool);
 }
 
-svn_error_t *svn_ra_get_commit_editor2(svn_ra_session_t *session,
+svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session,
                                        const svn_delta_editor_t **editor,
                                        void **edit_baton,
                                        const char *log_msg,
+ apr_hash_t *revprop_table,
                                        svn_commit_callback2_t callback,
                                        void *callback_baton,
                                        apr_hash_t *lock_tokens,
@@ -391,10 +392,26 @@
                                        apr_pool_t *pool)
 {
   return session->vtable->get_commit_editor(session, editor, edit_baton,
- log_msg, callback, callback_baton,
- lock_tokens, keep_locks, pool);
+ log_msg, revprop_table, callback,
+ callback_baton, lock_tokens,
+ keep_locks, pool);
 }
 
+svn_error_t *svn_ra_get_commit_editor2(svn_ra_session_t *session,
+ const svn_delta_editor_t **editor,
+ void **edit_baton,
+ const char *log_msg,
+ svn_commit_callback2_t callback,
+ void *callback_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *pool)
+{
+ return svn_ra_get_commit_editor3(session, editor, edit_baton, log_msg,
+ NULL, callback, callback_baton,
+ lock_tokens, keep_locks, pool);
+}
+
 svn_error_t *svn_ra_get_commit_editor(svn_ra_session_t *session,
                                       const svn_delta_editor_t **editor,
                                       void **edit_baton,
Index: subversion/libsvn_ra/ra_loader.h
===================================================================
--- subversion/libsvn_ra/ra_loader.h (revision 23757)
+++ subversion/libsvn_ra/ra_loader.h (working copy)
@@ -84,6 +84,7 @@
                                     const svn_delta_editor_t **editor,
                                     void **edit_baton,
                                     const char *log_msg,
+ apr_hash_t *revprop_table,
                                     svn_commit_callback2_t callback,
                                     void *callback_baton,
                                     apr_hash_t *lock_tokens,
Index: subversion/include/svn_ra_svn.h
===================================================================
--- subversion/include/svn_ra_svn.h (revision 23757)
+++ subversion/include/svn_ra_svn.h (working copy)
@@ -185,6 +185,13 @@
 svn_error_t *svn_ra_svn_write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
                                    const char *word);
 
+/** Write a list of properties over the net. @a props is allowed to be NULL,
+ * in which case an empty list will be written out.
+ */
+svn_error_t *svn_ra_svn_write_proplist(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ apr_hash_t *props);
+
 /** Begin a list. Writes will be buffered until the next read or flush. */
 svn_error_t *svn_ra_svn_start_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool);
 
@@ -291,6 +298,12 @@
 svn_error_t *svn_ra_svn_read_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
                                    const char *fmt, ...);
 
+/** Parse an array of @c svn_sort__item_t structures as a list of
+ * properties, storing the properties in a hash table. */
+svn_error_t *svn_ra_svn_parse_proplist(apr_array_header_t *list,
+ apr_pool_t *pool,
+ apr_hash_t **props);
+
 /** Read a command response from the network and parse it as a tuple, using
  * the format string notation from svn_ra_svn_parse_tuple().
  */
Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h (revision 23757)
+++ subversion/include/svn_repos.h (working copy)
@@ -702,6 +702,10 @@
  * Iff @a log_msg is not @c NULL, store it as the log message
  * associated with the commit transaction.
  *
+ * Iff @a revprop_table is not @c NULL, store its revision properties with
+ * the commit transaction. @a revprop_table cannot contain any standard
+ * Subversion properties.
+ *
  * 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
@@ -721,7 +725,31 @@
  * 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.
+ */
+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,
+ const char *base_path,
+ const char *user,
+ const char *log_msg,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t callback,
+ void *callback_baton,
+ svn_repos_authz_callback_t authz_callback,
+ void *authz_baton,
+ apr_pool_t *pool);
+
+/*
+ * Similar to svn_repos_get_commit_editor5(), but with @a revprop_table
+ * set to @c NULL.
+ *
  * @since New in 1.4.
+ *
+ * @deprecated Provided for backward compatibility with the 1.4 API.
  */
 svn_error_t *
 svn_repos_get_commit_editor4(const svn_delta_editor_t **editor,
@@ -1136,18 +1164,37 @@
                                      svn_fs_txn_t *txn,
                                      apr_pool_t *pool);
 
-/** Like svn_fs_begin_txn(), but use @a author and @a log_msg to set the
- * corresponding properties on transaction @a *txn_p. @a repos is the
- * repository object which contains the filesystem. @a rev, @a *txn_p, and
- * @a pool are as in svn_fs_begin_txn().
+/** Like svn_fs_begin_txn(), but use @a author, @a log_msg, and
+ * @a revprop_table to set the corresponding properties on transaction
+ * @a *txn_p. @a repos is the repository object which contains the
+ * filesystem. @a rev, @a *txn_p, and @a pool are as in svn_fs_begin_txn().
  *
  * 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 @c SVN_ERR_REPOS_HOOK_FAILURE is returned.
  *
- * @a log_msg may be @c NULL to indicate the message is not (yet) available.
- * The caller will need to attach it to the transaction at a later time.
+ * @a author, @a log_msg, and @a revprop_table may be @c NULL to indicate
+ * their value is not (yet) available. The caller will need to attach it
+ * to the transaction at a later time.
+ *
+ * If @a revprop_table contains an author or log message property, they
+ * will be overwritten by @a author and @a log_msg values.
  */
+svn_error_t *svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
+ svn_repos_t *repos,
+ svn_revnum_t rev,
+ const char *author,
+ const char *log_msg,
+ apr_hash_t *revprop_table,
+ apr_pool_t *pool);
+
+
+/**
+ * Same as svn_repos_fs_begin_txn_for_commit2(), but with @a revprop_table
+ * always set to NULL.
+ *
+ * @deprecated Provided for backward compatibility with the 1.4 API.
+ */
 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/include/svn_props.h
===================================================================
--- subversion/include/svn_props.h (revision 23757)
+++ subversion/include/svn_props.h (working copy)
@@ -105,6 +105,11 @@
 svn_boolean_t svn_prop_is_svn_prop(const char *prop_name);
 
 
+/** Return @c TRUE iff @a props has a property which name represents the
+ * name of a Subversion property. Returns FALSE when @a props is NULL.
+ */
+svn_boolean_t svn_prop_has_svn_props(apr_hash_t *props, apr_pool_t *pool);
+
 /** If @a prop_name requires that its value be stored as UTF8/LF in the
  * repository, then return @c TRUE. Else return @c FALSE. This is for
  * users of libsvn_client or libsvn_fs, since it their responsibility
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h (revision 23757)
+++ subversion/include/svn_client.h (working copy)
@@ -762,6 +762,11 @@
   /** MIME types map.
    * @since New in 1.5. */
   apr_hash_t *mimetypes_map;
+
+ /** Table holding the extra revision properties to be set. This table
+ * cannot contain any standard Subversion properties.
+ * @since New in 1.5. */
+ apr_hash_t *revprop_table;
 
 } svn_client_ctx_t;
 
Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h (revision 23757)
+++ subversion/include/svn_ra.h (working copy)
@@ -568,11 +568,15 @@
 
 /**
  * Set @a *editor and @a *edit_baton to an editor for committing changes
- * to the repository of @a session, using @a log_msg as the log message. The
- * revisions being committed against are passed to the editor
- * functions, starting with the rev argument to @c open_root. The path
- * root of the commit is in the @a session's URL.
+ * to the repository of @a session, using @a log_msg as the log message and
+ * setting the revision properties from @a revprop_table. The revisions
+ * being committed against are passed to the editor functions, starting
+ * with the rev argument to @c open_root. The path root of the commit is
+ * in the @a session's URL.
  *
+ * @a revprop_table cannot contain any standard Subversion properties,
+ * but @a revprop_table can be NULL.
+ *
  * Before @c close_edit returns, but after the commit has succeeded,
  * it will invoke @a callback with the new revision number, the
  * commit date (as a <tt>const char *</tt>), commit author (as a
@@ -598,8 +602,27 @@
  *
  * Use @a pool for memory allocation.
  *
+ * @since New in 1.5.
+ */
+svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session,
+ const svn_delta_editor_t **editor,
+ void **edit_baton,
+ const char *log_msg,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t callback,
+ void *callback_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *pool);
+
+/**
+ * Same as svn_ra_get_commit_editor2(), but with @c revprop_table set
+ * to NULL.
+ *
  * @since New in 1.4.
- */
+ *
+ * @deprecated Provided for backward compatibility with the 1.4 API.
+ */
 svn_error_t *svn_ra_get_commit_editor2(svn_ra_session_t *session,
                                        const svn_delta_editor_t **editor,
                                        void **edit_baton,
Index: subversion/libsvn_subr/properties.c
===================================================================
--- subversion/libsvn_subr/properties.c (revision 23757)
+++ subversion/libsvn_subr/properties.c (working copy)
@@ -38,6 +38,26 @@
 }
 
 
+svn_boolean_t
+svn_prop_has_svn_props(apr_hash_t *props, apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+ const void *prop_name;
+
+ if (! props)
+ return FALSE;
+
+ for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
+ {
+ apr_hash_this(hi, &prop_name, NULL, NULL);
+ if (svn_prop_is_svn_prop((const char *) prop_name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
 svn_prop_kind_t
 svn_property_kind(int *prefix_len,
                   const char *prop_name)
Index: subversion/libsvn_ra_local/ra_plugin.c
===================================================================
--- subversion/libsvn_ra_local/ra_plugin.c (revision 23757)
+++ subversion/libsvn_ra_local/ra_plugin.c (working copy)
@@ -490,6 +490,7 @@
                                 const svn_delta_editor_t **editor,
                                 void **edit_baton,
                                 const char *log_msg,
+ apr_hash_t *revprop_table,
                                 svn_commit_callback2_t callback,
                                 void *callback_baton,
                                 apr_hash_t *lock_tokens,
@@ -537,11 +538,11 @@
     }
               
   /* Get the repos commit-editor */
- SVN_ERR(svn_repos_get_commit_editor4
+ SVN_ERR(svn_repos_get_commit_editor5
           (editor, edit_baton, sess_baton->repos, NULL,
            svn_path_uri_decode(sess_baton->repos_url, pool),
            sess_baton->fs_path->data,
- sess_baton->username, log_msg,
+ sess_baton->username, log_msg, revprop_table,
            deltify_etc, db, NULL, NULL, pool));
 
   return SVN_NO_ERROR;
Index: subversion/libsvn_client/delete.c
===================================================================
--- subversion/libsvn_client/delete.c (revision 23757)
+++ subversion/libsvn_client/delete.c (working copy)
@@ -189,8 +189,9 @@
 
   /* Fetch RA commit editor */
   SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
- SVN_ERR(svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton,
- log_msg, svn_client__commit_callback,
+ SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+ log_msg, ctx->revprop_table,
+ svn_client__commit_callback,
                                     commit_baton,
                                     NULL, TRUE, /* No lock tokens */
                                     pool));
Index: subversion/libsvn_client/prop_commands.c
===================================================================
--- subversion/libsvn_client/prop_commands.c (revision 23757)
+++ subversion/libsvn_client/prop_commands.c (working copy)
@@ -267,8 +267,8 @@
 
   /* Fetch RA commit editor. */
   SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
- SVN_ERR(svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton,
- message,
+ SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+ message, ctx->revprop_table,
                                     svn_client__commit_callback,
                                     commit_baton,
                                     NULL, TRUE, /* No lock tokens */
Index: subversion/libsvn_client/copy.c
===================================================================
--- subversion/libsvn_client/copy.c (revision 23757)
+++ subversion/libsvn_client/copy.c (working copy)
@@ -667,8 +667,8 @@
 
   /* Fetch RA commit editor. */
   SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
- SVN_ERR(svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton,
- message,
+ SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+ message, ctx->revprop_table,
                                     svn_client__commit_callback,
                                     commit_baton,
                                     NULL, TRUE, /* No lock tokens */
@@ -939,8 +939,8 @@
 
   /* Fetch RA commit editor. */
   SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
- if ((cmt_err = svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton,
- message,
+ if ((cmt_err = svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+ message, ctx->revprop_table,
                                            svn_client__commit_callback,
                                            commit_baton,
                                            NULL, TRUE, /* No lock tokens */
Index: subversion/libsvn_client/add.c
===================================================================
--- subversion/libsvn_client/add.c (revision 23757)
+++ subversion/libsvn_client/add.c (working copy)
@@ -600,8 +600,9 @@
 
   /* Fetch RA commit editor */
   SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
- SVN_ERR(svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton,
- log_msg, svn_client__commit_callback,
+ SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+ log_msg, ctx->revprop_table,
+ svn_client__commit_callback,
                                     commit_baton,
                                     NULL, TRUE, /* No lock tokens */
                                     pool));
Index: subversion/libsvn_client/commit.c
===================================================================
--- subversion/libsvn_client/commit.c (revision 23757)
+++ subversion/libsvn_client/commit.c (working copy)
@@ -613,7 +613,8 @@
   
   /* Fetch RA commit editor. */
   SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
- return svn_ra_get_commit_editor2(*ra_session, editor, edit_baton, log_msg,
+ return svn_ra_get_commit_editor3(*ra_session, editor, edit_baton,
+ log_msg, ctx->revprop_table,
                                    svn_client__commit_callback,
                                    commit_baton, lock_tokens, keep_locks,
                                    pool);
Index: subversion/tests/cmdline/svneditor.py
===================================================================
--- subversion/tests/cmdline/svneditor.py (revision 23757)
+++ subversion/tests/cmdline/svneditor.py (working copy)
@@ -45,6 +45,9 @@
 def foo_to_bar(m):
     return m.replace('foo', 'bar')
 
+def append_foo(m):
+ return m + 'foo\n'
+
 def identity(m):
     return m
 
Index: subversion/tests/cmdline/commit_tests.py
===================================================================
--- subversion/tests/cmdline/commit_tests.py (revision 23757)
+++ subversion/tests/cmdline/commit_tests.py (working copy)
@@ -2004,6 +2004,288 @@
                                      'commit', '-m', 'log message',
                                      wc_dir)
 
+
+def mkdir_with_revprop(sbox):
+ "set revision props during remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug=42', remote_dir)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '42', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def delete_with_revprop(sbox):
+ "set revision props during remote delete"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
+ remote_dir)
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'delete', '-m', 'msg',
+ '--with-revprop', 'bug=52', remote_dir)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 3:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 3, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '52', [], 'propget', 'bug',
+ '--revprop', '-r', 3, sbox.repo_url)
+
+
+def commit_with_revprop(sbox):
+ "set revision props during commit"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ make_standard_slew_of_changes(wc_dir)
+
+ omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
+ gloo_path = os.path.join(wc_dir, 'A', 'D', 'H', 'gloo')
+ expected_output = svntest.wc.State(wc_dir, {
+ 'A/D/H/omega' : Item(verb='Sending'),
+ 'A/D/H/gloo' : Item(verb='Adding'),
+ })
+
+ expected_status = get_standard_state(wc_dir)
+ expected_status.tweak('A/D/H/omega', wc_rev=2, status=' ')
+ expected_status.tweak('A/D/H/gloo', wc_rev=2, status=' ')
+
+ svntest.actions.run_and_verify_commit(wc_dir,
+ expected_output,
+ expected_status,
+ None, None, None, None, None,
+ '-m', 'msg',
+ '--with-revprop', 'bug=62',
+ omega_path, gloo_path)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '62', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def import_with_revprop(sbox):
+ "set revision props during import"
+
+ sbox.build()
+ local_dir = os.path.join(sbox.wc_dir, 'folder')
+ local_file = os.path.join(sbox.wc_dir, 'folder', 'file')
+ os.mkdir(local_dir)
+ svntest.main.file_write(local_file, "xxxx")
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'import', '-m', 'msg',
+ '--with-revprop', 'bug=72', local_dir,
+ sbox.repo_url)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '72', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def copy_R2R_with_revprop(sbox):
+ "set revision props during repos-to-repos copy"
+
+ sbox.build()
+ remote_dir1 = sbox.repo_url + "/dir1"
+ remote_dir2 = sbox.repo_url + "/dir2"
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
+ remote_dir1)
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'copy', '-m', 'msg',
+ '--with-revprop', 'bug=82', remote_dir1,
+ remote_dir2)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 3:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 3, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '82', [], 'propget', 'bug',
+ '--revprop', '-r', 3, sbox.repo_url)
+
+
+def copy_WC2R_with_revprop(sbox):
+ "set revision props during wc-to-repos copy"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+ local_dir = os.path.join(sbox.wc_dir, 'folder')
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', local_dir)
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'copy', '-m', 'msg',
+ '--with-revprop', 'bug=92', local_dir,
+ remote_dir)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '92', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def move_R2R_with_revprop(sbox):
+ "set revision props during repos-to-repos move"
+
+ sbox.build()
+ remote_dir1 = sbox.repo_url + "/dir1"
+ remote_dir2 = sbox.repo_url + "/dir2"
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
+ remote_dir1)
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'move', '-m', 'msg',
+ '--with-revprop', 'bug=102', remote_dir1,
+ remote_dir2)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 3:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 3, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '102', [], 'propget', 'bug',
+ '--revprop', '-r', 3, sbox.repo_url)
+
+
+def propedit_with_revprop(sbox):
+ "set revision props during remote property edit"
+
+ sbox.build()
+ svntest.main.use_editor('append_foo')
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'propedit', '-m', 'msg',
+ '--with-revprop', 'bug=112', 'prop',
+ sbox.repo_url)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '112', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def set_multiple_props_with_revprop(sbox):
+ "set multiple revision props during remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug=32',
+ '--with-revprop', 'ref=22', remote_dir)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n', ' ref\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '32', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '22', [], 'propget', 'ref',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def set_std_props_with_revprop(sbox):
+ "set standard revision props during remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+ expected = 'svn: Revision properties include a standard Subversion property'
+
+ svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
+ '--with-revprop', 'svn:author=42', remote_dir)
+ svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
+ '--with-revprop', 'svn:log=42', remote_dir)
+ svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
+ '--with-revprop', 'svn:date=42', remote_dir)
+ svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
+ '--with-revprop', 'svn:foo=bar', remote_dir)
+
+
+def use_empty_value_in_revprop_pair(sbox):
+ "set revprop without value ('') during remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug=',
+ '--with-revprop', 'ref=', remote_dir)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n', ' ref\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'ref',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def no_equals_in_revprop_pair(sbox):
+ "set revprop without '=' during remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug',
+ '--with-revprop', 'ref', remote_dir)
+
+ expected = svntest.actions.UnorderedOutput(
+ ['Unversioned properties on revision 2:\n',
+ ' svn:author\n',' svn:date\n', ' svn:log\n',
+ ' bug\n', ' ref\n'])
+ svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'bug',
+ '--revprop', '-r', 2, sbox.repo_url)
+ svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'ref',
+ '--revprop', '-r', 2, sbox.repo_url)
+
+
+def use_empty_string_as_revprop_pair(sbox):
+ "use '' for revision property during remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+
+ svntest.actions.run_and_verify_svn(None, [],
+ 'svn: Revision property pair is empty',
+ 'mkdir', '-m', 'msg',
+ '--with-revprop', '',
+ remote_dir)
+
 ########################################################################
 # Run the tests
 
@@ -2044,7 +2326,20 @@
               local_mods_are_not_commits,
               post_commit_hook_test,
               commit_same_folder_in_targets,
- commit_inconsistent_eol
+ commit_inconsistent_eol,
+ mkdir_with_revprop,
+ delete_with_revprop,
+ commit_with_revprop,
+ import_with_revprop,
+ copy_R2R_with_revprop,
+ copy_WC2R_with_revprop,
+ move_R2R_with_revprop,
+ propedit_with_revprop,
+ set_multiple_props_with_revprop,
+ set_std_props_with_revprop,
+ use_empty_value_in_revprop_pair,
+ no_equals_in_revprop_pair,
+ use_empty_string_as_revprop_pair,
              ]
 
 if __name__ == '__main__':
Index: subversion/tests/cmdline/svntest/actions.py
===================================================================
--- subversion/tests/cmdline/svntest/actions.py (revision 23757)
+++ subversion/tests/cmdline/svntest/actions.py (working copy)
@@ -50,7 +50,13 @@
   run_and_verify_* API"""
   pass
 
+class UnorderedOutput:
+ """Simple class to mark unorder output"""
 
+ def __init__(self, output):
+ self.output = output
+
+
 def setup_pristine_repository():
   """Create the pristine repository, 'svn import' the greek tree and
   checkout the pristine working copy"""
@@ -178,6 +184,10 @@
      - If it is a single string, invoke match_or_fail() on MESSAGE,
        the expected output, and the actual output.
 
+ - If it is an instance of UnorderedOutput, invoke
+ compare_unordered_and_display_lines() on MESSAGE, the expected
+ output, and the actual output.
+
   If EXPECTED_STDOUT is None, do not check stdout.
   EXPECTED_STDERR may not be None.
 
@@ -200,6 +210,9 @@
       compare_and_display_lines(message, output_type.upper(), expected, actual)
     elif type(expected) is type(''):
       match_or_fail(message, output_type.upper(), expected, actual)
+ elif isinstance(expected, UnorderedOutput):
+ compare_unordered_and_display_lines(message, output_type.upper(),
+ expected.output, actual)
     elif expected == SVNAnyOutput:
       if len(actual) == 0:
         if message is not None:
@@ -872,17 +885,21 @@
     tree.dump_tree(actual)
 
 
-def display_lines(message, label, expected, actual, expected_is_regexp=None):
+def display_lines(message, label, expected, actual, expected_is_regexp=None,
+ expected_is_unordered=None):
   """Print MESSAGE, unless it is None, then print EXPECTED (labeled
   with LABEL) followed by ACTUAL (also labeled with LABEL).
   Both EXPECTED and ACTUAL may be strings or lists of strings."""
   if message is not None:
     print message
   if expected is not None:
+ output = 'EXPECTED %s' % label
     if expected_is_regexp:
- print 'EXPECTED', label + ' (regexp):'
- else:
- print 'EXPECTED', label + ':'
+ output += ' (regexp)'
+ if expected_is_unordered:
+ output += ' (unordered)'
+ output += ':'
+ print output
     map(sys.stdout.write, expected)
     if expected_is_regexp:
       map(sys.stdout.write, '\n')
@@ -903,6 +920,15 @@
     display_lines(message, label, expected, actual)
     raise main.SVNLineUnequal
 
+def compare_unordered_and_display_lines(message, label, expected, actual):
+ """Compare two sets of output lines, and print them if they differ,
+ but disregard the order of the lines. MESSAGE is ignored if None."""
+ try:
+ main.compare_unordered_output(expected, actual)
+ except Failure:
+ display_lines(message, label, expected, actual, expected_is_unordered=True)
+ raise main.SVNLineUnequal
+
 def match_or_fail(message, label, expected, actual):
   """Make sure that regexp EXPECTED matches at least one line in list ACTUAL.
   If no match, then print MESSAGE (if it's not None), followed by
Index: subversion/libsvn_repos/fs-wrap.c
===================================================================
--- subversion/libsvn_repos/fs-wrap.c (revision 23757)
+++ subversion/libsvn_repos/fs-wrap.c (working copy)
@@ -64,13 +64,41 @@
 /*** Transaction creation wrappers. ***/
 
 svn_error_t *
-svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p,
- svn_repos_t *repos,
- svn_revnum_t rev,
- const char *author,
- const char *log_msg,
- apr_pool_t *pool)
+svn_repos__change_txn_props(svn_fs_txn_t *txn,
+ apr_hash_t *txnprop_table,
+ apr_pool_t *pool)
 {
+ apr_pool_t *iterpool;
+ apr_hash_index_t *hi;
+ const void *key;
+ void *value;
+ const char *propname;
+ const svn_string_t *propval;
+
+ iterpool = svn_pool_create(pool);
+ for (hi = apr_hash_first(pool, txnprop_table); hi; hi = apr_hash_next(hi))
+ {
+ svn_pool_clear(iterpool);
+ apr_hash_this(hi, &key, NULL, &value);
+ propname = key;
+ propval = value;
+ SVN_ERR(svn_repos_fs_change_txn_prop(txn, propname, propval, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
+ svn_repos_t *repos,
+ svn_revnum_t rev,
+ const char *author,
+ const char *log_msg,
+ apr_hash_t *revprop_table,
+ apr_pool_t *pool)
+{
   /* Run start-commit hooks. */
   SVN_ERR(svn_repos__hooks_start_commit(repos, author, pool));
 
@@ -78,10 +106,14 @@
   SVN_ERR(svn_fs_begin_txn2(txn_p, repos->fs, rev,
                             SVN_FS_TXN_CHECK_LOCKS, pool));
 
- /* We pass the author and log message to the filesystem by adding
- them as properties on the txn. Later, when we commit the txn,
- these properties will be copied into the newly created revision. */
+ /* We pass the author, log message, and arbitrary revision properties to the
+ filesystem by adding them as properties on the txn. Later, when we commit
+ the txn, these properties will be copied into the newly created revision. */
 
+ /* Set the (arbitrary) revision properties. */
+ if (revprop_table)
+ SVN_ERR(svn_repos__change_txn_props(*txn_p, revprop_table, pool));
+
   /* User (author). */
   if (author)
     {
@@ -107,12 +139,25 @@
         SVN_ERR(svn_fs_change_txn_prop(*txn_p, SVN_PROP_REVISION_LOG,
                                        &l, pool));
     }
-
+
   return SVN_NO_ERROR;
 }
 
 
 svn_error_t *
+svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p,
+ svn_repos_t *repos,
+ svn_revnum_t rev,
+ const char *author,
+ const char *log_msg,
+ apr_pool_t *pool)
+{
+ return svn_repos_fs_begin_txn_for_commit2(txn_p, repos, rev, author,
+ log_msg, NULL, pool);
+}
+
+
+svn_error_t *
 svn_repos_fs_begin_txn_for_update(svn_fs_txn_t **txn_p,
                                   svn_repos_t *repos,
                                   svn_revnum_t rev,
Index: subversion/libsvn_repos/commit.c
===================================================================
--- subversion/libsvn_repos/commit.c (revision 23757)
+++ subversion/libsvn_repos/commit.c (working copy)
@@ -30,6 +30,7 @@
 #include "svn_repos.h"
 #include "svn_md5.h"
 #include "svn_props.h"
+#include "repos.h"
 #include "svn_private_config.h"
 
 
@@ -49,6 +50,9 @@
   /* Commit message for this commit. */
   const char *log_msg;
 
+ /* Extra revision properties to set for this commit. */
+ apr_hash_t *revprop_table;
+
   /* Callback to run when the commit is done. */
   svn_commit_callback2_t commit_callback;
   void *commit_callback_baton;
@@ -179,17 +183,20 @@
      make our own. */
   if (eb->txn_owner)
     {
- SVN_ERR(svn_repos_fs_begin_txn_for_commit(&(eb->txn),
- eb->repos,
- youngest,
- eb->user,
- eb->log_msg,
- eb->pool));
+ SVN_ERR(svn_repos_fs_begin_txn_for_commit2(&(eb->txn),
+ eb->repos,
+ youngest,
+ eb->user,
+ eb->log_msg,
+ eb->revprop_table,
+ eb->pool));
     }
   else /* Even if we aren't the owner of the transaction, we might
           have been instructed to set some properties. */
     {
       svn_string_t propval;
+ if (eb->revprop_table)
+ SVN_ERR(svn_repos__change_txn_props(eb->txn, eb->revprop_table, pool));
       if (eb->user)
         {
           propval.data = eb->user;
@@ -760,11 +767,40 @@
 }
 
 
+static apr_hash_t *
+revprop_table_dup(apr_hash_t *revprop_table,
+ apr_pool_t *pool)
+{
+ apr_hash_t *new_revprop_table = NULL;
+ const void *key;
+ apr_ssize_t klen;
+ void *value;
+ const char *propname;
+ const svn_string_t *propval;
+ apr_hash_index_t *hi;
+
+ if (revprop_table)
+ {
+ new_revprop_table = apr_hash_make(pool);
+
+ for (hi = apr_hash_first(pool, revprop_table); hi; hi = apr_hash_next(hi))
+ {
+ apr_hash_this(hi, &key, &klen, &value);
+ propname = apr_pstrdup(pool, (const char *) key);
+ propval = svn_string_dup((const svn_string_t *) value, pool);
+ apr_hash_set(new_revprop_table, propname, klen, propval);
+ }
+ }
+
+ return new_revprop_table;
+}
+
+
 
 /*** Public interfaces. ***/
 
 svn_error_t *
-svn_repos_get_commit_editor4(const svn_delta_editor_t **editor,
+svn_repos_get_commit_editor5(const svn_delta_editor_t **editor,
                              void **edit_baton,
                              svn_repos_t *repos,
                              svn_fs_txn_t *txn,
@@ -772,6 +808,7 @@
                              const char *base_path,
                              const char *user,
                              const char *log_msg,
+ apr_hash_t *revprop_table,
                              svn_commit_callback2_t callback,
                              void *callback_baton,
                              svn_repos_authz_callback_t authz_callback,
@@ -782,6 +819,12 @@
   apr_pool_t *subpool = svn_pool_create(pool);
   struct edit_baton *eb;
 
+ /* No standard properties allowed in revision properties table. */
+ if (revprop_table && svn_prop_has_svn_props(revprop_table, pool))
+ return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
+ _("Revision properties include a standard "
+ "Subversion property"));
+
   /* Do a global authz access lookup. Users with no write access
      whatsoever to the repository don't get a commit editor. */
   if (authz_callback)
@@ -817,6 +860,7 @@
   eb->pool = subpool;
   eb->user = user ? apr_pstrdup(subpool, user) : NULL;
   eb->log_msg = apr_pstrdup(subpool, log_msg);
+ eb->revprop_table = revprop_table_dup(revprop_table, subpool);
   eb->commit_callback = callback;
   eb->commit_callback_baton = callback_baton;
   eb->authz_callback = authz_callback;
@@ -836,7 +880,31 @@
   return SVN_NO_ERROR;
 }
 
+
 svn_error_t *
+svn_repos_get_commit_editor4(const svn_delta_editor_t **editor,
+ void **edit_baton,
+ svn_repos_t *repos,
+ svn_fs_txn_t *txn,
+ const char *repos_url,
+ const char *base_path,
+ const char *user,
+ const char *log_msg,
+ svn_commit_callback2_t callback,
+ void *callback_baton,
+ svn_repos_authz_callback_t authz_callback,
+ void *authz_baton,
+ apr_pool_t *pool)
+{
+ return svn_repos_get_commit_editor5(editor, edit_baton, repos, txn,
+ repos_url, base_path, user,
+ log_msg, NULL, callback,
+ callback_baton, authz_callback,
+ authz_baton, pool);
+}
+
+
+svn_error_t *
 svn_repos_get_commit_editor3(const svn_delta_editor_t **editor,
                              void **edit_baton,
                              svn_repos_t *repos,
Index: subversion/libsvn_repos/repos.h
===================================================================
--- subversion/libsvn_repos/repos.h (revision 23757)
+++ subversion/libsvn_repos/repos.h (working copy)
@@ -253,6 +253,12 @@
                          const char *path2,
                          apr_pool_t *pool);
 
+/** Change the transaction property values in transaction TXN using the
+ (key,value)-pairs in TXNPROP_TABLE. */
+svn_error_t *
+svn_repos__change_txn_props(svn_fs_txn_t *txn,
+ apr_hash_t *txnprop_table,
+ apr_pool_t *pool);
 
 #ifdef __cplusplus
 }
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c (revision 23757)
+++ subversion/libsvn_ra_svn/client.c (working copy)
@@ -142,28 +142,6 @@
   return SVN_NO_ERROR;
 }
 
-/* Convert a property list received from the server into a hash table. */
-static svn_error_t *parse_proplist(apr_array_header_t *list, apr_pool_t *pool,
- apr_hash_t **props)
-{
- char *name;
- svn_string_t *value;
- svn_ra_svn_item_t *elt;
- int i;
-
- *props = apr_hash_make(pool);
- for (i = 0; i < list->nelts; i++)
- {
- elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
- if (elt->kind != SVN_RA_SVN_LIST)
- return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
- _("Proplist element not a list"));
- SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, pool, "cs", &name, &value));
- apr_hash_set(*props, name, APR_HASH_KEY_STRING, value);
- }
- return SVN_NO_ERROR;
-}
-
 /* Set *DIFFS to an array of svn_prop_t, allocated in POOL, based on the
    property diffs in LIST, received from the server. */
 static svn_error_t *parse_prop_diffs(apr_array_header_t *list,
@@ -769,7 +747,7 @@
   SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "rev-proplist", "r", rev));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "l", &proplist));
- SVN_ERR(parse_proplist(proplist, pool, props));
+ SVN_ERR(svn_ra_svn_parse_proplist(proplist, pool, props));
   return SVN_NO_ERROR;
 }
 
@@ -806,7 +784,8 @@
 static svn_error_t *ra_svn_commit(svn_ra_session_t *session,
                                   const svn_delta_editor_t **editor,
                                   void **edit_baton,
- const char *log_msg,
+ const char *log_msg,
+ apr_hash_t *revprop_table,
                                   svn_commit_callback2_t callback,
                                   void *callback_baton,
                                   apr_hash_t *lock_tokens,
@@ -837,7 +816,9 @@
         }
       svn_pool_destroy(iterpool);
     }
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)b)", keep_locks));
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)b(!", keep_locks));
+ SVN_ERR(svn_ra_svn_write_proplist(conn, pool, revprop_table));
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))"));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, ""));
 
@@ -880,7 +861,7 @@
   if (fetched_rev)
     *fetched_rev = rev;
   if (props)
- SVN_ERR(parse_proplist(proplist, pool, props));
+ SVN_ERR(svn_ra_svn_parse_proplist(proplist, pool, props));
 
   /* We're done if the contents weren't wanted. */
   if (!stream)
@@ -981,7 +962,7 @@
   if (fetched_rev)
     *fetched_rev = rev;
   if (props)
- SVN_ERR(parse_proplist(proplist, pool, props));
+ SVN_ERR(svn_ra_svn_parse_proplist(proplist, pool, props));
 
   /* We're done if dirents aren't wanted. */
   if (!dirents)
@@ -1378,7 +1359,7 @@
                                      "crll", &p, &rev, &rev_proplist,
                                      &proplist));
       p = svn_path_canonicalize(p, rev_pool);
- SVN_ERR(parse_proplist(rev_proplist, rev_pool, &rev_props));
+ SVN_ERR(svn_ra_svn_parse_proplist(rev_proplist, rev_pool, &rev_props));
       SVN_ERR(parse_prop_diffs(proplist, rev_pool, &props));
 
       /* Get the first delta chunk so we know if there is a delta. */
Index: subversion/libsvn_ra_svn/protocol
===================================================================
--- subversion/libsvn_ra_svn/protocol (revision 23757)
+++ subversion/libsvn_ra_svn/protocol (working copy)
@@ -252,7 +252,7 @@
 
   commit
     params: ( logmsg:string ? ( ( lock-path:string lock-token:string ) ... )
- keep-locks:bool )
+ keep-locks:bool ? rev-props:proplist )
     response: ( )
     Upon receiving response, client switches to editor command set.
     Upon successful completion of edit, server sends auth-request.
Index: subversion/libsvn_ra_svn/marshal.c
===================================================================
--- subversion/libsvn_ra_svn/marshal.c (revision 23757)
+++ subversion/libsvn_ra_svn/marshal.c (working copy)
@@ -394,6 +394,33 @@
   return writebuf_printf(conn, pool, "%s ", word);
 }
 
+svn_error_t *svn_ra_svn_write_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ apr_hash_t *props)
+{
+ apr_pool_t *iterpool;
+ apr_hash_index_t *hi;
+ const void *key;
+ void *val;
+ const char *propname;
+ svn_string_t *propval;
+
+ if (props)
+ {
+ iterpool = svn_pool_create(pool);
+ for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
+ {
+ svn_pool_clear(iterpool);
+ apr_hash_this(hi, &key, NULL, &val);
+ propname = key;
+ propval = val;
+ SVN_ERR(svn_ra_svn_write_tuple(conn, iterpool, "cs", propname, propval));
+ }
+ svn_pool_destroy(iterpool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
 svn_error_t *svn_ra_svn_start_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
 {
   return writebuf_write(conn, pool, "( ", 2);
@@ -751,6 +778,30 @@
   return err;
 }
 
+svn_error_t *svn_ra_svn_parse_proplist(apr_array_header_t *list,
+ apr_pool_t *pool,
+ apr_hash_t **props)
+{
+ char *name;
+ svn_string_t *value;
+ svn_ra_svn_item_t *elt;
+ int i;
+
+ *props = apr_hash_make(pool);
+ for (i = 0; i < list->nelts; i++)
+ {
+ elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
+ if (elt->kind != SVN_RA_SVN_LIST)
+ return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+ _("Proplist element not a list"));
+ SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, pool, "cs", &name, &value));
+ apr_hash_set(*props, name, APR_HASH_KEY_STRING, value);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
 
 svn_error_t *svn_ra_svn__handle_failure_status(apr_array_header_t *params,
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (revision 23757)
+++ subversion/svn/cl.h (working copy)
@@ -81,7 +81,8 @@
   svn_cl__targets_opt,
   svn_cl__version_opt,
   svn_cl__xml_opt,
- svn_cl__keep_local_opt
+ svn_cl__keep_local_opt,
+ svn_cl__with_revprop_opt
 } svn_cl__longopt_t;
 
 
@@ -151,6 +152,7 @@
   const char *changelist; /* operate on this changelist */
   svn_boolean_t keep_changelist; /* don't remove changelist after commit */
   svn_boolean_t keep_local; /* delete path only from repository */
+ apr_hash_t *revprop_table; /* table with revision properties to set */
 
 } svn_cl__opt_state_t;
 
Index: subversion/svn/move-cmd.c
===================================================================
--- subversion/svn/move-cmd.c (revision 23757)
+++ subversion/svn/move-cmd.c (working copy)
@@ -70,16 +70,19 @@
   if (! svn_path_is_url(dst_path))
     {
       ctx->log_msg_func3 = NULL;
- if (opt_state->message || opt_state->filedata)
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
         return svn_error_create
           (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
- _("Local, non-commit operations do not take a log message"));
+ _("Local, non-commit operations do not take a log message"
+ "or revision properties"));
     }
 
   if (ctx->log_msg_func3)
     SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                        NULL, ctx->config, pool));
 
+ ctx->revprop_table = opt_state->revprop_table;
+
   err = svn_client_move5(&commit_info, targets, dst_path, opt_state->force,
                          TRUE, ctx, pool);
 
Index: subversion/svn/mkdir-cmd.c
===================================================================
--- subversion/svn/mkdir-cmd.c (revision 23757)
+++ subversion/svn/mkdir-cmd.c (working copy)
@@ -59,11 +59,12 @@
   if (! svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *)))
     {
       ctx->log_msg_func3 = NULL;
- if (opt_state->message || opt_state->filedata)
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
         {
           return svn_error_create
             (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
- _("Local, non-commit operations do not take a log message"));
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
         }
     }
   else
@@ -72,6 +73,8 @@
                                          NULL, ctx->config, subpool));
     }
 
+ ctx->revprop_table = opt_state->revprop_table;
+
   err = svn_client_mkdir2(&commit_info, targets, ctx, subpool);
 
   if (ctx->log_msg_func3)
Index: subversion/svn/copy-cmd.c
===================================================================
--- subversion/svn/copy-cmd.c (revision 23757)
+++ subversion/svn/copy-cmd.c (working copy)
@@ -118,16 +118,19 @@
   if (! dst_is_url)
     {
       ctx->log_msg_func3 = NULL;
- if (opt_state->message || opt_state->filedata)
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
         return svn_error_create
           (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
- _("Local, non-commit operations do not take a log message"));
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
     }
 
   if (ctx->log_msg_func3)
     SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                        NULL, ctx->config, pool));
 
+ ctx->revprop_table = opt_state->revprop_table;
+
   err = svn_client_copy4(&commit_info, sources,
                          dst_path, TRUE, ctx, pool);
 
Index: subversion/svn/commit-cmd.c
===================================================================
--- subversion/svn/commit-cmd.c (revision 23757)
+++ subversion/svn/commit-cmd.c (working copy)
@@ -90,6 +90,8 @@
                                      opt_state, base_dir,
                                      ctx->config, pool));
 
+ ctx->revprop_table = opt_state->revprop_table;
+
   /* Commit. */
   SVN_ERR(svn_cl__cleanup_log_msg
           (ctx->log_msg_baton3, svn_client_commit4(&commit_info,
Index: subversion/svn/delete-cmd.c
===================================================================
--- subversion/svn/delete-cmd.c (revision 23757)
+++ subversion/svn/delete-cmd.c (working copy)
@@ -57,11 +57,12 @@
   if (! svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *)))
     {
       ctx->log_msg_func3 = NULL;
- if (opt_state->message || opt_state->filedata)
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
         {
           return svn_error_create
             (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
- _("Local, non-commit operations do not take a log message"));
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
         }
     }
   else
@@ -70,6 +71,8 @@
                                          NULL, ctx->config, pool));
     }
 
+ ctx->revprop_table = opt_state->revprop_table;
+
   err = svn_client_delete3(&commit_info, targets, opt_state->force,
                            opt_state->keep_local, ctx, pool);
   if (err)
Index: subversion/svn/import-cmd.c
===================================================================
--- subversion/svn/import-cmd.c (revision 23757)
+++ subversion/svn/import-cmd.c (working copy)
@@ -106,6 +106,9 @@
 
   SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                      NULL, ctx->config, pool));
+
+ ctx->revprop_table = opt_state->revprop_table;
+
   SVN_ERR(svn_cl__cleanup_log_msg
           (ctx->log_msg_baton3, svn_client_import2(&commit_info,
                                                    path,
Index: subversion/svn/main.c
===================================================================
--- subversion/svn/main.c (revision 23757)
+++ subversion/svn/main.c (working copy)
@@ -191,7 +191,11 @@
   {"keep-changelist", svn_cl__keep_changelist_opt, 0,
                     N_("don't delete changelist after commit")},
   {"keep-local", svn_cl__keep_local_opt, 0,
- N_("keep path in working copy")},
+ N_("keep path in working copy")},
+ {"with-revprop", svn_cl__with_revprop_opt, 1,
+ N_("set revision property ARG in new revision\n"
+ " "
+ "using the name=value format")},
   {0, 0, 0, 0}
 };
 
@@ -225,7 +229,8 @@
 #define SVN_CL__LOG_MSG_OPTIONS 'm', 'F', \
                                 svn_cl__force_log_opt, \
                                 svn_cl__editor_cmd_opt, \
- svn_cl__encoding_opt
+ svn_cl__encoding_opt, \
+ svn_cl__with_revprop_opt
 
 const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
 {
@@ -873,6 +878,39 @@
 }
 
 
+static svn_error_t *
+add_revprop_from_string(apr_hash_t **revprop_table_p,
+ const char *revprop_pair,
+ apr_pool_t *pool)
+{
+ const char *sep, *propname;
+ svn_string_t *propval;
+
+ if (! *revprop_pair)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Revision property pair is empty"));
+
+ if (! *revprop_table_p)
+ *revprop_table_p = apr_hash_make(pool);
+
+ sep = strchr(revprop_pair, '=');
+ if (sep)
+ {
+ propname = apr_pstrndup(pool, revprop_pair, sep - revprop_pair);
+ propval = svn_string_create(sep + 1, pool);
+ }
+ else
+ {
+ propname = apr_pstrdup(pool, revprop_pair);
+ propval = svn_string_create("", pool);
+ }
+
+ apr_hash_set(*revprop_table_p, propname, APR_HASH_KEY_STRING, propval);
+
+ return SVN_NO_ERROR;
+}
+
+
 
 /*** Main. ***/
 
@@ -1272,6 +1310,11 @@
       case svn_cl__keep_local_opt:
         opt_state.keep_local = TRUE;
         break;
+ case svn_cl__with_revprop_opt:
+ err = add_revprop_from_string(&opt_state.revprop_table, opt_arg, pool);
+ if (err != SVN_NO_ERROR)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ break;
       default:
         /* Hmmm. Perhaps this would be a good place to squirrel away
            opts that commands like svn diff might need. Hmmm indeed. */
Index: subversion/svn/propedit-cmd.c
===================================================================
--- subversion/svn/propedit-cmd.c (revision 23757)
+++ subversion/svn/propedit-cmd.c (working copy)
@@ -193,11 +193,13 @@
             }
           else
             {
- if (opt_state->message || opt_state->filedata)
+ if (opt_state->message || opt_state->filedata ||
+ opt_state->revprop_table)
                 {
                   return svn_error_create
                     (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
- _("Local, non-commit operations do not take a log message"));
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
                 }
               
               /* Split the path if it is a file path. */
@@ -245,6 +247,8 @@
                                                    opt_state, NULL, ctx->config,
                                                    subpool));
 
+ ctx->revprop_table = opt_state->revprop_table;
+
               err = svn_client_propset3(&commit_info,
                                         pname_utf8, edited_propval, target,
                                         FALSE, opt_state->force,
Index: subversion/libsvn_ra_dav/ra_dav.h
===================================================================
--- subversion/libsvn_ra_dav/ra_dav.h (revision 23757)
+++ subversion/libsvn_ra_dav/ra_dav.h (working copy)
@@ -215,6 +215,7 @@
                                             const svn_delta_editor_t **editor,
                                             void **edit_baton,
                                             const char *log_msg,
+ apr_hash_t *revprop_table,
                                             svn_commit_callback2_t callback,
                                             void *callback_baton,
                                             apr_hash_t *lock_tokens,
Index: subversion/libsvn_ra_dav/commit.c
===================================================================
--- subversion/libsvn_ra_dav/commit.c (revision 23757)
+++ subversion/libsvn_ra_dav/commit.c (working copy)
@@ -1276,16 +1276,25 @@
 }
 
 
-static svn_error_t * apply_log_message(commit_ctx_t *cc,
- const char *log_msg,
- apr_pool_t *pool)
+static svn_error_t * apply_revprops(commit_ctx_t *cc,
+ const char *log_msg,
+ apr_hash_t *revprop_table,
+ apr_pool_t *pool)
 {
+ svn_string_t *log_str;
   const svn_string_t *vcc;
   const svn_string_t *baseline_url;
   version_rsrc_t baseline_rsrc = { SVN_INVALID_REVNUM };
   svn_error_t *err = NULL;
   int retry_count = 5;
 
+ /* prepare the revision properties to be set */
+ if (! revprop_table)
+ revprop_table = apr_hash_make(pool);
+ log_str = svn_string_create(log_msg, pool);
+ apr_hash_set(revprop_table, SVN_PROP_PREFIX "log", APR_HASH_KEY_STRING,
+ log_str);
+
   /* ### this whole sequence can/should be replaced with an expand-property
      ### REPORT when that is available on the server. */
 
@@ -1308,7 +1317,7 @@
     baseline_rsrc.pool = pool;
     baseline_rsrc.vsn_url = baseline_url->data;
     
- /* To set the log message, we must checkout the latest baseline
+ /* To set the revision properties, we must checkout the latest baseline
        and get back a mutable "working" baseline. */
     err = checkout_resource(cc, &baseline_rsrc, FALSE, NULL, pool);
 
@@ -1330,23 +1339,15 @@
   if (err)
     return err;
 
- {
- apr_hash_t *prop_changes = apr_hash_make(pool);
- svn_string_t *log_str = svn_string_create(log_msg, pool);
-
- apr_hash_set(prop_changes, SVN_PROP_PREFIX "log",
- APR_HASH_KEY_STRING, log_str);
- SVN_ERR(svn_ra_dav__do_proppatch(cc->ras, baseline_rsrc.wr_url,
- prop_changes, NULL, NULL, pool));
- }
-
- return SVN_NO_ERROR;
+ return svn_ra_dav__do_proppatch(cc->ras, baseline_rsrc.wr_url, revprop_table,
+ NULL, NULL, pool);
 }
 
 svn_error_t * svn_ra_dav__get_commit_editor(svn_ra_session_t *session,
                                             const svn_delta_editor_t **editor,
                                             void **edit_baton,
                                             const char *log_msg,
+ apr_hash_t *revprop_table,
                                             svn_commit_callback2_t callback,
                                             void *callback_baton,
                                             apr_hash_t *lock_tokens,
@@ -1358,6 +1359,12 @@
   commit_ctx_t *cc;
   svn_error_t *err;
 
+ /* No standard properties allowed in revision properties table. */
+ if (revprop_table && svn_prop_has_svn_props(revprop_table, pool))
+ return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
+ _("Revision properties include a standard "
+ "Subversion property"));
+
   /* Build the main commit editor's baton. */
   cc = apr_pcalloc(pool, sizeof(*cc));
   cc->ras = ras;
@@ -1390,7 +1397,7 @@
   ** Find the latest baseline resource, check it out, and then apply the
   ** log message onto the thing.
   */
- err = apply_log_message(cc, log_msg, pool);
+ err = apply_revprops(cc, log_msg, revprop_table, pool);
   /* If the caller gets an error during the editor drive, we rely on them
      to call abort_edit() so that we can clear up the activity. But if we
      got an error here, we need to clear up the activity ourselves. */
Index: subversion/svnserve/serve.c
===================================================================
--- subversion/svnserve/serve.c (revision 23757)
+++ subversion/svnserve/serve.c (working copy)
@@ -590,31 +590,6 @@
 
 /* --- MAIN COMMAND SET --- */
 
-/* Write out a property list. PROPS is allowed to be NULL, in which case
- * an empty list will be written out; this happens if the client could
- * have asked for props but didn't. */
-static svn_error_t *write_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
- apr_hash_t *props)
-{
- apr_hash_index_t *hi;
- const void *namevar;
- void *valuevar;
- const char *name;
- svn_string_t *value;
-
- if (props)
- {
- for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
- {
- apr_hash_this(hi, &namevar, NULL, &valuevar);
- name = namevar;
- value = valuevar;
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "cs", name, value));
- }
- }
- return SVN_NO_ERROR;
-}
-
 /* Write out a list of property diffs. PROPDIFFS is an array of svn_prop_t
  * values. */
 static svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn,
@@ -779,7 +754,7 @@
                                              authz_check_access_cb_func(b), b,
                                              pool));
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success"));
- SVN_ERR(write_proplist(conn, pool, props));
+ SVN_ERR(svn_ra_svn_write_proplist(conn, pool, props));
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))"));
   return SVN_NO_ERROR;
 }
@@ -925,8 +900,10 @@
              *date = NULL,
              *author = NULL,
              *post_commit_err = NULL;
- apr_array_header_t *lock_tokens;
- svn_boolean_t keep_locks;
+ apr_array_header_t *lock_tokens = NULL;
+ svn_boolean_t keep_locks = TRUE;
+ apr_array_header_t *revprop_list = NULL;
+ apr_hash_t *revprop_table = NULL;
   const svn_delta_editor_t *editor;
   void *edit_baton;
   svn_boolean_t aborted;
@@ -935,14 +912,22 @@
 
   if (params->nelts == 1)
     {
- /* Clients before 1.2 don't send lock-tokens and keep-locks fields. */
+ /* Clients before 1.2 don't send lock-tokens, keep-locks,
+ and rev-props fields. */
       SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c", &log_msg));
- lock_tokens = NULL;
- keep_locks = TRUE;
     }
+ else if (params->nelts == 3)
+ {
+ /* Clients before 1.5 don't send the rev-props field. */
+ SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "clb", &log_msg,
+ &lock_tokens, &keep_locks));
+ }
   else
- SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "clb", &log_msg,
- &lock_tokens, &keep_locks));
+ {
+ SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "clbl", &log_msg,
+ &lock_tokens, &keep_locks,
+ &revprop_list));
+ }
 
   /* The handling for locks is a little problematic, because the
      protocol won't let us send several auth requests once one has
@@ -958,17 +943,21 @@
   if (lock_tokens && lock_tokens->nelts)
     SVN_CMD_ERR(add_lock_tokens(conn, lock_tokens, b, pool));
 
+ if (revprop_list)
+ SVN_ERR(svn_ra_svn_parse_proplist(revprop_list, pool, &revprop_table));
+
   ccb.pool = pool;
   ccb.new_rev = &new_rev;
   ccb.date = &date;
   ccb.author = &author;
   ccb.post_commit_err = &post_commit_err;
   /* ### Note that svn_repos_get_commit_editor actually wants a decoded URL. */
- SVN_CMD_ERR(svn_repos_get_commit_editor4
+ SVN_CMD_ERR(svn_repos_get_commit_editor5
               (&editor, &edit_baton, b->repos, NULL,
                svn_path_uri_decode(b->repos_url, pool),
                b->fs_path->data, b->user,
- log_msg, commit_done, &ccb,
+ log_msg, revprop_table,
+ commit_done, &ccb,
                authz_commit_cb, baton, pool));
   SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
   SVN_ERR(svn_ra_svn_drive_editor(conn, pool, editor, edit_baton, &aborted));
@@ -1039,7 +1028,7 @@
   /* Send successful command response with revision and props. */
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((?c)r(!", "success",
                                  hex_digest, rev));
- SVN_ERR(write_proplist(conn, pool, props));
+ SVN_ERR(svn_ra_svn_write_proplist(conn, pool, props));
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))"));
 
   /* Now send the file's contents. */
@@ -1219,7 +1208,7 @@
 
   /* Write out response. */
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(r(!", "success", rev));
- SVN_ERR(write_proplist(conn, pool, props));
+ SVN_ERR(svn_ra_svn_write_proplist(conn, pool, props));
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(!"));
   if (want_contents)
     {
@@ -1621,7 +1610,7 @@
 
   SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "cr(!",
                                  path, rev));
- SVN_ERR(write_proplist(frb->conn, pool, rev_props));
+ SVN_ERR(svn_ra_svn_write_proplist(frb->conn, pool, rev_props));
   SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "!)(!"));
   SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs));
   SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "!)"));

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sun Mar 11 12:29:16 2007

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.