Author: rhuijben
Date: Sat Dec 14 17:09:14 2013
New Revision: 1550940

URL: http://svn.apache.org/r1550940
Log:
Following up on r1550848 and r1550938, automatically anchor the mtcc handling
on an ancestor when that is needed for certain operations.

* subversion/include/svn_client.h
  (svn_client_mtcc_get_relpath): Remove function.
  (svn_client_mtcc_check_path): Add argument.

* subversion/libsvn_client/mtcc.c
  (svn_client_mtcc_get_relpath): Rename to ...
  (mtcc_reparent): ... this function. Make static and make the new url
     an argument instead of calculating it.
  (mtcc_verify_create): Allow creating the "" path if there is no operation
     recorded yet. Update caller.

  (svn_client_mtcc_add_add_file,
   svn_client_mtcc_add_delete,
   svn_client_mtcc_add_mkdir,
   svn_client_mtcc_add_propset,
   svn_client_mtcc_add_update_file): Support adding "" when no operation is
     recorded yet.

  (svn_client_mtcc_check_path): Allow verifying the recorded node kind with
     the repository kind.

* subversion/libsvn_client/mtcc.h
  (svn_client_mtcc_op_t): Add boolean.
  (MTCC_UNMODIFIED): New macro.

* subversion/tests/libsvn_client/mtcc-test.c
  (cstr_stream): New helper function.
  (make_greek_tree,
   test_update_files): Update caller.
  (test_anchoring): New test.
  (test_funcs): Add test_anchoring.

Modified:
    subversion/trunk/subversion/include/svn_client.h
    subversion/trunk/subversion/libsvn_client/mtcc.c
    subversion/trunk/subversion/libsvn_client/mtcc.h
    subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c

Modified: subversion/trunk/subversion/include/svn_client.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1550940&r1=1550939&r2=1550940&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Sat Dec 14 17:09:14 2013
@@ -6686,18 +6686,6 @@ svn_client_mtcc_create(svn_client_mtcc_t
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool);
 
-/** Calculates a relative path @a *relpath for @a url, reparenting the
- * @a mtcc to a higher anchor url if necessary.
- *
- * @since New in 1.9.
- */
-svn_error_t *
-svn_client_mtcc_get_relpath(const char **relpath,
-                            const char *url,
-                            svn_client_mtcc_t *mtcc,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool);
-
 /** Adds a file add operation of @a relpath to @a mtcc. If @a src_checksum
  * is not null it will be provided to the repository to verify if the file
  * was transfered succesfull.
@@ -6817,6 +6805,9 @@ svn_client_mtcc_add_update_file(const ch
  * This value might be from the cache (in case of modifications, copies)
  * or fetched from the repository.
  *
+ * If @a check_repository is TRUE, verify the node type with the repository at
+ * least once and cache the result for further checks.
+ *
  * When a node does not exist this functions sets @a *kind to @c svn_node_node.
  *
  * @since New in 1.9.
@@ -6824,6 +6815,7 @@ svn_client_mtcc_add_update_file(const ch
 svn_error_t *
 svn_client_mtcc_check_path(svn_node_kind_t *kind,
                            const char *relpath,
+                           svn_boolean_t check_repository,
                            svn_client_mtcc_t *mtcc,
                            apr_pool_t *scratch_pool);
 

Modified: subversion/trunk/subversion/libsvn_client/mtcc.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/mtcc.c?rev=1550940&r1=1550939&r2=1550940&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/mtcc.c (original)
+++ subversion/trunk/subversion/libsvn_client/mtcc.c Sat Dec 14 17:09:14 2013
@@ -287,44 +287,34 @@ update_copy_src(svn_client_mtcc_op_t *op
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_client_mtcc_get_relpath(const char **relpath,
-                            const char *url,
-                            svn_client_mtcc_t *mtcc,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
+static svn_error_t *
+mtcc_reparent(const char *new_anchor_url,
+              svn_client_mtcc_t *mtcc,
+              apr_pool_t *scratch_pool)
 {
-  svn_error_t *err;
-  const char *new_anchor;
   const char *session_url;
   const char *up;
 
-  err = svn_ra_get_path_relative_to_session(mtcc->ra_session, relpath, url,
-                                            result_pool);
-
-  if (! err || err->apr_err != SVN_ERR_RA_ILLEGAL_URL)
-    return svn_error_trace(err);
-
-  svn_error_clear(err);
-
   SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url,
                                  scratch_pool));
 
-  new_anchor = svn_uri_get_longest_ancestor(url, session_url, scratch_pool);
+  up = svn_uri_skip_ancestor(new_anchor_url, session_url, scratch_pool);
 
-  if (svn_path_is_empty(new_anchor))
+  if (! up)
     {
       return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
-                               _("'%s' is not in the same repository as '%s'"),
-                               url, session_url);
+                               _("'%s' is not an ancestor of  '%s'"),
+                               new_anchor_url, session_url);
+    }
+  else if (!*up)
+    {
+      return SVN_NO_ERROR; /* Same url */
     }
-
-  up = svn_uri_skip_ancestor(new_anchor, session_url, scratch_pool);
 
   /* Update copy origins recursively...:( */
   SVN_ERR(update_copy_src(mtcc->root_op, up, mtcc->pool));
 
-  SVN_ERR(svn_ra_reparent(mtcc->ra_session, new_anchor, scratch_pool));
+  SVN_ERR(svn_ra_reparent(mtcc->ra_session, new_anchor_url, scratch_pool));
 
   /* Create directory open operations for new ancestors */
   while (*up)
@@ -342,9 +332,7 @@ svn_client_mtcc_get_relpath(const char *
       mtcc->root_op = root_op;
     }
 
-  return svn_error_trace(
-            svn_ra_get_path_relative_to_session(mtcc->ra_session, relpath, url,
-                                                result_pool));
+  return SVN_NO_ERROR;
 }
 
 /* Check if it is safe to create a new node at NEW_RELPATH. Return a proper
@@ -354,26 +342,32 @@ mtcc_verify_create(svn_client_mtcc_t *mt
                    const char *new_relpath,
                    apr_pool_t *scratch_pool)
 {
-  svn_client_mtcc_op_t *op;
   svn_node_kind_t kind;
 
-  SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, FALSE,
-                       FALSE, mtcc->pool, scratch_pool));
+  if (*new_relpath || !MTCC_UNMODIFIED(mtcc))
+    {
+      svn_client_mtcc_op_t *op;
 
-  if (op)
-    return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
-                             _("Can't copy to '%s': target already operated 
on"),
-                             new_relpath);
+      SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, FALSE,
+                           FALSE, mtcc->pool, scratch_pool));
 
-  SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE,
-                       FALSE, mtcc->pool, scratch_pool));
+      if (op)
+        return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+                                 _("Can't create '%s': target already "
+                                   "operated on"),
+                                 new_relpath);
 
-  if (op)
-    return SVN_NO_ERROR; /* Node is explicitly deleted. We can replace */
+      SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE,
+                           FALSE, mtcc->pool, scratch_pool));
+
+      if (op)
+        return SVN_NO_ERROR; /* Node is explicitly deleted. We can replace */
+    }
 
   /* mod_dav_svn allows overwriting existing directories. Let's hide that
      for users of this api */
-  SVN_ERR(svn_client_mtcc_check_path(&kind, new_relpath, mtcc, scratch_pool));
+  SVN_ERR(svn_client_mtcc_check_path(&kind, new_relpath, FALSE,
+                                     mtcc, scratch_pool));
 
   if (kind != svn_node_none)
     return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
@@ -397,14 +391,22 @@ svn_client_mtcc_add_add_file(const char 
 
   SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool));
 
-  SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE,
-                       TRUE, mtcc->pool, scratch_pool));
-
-  if (!op || !created)
+  if (!*relpath && MTCC_UNMODIFIED(mtcc))
     {
-      return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                               _("Can't add file at '%s'"),
-                               relpath);
+      /* Turn the root operation into a file addition */
+      op = mtcc->root_op;
+    }
+  else
+    {
+      SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE,
+                           TRUE, mtcc->pool, scratch_pool));
+
+      if (!op || !created)
+        {
+          return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                                   _("Can't add file at '%s'"),
+                                   relpath);
+        }
     }
 
   op->kind = OP_ADD_FILE;
@@ -472,7 +474,8 @@ svn_client_mtcc_add_delete(const char *r
 
   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
 
-  SVN_ERR(svn_client_mtcc_check_path(&kind, relpath, mtcc, scratch_pool));
+  SVN_ERR(svn_client_mtcc_check_path(&kind, relpath, FALSE,
+                                     mtcc, scratch_pool));
 
   if (kind == svn_node_none)
     return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
@@ -480,17 +483,27 @@ svn_client_mtcc_add_delete(const char *r
                                 "does not exist"),
                              relpath);
 
-  SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, TRUE,
-                       TRUE, mtcc->pool, scratch_pool));
-
-  if (!op || !created)
+  if (! *relpath || MTCC_UNMODIFIED(mtcc))
     {
-      return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                               _("Can't delete node at '%s'"),
-                               relpath);
+      /* Turn root operation into delete */
+      op = mtcc->root_op;
+    }
+  else
+    {
+      SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, TRUE,
+                           TRUE, mtcc->pool, scratch_pool));
+
+      if (!op || !created)
+        {
+          return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                                   _("Can't delete node at '%s'"),
+                                   relpath);
+        }
     }
 
   op->kind = OP_DELETE;
+  op->children = NULL;
+  op->prop_mods = NULL;
 
   return SVN_NO_ERROR;
 }
@@ -506,6 +519,14 @@ svn_client_mtcc_add_mkdir(const char *re
 
   SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool));
 
+  if (! *relpath && MTCC_UNMODIFIED(mtcc))
+    {
+      /* Turn the root of the operation in an MKDIR */
+      mtcc->root_op->kind = OP_ADD_DIR;
+
+      return SVN_NO_ERROR;
+    }
+
   SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE,
                        FALSE, mtcc->pool, scratch_pool));
 
@@ -581,29 +602,50 @@ svn_client_mtcc_add_propset(const char *
       /* ### TODO: Call svn_wc_canonicalize_svn_prop() */
     }
 
-  SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE,
-                       FALSE, mtcc->pool, scratch_pool));
-
-  if (!op)
+  if (!*relpath && MTCC_UNMODIFIED(mtcc))
     {
       svn_node_kind_t kind;
-      svn_boolean_t created;
 
-      /* ### TODO: Check if this node is within a newly copied directory,
-                   and update origin values accordingly */
+      /* Probing the node for an unmodified root will fix the node type to
+         a file if necessary */
 
-      SVN_ERR(svn_client_mtcc_check_path(&kind, relpath, mtcc, scratch_pool));
+      SVN_ERR(svn_client_mtcc_check_path(&kind, relpath, FALSE,
+                                         mtcc, scratch_pool));
 
       if (kind == svn_node_none)
         return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                                  _("Can't set properties at not existing 
'%s'"),
-                                 relpath);
+                                   relpath);
+
+      op = mtcc->root_op;
+    }
+  else
+    {
+      SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE,
+                           FALSE, mtcc->pool, scratch_pool));
 
-      SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE,
-                           (kind != svn_node_dir),
-                           mtcc->pool, scratch_pool));
+      if (!op)
+        {
+          svn_node_kind_t kind;
+          svn_boolean_t created;
+
+          /* ### TODO: Check if this node is within a newly copied directory,
+                       and update origin values accordingly */
+
+          SVN_ERR(svn_client_mtcc_check_path(&kind, relpath, FALSE,
+                                             mtcc, scratch_pool));
 
-      SVN_ERR_ASSERT(op != NULL);
+          if (kind == svn_node_none)
+            return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                                     _("Can't set properties at not existing 
'%s'"),
+                                     relpath);
+
+          SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, 
FALSE,
+                               (kind != svn_node_dir),
+                               mtcc->pool, scratch_pool));
+
+          SVN_ERR_ASSERT(op != NULL);
+        }
     }
 
   if (!op->prop_mods)
@@ -638,7 +680,8 @@ svn_client_mtcc_add_update_file(const ch
   svn_node_kind_t kind;
   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream);
 
-  SVN_ERR(svn_client_mtcc_check_path(&kind, relpath, mtcc, scratch_pool));
+  SVN_ERR(svn_client_mtcc_check_path(&kind, relpath, FALSE,
+                                     mtcc, scratch_pool));
 
   if (kind != svn_node_file)
     return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
@@ -671,6 +714,7 @@ svn_client_mtcc_add_update_file(const ch
 svn_error_t *
 svn_client_mtcc_check_path(svn_node_kind_t *kind,
                            const char *relpath,
+                           svn_boolean_t check_repository,
                            svn_client_mtcc_t *mtcc,
                            apr_pool_t *scratch_pool)
 {
@@ -680,32 +724,77 @@ svn_client_mtcc_check_path(svn_node_kind
 
   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
 
+  if (!*relpath
+      && !mtcc->root_op->performed_stat
+      && MTCC_UNMODIFIED(mtcc))
+    {
+      /* We know nothing about the root. Perhaps it is a file? */
+      SVN_ERR(svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision,
+                                kind, scratch_pool));
+
+      mtcc->root_op->performed_stat = TRUE;
+      if (*kind == svn_node_file)
+        {
+          mtcc->root_op->kind = OP_OPEN_FILE;
+          mtcc->root_op->children = NULL;
+        }
+      return SVN_NO_ERROR;
+    }
+
   SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE,
                        FALSE, mtcc->pool, scratch_pool));
 
-  if (op)
+  if (!op || (check_repository && !op->performed_stat))
     {
-      if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+      SVN_ERR(svn_client_mtcc_get_origin(&origin_relpath, &origin_rev,
+                                     relpath, mtcc,
+                                     scratch_pool, scratch_pool));
+
+      SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath,
+                                origin_rev, kind, scratch_pool));
+
+      if (op && *kind == svn_node_dir)
         {
-          *kind = svn_node_dir;
-          return SVN_NO_ERROR;
+          if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+            op->performed_stat = TRUE;
+          else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+            return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+                                     _("Can't perform directory operation "
+                                       "on '%s' as it is not a directory"),
+                                     relpath);
         }
-      else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+      else if (op && *kind == svn_node_dir)
         {
-          *kind = svn_node_file;
-          return SVN_NO_ERROR;
+          if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+            op->performed_stat = TRUE;
+          else if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+            return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+                                     _("Can't perform file operation "
+                                       "on '%s' as it is not a file"),
+                                     relpath);
+        }
+      else if (op && (op->kind == OP_OPEN_DIR || op->kind == OP_OPEN_FILE))
+        {
+          return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                                   _("Can't open '%s' as it does not exist"),
+                                   relpath);
         }
-      SVN_ERR_MALFUNCTION(); /* No other kinds defined as delete is filtered */
-    }
-
-  SVN_ERR(svn_client_mtcc_get_origin(&origin_relpath, &origin_rev,
-                                     relpath, mtcc,
-                                     scratch_pool, scratch_pool));
 
-  SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath,
-                            origin_rev, kind, scratch_pool));
+      return SVN_NO_ERROR;
+    }
 
-  return SVN_NO_ERROR;
+  /* op != NULL */
+  if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+    {
+      *kind = svn_node_dir;
+      return SVN_NO_ERROR;
+    }
+  else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+    {
+      *kind = svn_node_file;
+      return SVN_NO_ERROR;
+    }
+  SVN_ERR_MALFUNCTION(); /* No other kinds defined as delete is filtered */
 }
 
 static svn_error_t *
@@ -981,6 +1070,20 @@ svn_client_mtcc_commit(apr_hash_t *revpr
 
   SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url, 
scratch_pool));
 
+  if (mtcc->root_op->kind != OP_OPEN_DIR)
+    {
+      const char *name;
+
+      svn_uri_split(&session_url, &name, session_url, scratch_pool);
+
+      if (*name)
+        {
+          SVN_ERR(mtcc_reparent(session_url, mtcc, scratch_pool));
+
+          SVN_ERR(svn_ra_reparent(mtcc->ra_session, session_url, 
scratch_pool));
+        }
+    }
+
     /* Create new commit items and add them to the array. */
   if (SVN_CLIENT__HAS_LOG_MSG_FUNC(mtcc->ctx))
     {
@@ -1004,6 +1107,40 @@ svn_client_mtcc_commit(apr_hash_t *revpr
   SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
                                            log_msg, mtcc->ctx, scratch_pool));
 
+  /* Ugly corner case: The ra session might have died while we were waiting
+     for the callback */
+  {
+    svn_node_kind_t kind;
+    svn_error_t *err = svn_ra_check_path(mtcc->ra_session, "",
+                                         mtcc->base_revision, &kind,
+                                         scratch_pool);
+
+    if (err)
+      {
+        svn_error_t *err2 = svn_client_open_ra_session2(&mtcc->ra_session,
+                                                        session_url,
+                                                        NULL, mtcc->ctx,
+                                                        mtcc->pool,
+                                                        scratch_pool);
+
+        if (err2)
+          {
+            svn_pool_destroy(mtcc->pool);
+            return svn_error_trace(svn_error_compose_create(err, err2));
+          }
+        svn_error_clear(err);
+
+        SVN_ERR(svn_ra_check_path(mtcc->ra_session, "",
+                                  mtcc->base_revision, &kind, scratch_pool));
+      }
+
+    if (kind != svn_node_dir)
+      return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+                               _("Can't commit to '%s' because it "
+                                 "is not a directory"),
+                               session_url);
+  }
+
   SVN_ERR(svn_ra_get_commit_editor3(mtcc->ra_session, &editor, &edit_baton,
                                     commit_revprops,
                                     commit_callback, commit_baton,

Modified: subversion/trunk/subversion/libsvn_client/mtcc.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/mtcc.h?rev=1550940&r1=1550939&r2=1550940&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/mtcc.h (original)
+++ subversion/trunk/subversion/libsvn_client/mtcc.h Sat Dec 14 17:09:14 2013
@@ -50,8 +50,15 @@ typedef struct svn_client_mtcc_op_t
 
   apr_array_header_t *prop_mods;        /* For all except DELETE
                                            List of svn_prop_t */
+
+  svn_boolean_t performed_stat;         /* Verified type with repository */
 } svn_client_mtcc_op_t;
 
+/* Check if the mtcc doesn't contain any modifications yet */
+#define MTCC_UNMODIFIED(mtcc)                                                  
\
+    ((mtcc->root_op->kind == OP_OPEN_DIR)                                      
\
+     && (mtcc->root_op->prop_mods == NULL || !mtcc->root_op->prop_mods->nelts) 
\
+     && (mtcc->root_op->children == NULL || !mtcc->root_op->children->nelts))
 
 struct svn_client_mtcc_t
 {

Modified: subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c?rev=1550940&r1=1550939&r2=1550940&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c Sat Dec 14 
17:09:14 2013
@@ -52,6 +52,13 @@ verify_commit_callback(const svn_commit_
   return SVN_NO_ERROR;
 }
 
+/* Create a stream from a c string */
+static svn_stream_t *
+cstr_stream(const char *data, apr_pool_t *result_pool)
+{
+  return svn_stream_from_string(svn_string_create(data, result_pool),
+                                result_pool);
+}
 
 static svn_error_t *
 verify_mtcc_commit(svn_client_mtcc_t *mtcc,
@@ -92,11 +99,9 @@ make_greek_tree(const char *repos_url,
         {
           SVN_ERR(svn_client_mtcc_add_add_file(
                                 svn_test__greek_tree_nodes[i].path,
-                                svn_stream_from_string(
-                                    svn_string_create(
+                                cstr_stream(
                                         svn_test__greek_tree_nodes[i].contents,
                                         subpool),
-                                    subpool),
                                 NULL /* src_checksum */,
                                 mtcc, subpool));
         }
@@ -261,25 +266,17 @@ test_update_files(const svn_test_opts_t 
 
   /* Update iota with knowledge of the old data */
   SVN_ERR(svn_client_mtcc_add_update_file(svn_test__greek_tree_nodes[0].path,
-                                          svn_stream_from_string(
-                                            svn_string_create(
-                                                "new-iota", pool),
+                                          cstr_stream("new-iota", pool),
+                                          NULL,
+                                          cstr_stream(
+                                            svn_test__greek_tree_nodes[0]
+                                                        .contents,
                                             pool),
-                                            NULL,
-                                            svn_stream_from_string(
-                                              svn_string_create(
-                                                svn_test__greek_tree_nodes[0]
-                                                    .contents,
-                                                pool),
-                                              pool),
-                                            NULL,
-                                            mtcc, pool));
+                                          NULL,
+                                          mtcc, pool));
 
   SVN_ERR(svn_client_mtcc_add_update_file("A/mu",
-                                          svn_stream_from_string(
-                                            svn_string_create(
-                                                "new-MU", pool),
-                                            pool),
+                                          cstr_stream("new-MU", pool),
                                           NULL,
                                           NULL, NULL,
                                           mtcc, pool));
@@ -331,6 +328,94 @@ test_overwrite(const svn_test_opts_t *op
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_anchoring(const svn_test_opts_t *opts,
+               apr_pool_t *pool)
+{
+  svn_client_mtcc_t *mtcc;
+  svn_client_ctx_t *ctx;
+  const char *repos_abspath;
+  const char *repos_url;
+  svn_repos_t* repos;
+
+  repos_abspath = svn_test_data_path("mtcc-anchoring", pool);
+  SVN_ERR(svn_dirent_get_absolute(&repos_abspath, repos_abspath, pool));
+  SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_url, repos_abspath, pool));
+  SVN_ERR(svn_test__create_repos(&repos, repos_abspath, opts, pool));
+
+  SVN_ERR(make_greek_tree(repos_url, pool));
+
+  SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
+
+  /* Update a file as root operation */
+  SVN_ERR(svn_client_mtcc_create(&mtcc,
+                                 svn_path_url_add_component2(repos_url, "iota",
+                                                             pool),
+                                 1, ctx, pool, pool));
+  SVN_ERR(svn_client_mtcc_add_update_file("",
+                                          cstr_stream("new-iota", pool),
+                                          NULL, NULL, NULL,
+                                          mtcc, pool));
+  SVN_ERR(svn_client_mtcc_add_propset("", "key",
+                                      svn_string_create("value", pool),
+                                      FALSE, mtcc, pool));
+
+  SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
+
+  /* Add a directory as root operation */
+  SVN_ERR(svn_client_mtcc_create(&mtcc,
+                                 svn_path_url_add_component2(repos_url, "BB",
+                                                             pool),
+                                 2, ctx, pool, pool));
+  SVN_ERR(svn_client_mtcc_add_mkdir("", mtcc, pool));
+  SVN_ERR(verify_mtcc_commit(mtcc, 3, pool));
+
+  /* Add a file as root operation */
+  SVN_ERR(svn_client_mtcc_create(&mtcc,
+                                 svn_path_url_add_component2(repos_url, "new",
+                                                             pool),
+                                 3, ctx, pool, pool));
+  SVN_ERR(svn_client_mtcc_add_add_file("", cstr_stream("new", pool), NULL,
+                                       mtcc, pool));
+  SVN_ERR(verify_mtcc_commit(mtcc, 4, pool));
+
+  /* Delete as root operation */
+  SVN_ERR(svn_client_mtcc_create(&mtcc,
+                                 svn_path_url_add_component2(repos_url, "new",
+                                                             pool),
+                                 4, ctx, pool, pool));
+  SVN_ERR(svn_client_mtcc_add_delete("", mtcc, pool));
+  SVN_ERR(verify_mtcc_commit(mtcc, 5, pool));
+
+  /* Propset file as root operation */
+  SVN_ERR(svn_client_mtcc_create(&mtcc,
+                                 svn_path_url_add_component2(repos_url, "A/mu",
+                                                             pool),
+                                 5, ctx, pool, pool));
+  SVN_ERR(svn_client_mtcc_add_propset("", "key",
+                                      svn_string_create("val", pool),
+                                      FALSE, mtcc, pool));
+  SVN_ERR(verify_mtcc_commit(mtcc, 6, pool));
+
+  /* Propset dir as root operation */
+  SVN_ERR(svn_client_mtcc_create(&mtcc,
+                                 svn_path_url_add_component2(repos_url, "A",
+                                                             pool),
+                                 6, ctx, pool, pool));
+  SVN_ERR(svn_client_mtcc_add_propset("", "key",
+                                      svn_string_create("val", pool),
+                                      FALSE, mtcc, pool));
+  SVN_ERR(verify_mtcc_commit(mtcc, 7, pool));
+
+  /* Propset reposroot as root operation */
+  SVN_ERR(svn_client_mtcc_create(&mtcc, repos_url, 7, ctx, pool, pool));
+  SVN_ERR(svn_client_mtcc_add_propset("", "key",
+                                      svn_string_create("val", pool),
+                                      FALSE, mtcc, pool));
+  SVN_ERR(verify_mtcc_commit(mtcc, 8, pool));
+
+  return SVN_NO_ERROR;
+}
 
 /* ========================================================================== 
*/
 
@@ -352,6 +437,8 @@ struct svn_test_descriptor_t test_funcs[
                        "test update files"),
     SVN_TEST_OPTS_PASS(test_overwrite,
                        "test overwrite"),
+    SVN_TEST_OPTS_PASS(test_anchoring,
+                       "test mtcc anchoring for root operations"),
     SVN_TEST_NULL
   };
  


Reply via email to