subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1658462 [12/31] - in /subversion/branches/move-tracking-2: ./ build/ build/generator/ build/generator/templates/ notes/ subversion/ subversion/bindings/cxxhl/include/svncxxhl/ subversion/bindings/javahl/native/ subversion/bindings/javahl/n...
Date Mon, 09 Feb 2015 16:46:21 GMT
Modified: subversion/branches/move-tracking-2/subversion/mod_dav_svn/liveprops.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/mod_dav_svn/liveprops.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/mod_dav_svn/liveprops.c (original)
+++ subversion/branches/move-tracking-2/subversion/mod_dav_svn/liveprops.c Mon Feb  9 16:46:16 2015
@@ -36,6 +36,7 @@
 #include "svn_time.h"
 #include "svn_dav.h"
 #include "svn_props.h"
+#include "svn_ctype.h"
 
 #include "private/svn_dav_protocol.h"
 
@@ -422,7 +423,43 @@ insert_prop_internal(const dav_resource
         if (last_author == NULL)
           return DAV_PROP_INSERT_NOTDEF;
 
-        value = apr_xml_quote_string(scratch_pool, last_author->data, 1);
+        if (svn_xml_is_xml_safe(last_author->data, last_author->len)
+            || !resource->info->repos->is_svn_client)
+          value = apr_xml_quote_string(scratch_pool, last_author->data, 1);
+        else
+          {
+            /* We are talking to a Subversion client, which will (like any proper
+               xml parser) error out if we produce control characters in XML.
+
+               However Subversion clients process both the generic
+               <creator-displayname /> as the custom element for svn:author.
+
+               Let's skip outputting the invalid characters here to make the XML
+               valid, so clients can see the custom element.
+
+               Subversion Clients will then either use a slightly invalid
+               author (unlikely) or more likely use the second result, which
+               will be transferred with full escaping capabilities.
+
+               We have tests in place to assert proper behavior over the RA layer.
+             */
+            apr_size_t i;
+            svn_stringbuf_t *buf;
+
+            buf = svn_stringbuf_create_from_string(last_author, scratch_pool);
+
+            for (i = 0; i < buf->len; i++)
+              {
+                char c = buf->data[i];
+
+                if (svn_ctype_iscntrl(c))
+                  {
+                    svn_stringbuf_remove(buf, i--, 1);
+                  }
+              }
+
+            value = apr_xml_quote_string(scratch_pool, buf->data, 1);
+          }
         break;
       }
 

Modified: subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c (original)
+++ subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c Mon Feb  9 16:46:16 2015
@@ -1035,6 +1035,28 @@ prep_working(dav_resource_combined *comb
   comb->res.exists = (kind != svn_node_none);
   comb->res.collection = (kind == svn_node_dir);
 
+  if (comb->res.exists
+      && comb->priv.r->method_number == M_MKCOL
+      && comb->priv.repos->is_svn_client)
+    {
+      /* mod_dav will now continue returning a generic HTTP_METHOD_NOT_ALLOWED
+         error, which doesn't produce nice output on SVN, nor gives any details
+         on why the operation failed.
+
+         Let's error out a bit earlier and produce an error message that is
+         easier to understand for both clients and users. */
+
+      /* It would be nice if we could error out a bit later (see issue #2295),
+         like in create_collection(), but mod_dav outsmarts us by just
+         returning the error when the node exists. */
+
+      return dav_svn__convert_err(
+                  svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+                                    "Path already exists, path '%s'",
+                                    comb->priv.repos_path),
+                  HTTP_METHOD_NOT_ALLOWED, NULL, pool);
+    }
+
   return NULL;
 }
 

Modified: subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c (original)
+++ subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c Mon Feb  9 16:46:16 2015
@@ -116,51 +116,51 @@ dav_svn__convert_err(svn_error_t *serr,
                      const char *message,
                      apr_pool_t *pool)
 {
-    dav_error *derr;
+  dav_error *derr;
 
-    /* Remove the trace-only error chain links.  We need predictable
-       protocol behavior regardless of whether or not we're in a
-       debugging build. */
-    svn_error_t *purged_serr = svn_error_purge_tracing(serr);
-
-    /* ### someday mod_dav_svn will send back 'rich' error tags, much
-       finer grained than plain old svn_error_t's.  But for now, all
-       svn_error_t's are marshalled to the client via the single
-       generic <svn:error/> tag nestled within a <D:error> block. */
-
-    /* Examine the Subverion error code, and select the most
-       appropriate HTTP status code.  If no more appropriate HTTP
-       status code maps to the Subversion error code, use the one
-       suggested status provided by the caller. */
-    switch (purged_serr->apr_err)
-      {
-      case SVN_ERR_FS_NOT_FOUND:
-        status = HTTP_NOT_FOUND;
-        break;
-      case SVN_ERR_UNSUPPORTED_FEATURE:
-        status = HTTP_NOT_IMPLEMENTED;
-        break;
-      case SVN_ERR_FS_LOCK_OWNER_MISMATCH:
-      case SVN_ERR_FS_PATH_ALREADY_LOCKED:
-        status = HTTP_LOCKED;
-        break;
-      case SVN_ERR_FS_PROP_BASEVALUE_MISMATCH:
-        status = HTTP_PRECONDITION_FAILED;
-        break;
-        /* add other mappings here */
-      }
-
-    derr = build_error_chain(pool, purged_serr, status);
-    if (message != NULL
-        && !svn_error_find_cause(purged_serr, SVN_ERR_REPOS_HOOK_FAILURE))
-      /* Don't hide hook failures; we might hide the error text */
-      derr = dav_push_error(pool, status, purged_serr->apr_err,
-                            message, derr);
+  /* Remove the trace-only error chain links.  We need predictable
+     protocol behavior regardless of whether or not we're in a
+     debugging build. */
+  svn_error_t *purged_serr = svn_error_purge_tracing(serr);
+
+  /* ### someday mod_dav_svn will send back 'rich' error tags, much
+     finer grained than plain old svn_error_t's.  But for now, all
+     svn_error_t's are marshalled to the client via the single
+     generic <svn:error/> tag nestled within a <D:error> block. */
+
+  /* Examine the Subverion error code, and select the most
+     appropriate HTTP status code.  If no more appropriate HTTP
+     status code maps to the Subversion error code, use the one
+     suggested status provided by the caller. */
+  switch (purged_serr->apr_err)
+    {
+    case SVN_ERR_FS_NOT_FOUND:
+      status = HTTP_NOT_FOUND;
+      break;
+    case SVN_ERR_UNSUPPORTED_FEATURE:
+      status = HTTP_NOT_IMPLEMENTED;
+      break;
+    case SVN_ERR_FS_LOCK_OWNER_MISMATCH:
+    case SVN_ERR_FS_PATH_ALREADY_LOCKED:
+      status = HTTP_LOCKED;
+      break;
+    case SVN_ERR_FS_PROP_BASEVALUE_MISMATCH:
+      status = HTTP_PRECONDITION_FAILED;
+      break;
+      /* add other mappings here */
+    }
+
+  derr = build_error_chain(pool, purged_serr, status);
+  if (message != NULL
+      && !svn_error_find_cause(purged_serr, SVN_ERR_REPOS_HOOK_FAILURE))
+    /* Don't hide hook failures; we might hide the error text */
+    derr = dav_push_error(pool, status, purged_serr->apr_err,
+                          message, derr);
 
-    /* Now, destroy the Subversion error. */
-    svn_error_clear(serr);
+  /* Now, destroy the Subversion error. */
+  svn_error_clear(serr);
 
-    return derr;
+  return derr;
 }
 
 
@@ -571,11 +571,12 @@ dav_svn__sanitize_error(svn_error_t *ser
                         "%s", purged_serr->message);
         }
 
-        svn_error_clear(serr);
-      }
-    return dav_svn__convert_err(safe_err, http_status,
-                                apr_psprintf(r->pool, "%s", safe_err->message),
-                                r->pool);
+      svn_error_clear(serr);
+    }
+
+  return dav_svn__convert_err(safe_err, http_status,
+                              apr_psprintf(r->pool, "%s", safe_err->message),
+                              r->pool);
 }
 
 
@@ -856,7 +857,7 @@ dav_svn__parse_request_skel(svn_skel_t *
   *skel = NULL;
   status = request_body_to_string(&skel_str, r, pool);
   if (status != OK)
-    return OK;
+    return status;
 
   *skel = svn_skel__parse(skel_str->data, skel_str->len, pool);
   return OK;

Modified: subversion/branches/move-tracking-2/subversion/svn/auth-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svn/auth-cmd.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svn/auth-cmd.c (original)
+++ subversion/branches/move-tracking-2/subversion/svn/auth-cmd.c Mon Feb  9 16:46:16 2015
@@ -41,6 +41,9 @@
 #include "svn_config.h"
 #include "svn_auth.h"
 #include "svn_sorts.h"
+#include "svn_base64.h"
+#include "svn_x509.h"
+#include "svn_time.h"
 
 #include "private/svn_cmdline_private.h"
 #include "private/svn_token.h"
@@ -150,9 +153,6 @@ match_credential(svn_boolean_t *match,
                 continue; /* don't match secrets */
               else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0)
                 continue; /* don't match base64 data */
-              else if (strcmp(key, SVN_CONFIG_AUTHN_HOSTNAME_KEY) == 0 ||
-                       strcmp(key, SVN_CONFIG_AUTHN_FINGERPRINT_KEY) == 0)
-                *match = match_pattern(pattern, value->data, TRUE, iterpool);
               else
                 *match = match_pattern(pattern, value->data, FALSE, iterpool);
 
@@ -168,6 +168,63 @@ match_credential(svn_boolean_t *match,
 }
 
 static svn_error_t *
+show_cert(const svn_string_t *pem_cert, apr_pool_t *scratch_pool)
+{
+  const svn_string_t *der_cert;
+  svn_x509_certinfo_t *certinfo;
+  const apr_array_header_t *hostnames;
+  svn_error_t *err;
+
+  /* Convert header-less PEM to DER by undoing base64 encoding. */
+  der_cert = svn_base64_decode_string(pem_cert, scratch_pool);
+
+  err = svn_x509_parse_cert(&certinfo, der_cert->data, der_cert->len,
+                            scratch_pool, scratch_pool);
+  if (err)
+    {
+      /* Just display X.509 parsing errors as warnings and continue */
+      svn_handle_warning2(stderr, err, "svn: ");
+      svn_error_clear(err);
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_cmdline_printf(scratch_pool, _("Subject: %s\n"),
+                             svn_x509_certinfo_get_subject(certinfo, scratch_pool)));
+  SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid from: %s\n"),
+                             svn_time_to_human_cstring(
+                                 svn_x509_certinfo_get_valid_from(certinfo),
+                                 scratch_pool)));
+  SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid until: %s\n"),
+                             svn_time_to_human_cstring(
+                                 svn_x509_certinfo_get_valid_to(certinfo),
+                                 scratch_pool)));
+  SVN_ERR(svn_cmdline_printf(scratch_pool, _("Issuer: %s\n"),
+                             svn_x509_certinfo_get_issuer(certinfo, scratch_pool)));
+  SVN_ERR(svn_cmdline_printf(scratch_pool, _("Fingerprint: %s\n"),
+                             svn_checksum_to_cstring_display(
+                                 svn_x509_certinfo_get_digest(certinfo),
+                                 scratch_pool)));
+
+  hostnames = svn_x509_certinfo_get_hostnames(certinfo);
+  if (hostnames && !apr_is_empty_array(hostnames))
+    {
+      int i;
+      svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool);
+      for (i = 0; i < hostnames->nelts; ++i)
+        {
+          const char *hostname = APR_ARRAY_IDX(hostnames, i, const char*);
+          if (i > 0)
+            svn_stringbuf_appendbytes(buf, ", ", 2);
+          svn_stringbuf_appendbytes(buf, hostname, strlen(hostname));
+        }
+      SVN_ERR(svn_cmdline_printf(scratch_pool, _("Hostnames: %s\n"),
+                                 buf->data));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 list_credential(const char *cred_kind,
                 const char *realmstring,
                 apr_array_header_t *cred_items,
@@ -188,7 +245,7 @@ list_credential(const char *cred_kind,
       svn_sort__item_t item;
       const char *key;
       svn_string_t *value;
-      
+
       svn_pool_clear(iterpool);
       item = APR_ARRAY_IDX(cred_items, i, svn_sort__item_t);
       key = item.key;
@@ -218,20 +275,7 @@ list_credential(const char *cred_kind,
       else if (strcmp(key, SVN_CONFIG_AUTHN_USERNAME_KEY) == 0)
         SVN_ERR(svn_cmdline_printf(iterpool, _("Username: %s\n"), value->data));
       else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0)
-        continue; /* don't show data which is not human-readable */
-      else if (strcmp(key, SVN_CONFIG_AUTHN_HOSTNAME_KEY) == 0)
-        SVN_ERR(svn_cmdline_printf(iterpool, _("Hostname: %s\n"), value->data));
-      else if (strcmp(key, SVN_CONFIG_AUTHN_VALID_FROM_KEY) == 0)
-        SVN_ERR(svn_cmdline_printf(iterpool, _("Valid from: %s\n"),
-                                   value->data));
-      else if (strcmp(key, SVN_CONFIG_AUTHN_VALID_UNTIL_KEY) == 0)
-        SVN_ERR(svn_cmdline_printf(iterpool, _("Valid until: %s\n"),
-                                   value->data));
-      else if (strcmp(key, SVN_CONFIG_AUTHN_ISSUER_DN_KEY) == 0)
-        SVN_ERR(svn_cmdline_printf(iterpool, _("Issuer: %s\n"), value->data));
-      else if (strcmp(key, SVN_CONFIG_AUTHN_FINGERPRINT_KEY) == 0)
-        SVN_ERR(svn_cmdline_printf(iterpool, _("Fingerprint: %s\n"),
-                                   value->data));
+       SVN_ERR(show_cert(value, iterpool));
       else if (strcmp(key, SVN_CONFIG_AUTHN_FAILURES_KEY) == 0)
         SVN_ERR(show_cert_failures(value->data, iterpool));
       else

Modified: subversion/branches/move-tracking-2/subversion/svn/conflict-callbacks.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svn/conflict-callbacks.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svn/conflict-callbacks.c (original)
+++ subversion/branches/move-tracking-2/subversion/svn/conflict-callbacks.c Mon Feb  9 16:46:16 2015
@@ -792,7 +792,7 @@ handle_text_conflict(svn_wc_conflict_res
       else if (strcmp(opt->code, "df") == 0)
         {
           /* Re-check preconditions. */
-          if (! diff_allowed || desc->my_abspath)
+          if (! diff_allowed || ! desc->my_abspath)
             {
               SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
                              _("Invalid option; there's no "

Modified: subversion/branches/move-tracking-2/subversion/svn/notify.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svn/notify.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svn/notify.c (original)
+++ subversion/branches/move-tracking-2/subversion/svn/notify.c Mon Feb  9 16:46:16 2015
@@ -52,7 +52,7 @@ struct notify_baton
   svn_boolean_t is_export;
   svn_boolean_t is_wc_to_repos_copy;
   svn_boolean_t sent_first_txdelta;
-  svn_boolean_t in_external;
+  int in_external;
   svn_boolean_t had_print_error; /* Used to not keep printing error messages
                                     when we've already had one print error. */
 
@@ -636,7 +636,7 @@ notify_body(struct notify_baton *nb,
 
     case svn_wc_notify_update_external:
       /* Remember that we're now "inside" an externals definition. */
-      nb->in_external = TRUE;
+      ++nb->in_external;
 
       /* Currently this is used for checkouts and switches too.  If we
          want different output, we'll have to add new actions. */
@@ -653,7 +653,7 @@ notify_body(struct notify_baton *nb,
       if (nb->in_external)
         {
           svn_handle_warning2(stderr, n->err, "svn: ");
-          nb->in_external = FALSE;
+          --nb->in_external;
           SVN_ERR(svn_cmdline_printf(pool, "\n"));
         }
       /* Otherwise, we'll just print two warnings.  Why?  Because
@@ -752,7 +752,7 @@ notify_body(struct notify_baton *nb,
 
       if (nb->in_external)
         {
-          nb->in_external = FALSE;
+          --nb->in_external;
           SVN_ERR(svn_cmdline_printf(pool, "\n"));
         }
       break;
@@ -1117,7 +1117,7 @@ svn_cl__get_notifier(svn_wc_notify_func2
   nb->is_checkout = FALSE;
   nb->is_export = FALSE;
   nb->is_wc_to_repos_copy = FALSE;
-  nb->in_external = FALSE;
+  nb->in_external = 0;
   nb->had_print_error = FALSE;
   nb->conflict_stats = conflict_stats;
   SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool));

Modified: subversion/branches/move-tracking-2/subversion/svn/resolve-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svn/resolve-cmd.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svn/resolve-cmd.c (original)
+++ subversion/branches/move-tracking-2/subversion/svn/resolve-cmd.c Mon Feb  9 16:46:16 2015
@@ -123,7 +123,7 @@ svn_cl__resolve(apr_getopt_t *os,
   svn_pool_destroy(iterpool);
 
   if (had_error)
-    return svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL,
+    return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
                             _("Failure occurred resolving one or more "
                               "conflicts"));
 

Modified: subversion/branches/move-tracking-2/subversion/svn/revert-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svn/revert-cmd.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svn/revert-cmd.c (original)
+++ subversion/branches/move-tracking-2/subversion/svn/revert-cmd.c Mon Feb  9 16:46:16 2015
@@ -70,6 +70,7 @@ svn_cl__revert(apr_getopt_t *os,
   err = svn_client_revert3(targets, opt_state->depth,
                            opt_state->changelists,
                            FALSE /* clear_changelists */,
+                           FALSE /* metadata_only */,
                            ctx, scratch_pool);
   if (err
       && (err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH)

Modified: subversion/branches/move-tracking-2/subversion/svn/svn.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svn/svn.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svn/svn.c (original)
+++ subversion/branches/move-tracking-2/subversion/svn/svn.c Mon Feb  9 16:46:16 2015
@@ -1300,7 +1300,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "\n"
      "  1. Removes versioned props in working copy.\n"
      "  2. Removes unversioned remote prop on repos revision.\n"
-     "     TARGET only determines which repository to access.\n"),
+     "     TARGET only determines which repository to access.\n"
+     "\n"
+     "  See 'svn help propset' for descriptions of the svn:* special properties.\n"),
     {'q', 'R', opt_depth, 'r', opt_revprop, opt_changelist} },
 
   { "propedit", svn_cl__propedit, {"pedit", "pe"}, N_
@@ -1312,7 +1314,7 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  2. Edits unversioned remote prop on repos revision.\n"
      "     TARGET only determines which repository to access.\n"
      "\n"
-     "  See 'svn help propset' for more on setting properties.\n"),
+     "  See 'svn help propset' for descriptions of the svn:* special properties.\n"),
     {'r', opt_revprop, SVN_CL__LOG_MSG_OPTIONS, opt_force} },
 
   { "propget", svn_cl__propget, {"pget", "pg"}, N_
@@ -1333,7 +1335,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  By default, an extra newline is printed after the property value so that\n"
      "  the output looks pretty.  With a single TARGET, depth 'empty' and without\n"
      "  --show-inherited-props, you can use the --strict option to disable this\n"
-     "  (useful when redirecting a binary property value to a file, for example).\n"),
+     "  (useful when redirecting a binary property value to a file, for example).\n"
+     "\n"
+     "  See 'svn help propset' for descriptions of the svn:* special properties.\n"),
     {'v', 'R', opt_depth, 'r', opt_revprop, opt_strict, opt_xml,
      opt_changelist, opt_show_inherited_props },
     {{'v', N_("print path, name and value on separate lines")},
@@ -1350,7 +1354,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "     TARGET only determines which repository to access.\n"
      "\n"
      "  With --verbose, the property values are printed as well, like 'svn propget\n"
-     "  --verbose'.  With --quiet, the paths are not printed.\n"),
+     "  --verbose'.  With --quiet, the paths are not printed.\n"
+     "\n"
+     "  See 'svn help propset' for descriptions of the svn:* special properties.\n"),
     {'v', 'R', opt_depth, 'r', 'q', opt_revprop, opt_xml, opt_changelist,
      opt_show_inherited_props },
     {{'v', N_("print path, name and value on separate lines")},

Modified: subversion/branches/move-tracking-2/subversion/svndumpfilter/svndumpfilter.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svndumpfilter/svndumpfilter.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svndumpfilter/svndumpfilter.c (original)
+++ subversion/branches/move-tracking-2/subversion/svndumpfilter/svndumpfilter.c Mon Feb  9 16:46:16 2015
@@ -278,7 +278,7 @@ struct node_baton_t
   svn_filesize_t tcl;
 
   /* Pointers to dumpfile data. */
-  svn_stringbuf_t *header;
+  apr_array_header_t *headers;
   svn_stringbuf_t *props;
 
   /* Expect deltas? */
@@ -287,6 +287,8 @@ struct node_baton_t
 
   /* We might need the node path in a parse error message. */
   char *node_path;
+
+  apr_pool_t *node_pool;
 };
 
 
@@ -499,6 +501,7 @@ new_node_record(void **node_baton,
   *node_baton = apr_palloc(pool, sizeof(struct node_baton_t));
   nb          = *node_baton;
   nb->rb      = rev_baton;
+  nb->node_pool = pool;
   pb          = nb->rb->pb;
 
   node_path = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH);
@@ -570,7 +573,7 @@ new_node_record(void **node_baton,
       nb->has_text_delta = FALSE;
       nb->writing_begun = FALSE;
       nb->tcl = tcl ? svn__atoui64(tcl) : 0;
-      nb->header = svn_stringbuf_create_empty(pool);
+      nb->headers = svn_repos__dumpfile_headers_create(pool);
       nb->props = svn_stringbuf_create_empty(pool);
       nb->node_path = apr_pstrdup(pool, node_path);
 
@@ -582,23 +585,20 @@ new_node_record(void **node_baton,
 
       /* A node record is required to begin with 'Node-path', skip the
          leading '/' to match the form used by 'svnadmin dump'. */
-      SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
-                                pool, "%s: %s\n",
-                                SVN_REPOS_DUMPFILE_NODE_PATH, node_path + 1));
+      svn_repos__dumpfile_header_push(
+        nb->headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_path + 1);
 
       /* Node-kind is next and is optional. */
       kind = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND);
       if (kind)
-        SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
-                                  pool, "%s: %s\n",
-                                  SVN_REPOS_DUMPFILE_NODE_KIND, kind));
+        svn_repos__dumpfile_header_push(
+          nb->headers, SVN_REPOS_DUMPFILE_NODE_KIND, kind);
 
       /* Node-action is next and required. */
       action = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION);
       if (action)
-        SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
-                                  pool, "%s: %s\n",
-                                  SVN_REPOS_DUMPFILE_NODE_ACTION, action));
+        svn_repos__dumpfile_header_push(
+          nb->headers, SVN_REPOS_DUMPFILE_NODE_ACTION, action);
       else
         return svn_error_createf(SVN_ERR_INCOMPLETE_DATA, 0,
                                  _("Missing Node-action for path '%s'"),
@@ -645,18 +645,14 @@ new_node_record(void **node_baton,
                 return svn_error_createf
                   (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
                    _("No valid copyfrom revision in filtered stream"));
-              SVN_ERR(svn_stream_printf
-                      (nb->rb->pb->out_stream, pool,
-                       SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV ": %ld\n",
-                       cf_renum_val->rev));
+              svn_repos__dumpfile_header_pushf(
+                nb->headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV,
+                "%ld", cf_renum_val->rev);
               continue;
             }
 
           /* passthru: put header straight to output */
-
-          SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
-                                    pool, "%s: %s\n",
-                                    key, val));
+          svn_repos__dumpfile_header_push(nb->headers, key, val);
         }
     }
 
@@ -664,65 +660,6 @@ new_node_record(void **node_baton,
 }
 
 
-/* Output node header and props to dumpstream
-   This will be called by set_fulltext() after setting nb->has_text to TRUE,
-   if the node has any text, or by close_node() otherwise. This must only
-   be called if nb->writing_begun is FALSE. */
-static svn_error_t *
-output_node(struct node_baton_t *nb)
-{
-  int bytes_used;
-  char buf[SVN_KEYLINE_MAXLEN];
-
-  nb->writing_begun = TRUE;
-
-  /* when there are no props nb->props->len would be zero and won't mess up
-     Content-Length. */
-  if (nb->has_props)
-    svn_stringbuf_appendcstr(nb->props, "PROPS-END\n");
-
-  /* 1. recalculate & check text-md5 if present. Passed through right now. */
-
-  /* 2. recalculate and add content-lengths */
-
-  if (nb->has_props)
-    {
-      svn_stringbuf_appendcstr(nb->header,
-                               SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH);
-      bytes_used = apr_snprintf(buf, sizeof(buf), ": %" APR_SIZE_T_FMT,
-                                nb->props->len);
-      svn_stringbuf_appendbytes(nb->header, buf, bytes_used);
-      svn_stringbuf_appendbyte(nb->header, '\n');
-    }
-  if (nb->has_text)
-    {
-      svn_stringbuf_appendcstr(nb->header,
-                               SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH);
-      bytes_used = apr_snprintf(buf, sizeof(buf), ": %" SVN_FILESIZE_T_FMT,
-                                nb->tcl);
-      svn_stringbuf_appendbytes(nb->header, buf, bytes_used);
-      svn_stringbuf_appendbyte(nb->header, '\n');
-    }
-  svn_stringbuf_appendcstr(nb->header, SVN_REPOS_DUMPFILE_CONTENT_LENGTH);
-  bytes_used = apr_snprintf(buf, sizeof(buf), ": %" SVN_FILESIZE_T_FMT,
-                            (svn_filesize_t) (nb->props->len + nb->tcl));
-  svn_stringbuf_appendbytes(nb->header, buf, bytes_used);
-  svn_stringbuf_appendbyte(nb->header, '\n');
-
-  /* put an end to headers */
-  svn_stringbuf_appendbyte(nb->header, '\n');
-
-  /* 3. output all the stuff */
-
-  SVN_ERR(svn_stream_write(nb->rb->pb->out_stream,
-                           nb->header->data , &(nb->header->len)));
-  SVN_ERR(svn_stream_write(nb->rb->pb->out_stream,
-                           nb->props->data , &(nb->props->len)));
-
-  return SVN_NO_ERROR;
-}
-
-
 /* Examine the mergeinfo in INITIAL_VAL, omitting missing merge
    sources or renumbering revisions in rangelists as appropriate, and
    return the (possibly new) mergeinfo in *FINAL_VAL (allocated from
@@ -921,7 +858,20 @@ set_fulltext(svn_stream_t **stream, void
     {
       nb->has_text = TRUE;
       if (! nb->writing_begun)
-        SVN_ERR(output_node(nb));
+        {
+          nb->writing_begun = TRUE;
+          if (nb->has_props)
+            {
+              svn_stringbuf_appendcstr(nb->props, "PROPS-END\n");
+            }
+          SVN_ERR(svn_repos__dump_node_record(nb->rb->pb->out_stream,
+                                              nb->headers,
+                                              nb->has_props ? nb->props : NULL,
+                                              nb->has_text,
+                                              nb->tcl,
+                                              TRUE /*content_length_always*/,
+                                              nb->node_pool));
+        }
       *stream = nb->rb->pb->out_stream;
     }
 
@@ -942,7 +892,20 @@ close_node(void *node_baton)
 
   /* If the node was not flushed already to output its text, do it now. */
   if (! nb->writing_begun)
-    SVN_ERR(output_node(nb));
+    {
+      nb->writing_begun = TRUE;
+      if (nb->has_props)
+        {
+          svn_stringbuf_appendcstr(nb->props, "PROPS-END\n");
+        }
+      SVN_ERR(svn_repos__dump_node_record(nb->rb->pb->out_stream,
+                                          nb->headers,
+                                          nb->has_props ? nb->props : NULL,
+                                          nb->has_text,
+                                          nb->tcl,
+                                          TRUE /*content_length_always*/,
+                                          nb->node_pool));
+    }
 
   /* put an end to node. */
   SVN_ERR(svn_stream_write(nb->rb->pb->out_stream, "\n\n", &len));

Modified: subversion/branches/move-tracking-2/subversion/svnmucc/svnmucc.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnmucc/svnmucc.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnmucc/svnmucc.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnmucc/svnmucc.c Mon Feb  9 16:46:16 2015
@@ -222,7 +222,7 @@ execute(const apr_array_header_t *action
                                 mtcc, iterpool);
 
   svn_pool_destroy(iterpool);
-  return svn_error_trace(err);;
+  return svn_error_trace(err);
 }
 
 static svn_error_t *

Modified: subversion/branches/move-tracking-2/subversion/svnrdump/dump_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnrdump/dump_editor.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnrdump/dump_editor.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnrdump/dump_editor.c Mon Feb  9 16:46:16 2015
@@ -30,6 +30,7 @@
 #include "svn_subst.h"
 #include "svn_dirent_uri.h"
 
+#include "private/svn_repos_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_editor.h"
@@ -39,25 +40,16 @@
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
-#if 0
-#define LDR_DBG(x) SVN_DBG(x)
-#else
-#define LDR_DBG(x) while(0)
-#endif
 
 /* A directory baton used by all directory-related callback functions
  * in the dump editor.  */
 struct dir_baton
 {
   struct dump_edit_baton *eb;
-  struct dir_baton *parent_dir_baton;
 
   /* Pool for per-directory allocations */
   apr_pool_t *pool;
 
-  /* is this directory a new addition to this revision? */
-  svn_boolean_t added;
-
   /* the path to this directory */
   const char *repos_relpath; /* a relpath */
 
@@ -65,6 +57,9 @@ struct dir_baton
   const char *copyfrom_path; /* a relpath */
   svn_revnum_t copyfrom_rev;
 
+  /* Headers accumulated so far for this directory */
+  apr_array_header_t *headers;
+
   /* Properties which were modified during change_dir_prop. */
   apr_hash_t *props;
 
@@ -87,7 +82,6 @@ struct dir_baton
 struct file_baton
 {
   struct dump_edit_baton *eb;
-  struct dir_baton *parent_dir_baton;
 
   /* Pool for per-file allocations */
   apr_pool_t *pool;
@@ -145,11 +139,10 @@ struct dump_edit_baton {
   /* The revision we're currently dumping. */
   svn_revnum_t current_revision;
 
-  /* The kind (file or directory) and baton of the item whose block of
+  /* The baton of the directory node whose block of
      dump stream data has not been fully completed; NULL if there's no
      such item. */
-  svn_node_kind_t pending_kind;
-  void *pending_baton;
+  struct dir_baton *pending_db;
 };
 
 /* Make a directory baton to represent the directory at PATH (relative
@@ -161,16 +154,15 @@ struct dump_edit_baton {
  * copy source.
  *
  * PB is the directory baton of this directory's parent, or NULL if
- * this is the top-level directory of the edit.  ADDED indicates if
- * this directory is newly added in this revision.  Perform all
- * allocations in POOL.  */
+ * this is the top-level directory of the edit.
+ *
+ * Perform all allocations in POOL.  */
 static struct dir_baton *
 make_dir_baton(const char *path,
                const char *copyfrom_path,
                svn_revnum_t copyfrom_rev,
                void *edit_baton,
                struct dir_baton *pb,
-               svn_boolean_t added,
                apr_pool_t *pool)
 {
   struct dump_edit_baton *eb = edit_baton;
@@ -189,14 +181,13 @@ make_dir_baton(const char *path,
     copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool);
 
   new_db->eb = eb;
-  new_db->parent_dir_baton = pb;
   new_db->pool = pool;
   new_db->repos_relpath = repos_relpath;
   new_db->copyfrom_path = copyfrom_path
                             ? svn_relpath_canonicalize(copyfrom_path, pool)
                             : NULL;
   new_db->copyfrom_rev = copyfrom_rev;
-  new_db->added = added;
+  new_db->headers = NULL;
   new_db->props = apr_hash_make(pool);
   new_db->deleted_props = apr_hash_make(pool);
   new_db->deleted_entries = apr_hash_make(pool);
@@ -216,7 +207,6 @@ make_file_baton(const char *path,
   struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb));
 
   new_fb->eb = pb->eb;
-  new_fb->parent_dir_baton = pb;
   new_fb->pool = pool;
   new_fb->repos_relpath = svn_relpath_canonicalize(path, pool);
   new_fb->props = apr_hash_make(pool);
@@ -229,9 +219,11 @@ make_file_baton(const char *path,
   return new_fb;
 }
 
-/* Return in *HEADER and *CONTENT the headers and content for PROPS. */
+/* Append to HEADERS the required headers, and set *CONTENT to the property
+ * content section, to represent the property delta of PROPS/DELETED_PROPS.
+ */
 static svn_error_t *
-get_props_content(svn_stringbuf_t **header,
+get_props_content(apr_array_header_t *headers,
                   svn_stringbuf_t **content,
                   apr_hash_t *props,
                   apr_hash_t *deleted_props,
@@ -240,10 +232,8 @@ get_props_content(svn_stringbuf_t **head
 {
   svn_stream_t *content_stream;
   apr_hash_t *normal_props;
-  const char *buf;
 
   *content = svn_stringbuf_create_empty(result_pool);
-  *header = svn_stringbuf_create_empty(result_pool);
 
   content_stream = svn_stream_from_stringbuf(*content, scratch_pool);
 
@@ -254,42 +244,60 @@ get_props_content(svn_stringbuf_t **head
   SVN_ERR(svn_stream_close(content_stream));
 
   /* Prop-delta: true */
-  *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA
-                                  ": true\n");
-
-  /* Prop-content-length: 193 */
-  buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
-                     ": %" APR_SIZE_T_FMT "\n", (*content)->len);
-  svn_stringbuf_appendcstr(*header, buf);
+  svn_repos__dumpfile_header_push(
+    headers, SVN_REPOS_DUMPFILE_PROP_DELTA, "true");
 
   return SVN_NO_ERROR;
 }
 
+/* A special case of dump_node(), for a delete record.
+ *
+ * The only thing special about this version is it only writes one blank
+ * line, not two, after the headers. Why? Historical precedent for the
+ * case where a delete record is used as part of a (delete + add-with-history)
+ * in implementing a replacement.
+ */
 static svn_error_t *
-do_dump_newlines(struct dump_edit_baton *eb,
-                 svn_boolean_t *trigger_var,
+dump_node_delete(svn_stream_t *stream,
+                 const char *node_relpath,
                  apr_pool_t *pool)
 {
-  if (trigger_var && *trigger_var)
-    {
-      SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
-      *trigger_var = FALSE;
-    }
+  apr_array_header_t *headers = svn_repos__dumpfile_headers_create(pool);
+
+  assert(svn_relpath_is_canonical(node_relpath));
+
+  /* Node-path: ... */
+  svn_repos__dumpfile_header_push(
+    headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath);
+
+  /* Node-action: delete */
+  svn_repos__dumpfile_header_push(
+    headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete");
+
+  SVN_ERR(svn_repos__dump_headers(stream, headers, TRUE, pool));
   return SVN_NO_ERROR;
 }
 
-/*
- * Write out a node record for PATH of type KIND under EB->FS_ROOT.
+/* Set *HEADERS_P to contain some headers for the node at PATH of type KIND.
+ *
  * ACTION describes what is happening to the node (see enum
- * svn_node_action). Write record to writable EB->STREAM.
+ * svn_node_action).
  *
  * If the node was itself copied, IS_COPY is TRUE and the
  * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV.
  * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this
  * node is part of a copied subtree.
+ *
+ * Iff ACTION is svn_node_action_replace and IS_COPY, then first write a
+ * complete deletion record to the dump stream.
+ *
+ * If ACTION is svn_node_action_delete, then the node record will be
+ * complete. (The caller may want to write two blank lines after the
+ * header block.)
  */
 static svn_error_t *
-dump_node(struct dump_edit_baton *eb,
+dump_node(apr_array_header_t **headers_p,
+          struct dump_edit_baton *eb,
           const char *repos_relpath,
           struct dir_baton *db,
           struct file_baton *fb,
@@ -300,6 +308,7 @@ dump_node(struct dump_edit_baton *eb,
           apr_pool_t *pool)
 {
   const char *node_relpath = repos_relpath;
+  apr_array_header_t *headers = svn_repos__dumpfile_headers_create(pool);
 
   assert(svn_relpath_is_canonical(repos_relpath));
   assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path));
@@ -311,17 +320,16 @@ dump_node(struct dump_edit_baton *eb,
                                     node_relpath, pool);
 
   /* Node-path: ... */
-  SVN_ERR(svn_stream_printf(eb->stream, pool,
-                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
-                            node_relpath));
+  svn_repos__dumpfile_header_push(
+    headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath);
 
   /* Node-kind: "file" | "dir" */
   if (fb)
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
+    svn_repos__dumpfile_header_push(
+      headers, SVN_REPOS_DUMPFILE_NODE_KIND, "file");
   else if (db)
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
+    svn_repos__dumpfile_header_push(
+      headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir");
 
 
   /* Write the appropriate Node-action header */
@@ -333,31 +341,22 @@ dump_node(struct dump_edit_baton *eb,
          do here but print node action information.
 
          Node-action: change.  */
-      SVN_ERR(svn_stream_puts(eb->stream,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n"));
+      svn_repos__dumpfile_header_push(
+        headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "change");
       break;
 
-    case svn_node_action_replace:
-      if (is_copy)
-        {
-          /* More complex case: is_copy is true, and copyfrom_path/
-             copyfrom_rev are present: delete the original, and then re-add
-             it */
+    case svn_node_action_delete:
+      /* Node-action: delete */
+      svn_repos__dumpfile_header_push(
+        headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete");
+      break;
 
-          SVN_ERR(svn_stream_puts(eb->stream,
-                                  SVN_REPOS_DUMPFILE_NODE_ACTION
-                                  ": delete\n\n"));
-
-          /* Recurse: Print an additional add-with-history record. */
-          SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add,
-                            is_copy, copyfrom_path, copyfrom_rev, pool));
-        }
-      else
+    case svn_node_action_replace:
+      if (! is_copy)
         {
           /* Node-action: replace */
-          SVN_ERR(svn_stream_puts(eb->stream,
-                                  SVN_REPOS_DUMPFILE_NODE_ACTION
-                                  ": replace\n"));
+          svn_repos__dumpfile_header_push(
+            headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "replace");
 
           /* Wait for a change_*_prop to be called before dumping
              anything */
@@ -365,35 +364,36 @@ dump_node(struct dump_edit_baton *eb,
             fb->dump_props = TRUE;
           else if (db)
             db->dump_props = TRUE;
+          break;
         }
-      break;
-
-    case svn_node_action_delete:
-      /* Node-action: delete */
-      SVN_ERR(svn_stream_puts(eb->stream,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n"));
+      else
+        {
+          /* More complex case: is_copy is true, and copyfrom_path/
+             copyfrom_rev are present: delete the original, and then re-add
+             it */
+          /* ### Why not write a 'replace' record? Don't know. */
 
-      /* We can leave this routine quietly now. Nothing more to do-
-         print a couple of newlines because we're not dumping props or
-         text. */
-      SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
+          /* ### Unusually, we end this 'delete' node record with only a single
+                 blank line after the header block -- no extra blank line. */
+          SVN_ERR(dump_node_delete(eb->stream, repos_relpath, pool));
 
-      break;
+          /* The remaining action is a non-replacing add-with-history */
+          /* action = svn_node_action_add; */
+        }
+      /* FALL THROUGH to 'add' */
 
     case svn_node_action_add:
       /* Node-action: add */
-      SVN_ERR(svn_stream_puts(eb->stream,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+      svn_repos__dumpfile_header_push(
+        headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add");
 
       if (is_copy)
         {
           /* Node-copyfrom-rev / Node-copyfrom-path */
-          SVN_ERR(svn_stream_printf(eb->stream, pool,
-                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
-                                    ": %ld\n"
-                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
-                                    ": %s\n",
-                                    copyfrom_rev, copyfrom_path));
+          svn_repos__dumpfile_header_pushf(
+            headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, "%ld", copyfrom_rev);
+          svn_repos__dumpfile_header_push(
+            headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, copyfrom_path);
 
           /* Ugly hack: If a directory was copied from a previous
              revision, nothing like close_file() will be called to write two
@@ -431,6 +431,11 @@ dump_node(struct dump_edit_baton *eb,
 
       break;
     }
+
+  /* Return the headers so far. We don't necessarily have all the headers
+     yet -- there may be property-related and content length headers to
+     come, if this was not a 'delete' record. */
+  *headers_p = headers;
   return SVN_NO_ERROR;
 }
 
@@ -439,35 +444,28 @@ dump_mkdir(struct dump_edit_baton *eb,
            const char *repos_relpath,
            apr_pool_t *pool)
 {
-  svn_stringbuf_t *prop_header, *prop_content;
-  apr_size_t len;
-  const char *buf;
+  svn_stringbuf_t *prop_content;
+  apr_array_header_t *headers = svn_repos__dumpfile_headers_create(pool);
 
   /* Node-path: ... */
-  SVN_ERR(svn_stream_printf(eb->stream, pool,
-                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
-                            repos_relpath));
+  svn_repos__dumpfile_header_push(
+    headers, SVN_REPOS_DUMPFILE_NODE_PATH, repos_relpath);
 
   /* Node-kind: dir */
-  SVN_ERR(svn_stream_printf(eb->stream, pool,
-                            SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
+  svn_repos__dumpfile_header_push(
+    headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir");
 
   /* Node-action: add */
-  SVN_ERR(svn_stream_puts(eb->stream,
-                          SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+  svn_repos__dumpfile_header_push(
+    headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add");
 
   /* Dump the (empty) property block. */
-  SVN_ERR(get_props_content(&prop_header, &prop_content,
+  SVN_ERR(get_props_content(headers, &prop_content,
                             apr_hash_make(pool), apr_hash_make(pool),
                             pool, pool));
-  len = prop_header->len;
-  SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len));
-  len = prop_content->len;
-  buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH
-                     ": %" APR_SIZE_T_FMT "\n", len);
-  SVN_ERR(svn_stream_puts(eb->stream, buf));
-  SVN_ERR(svn_stream_puts(eb->stream, "\n"));
-  SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len));
+  SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, prop_content,
+                                      FALSE, 0, FALSE /*content_length_always*/,
+                                      pool));
 
   /* Newlines to tie it all off. */
   SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
@@ -475,86 +473,50 @@ dump_mkdir(struct dump_edit_baton *eb,
   return SVN_NO_ERROR;
 }
 
-/* Dump pending items from the specified node, to allow starting the dump
-   of a child node */
+/* Dump pending headers and properties for the directory EB->pending_db (if
+ * not null), to allow starting the dump of a child node */
 static svn_error_t *
-dump_pending(struct dump_edit_baton *eb,
-             apr_pool_t *scratch_pool)
+dump_pending_dir(struct dump_edit_baton *eb,
+                 apr_pool_t *scratch_pool)
 {
-  svn_boolean_t dump_props = FALSE;
-  apr_hash_t *props, *deleted_props;
+  struct dir_baton *db = eb->pending_db;
+  svn_stringbuf_t *prop_content = NULL;
 
-  if (! eb->pending_baton)
+  if (! db)
     return SVN_NO_ERROR;
 
   /* Some pending properties to dump? */
-  if (eb->pending_kind == svn_node_dir)
+  if (db->dump_props)
     {
-      struct dir_baton *db = eb->pending_baton;
-
-      if (db->dump_props)
-        {
-          dump_props = TRUE;
-          props = db->props;
-          deleted_props = db->deleted_props;
-
-          db->dump_props = FALSE;
-        }
-    }
-  else if (eb->pending_kind == svn_node_file)
-    {
-      struct file_baton *fb = eb->pending_baton;
-
-      if (fb->dump_props)
-        {
-          dump_props = TRUE;
-          props = fb->props;
-          deleted_props = fb->deleted_props;
-          fb->dump_props = FALSE;
-        }
+      SVN_ERR(get_props_content(db->headers, &prop_content,
+                                db->props, db->deleted_props,
+                                scratch_pool, scratch_pool));
     }
-  else
-    abort();
+  SVN_ERR(svn_repos__dump_node_record(eb->stream, db->headers, prop_content,
+                                      FALSE, 0, FALSE /*content_length_always*/,
+                                      scratch_pool));
 
-  if (dump_props)
+  if (db->dump_props)
     {
-      svn_stringbuf_t *header, *content;
-      apr_size_t len;
-
-      SVN_ERR(get_props_content(&header, &content, props, deleted_props,
-                                scratch_pool, scratch_pool));
-      len = header->len;
-      SVN_ERR(svn_stream_write(eb->stream, header->data, &len));
-
-      /* Content-length: 14 */
-      SVN_ERR(svn_stream_printf(eb->stream, scratch_pool,
-                                SVN_REPOS_DUMPFILE_CONTENT_LENGTH
-                                ": %" APR_SIZE_T_FMT "\n\n",
-                                content->len));
-
-      len = content->len;
-      SVN_ERR(svn_stream_write(eb->stream, content->data, &len));
-
       /* No text is going to be dumped. Write a couple of newlines and
          wait for the next node/ revision. */
       SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
 
       /* Cleanup so that data is never dumped twice. */
-      apr_hash_clear(props);
-      apr_hash_clear(deleted_props);
+      apr_hash_clear(db->props);
+      apr_hash_clear(db->deleted_props);
+      db->dump_props = FALSE;
     }
 
-  if (eb->pending_kind == svn_node_dir)
+  /* Some pending newlines to dump? */
+  if (db->dump_newlines)
     {
-      struct dir_baton *db = eb->pending_baton;
-
-      /* Some pending newlines to dump? */
-      SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool));
+      SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
+      db->dump_newlines = FALSE;
     }
 
   /* Anything that was pending is pending no longer. */
-  eb->pending_baton = NULL;
-  eb->pending_kind = svn_node_none;
+  eb->pending_db = NULL;
 
   return SVN_NO_ERROR;
 }
@@ -575,8 +537,6 @@ open_root(void *edit_baton,
   /* Clear the per-revision pool after each revision */
   svn_pool_clear(eb->pool);
 
-  LDR_DBG(("open_root %p\n", *root_baton));
-
   if (eb->update_anchor_relpath)
     {
       int i;
@@ -609,15 +569,15 @@ open_root(void *edit_baton,
               /* ... but for the source directory itself, we'll defer
                  to letting the typical plumbing handle this task. */
               new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
-                                      edit_baton, NULL, TRUE, pool);
-              SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db,
+                                      edit_baton, NULL, pool);
+              SVN_ERR(dump_node(&new_db->headers,
+                                eb, new_db->repos_relpath, new_db,
                                 NULL, svn_node_action_add, FALSE,
                                 NULL, SVN_INVALID_REVNUM, pool));
 
               /* Remember that we've started but not yet finished
                  handling this directory. */
-              eb->pending_baton = new_db;
-              eb->pending_kind = svn_node_dir;
+              eb->pending_db = new_db;
             }
         }
       svn_pool_destroy(iterpool);
@@ -626,7 +586,7 @@ open_root(void *edit_baton,
   if (! new_db)
     {
       new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
-                              edit_baton, NULL, FALSE, pool);
+                              edit_baton, NULL, pool);
     }
 
   *root_baton = new_db;
@@ -641,9 +601,7 @@ delete_entry(const char *path,
 {
   struct dir_baton *pb = parent_baton;
 
-  LDR_DBG(("delete_entry %s\n", path));
-
-  SVN_ERR(dump_pending(pb->eb, pool));
+  SVN_ERR(dump_pending_dir(pb->eb, pool));
 
   /* We don't dump this deletion immediate.  Rather, we add this path
      to the deleted_entries of the parent directory baton.  That way,
@@ -663,39 +621,37 @@ add_directory(const char *path,
               void **child_baton)
 {
   struct dir_baton *pb = parent_baton;
-  void *val;
+  void *was_deleted;
   struct dir_baton *new_db;
   svn_boolean_t is_copy;
 
-  LDR_DBG(("add_directory %s\n", path));
-
-  SVN_ERR(dump_pending(pb->eb, pool));
+  SVN_ERR(dump_pending_dir(pb->eb, pool));
 
   new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb,
-                          pb, TRUE, pb->pool);
+                          pb, pb->pool);
 
   /* This might be a replacement -- is the path already deleted? */
-  val = svn_hash_gets(pb->deleted_entries, path);
+  was_deleted = svn_hash_gets(pb->deleted_entries, path);
 
   /* Detect an add-with-history */
   is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
   /* Dump the node */
-  SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL,
-                    val ? svn_node_action_replace : svn_node_action_add,
+  SVN_ERR(dump_node(&new_db->headers,
+                    pb->eb, new_db->repos_relpath, new_db, NULL,
+                    was_deleted ? svn_node_action_replace : svn_node_action_add,
                     is_copy,
                     is_copy ? new_db->copyfrom_path : NULL,
                     is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
                     pool));
 
-  if (val)
+  if (was_deleted)
     /* Delete the path, it's now been dumped */
     svn_hash_sets(pb->deleted_entries, path, NULL);
 
   /* Remember that we've started, but not yet finished handling this
      directory. */
-  pb->eb->pending_baton = new_db;
-  pb->eb->pending_kind = svn_node_dir;
+  pb->eb->pending_db = new_db;
 
   *child_baton = new_db;
   return SVN_NO_ERROR;
@@ -713,9 +669,7 @@ open_directory(const char *path,
   const char *copyfrom_path = NULL;
   svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
 
-  LDR_DBG(("open_directory %s\n", path));
-
-  SVN_ERR(dump_pending(pb->eb, pool));
+  SVN_ERR(dump_pending_dir(pb->eb, pool));
 
   /* If the parent directory has explicit comparison path and rev,
      record the same for this one. */
@@ -728,7 +682,7 @@ open_directory(const char *path,
     }
 
   new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
-                          FALSE, pb->pool);
+                          pb->pool);
 
   *child_baton = new_db;
   return SVN_NO_ERROR;
@@ -742,12 +696,10 @@ close_directory(void *dir_baton,
   apr_hash_index_t *hi;
   svn_boolean_t this_pending;
 
-  LDR_DBG(("close_directory %p\n", dir_baton));
-
   /* Remember if this directory is the one currently pending. */
-  this_pending = (db->eb->pending_baton == db);
+  this_pending = (db->eb->pending_db == db);
 
-  SVN_ERR(dump_pending(db->eb, pool));
+  SVN_ERR(dump_pending_dir(db->eb, pool));
 
   /* If this directory was pending, then dump_pending() should have
      taken care of all the props and such.  Of course, the only way
@@ -760,12 +712,12 @@ close_directory(void *dir_baton,
      directory. */
   if ((! this_pending) && (db->dump_props))
     {
-      SVN_ERR(dump_node(db->eb, db->repos_relpath, db, NULL,
+      SVN_ERR(dump_node(&db->headers,
+                        db->eb, db->repos_relpath, db, NULL,
                         svn_node_action_change, FALSE,
                         NULL, SVN_INVALID_REVNUM, pool));
-      db->eb->pending_baton = db;
-      db->eb->pending_kind = svn_node_dir;
-      SVN_ERR(dump_pending(db->eb, pool));
+      db->eb->pending_db = db;
+      SVN_ERR(dump_pending_dir(db->eb, pool));
     }
 
   /* Dump the deleted directory entries */
@@ -774,8 +726,9 @@ close_directory(void *dir_baton,
     {
       const char *path = apr_hash_this_key(hi);
 
-      SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete,
-                        FALSE, NULL, SVN_INVALID_REVNUM, pool));
+      SVN_ERR(dump_node_delete(db->eb->stream, path, pool));
+      /* This deletion record is complete -- write an extra newline */
+      SVN_ERR(svn_stream_puts(db->eb->stream, "\n"));
     }
 
   /* ### should be unnecessary */
@@ -794,17 +747,15 @@ add_file(const char *path,
 {
   struct dir_baton *pb = parent_baton;
   struct file_baton *fb;
-  void *val;
+  void *was_deleted;
 
-  LDR_DBG(("add_file %s\n", path));
-
-  SVN_ERR(dump_pending(pb->eb, pool));
+  SVN_ERR(dump_pending_dir(pb->eb, pool));
 
   /* Make the file baton. */
   fb = make_file_baton(path, pb, pool);
 
   /* This might be a replacement -- is the path already deleted? */
-  val = svn_hash_gets(pb->deleted_entries, path);
+  was_deleted = svn_hash_gets(pb->deleted_entries, path);
 
   /* Detect add-with-history. */
   if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev))
@@ -813,10 +764,10 @@ add_file(const char *path,
       fb->copyfrom_rev = copyfrom_rev;
       fb->is_copy = TRUE;
     }
-  fb->action = val ? svn_node_action_replace : svn_node_action_add;
+  fb->action = was_deleted ? svn_node_action_replace : svn_node_action_add;
 
   /* Delete the path, it's now been dumped. */
-  if (val)
+  if (was_deleted)
     svn_hash_sets(pb->deleted_entries, path, NULL);
 
   *file_baton = fb;
@@ -833,9 +784,7 @@ open_file(const char *path,
   struct dir_baton *pb = parent_baton;
   struct file_baton *fb;
 
-  LDR_DBG(("open_file %s\n", path));
-
-  SVN_ERR(dump_pending(pb->eb, pool));
+  SVN_ERR(dump_pending_dir(pb->eb, pool));
 
   /* Make the file baton. */
   fb = make_file_baton(path, pb, pool);
@@ -863,13 +812,11 @@ change_dir_prop(void *parent_baton,
   struct dir_baton *db = parent_baton;
   svn_boolean_t this_pending;
 
-  LDR_DBG(("change_dir_prop %p\n", parent_baton));
-
   /* This directory is not pending, but something else is, so handle
      the "something else".  */
-  this_pending = (db->eb->pending_baton == db);
+  this_pending = (db->eb->pending_db == db);
   if (! this_pending)
-    SVN_ERR(dump_pending(db->eb, pool));
+    SVN_ERR(dump_pending_dir(db->eb, pool));
 
   if (svn_property_kind2(name) != svn_prop_regular_kind)
     return SVN_NO_ERROR;
@@ -897,8 +844,6 @@ change_file_prop(void *file_baton,
 {
   struct file_baton *fb = file_baton;
 
-  LDR_DBG(("change_file_prop %p\n", file_baton));
-
   if (svn_property_kind2(name) != svn_prop_regular_kind)
     return SVN_NO_ERROR;
 
@@ -927,8 +872,6 @@ apply_textdelta(void *file_baton, const
   struct dump_edit_baton *eb = fb->eb;
   svn_stream_t *delta_filestream;
 
-  LDR_DBG(("apply_textdelta %p\n", file_baton));
-
   /* Use a temporary file to measure the Text-content-length */
   delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool);
 
@@ -952,14 +895,13 @@ close_file(void *file_baton,
   struct file_baton *fb = file_baton;
   struct dump_edit_baton *eb = fb->eb;
   apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
-  svn_stringbuf_t *propstring;
+  svn_stringbuf_t *propstring = NULL;
+  apr_array_header_t *headers;
 
-  LDR_DBG(("close_file %p\n", file_baton));
+  SVN_ERR(dump_pending_dir(eb, pool));
 
-  SVN_ERR(dump_pending(eb, pool));
-
-  /* Dump the node. */
-  SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb,
+  /* Start dumping this node, by collecting some basic headers for it. */
+  SVN_ERR(dump_node(&headers, eb, fb->repos_relpath, NULL, fb,
                     fb->action, fb->is_copy, fb->copyfrom_path,
                     fb->copyfrom_rev, pool));
 
@@ -968,13 +910,9 @@ close_file(void *file_baton,
      the text headers too (if present). */
   if (fb->dump_props)
     {
-      svn_stringbuf_t *header;
-      apr_size_t len;
-
-      SVN_ERR(get_props_content(&header, &propstring, fb->props, fb->deleted_props,
+      SVN_ERR(get_props_content(headers, &propstring,
+                                fb->props, fb->deleted_props,
                                 pool, pool));
-      len = header->len;
-      SVN_ERR(svn_stream_write(eb->stream, header->data, &len));
     }
 
   /* Dump the text headers */
@@ -983,9 +921,8 @@ close_file(void *file_baton,
       apr_status_t err;
 
       /* Text-delta: true */
-      SVN_ERR(svn_stream_puts(eb->stream,
-                              SVN_REPOS_DUMPFILE_TEXT_DELTA
-                              ": true\n"));
+      svn_repos__dumpfile_header_push(
+        headers, SVN_REPOS_DUMPFILE_TEXT_DELTA, "true");
 
       err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file);
       if (err)
@@ -993,43 +930,22 @@ close_file(void *file_baton,
 
       if (fb->base_checksum)
         /* Text-delta-base-md5: */
-        SVN_ERR(svn_stream_printf(eb->stream, pool,
-                                  SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
-                                  ": %s\n",
-                                  fb->base_checksum));
-
-      /* Text-content-length: 39 */
-      SVN_ERR(svn_stream_printf(eb->stream, pool,
-                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
-                                ": %lu\n",
-                                (unsigned long)info->size));
+        svn_repos__dumpfile_header_push(
+          headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5, fb->base_checksum);
 
       /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */
-      SVN_ERR(svn_stream_printf(eb->stream, pool,
-                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
-                                ": %s\n",
-                                text_checksum));
+      svn_repos__dumpfile_header_push(
+        headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, text_checksum);
     }
 
-  /* Content-length: 1549 */
-  /* If both text and props are absent, skip this header */
-  if (fb->dump_props)
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
-                              ": %ld\n\n",
-                              (unsigned long)info->size + propstring->len));
-  else if (fb->dump_text)
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
-                              ": %ld\n\n",
-                              (unsigned long)info->size));
+  /* Dump the headers and props now */
+  SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, propstring,
+                                      fb->dump_text, info->size,
+                                      FALSE /*content_length_always*/,
+                                      pool));
 
-  /* Dump the props now */
   if (fb->dump_props)
     {
-      SVN_ERR(svn_stream_write(eb->stream, propstring->data,
-                               &(propstring->len)));
-
       /* Cleanup */
       fb->dump_props = FALSE;
       apr_hash_clear(fb->props);
@@ -1196,7 +1112,7 @@ svn_rdump__get_dump_editor(const svn_del
   eb->ra_session = ra_session;
   eb->update_anchor_relpath = update_anchor_relpath;
   eb->current_revision = revision;
-  eb->pending_kind = svn_node_none;
+  eb->pending_db = NULL;
 
   /* Create a special per-revision pool */
   eb->pool = svn_pool_create(pool);

Modified: subversion/branches/move-tracking-2/subversion/svnrdump/load_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnrdump/load_editor.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnrdump/load_editor.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnrdump/load_editor.c Mon Feb  9 16:46:16 2015
@@ -41,14 +41,9 @@
 
 #define SVNRDUMP_PROP_LOCK SVN_PROP_PREFIX "rdump-lock"
 
-#if 0
-#define LDR_DBG(x) SVN_DBG(x)
-#else
-#define LDR_DBG(x) while(0)
-#endif
+#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
 
-
 /**
  * General baton used by the parser functions.
  */
@@ -102,6 +97,15 @@ struct directory_baton
 {
   void *baton;
   const char *relpath;
+
+  /* The copy-from source of this directory, no matter whether it is
+     copied explicitly (the root node of a copy) or implicitly (being an
+     existing child of a copied directory). For a node that is newly
+     added (without history), even inside a copied parent, these are
+     NULL and SVN_INVALID_REVNUM. */
+  const char *copyfrom_path;
+  svn_revnum_t copyfrom_rev;
+
   struct directory_baton *parent;
 };
 
@@ -115,12 +119,20 @@ struct node_baton
   svn_node_kind_t kind;
   enum svn_node_action action;
 
+  /* Is this directory explicitly added? If not, then it already existed
+     or is a child of a copy. */
+  svn_boolean_t is_added;
+
   svn_revnum_t copyfrom_rev;
   const char *copyfrom_path;
+  const char *copyfrom_url;
 
   void *file_baton;
   const char *base_checksum;
 
+  /* (const char *name) -> (svn_prop_t *) */
+  apr_hash_t *prop_changes;
+
   struct revision_baton *rb;
 };
 
@@ -387,6 +399,7 @@ new_revision_record(void **revision_bato
   pb = parse_baton;
   rb->pool = svn_pool_create(pool);
   rb->pb = pb;
+  rb->db = NULL;
 
   for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi))
     {
@@ -435,6 +448,47 @@ uuid_record(const char *uuid,
   return SVN_NO_ERROR;
 }
 
+/* Push information about another directory onto the linked list RB->db.
+ *
+ * CHILD_BATON is the baton returned by the commit editor. RELPATH is the
+ * repository-relative path of this directory. IS_ADDED is true iff this
+ * directory is being added (with or without history). If added with
+ * history then COPYFROM_PATH/COPYFROM_REV are the copyfrom source, else
+ * are NULL/SVN_INVALID_REVNUM.
+ */
+static void
+push_directory(struct revision_baton *rb,
+               void *child_baton,
+               const char *relpath,
+               svn_boolean_t is_added,
+               const char *copyfrom_path,
+               svn_revnum_t copyfrom_rev)
+{
+  struct directory_baton *child_db = apr_pcalloc(rb->pool, sizeof (*child_db));
+
+  SVN_ERR_ASSERT_NO_RETURN(
+    is_added || (copyfrom_path == NULL && copyfrom_rev == SVN_INVALID_REVNUM));
+
+  /* If this node is an existing (not newly added) child of a copied node,
+     calculate where it was copied from. */
+  if (!is_added
+      && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev))
+    {
+      const char *name = svn_relpath_basename(relpath, NULL);
+
+      copyfrom_path = svn_relpath_join(rb->db->copyfrom_path, name,
+                                       rb->pool);
+      copyfrom_rev = rb->db->copyfrom_rev;
+    }
+
+  child_db->baton = child_baton;
+  child_db->relpath = relpath;
+  child_db->copyfrom_path = copyfrom_path;
+  child_db->copyfrom_rev = copyfrom_rev;
+  child_db->parent = rb->db;
+  rb->db = child_db;
+}
+
 static svn_error_t *
 new_node_record(void **node_baton,
                 apr_hash_t *headers,
@@ -445,17 +499,17 @@ new_node_record(void **node_baton,
   const struct svn_delta_editor_t *commit_editor = rb->pb->commit_editor;
   void *commit_edit_baton = rb->pb->commit_edit_baton;
   struct node_baton *nb;
-  struct directory_baton *child_db;
   apr_hash_index_t *hi;
   void *child_baton;
-  char *relpath_compose;
   const char *nb_dirname;
 
   nb = apr_pcalloc(rb->pool, sizeof(*nb));
   nb->rb = rb;
-
+  nb->is_added = FALSE;
   nb->copyfrom_path = NULL;
+  nb->copyfrom_url = NULL;
   nb->copyfrom_rev = SVN_INVALID_REVNUM;
+  nb->prop_changes = apr_hash_make(rb->pool);
 
   /* If the creation of commit_editor is pending, create it now and
      open_root on it; also create a top-level directory baton. */
@@ -486,14 +540,9 @@ new_node_record(void **node_baton,
                                        rb->rev - rb->rev_offset - 1,
                                        rb->pool, &child_baton));
 
-      LDR_DBG(("Opened root %p\n", child_baton));
-
-      /* child_db corresponds to the root directory baton here */
-      child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
-      child_db->baton = child_baton;
-      child_db->relpath = "";
-      child_db->parent = NULL;
-      rb->db = child_db;
+      /* child_baton corresponds to the root directory baton here */
+      push_directory(rb, child_baton, "", TRUE /*is_added*/,
+                     NULL, SVN_INVALID_REVNUM);
     }
 
   for (hi = apr_hash_first(rb->pool, headers); hi; hi = apr_hash_next(hi))
@@ -526,6 +575,9 @@ new_node_record(void **node_baton,
         nb->copyfrom_path = apr_pstrdup(rb->pool, hval);
     }
 
+  /* Before handling the new node, ensure depth-first editing order by
+     traversing the directory hierarchy from the old node's to the new
+     node's parent directory. */
   nb_dirname = svn_relpath_dirname(nb->path, pool);
   if (svn_path_compare_paths(nb_dirname,
                              rb->db->relpath) != 0)
@@ -536,9 +588,6 @@ new_node_record(void **node_baton,
       int i;
       apr_size_t n;
 
-      /* Before attempting to handle the action, call open_directory
-         for all the path components and set the directory baton
-         accordingly */
       ancestor_path =
         svn_relpath_get_longest_ancestor(nb_dirname,
                                          rb->db->relpath, pool);
@@ -556,14 +605,13 @@ new_node_record(void **node_baton,
           /* Don't worry about destroying the actual rb->db object,
              since the pool we're using has the lifetime of one
              revision anyway */
-          LDR_DBG(("Closing dir %p\n", rb->db->baton));
           SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
           rb->db = rb->db->parent;
         }
 
       for (i = 0; i < residual_open_path->nelts; i ++)
         {
-          relpath_compose =
+          char *relpath_compose =
             svn_relpath_join(rb->db->relpath,
                              APR_ARRAY_IDX(residual_open_path, i, const char *),
                              rb->pool);
@@ -571,12 +619,8 @@ new_node_record(void **node_baton,
                                                 rb->db->baton,
                                                 rb->rev - rb->rev_offset - 1,
                                                 rb->pool, &child_baton));
-          LDR_DBG(("Opened dir %p\n", child_baton));
-          child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
-          child_db->baton = child_baton;
-          child_db->relpath = relpath_compose;
-          child_db->parent = rb->db;
-          rb->db = child_db;
+          push_directory(rb, child_baton, relpath_compose, TRUE /*is_added*/,
+                         NULL, SVN_INVALID_REVNUM);
         }
     }
 
@@ -604,7 +648,8 @@ new_node_record(void **node_baton,
       if (rb->pb->parent_dir)
         nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir,
                                              nb->copyfrom_path, rb->pool);
-      nb->copyfrom_path = svn_path_url_add_component2(rb->pb->root_url,
+      /* Convert to a URL, as the commit editor requires. */
+      nb->copyfrom_url = svn_path_url_add_component2(rb->pb->root_url,
                                                       nb->copyfrom_path,
                                                       rb->pool);
     }
@@ -614,7 +659,6 @@ new_node_record(void **node_baton,
     {
     case svn_node_action_delete:
     case svn_node_action_replace:
-      LDR_DBG(("Deleting entry %s in %p\n", nb->path, rb->db->baton));
       SVN_ERR(commit_editor->delete_entry(nb->path, rb->rev - rb->rev_offset,
                                           rb->db->baton, rb->pool));
       if (nb->action == svn_node_action_delete)
@@ -622,28 +666,22 @@ new_node_record(void **node_baton,
       else
         /* FALL THROUGH */;
     case svn_node_action_add:
+      nb->is_added = TRUE;
       switch (nb->kind)
         {
         case svn_node_file:
           SVN_ERR(commit_editor->add_file(nb->path, rb->db->baton,
-                                          nb->copyfrom_path,
+                                          nb->copyfrom_url,
                                           nb->copyfrom_rev,
                                           rb->pool, &(nb->file_baton)));
-          LDR_DBG(("Added file %s to dir %p as %p\n",
-                   nb->path, rb->db->baton, nb->file_baton));
           break;
         case svn_node_dir:
           SVN_ERR(commit_editor->add_directory(nb->path, rb->db->baton,
-                                               nb->copyfrom_path,
+                                               nb->copyfrom_url,
                                                nb->copyfrom_rev,
                                                rb->pool, &child_baton));
-          LDR_DBG(("Added dir %s to dir %p as %p\n",
-                   nb->path, rb->db->baton, child_baton));
-          child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
-          child_db->baton = child_baton;
-          child_db->relpath = apr_pstrdup(rb->pool, nb->path);
-          child_db->parent = rb->db;
-          rb->db = child_db;
+          push_directory(rb, child_baton, nb->path, TRUE /*is_added*/,
+                         nb->copyfrom_path, nb->copyfrom_rev);
           break;
         default:
           break;
@@ -661,11 +699,8 @@ new_node_record(void **node_baton,
           SVN_ERR(commit_editor->open_directory(nb->path, rb->db->baton,
                                                 rb->rev - rb->rev_offset - 1,
                                                 rb->pool, &child_baton));
-          child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
-          child_db->baton = child_baton;
-          child_db->relpath = apr_pstrdup(rb->pool, nb->path);
-          child_db->parent = rb->db;
-          rb->db = child_db;
+          push_directory(rb, child_baton, nb->path, FALSE /*is_added*/,
+                         NULL, SVN_INVALID_REVNUM);
           break;
         }
       break;
@@ -721,8 +756,8 @@ set_node_property(void *baton,
   struct node_baton *nb = baton;
   struct revision_baton *rb = nb->rb;
   struct parse_baton *pb = rb->pb;
-  const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
   apr_pool_t *pool = nb->rb->pool;
+  svn_prop_t *prop;
 
   if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
     {
@@ -749,21 +784,11 @@ set_node_property(void *baton,
 
   SVN_ERR(svn_repos__validate_prop(name, value, pool));
 
-  switch (nb->kind)
-    {
-    case svn_node_file:
-      LDR_DBG(("Applying properties on %p\n", nb->file_baton));
-      SVN_ERR(commit_editor->change_file_prop(nb->file_baton, name,
-                                              value, pool));
-      break;
-    case svn_node_dir:
-      LDR_DBG(("Applying properties on %p\n", nb->rb->db->baton));
-      SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton, name,
-                                             value, pool));
-      break;
-    default:
-      break;
-    }
+  prop = apr_palloc(nb->rb->pool, sizeof (*prop));
+  prop->name = apr_pstrdup(pool, name);
+  prop->value = svn_string_dup(value, pool);
+  svn_hash_sets(nb->prop_changes, prop->name, prop);
+
   return SVN_NO_ERROR;
 }
 
@@ -772,44 +797,79 @@ delete_node_property(void *baton,
                      const char *name)
 {
   struct node_baton *nb = baton;
-  const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
   apr_pool_t *pool = nb->rb->pool;
+  svn_prop_t *prop;
 
   SVN_ERR(svn_repos__validate_prop(name, NULL, pool));
 
-  if (nb->kind == svn_node_file)
-    SVN_ERR(commit_editor->change_file_prop(nb->file_baton, name,
-                                            NULL, pool));
-  else
-    SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton, name,
-                                           NULL, pool));
+  prop = apr_palloc(pool, sizeof (*prop));
+  prop->name = apr_pstrdup(pool, name);
+  prop->value = NULL;
+  svn_hash_sets(nb->prop_changes, prop->name, prop);
 
   return SVN_NO_ERROR;
 }
 
+/* Delete all the properties of the node, if any.
+ *
+ * The commit editor doesn't have a method to delete a node's properties
+ * without knowing what they are, so we have to first find out what
+ * properties the node would have had. If it's copied (explicitly or
+ * implicitly), we look at the copy source. If it's only being changed,
+ * we look at the node's current path in the head revision.
+ */
 static svn_error_t *
 remove_node_props(void *baton)
 {
   struct node_baton *nb = baton;
+  struct revision_baton *rb = nb->rb;
   apr_pool_t *pool = nb->rb->pool;
   apr_hash_index_t *hi;
   apr_hash_t *props;
+  const char *orig_path;
+  svn_revnum_t orig_rev;
+
+  /* Find the path and revision that has the node's original properties */
+  if (ARE_VALID_COPY_ARGS(nb->copyfrom_path, nb->copyfrom_rev))
+    {
+      orig_path = nb->copyfrom_path;
+      orig_rev = nb->copyfrom_rev;
+    }
+  else if (!nb->is_added
+           && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev))
+    {
+      /* If this is a dir, then it's described by rb->db;
+         if this is a file, then it's a child of the dir in rb->db. */
+      orig_path = (nb->kind == svn_node_dir)
+                    ? rb->db->copyfrom_path
+                    : svn_relpath_join(rb->db->copyfrom_path,
+                                       svn_relpath_basename(nb->path, NULL),
+                                       rb->pool);
+      orig_rev = rb->db->copyfrom_rev;
+    }
+  else
+    {
+      /* ### Should we query at a known, fixed, "head" revision number
+         instead of passing SVN_INVALID_REVNUM and getting a moving target? */
+      orig_path = nb->path;
+      orig_rev = SVN_INVALID_REVNUM;
+    }
 
   if ((nb->action == svn_node_action_add
             || nb->action == svn_node_action_replace)
-      && ! SVN_IS_VALID_REVNUM(nb->copyfrom_rev))
+      && ! ARE_VALID_COPY_ARGS(orig_path, orig_rev))
     /* Add-without-history; no "old" properties to worry about. */
     return SVN_NO_ERROR;
 
   if (nb->kind == svn_node_file)
     {
-      SVN_ERR(svn_ra_get_file(nb->rb->pb->aux_session, nb->path,
-                              SVN_INVALID_REVNUM, NULL, NULL, &props, pool));
+      SVN_ERR(svn_ra_get_file(nb->rb->pb->aux_session,
+                              orig_path, orig_rev, NULL, NULL, &props, pool));
     }
   else  /* nb->kind == svn_node_dir */
     {
       SVN_ERR(svn_ra_get_dir2(nb->rb->pb->aux_session, NULL, NULL, &props,
-                              nb->path, SVN_INVALID_REVNUM, 0, pool));
+                              orig_path, orig_rev, 0, pool));
     }
 
   for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
@@ -834,7 +894,6 @@ set_fulltext(svn_stream_t **stream,
   void *handler_baton;
   apr_pool_t *pool = nb->rb->pool;
 
-  LDR_DBG(("Setting fulltext for %p\n", nb->file_baton));
   SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum,
                                          pool, &handler, &handler_baton));
   *stream = svn_txdelta_target_push(handler, handler_baton,
@@ -851,7 +910,6 @@ apply_textdelta(svn_txdelta_window_handl
   const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
   apr_pool_t *pool = nb->rb->pool;
 
-  LDR_DBG(("Applying textdelta to %p\n", nb->file_baton));
   SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum,
                                          pool, handler, handler_baton));
 
@@ -863,12 +921,34 @@ close_node(void *baton)
 {
   struct node_baton *nb = baton;
   const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
+  apr_pool_t *pool = nb->rb->pool;
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(pool, nb->prop_changes);
+       hi; hi = apr_hash_next(hi))
+    {
+      const char *name = apr_hash_this_key(hi);
+      svn_prop_t *prop = apr_hash_this_val(hi);
+
+      switch (nb->kind)
+        {
+        case svn_node_file:
+          SVN_ERR(commit_editor->change_file_prop(nb->file_baton,
+                                                  name, prop->value, pool));
+          break;
+        case svn_node_dir:
+          SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton,
+                                                 name, prop->value, pool));
+          break;
+        default:
+          break;
+        }
+    }
 
   /* Pass a file node closure through to the editor *unless* we
      deleted the file (which doesn't require us to open it). */
   if ((nb->kind == svn_node_file) && (nb->file_baton))
     {
-      LDR_DBG(("Closing file %p\n", nb->file_baton));
       SVN_ERR(commit_editor->close_file(nb->file_baton, NULL, nb->rb->pool));
     }
 
@@ -898,12 +978,10 @@ close_revision(void *baton)
          session itself */
       while (rb->db && rb->db->parent)
         {
-          LDR_DBG(("Closing dir %p\n", rb->db->baton));
           SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
           rb->db = rb->db->parent;
         }
       /* root dir's baton */
-      LDR_DBG(("Closing edit on %p\n", commit_edit_baton));
       SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
       SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool));
     }
@@ -921,8 +999,6 @@ close_revision(void *baton)
                                        rb->rev - rb->rev_offset - 1,
                                        rb->pool, &child_baton));
 
-      LDR_DBG(("Opened root %p\n", child_baton));
-      LDR_DBG(("Closing edit on %p\n", commit_edit_baton));
       SVN_ERR(commit_editor->close_directory(child_baton, rb->pool));
       SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool));
     }

Modified: subversion/branches/move-tracking-2/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnserve/serve.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnserve/serve.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnserve/serve.c Mon Feb  9 16:46:16 2015
@@ -1725,6 +1725,11 @@ static svn_error_t *get_dir(svn_ra_svn_c
                           &ab, root, full_path,
                           pool));
 
+  /* Fetch the directories' entries before starting the response, to allow
+     proper error handling in cases like when FULL_PATH doesn't exist */
+  if (want_contents)
+      SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool));
+
   /* Begin response ... */
   SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev));
   SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
@@ -1736,8 +1741,6 @@ static svn_error_t *get_dir(svn_ra_svn_c
       /* Use epoch for a placeholder for a missing date.  */
       const char *missing_date = svn_time_to_cstring(0, pool);
 
-      SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool));
-
       /* Transform the hash table's FS entries into dirents.  This probably
        * belongs in libsvn_repos. */
       subpool = svn_pool_create(pool);

Modified: subversion/branches/move-tracking-2/subversion/svnsync/sync.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnsync/sync.c?rev=1658462&r1=1658461&r2=1658462&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnsync/sync.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnsync/sync.c Mon Feb  9 16:46:16 2015
@@ -115,6 +115,7 @@ remove_r0_mergeinfo(const svn_string_t *
     {
       char *line = APR_ARRAY_IDX(lines, i, char *);
       char *colon;
+      char *rangelist;
 
       /* split at the last colon */
       colon = strrchr(line, ':');
@@ -124,13 +125,11 @@ remove_r0_mergeinfo(const svn_string_t *
                                  _("Missing colon in svn:mergeinfo "
                                    "property"));
 
+      rangelist = colon + 1;
+
       /* remove r0 */
       if (colon[1] == '0')
         {
-          char *rangelist;
-
-          rangelist = colon + 1;
-
           if (strncmp(rangelist, "0*,", 3) == 0)
             {
               rangelist += 3;
@@ -151,8 +150,11 @@ remove_r0_mergeinfo(const svn_string_t *
             {
               rangelist[0] = '1';
             }
+        }
 
-          /* reassemble */
+      /* reassemble */
+      if (rangelist[0])
+        {
           if (new_str->len)
             svn_stringbuf_appendbyte(new_str, '\n');
           svn_stringbuf_appendbytes(new_str, line, colon + 1 - line);
@@ -507,7 +509,7 @@ change_file_prop(void *file_baton,
       SVN_ERR(normalize_string(&value, &was_normalized,
                                eb->source_prop_encoding, pool, pool));
       /* Correct malformed mergeinfo. */
-      if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
+      if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
         {
           SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked,
                                       pool, pool));
@@ -617,7 +619,7 @@ change_dir_prop(void *dir_baton,
       SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding,
                                pool, pool));
       /* Maybe adjust svn:mergeinfo. */
-      if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
+      if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
         {
           SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked,
                                       pool, pool));



Mime
View raw message