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

[PATCH] (2nd approach) Fix issue #1976: Set arbitrary revision properties (revprops) during commit.

From: Gerco Ballintijn <gerco_at_ballintijn.com>
Date: 2007-02-24 02:18:09 CET

Hi,

Attached is my second attemp/approach for fixing issue #1976. This attempt
is based on adding an extra function to the editor interface. This function
changes the set of revision properties of the current edit/transaction. This
attempt is significantly different from my previous attemp where the revision
properties were added when creating an editor. The revision properties can
now be set at the end of edit, just before closing it.

Gerco.

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

This patch adds a "--with-revprop" option to the various URL-based
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 changes are made by calling a new editor function
called "change_rev_prop." This commit consists of the following types of
changes:

1. Code added to set the revision properties in the current FS transaction.
2. Code added to marshall and unmarshall the change-rev-prop command
   for the svn protocol, and deal with this new capability.
3. Code added to change revision properties in the current commit for
   the DAV protocol (client side only).
4. Code added various existing editors to pass through the change_rev_prop
   invocation (e.g., cancellation, debug, testing).
5. Code added to extract revision properties from the command line.
6. Code added to test the new functionality.

Note: The serf module and bindings are not included in this fix.

* subversion/incluide/svn_delta.h (svn_delta_editor_t): Added the
  function-pointer field change_rev_prop.

* subversion/include/svn_client.h (svn_client_ctx_t): Added the revprop_table
  field.

* subversion/include/svn_ra_svn.h (SVN_RA_SVN_CAP_CHANGE_REV_PROP):
        Added new capability for change-rev-prop editor command.

* subversion/libsvn_delta/debug_editor.c
  (change_rev_prop, svn_delta__get_debug_editor): Added the change_rev_prop()
  editor function; prints debug message and and propagates the change_rev_prop()
  call.

* subversion/libsvn_delta/default_editor.c (change_rev_prop, default_editor):
  Added the change_rev_prop() editor function; does nothing.

* subversion/libsvn_delta/cancel.c
  (change_rev_prop, svn_delta_get_cancellation_editor): Added the
  change_rev_prop() editor function; Checks for cancellation and propagates
  the change_rev_prop() call.

* subversion/libsvn_repos/commit.c
  (change_rev_prop, svn_repos_get_commit_editor4): Added the change_rev_prop()
  editor function; calls svn_fs_change_txn_prop() to do the actual work.

* subversion/libsvn_ra_svn/protocol
  (2.1 Capabilities): Added change-rev-prop capability description.
  (3.1.2. Editor Command Set): Added description of change-rev-prop command,
  and fixed typo.

* subversion/libsvn_ra_svn/client.c
  (svn_ra_svn__auth_response, open_session): Added the new
  SVN_RA_SVN_CAP_CHANGE_REV_PROP capability.

* subversion/libsvn_ra_svn/editor.c
  (ra_svn_change_rev_prop, svn_ra_svn_get_editor): Added the change_rev_prop()
  editor function; sends a change-rev-prop command if the other side has the
  capability.
  (ra_svn_handle_change_rev_prop, ra_svn_edit_commands): Added function to
  handle an incoming change-rev-prop command.

* subversion/libsvn_ra_svn/editorp.c
  (ra_svn_change_rev_prop, svn_ra_svn_get_editorp): Added the change_rev_prop()
  editor function; sends a change-rev-prop command if the other side has the
  capability.
  (ra_svn_handle_change_rev_prop, ra_svn_edit_cmds): Added function to
  handle an incoming change-rev-prop command.

* subversion/libsvn_ra_dav/commit.c
  (apply_rev_prop): New generic function to set revision properties;
  extracted from apply_log_message.
  (apply_log_message): Changed to call apply_rev_prop() with the proper
  parameters.
  (commit_change_rev_prop, svn_ra_dav__get_commit_editor): Added the
  change_rev_prop() editor function; calls apply_rev_prop to do the
  actual work.

* subversion/libsvn_client/client.h (svn_client__change_rev_prop),
  subversion/libsvn_client/util.c (svn_client__change_rev_prop): New function
  to call change_rev_prop() editor function for all properties in revprop_table.
  
* subversion/libsvn_client/add.c (mkdir_urls),
  subversion/libsvn_client/commit_util.c (svn_client__do_commit),
  subversion/libsvn_client/commit.c (import),
  subversion/libsvn_client/copy.c (repos_to_repos_copy),
  subversion/libsvn_client/delete.c (delete_urls),
  subversion/libsvn_client/prop_commands.c (propset_on_url)
  Added call to svn_client__change_rev_prop().

* subversion/libsvn_client/commit_util.c (change_rev_prop, get_test_editor):
  Added a change_rev_prop() function to the commit debug editor.

* 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),
  subversion/svn/import-cmd.c (svn_cl__import):
  Pass on the revprop_table field.

* subversion/svn/copy-cmd.c (svn_cl__copy),
  subversion/svn/delete-cmd.c (svn_cl__delete),
  subversion/svn/mkdir-cmd.c (svn_cl__mkdir),
  subversion/svn/move-cmd.c (svn_cl__move),
  subversion/svn/propedit-cmd.c (svn_cl__propedit):
  Check whether the revprop_table field is set only when actually commiting,
  and pass on the revprop_table field.

* subversion/tests/svn_test_editor.c (test_change_rev_prop, svn_test_get_editor):
  Added a change_rev_prop() function to the commit testing editor.

* 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): New function to
  perform a trivial change.
]]]

Index: subversion/include/svn_ra_svn.h
===================================================================
--- subversion/include/svn_ra_svn.h (revision 23480)
+++ subversion/include/svn_ra_svn.h (working copy)
@@ -43,6 +43,7 @@
 #define SVN_RA_SVN_CAP_EDIT_PIPELINE "edit-pipeline"
 #define SVN_RA_SVN_CAP_SVNDIFF1 "svndiff1"
 #define SVN_RA_SVN_CAP_ABSENT_ENTRIES "absent-entries"
+#define SVN_RA_SVN_CAP_CHANGE_REV_PROP "change-rev-prop"
 
 /** ra_svn passes @c svn_dirent_t fields over the wire as a list of
  * words, these are the values used to represent each field.
Index: subversion/include/svn_delta.h
===================================================================
--- subversion/include/svn_delta.h (revision 23480)
+++ subversion/include/svn_delta.h (working copy)
@@ -699,6 +699,19 @@
                                       svn_revnum_t target_revision,
                                       apr_pool_t *pool);
 
+ /** Change the value of the revision property for this edit.
+ * - @a edit_baton global baton for this change.
+ * - @a name is the name of the revision property to change.
+ * - @a value is the new value of the revision property, or @c NULL if
+ * the revision property should be removed altogether.
+ *
+ * All allocations should be performed in @a pool.
+ */
+ svn_error_t *(*change_rev_prop)(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool);
+
   /** Set @a *root_baton to a baton for the top directory of the change.
    * (This is the top of the subtree being changed, not necessarily
    * the root of the filesystem.) Like any other directory baton, the
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h (revision 23480)
+++ subversion/include/svn_client.h (working copy)
@@ -762,6 +762,10 @@
   /** MIME types map.
    * @since New in 1.5. */
   apr_hash_t *mimetypes_map;
+
+ /** Table holding the extra revision properties to be set.
+ * @since New in 1.5. */
+ apr_hash_t * revprop_table;
 
 } svn_client_ctx_t;
 
Index: subversion/libsvn_client/delete.c
===================================================================
--- subversion/libsvn_client/delete.c (revision 23480)
+++ subversion/libsvn_client/delete.c (working copy)
@@ -206,6 +206,15 @@
       return err;
     }
 
+ err = svn_client__set_rev_props(editor, edit_baton, ctx->revprop_table,
+ pool);
+ if (err)
+ {
+ /* At least try to abort the edit (and fs txn) before throwing err. */
+ svn_error_clear(editor->abort_edit(edit_baton, pool));
+ return err;
+ }
+
   /* Close the edit. */
   SVN_ERR(editor->close_edit(edit_baton, pool));
 
Index: subversion/libsvn_client/util.c
===================================================================
--- subversion/libsvn_client/util.c (revision 23480)
+++ subversion/libsvn_client/util.c (working copy)
@@ -119,3 +119,23 @@
   return new_item;
 }
 
+
+svn_error_t *
+svn_client__set_rev_props(const svn_delta_editor_t *editor, void *edit_baton,
+ apr_hash_t *revprop_table, apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+ const void *propname;
+ void *propval;
+
+ if (revprop_table)
+ {
+ for (hi = apr_hash_first(pool, revprop_table); hi; hi = apr_hash_next(hi))
+ {
+ apr_hash_this(hi, &propname, NULL, &propval);
+ SVN_ERR(editor->change_rev_prop(edit_baton, propname, propval, pool));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
Index: subversion/libsvn_client/client.h
===================================================================
--- subversion/libsvn_client/client.h (revision 23480)
+++ subversion/libsvn_client/client.h (working copy)
@@ -779,7 +779,6 @@
                                apr_pool_t *pool);
 
 
-
 /* Retrieve log messages using the first provided (non-NULL) callback
    in the set of *CTX->log_msg_func3, CTX->log_msg_func2, or
    CTX->log_msg_func. Other arguments same as
@@ -790,6 +789,15 @@
                         const apr_array_header_t *commit_items,
                         svn_client_ctx_t *ctx,
                         apr_pool_t *pool);
+
+
+/* Set the revision properties for this edit by calling the change_rev_prop()
+ * editor function of editor for all (key, value)-pairs in revprop_table.
+ */
+svn_error_t *
+svn_client__set_rev_props(const svn_delta_editor_t *editor, void *edit_baton,
+ apr_hash_t *revprop_table, apr_pool_t *pool);
+
 
 #ifdef __cplusplus
 }
Index: subversion/libsvn_client/prop_commands.c
===================================================================
--- subversion/libsvn_client/prop_commands.c (revision 23480)
+++ subversion/libsvn_client/prop_commands.c (working copy)
@@ -284,6 +284,15 @@
       return err;
     }
 
+ err = svn_client__set_rev_props(editor, edit_baton, ctx->revprop_table,
+ pool);
+ if (err)
+ {
+ /* At least try to abort the edit (and fs txn) before throwing err. */
+ svn_error_clear(editor->abort_edit(edit_baton, pool));
+ return err;
+ }
+
   /* Close the edit. */
   SVN_ERR(editor->close_edit(edit_baton, pool));
 
Index: subversion/libsvn_client/copy.c
===================================================================
--- subversion/libsvn_client/copy.c (revision 23480)
+++ subversion/libsvn_client/copy.c (working copy)
@@ -700,6 +700,15 @@
       return err;
     }
 
+ err = svn_client__set_rev_props(editor, edit_baton, ctx->revprop_table,
+ pool);
+ if (err)
+ {
+ /* At least try to abort the edit (and fs txn) before throwing err. */
+ svn_error_clear(editor->abort_edit(edit_baton, pool));
+ return err;
+ }
+
   /* Close the edit. */
   SVN_ERR(editor->close_edit(edit_baton, pool));
 
Index: subversion/libsvn_client/commit_util.c
===================================================================
--- subversion/libsvn_client/commit_util.c (revision 23480)
+++ subversion/libsvn_client/commit_util.c (working copy)
@@ -1410,6 +1410,9 @@
 
   svn_pool_destroy(subpool);
 
+ SVN_ERR(svn_client__set_rev_props(editor, edit_baton, ctx->revprop_table,
+ pool));
+
   /* Close the edit. */
   SVN_ERR(editor->close_edit(edit_baton, pool));
   return SVN_NO_ERROR;
@@ -1491,6 +1494,17 @@
 }
 
 static svn_error_t *
+change_rev_prop(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ struct edit_baton *eb = edit_baton;
+ fprintf(stderr, " RevPropSet (%s=%s)\n", name, value ? value->data : "");
+ return (*eb->real_editor->change_rev_prop(edit_baton, name, value, pool);
+}
+
+static svn_error_t *
 open_root(void *edit_baton,
           svn_revnum_t base_revision,
           apr_pool_t *dir_pool,
@@ -1689,6 +1703,7 @@
   /* We don't implement absent_file() or absent_directory() in this
      editor, because presumably commit would never send that. */
   ed->set_target_revision = set_target_revision;
+ ed->change_rev_prop = change_rev_prop;
   ed->open_root = open_root;
   ed->add_directory = add_directory;
   ed->open_directory = open_directory;
Index: subversion/libsvn_client/commit.c
===================================================================
--- subversion/libsvn_client/commit.c (revision 23480)
+++ subversion/libsvn_client/commit.c (working copy)
@@ -467,6 +467,7 @@
   apr_array_header_t *batons = NULL;
   const char *edit_path = "";
   import_ctx_t *import_ctx = apr_pcalloc(pool, sizeof(*import_ctx));
+ svn_error_t *err;
 
   /* Get a root dir baton. We pass an invalid revnum to open_root
      to mean "base this on the youngest revision". Should we have an
@@ -558,7 +559,10 @@
         }
     }
 
- if (import_ctx->repos_changed)
+ err = svn_client__set_rev_props(editor, edit_baton, ctx->revprop_table,
+ pool);
+
+ if (!err && import_ctx->repos_changed)
     SVN_ERR(editor->close_edit(edit_baton, pool));
   else
     SVN_ERR(editor->abort_edit(edit_baton, pool));
Index: subversion/libsvn_client/add.c
===================================================================
--- subversion/libsvn_client/add.c (revision 23480)
+++ subversion/libsvn_client/add.c (working copy)
@@ -605,7 +605,7 @@
                                     commit_baton,
                                     NULL, TRUE, /* No lock tokens */
                                     pool));
-
+
   /* Call the path-based editor driver. */
   err = svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM,
                               targets, path_driver_cb_func,
@@ -617,6 +617,15 @@
       return err;
     }
 
+ err = svn_client__set_rev_props(editor, edit_baton, ctx->revprop_table,
+ pool);
+ if (err)
+ {
+ /* At least try to abort the edit (and fs txn) before throwing err. */
+ svn_error_clear(editor->abort_edit(edit_baton, pool));
+ return err;
+ }
+
   /* Close the edit. */
   SVN_ERR(editor->close_edit(edit_baton, pool));
 
Index: subversion/tests/svn_test_editor.c
===================================================================
--- subversion/tests/svn_test_editor.c (revision 23480)
+++ subversion/tests/svn_test_editor.c (working copy)
@@ -201,6 +201,36 @@
 
 
 static svn_error_t *
+test_change_rev_prop(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ struct edit_baton *eb = edit_baton;
+ svn_stringbuf_t *str;
+
+ str = svn_stringbuf_createf(pool, "[%s] change_rev_prop\n",
+ eb->editor_name);
+ SVN_ERR(print(eb, 0, str));
+
+ /* We're done if non-verbose */
+ if (! eb->verbose)
+ return SVN_NO_ERROR;
+
+ str = svn_stringbuf_createf(pool, "name: %s\n", name);
+ SVN_ERR(print(eb, 1, str));
+
+ str = svn_stringbuf_createf(pool, "value: %s\n",
+ value ? value->data : "(null)");
+ SVN_ERR(print(eb, 1, str));
+
+ SVN_ERR(newline(eb));
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
 test_open_root(void *edit_baton,
                svn_revnum_t base_revision,
                apr_pool_t *pool,
@@ -555,6 +585,7 @@
   /* Set up the editor. */
   my_editor = svn_delta_default_editor(pool);
   my_editor->set_target_revision = test_set_target_revision;
+ my_editor->change_rev_prop = test_change_rev_prop;
   my_editor->open_root = test_open_root;
   my_editor->delete_entry = test_delete_entry;
   my_editor->add_directory = test_add_directory;
Index: subversion/tests/cmdline/svneditor.py
===================================================================
--- subversion/tests/cmdline/svneditor.py (revision 23480)
+++ 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 23480)
+++ subversion/tests/cmdline/commit_tests.py (working copy)
@@ -2004,6 +2004,280 @@
                                      '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"
+ outlines, errlines = svntest.main.run_svn(None, 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug=42',
+ remote_dir)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 2:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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)
+
+ outlines, errlines = svntest.main.run_svn(None, 'delete', '-m', 'msg',
+ '--with-revprop', 'bug=52',
+ remote_dir)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 3:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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()
+ local_file = os.path.join(sbox.wc_dir, 'file')
+ svntest.main.file_write(local_file, "xxxx")
+ svntest.actions.run_and_verify_svn(sbox.wc_dir, None, [], 'add', local_file)
+
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ outlines, errlines = svntest.main.run_svn(None, 'commit', '-m', 'msg',
+ '--with-revprop', 'bug=62')
+ os.chdir(was_cwd)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 2:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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")
+
+ outlines, errlines = svntest.main.run_svn(None, 'import', '-m', 'msg',
+ '--with-revprop', 'bug=72',
+ local_dir, sbox.repo_url)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 2:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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)
+
+ outlines, errlines = svntest.main.run_svn(None, 'copy', '-m', 'msg',
+ '--with-revprop', 'bug=82',
+ remote_dir1, remote_dir2)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 3:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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)
+
+ outlines, errlines = svntest.main.run_svn(None, 'copy', '-m', 'msg',
+ '--with-revprop', 'bug=92',
+ local_dir, remote_dir)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 2:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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)
+
+ outlines, errlines = svntest.main.run_svn(None, 'move', '-m', 'msg',
+ '--with-revprop', 'bug=102',
+ remote_dir1, remote_dir2)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 3:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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')
+ outlines, errlines = svntest.main.run_svn(None, 'propedit',
+ '-m', 'msg',
+ '--with-revprop', 'bug=112',
+ 'prop', sbox.repo_url)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 2:\n',
+ ' svn:log\n', ' svn:author\n',
+ ' bug\n', ' svn:date\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"
+ outlines, errlines = svntest.main.run_svn(None, 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug=32',
+ '--with-revprop', 'ref=22',
+ remote_dir)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 2:\n',
+ ' svn:log\n', ' svn:author\n', ' ref\n', ' bug\n',
+ ' svn:date\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 = r'svn: Cannot set standard \(revision\) properties using ' \
+ '--with-revprop'
+
+ 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)
+
+
+def use_empty_value_in_revprop_pair(sbox):
+ "use '' value for revision props in remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+ outlines, errlines = svntest.main.run_svn(None, 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug=',
+ '--with-revprop', 'ref=',
+ remote_dir)
+ if errlines != []:
+ raise svntest.Failure
+
+ expected = ['Unversioned properties on revision 2:\n',
+ ' svn:log\n', ' svn:author\n', ' ref\n', ' bug\n',
+ ' svn:date\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)
+
+
+def no_equals_in_revprop_pair(sbox):
+ "use no = in revision property during remote mkdir"
+
+ sbox.build()
+ remote_dir = sbox.repo_url + "/dir"
+ svntest.actions.run_and_verify_svn(None, [],
+ "svn: Revision property pair contains no '='",
+ 'mkdir', '-m', 'msg',
+ '--with-revprop', 'bug',
+ remote_dir)
+
+
+
 ########################################################################
 # Run the tests
 
@@ -2044,8 +2318,21 @@
               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_string_as_revprop_pair,
+ use_empty_value_in_revprop_pair,
+ no_equals_in_revprop_pair
+ ]
 
 if __name__ == '__main__':
   svntest.main.run_tests(test_list)
Index: subversion/libsvn_repos/commit.c
===================================================================
--- subversion/libsvn_repos/commit.c (revision 23480)
+++ subversion/libsvn_repos/commit.c (working copy)
@@ -161,6 +161,17 @@
 /*** Editor functions ***/
 
 static svn_error_t *
+change_rev_prop(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ struct edit_baton *eb = edit_baton;
+
+ return svn_fs_change_txn_prop(eb->txn, name, value, pool);
+}
+
+static svn_error_t *
 open_root(void *edit_baton,
           svn_revnum_t base_revision,
           apr_pool_t *pool,
@@ -800,6 +811,7 @@
   eb = apr_pcalloc(subpool, sizeof(*eb));
 
   /* Set up the editor. */
+ e->change_rev_prop = change_rev_prop;
   e->open_root = open_root;
   e->delete_entry = delete_entry;
   e->add_directory = add_directory;
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c (revision 23480)
+++ subversion/libsvn_ra_svn/client.c (working copy)
@@ -224,11 +224,12 @@
                                        svn_boolean_t compat)
 {
   if (compat)
- return svn_ra_svn_write_tuple(conn, pool, "nw(?c)(www)", (apr_uint64_t) 1,
+ return svn_ra_svn_write_tuple(conn, pool, "nw(?c)(wwww)", (apr_uint64_t) 1,
                                   mech, mech_arg,
                                   SVN_RA_SVN_CAP_EDIT_PIPELINE,
                                   SVN_RA_SVN_CAP_SVNDIFF1,
- SVN_RA_SVN_CAP_ABSENT_ENTRIES);
+ SVN_RA_SVN_CAP_ABSENT_ENTRIES,
+ SVN_RA_SVN_CAP_CHANGE_REV_PROP);
   else
     return svn_ra_svn_write_tuple(conn, pool, "w(?c)", mech, mech_arg);
 }
@@ -565,10 +566,11 @@
     }
   else
     {
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(www)c", (apr_uint64_t) 2,
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(wwww)c", (apr_uint64_t) 2,
                                      SVN_RA_SVN_CAP_EDIT_PIPELINE,
                                      SVN_RA_SVN_CAP_SVNDIFF1,
- SVN_RA_SVN_CAP_ABSENT_ENTRIES, url));
+ SVN_RA_SVN_CAP_ABSENT_ENTRIES,
+ SVN_RA_SVN_CAP_CHANGE_REV_PROP, url));
       SVN_ERR(handle_auth_request(sess, pool));
     }
 
Index: subversion/libsvn_ra_svn/protocol
===================================================================
--- subversion/libsvn_ra_svn/protocol (revision 23480)
+++ subversion/libsvn_ra_svn/protocol (working copy)
@@ -180,7 +180,10 @@
   absent-entries If the remote end announces support for this capability,
                     it will accept the absent-dir and absent-file editor
                     commands.
+ change-rev-prop If the remote end announces support for this capability,
+ it will accept the change-rev-prop editor command.
 
+
 3. Commands
 -----------
 
@@ -394,7 +397,7 @@
 
 3.1.2. Editor Command Set
 
-If edit pipelining is negotiated (see section 2.1), than an edit
+If edit pipelining is negotiated (see section 2.1), then an edit
 operation produces only one response, at close-edit or abort-edit
 time. However, the consumer may write an error response at any time
 during the edit in order to terminate the edit operation early; the
@@ -406,14 +409,18 @@
 the consumer must read and discard edit operations until writing
 unblocks or it reads an abort-edit.
 
-If edit pipelining is not negotiated, then the target-rev, open-root,
-delete-entry, add-dir, open-dir, close-dir, and close-file operations
-produce empty responses. Errors produced other operations are
-reported by the enclosing close-dir or close-file operation.
+If edit pipelining is not negotiated, then the target-rev,
+change-rev-prop, open-root, delete-entry, add-dir, open-dir, close-dir,
+and close-file operations produce empty responses. Errors produced
+other operations are reported by the enclosing close-dir or close-file
+operation.
 
   target-rev
     params: ( rev:number )
 
+ change-rev-prop
+ params: ( name:string [ value:string ] )
+
   open-root
     params: ( [ rev:number ] root-token:string )
 
Index: subversion/libsvn_ra_svn/editorp.c
===================================================================
--- subversion/libsvn_ra_svn/editorp.c (revision 23480)
+++ subversion/libsvn_ra_svn/editorp.c (working copy)
@@ -141,6 +141,23 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *ra_svn_change_rev_prop(void *edit_baton, const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ ra_svn_edit_baton_t *eb = edit_baton;
+
+ /* Avoid sending an unknown command if the other end doesn't support
+ change-rev-prop. */
+ if (! svn_ra_svn_has_capability(eb->conn, SVN_RA_SVN_CAP_CHANGE_REV_PROP))
+ return SVN_NO_ERROR;
+
+ SVN_ERR(check_for_error(eb, pool));
+ SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "change-rev-prop", "c(?s)",
+ name, value));
+ return SVN_NO_ERROR;
+}
+
 static svn_error_t *ra_svn_open_root(void *edit_baton, svn_revnum_t rev,
                                      apr_pool_t *pool, void **root_baton)
 {
@@ -406,6 +423,7 @@
   eb->got_status = FALSE;
 
   ra_svn_editor->set_target_revision = ra_svn_target_rev;
+ ra_svn_editor->change_rev_prop = ra_svn_change_rev_prop;
   ra_svn_editor->open_root = ra_svn_open_root;
   ra_svn_editor->delete_entry = ra_svn_delete_entry;
   ra_svn_editor->add_directory = ra_svn_add_dir;
@@ -469,6 +487,19 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *ra_svn_handle_change_rev_prop(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ apr_array_header_t *params,
+ ra_svn_driver_state_t *ds)
+{
+ const char *name;
+ svn_string_t *value;
+
+ SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?s)", &name, &value));
+ SVN_CMD_ERR(ds->editor->change_rev_prop(ds->edit_baton, name, value, pool));
+ return SVN_NO_ERROR;
+}
+
 static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn,
                                             apr_pool_t *pool,
                                             apr_array_header_t *params,
@@ -811,6 +842,7 @@
                           ra_svn_driver_state_t *ds);
 } ra_svn_edit_cmds[] = {
   { "target-rev", ra_svn_handle_target_rev },
+ { "change-rev-prop", ra_svn_handle_change_rev_prop },
   { "open-root", ra_svn_handle_open_root },
   { "delete-entry", ra_svn_handle_delete_entry },
   { "add-dir", ra_svn_handle_add_dir },
Index: subversion/libsvn_ra_svn/editor.c
===================================================================
--- subversion/libsvn_ra_svn/editor.c (revision 23480)
+++ subversion/libsvn_ra_svn/editor.c (working copy)
@@ -110,6 +110,22 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *ra_svn_change_rev_prop(void *edit_baton, const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ ra_svn_edit_baton_t *eb = edit_baton;
+
+ /* Avoid sending an unknown command if the other end doesn't support
+ change-rev-prop. */
+ if (! svn_ra_svn_has_capability(eb->conn, SVN_RA_SVN_CAP_CHANGE_REV_PROP))
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "change-rev-prop", "c(?s)",
+ name, value));
+ return SVN_NO_ERROR;
+}
+
 static svn_error_t *ra_svn_open_root(void *edit_baton, svn_revnum_t rev,
                                      apr_pool_t *pool, void **root_baton)
 {
@@ -330,6 +346,7 @@
 
   ra_svn_editor = svn_delta_default_editor(pool);
   ra_svn_editor->set_target_revision = ra_svn_target_rev;
+ ra_svn_editor->change_rev_prop = ra_svn_change_rev_prop;
   ra_svn_editor->open_root = ra_svn_open_root;
   ra_svn_editor->delete_entry = ra_svn_delete_entry;
   ra_svn_editor->add_directory = ra_svn_add_dir;
@@ -402,6 +419,21 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *ra_svn_handle_change_rev_prop(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ apr_array_header_t *params,
+ void *baton)
+{
+ ra_svn_driver_state_t *ds = baton;
+ const char *name;
+ svn_string_t *value;
+
+ SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?s)", &name, &value));
+ SVN_CMD_ERR(ds->editor->change_rev_prop(ds->edit_baton, name, value, pool));
+ SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
+ return SVN_NO_ERROR;
+}
+
 static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn,
                                             apr_pool_t *pool,
                                             apr_array_header_t *params,
@@ -720,6 +752,7 @@
 
 static const svn_ra_svn_cmd_entry_t ra_svn_edit_commands[] = {
   { "target-rev", ra_svn_handle_target_rev },
+ { "change-rev-prop", ra_svn_handle_change_rev_prop },
   { "open-root", ra_svn_handle_open_root },
   { "delete-entry", ra_svn_handle_delete_entry },
   { "add-dir", ra_svn_handle_add_dir },
Index: subversion/libsvn_delta/cancel.c
===================================================================
--- subversion/libsvn_delta/cancel.c (revision 23480)
+++ subversion/libsvn_delta/cancel.c (working copy)
@@ -56,6 +56,24 @@
 }
 
 static svn_error_t *
+change_rev_prop(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ struct edit_baton *eb = edit_baton;
+
+ SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+ SVN_ERR(eb->wrapped_editor->change_rev_prop(eb->wrapped_edit_baton,
+ name,
+ value,
+ pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 open_root(void *edit_baton,
           svn_revnum_t base_revision,
           apr_pool_t *pool,
@@ -351,6 +369,7 @@
       struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));
 
       tree_editor->set_target_revision = set_target_revision;
+ tree_editor->change_rev_prop = change_rev_prop;
       tree_editor->open_root = open_root;
       tree_editor->delete_entry = delete_entry;
       tree_editor->add_directory = add_directory;
Index: subversion/libsvn_delta/default_editor.c
===================================================================
--- subversion/libsvn_delta/default_editor.c (revision 23480)
+++ subversion/libsvn_delta/default_editor.c (working copy)
@@ -31,7 +31,19 @@
 {
   return SVN_NO_ERROR;
 }
+
+
 static svn_error_t *
+change_rev_prop(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
 add_item(const char *path,
          void *parent_baton,
          const char *copyfrom_path,
@@ -131,7 +143,8 @@
 
 static const svn_delta_editor_t default_editor =
 {
- set_target_revision,
+ set_target_revision,
+ change_rev_prop,
   open_root,
   delete_entry,
   add_item,
Index: subversion/libsvn_delta/debug_editor.c
===================================================================
--- subversion/libsvn_delta/debug_editor.c (revision 23480)
+++ subversion/libsvn_delta/debug_editor.c (working copy)
@@ -70,6 +70,26 @@
 }
 
 static svn_error_t *
+change_rev_prop(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ struct edit_baton *eb = edit_baton;
+
+ SVN_ERR(write_indent(eb, pool));
+ SVN_ERR(svn_stream_printf(eb->out, pool, "change_rev_prop : '%s' '%s'\n",
+ name, value->data));
+
+ SVN_ERR(eb->wrapped_editor->change_rev_prop(eb->wrapped_edit_baton,
+ name,
+ value,
+ pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 open_root(void *edit_baton,
           svn_revnum_t base_revision,
           apr_pool_t *pool,
@@ -403,6 +423,7 @@
   out = svn_stream_from_aprfile(errfp, pool);
 
   tree_editor->set_target_revision = set_target_revision;
+ tree_editor->change_rev_prop = change_rev_prop;
   tree_editor->open_root = open_root;
   tree_editor->delete_entry = delete_entry;
   tree_editor->add_directory = add_directory;
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (revision 23480)
+++ 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,7 +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 */
 } svn_cl__opt_state_t;
 
 
Index: subversion/svn/mkdir-cmd.c
===================================================================
--- subversion/svn/mkdir-cmd.c (revision 23480)
+++ subversion/svn/mkdir-cmd.c (working copy)
@@ -59,17 +59,19 @@
   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
     {
       SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                          NULL, ctx->config, subpool));
+ ctx->revprop_table = opt_state->revprop_table;
     }
 
   err = svn_client_mkdir2(&commit_info, targets, ctx, subpool);
Index: subversion/svn/move-cmd.c
===================================================================
--- subversion/svn/move-cmd.c (revision 23480)
+++ subversion/svn/move-cmd.c (working copy)
@@ -70,15 +70,17 @@
   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/copy-cmd.c
===================================================================
--- subversion/svn/copy-cmd.c (revision 23480)
+++ 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 23480)
+++ subversion/svn/commit-cmd.c (working copy)
@@ -89,6 +89,7 @@
   SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
                                      opt_state, base_dir,
                                      ctx->config, pool));
+ ctx->revprop_table = opt_state->revprop_table;
 
   /* Commit. */
   SVN_ERR(svn_cl__cleanup_log_msg
Index: subversion/svn/delete-cmd.c
===================================================================
--- subversion/svn/delete-cmd.c (revision 23480)
+++ subversion/svn/delete-cmd.c (working copy)
@@ -57,17 +57,19 @@
   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
     {
       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_delete3(&commit_info, targets, opt_state->force,
Index: subversion/svn/import-cmd.c
===================================================================
--- subversion/svn/import-cmd.c (revision 23480)
+++ 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 23480)
+++ 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[] =
 {
@@ -872,6 +877,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"));
+
+ sep = strchr(revprop_pair, '=');
+ if (! sep)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Revision property pair contains no '='"));
+
+ propname = apr_pstrndup(pool, revprop_pair, sep - revprop_pair);
+ if (svn_prop_is_svn_prop(propname))
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Cannot set standard (revision) properties "
+ "using --with-revprop"));
+
+ propval = svn_string_create(sep + 1, pool);
+ if (! *revprop_table_p)
+ *revprop_table_p = apr_hash_make(pool);
+
+ apr_hash_set(*revprop_table_p, propname, APR_HASH_KEY_STRING, propval);
+
+ return SVN_NO_ERROR;
+}
+
+
 
 /*** Main. ***/
 
@@ -1271,6 +1309,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 23480)
+++ subversion/svn/propedit-cmd.c (working copy)
@@ -193,11 +193,12 @@
             }
           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 +246,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/commit.c
===================================================================
--- subversion/libsvn_ra_dav/commit.c (revision 23480)
+++ subversion/libsvn_ra_dav/commit.c (working copy)
@@ -580,7 +580,93 @@
 }
 
 
+static svn_error_t * apply_rev_prop(commit_ctx_t *cc,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ 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;
 
+ /* ### this whole sequence can/should be replaced with an expand-property
+ ### REPORT when that is available on the server. */
+
+ /* fetch the DAV:version-controlled-configuration from the session's URL */
+ SVN_ERR(svn_ra_dav__get_one_prop(&vcc, cc->ras, cc->ras->root.path,
+ NULL, &svn_ra_dav__vcc_prop, pool));
+
+ /* ### we should use DAV:apply-to-version on the CHECKOUT so we can skip
+ ### retrieval of the baseline */
+
+ do {
+
+ svn_error_clear(err);
+
+ /* Get the latest baseline from VCC's DAV:checked-in property.
+ This should give us the HEAD revision of the moment. */
+ SVN_ERR(svn_ra_dav__get_one_prop(&baseline_url, cc->ras,
+ vcc->data, NULL,
+ &svn_ra_dav__checked_in_prop, pool));
+ baseline_rsrc.pool = pool;
+ baseline_rsrc.vsn_url = baseline_url->data;
+
+ /* To set the log message, we must checkout the latest baseline
+ and get back a mutable "working" baseline. */
+ err = checkout_resource(cc, &baseline_rsrc, FALSE, NULL, pool);
+
+ /* There's a small chance of a race condition here, if apache is
+ experiencing heavy commit concurrency or if the network has
+ long latency. It's possible that the value of HEAD changed
+ between the time we fetched the latest baseline and the time we
+ checkout that baseline. If that happens, apache will throw us
+ a BAD_BASELINE error (deltaV says you can only checkout the
+ latest baseline). We just ignore that specific error and
+ retry a few times, asking for the latest baseline again. */
+ if (err && err->apr_err != SVN_ERR_APMOD_BAD_BASELINE)
+ return err;
+
+ } while (err && (--retry_count > 0));
+
+ /* Yikes, if we couldn't hold onto HEAD after a few retries, throw a
+ real error.*/
+ if (err)
+ return err;
+
+ if (value)
+ {
+ apr_hash_t *prop_changes = apr_hash_make(pool);
+
+ apr_hash_set(prop_changes, name, APR_HASH_KEY_STRING, value);
+ SVN_ERR(svn_ra_dav__do_proppatch(cc->ras, baseline_rsrc.wr_url,
+ prop_changes, NULL, NULL, pool));
+ }
+ else
+ {
+ apr_array_header_t *prop_deletes = apr_array_make(pool, 1, sizeof(char *));
+
+ APR_ARRAY_PUSH(prop_deletes, const char *) = name;
+ SVN_ERR(svn_ra_dav__do_proppatch(cc->ras, baseline_rsrc.wr_url, NULL,
+ prop_deletes, NULL, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *commit_change_rev_prop(void *edit_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ commit_ctx_t *cc = edit_baton;
+
+ return apply_rev_prop(cc, name, value, pool);
+}
+
+
 static svn_error_t * commit_open_root(void *edit_baton,
                                       svn_revnum_t base_revision,
                                       apr_pool_t *dir_pool,
@@ -1280,69 +1366,12 @@
                                        const char *log_msg,
                                        apr_pool_t *pool)
 {
- 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;
+ svn_string_t *log_str = svn_string_create(log_msg, pool);
 
- /* ### this whole sequence can/should be replaced with an expand-property
- ### REPORT when that is available on the server. */
+ return apply_rev_prop(cc, SVN_PROP_PREFIX "log", log_str, pool);
+}
 
- /* fetch the DAV:version-controlled-configuration from the session's URL */
- SVN_ERR(svn_ra_dav__get_one_prop(&vcc, cc->ras, cc->ras->root.path,
- NULL, &svn_ra_dav__vcc_prop, pool));
 
- /* ### we should use DAV:apply-to-version on the CHECKOUT so we can skip
- ### retrieval of the baseline */
-
- do {
-
- svn_error_clear(err);
-
- /* Get the latest baseline from VCC's DAV:checked-in property.
- This should give us the HEAD revision of the moment. */
- SVN_ERR(svn_ra_dav__get_one_prop(&baseline_url, cc->ras,
- vcc->data, NULL,
- &svn_ra_dav__checked_in_prop, pool));
- baseline_rsrc.pool = pool;
- baseline_rsrc.vsn_url = baseline_url->data;
-
- /* To set the log message, we must checkout the latest baseline
- and get back a mutable "working" baseline. */
- err = checkout_resource(cc, &baseline_rsrc, FALSE, NULL, pool);
-
- /* There's a small chance of a race condition here, if apache is
- experiencing heavy commit concurrency or if the network has
- long latency. It's possible that the value of HEAD changed
- between the time we fetched the latest baseline and the time we
- checkout that baseline. If that happens, apache will throw us
- a BAD_BASELINE error (deltaV says you can only checkout the
- latest baseline). We just ignore that specific error and
- retry a few times, asking for the latest baseline again. */
- if (err && err->apr_err != SVN_ERR_APMOD_BAD_BASELINE)
- return err;
-
- } while (err && (--retry_count > 0));
-
- /* Yikes, if we couldn't hold onto HEAD after a few retries, throw a
- real error.*/
- 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;
-}
-
 svn_error_t * svn_ra_dav__get_commit_editor(svn_ra_session_t *session,
                                             const svn_delta_editor_t **editor,
                                             void **edit_baton,
@@ -1399,6 +1428,7 @@
   ** that must be committed to the server.
   */
   commit_editor = svn_delta_default_editor(pool);
+ commit_editor->change_rev_prop = commit_change_rev_prop;
   commit_editor->open_root = commit_open_root;
   commit_editor->delete_entry = commit_delete_entry;
   commit_editor->add_directory = commit_add_dir;
Index: subversion/svnserve/serve.c
===================================================================
--- subversion/svnserve/serve.c (revision 23480)
+++ subversion/svnserve/serve.c (working copy)
@@ -2236,10 +2236,11 @@
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(nn(!", "success",
                                  (apr_uint64_t) 1, (apr_uint64_t) 2));
   SVN_ERR(send_mechs(conn, pool, &b, READ_ACCESS, FALSE));
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(www))",
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(wwww))",
                                  SVN_RA_SVN_CAP_EDIT_PIPELINE,
                                  SVN_RA_SVN_CAP_SVNDIFF1,
- SVN_RA_SVN_CAP_ABSENT_ENTRIES));
+ SVN_RA_SVN_CAP_ABSENT_ENTRIES,
+ SVN_RA_SVN_CAP_CHANGE_REV_PROP));
 
   /* Read client response. Because the client response form changed
    * between version 1 and version 2, we have to do some of this by

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Feb 24 02:18:46 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.