You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2010/07/29 21:38:49 UTC

svn commit: r980551 [8/10] - in /subversion/branches/issue-2779-dev: ./ build/generator/ notes/tree-conflicts/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ subversion...

Modified: subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.c?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.c (original)
+++ subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.c Thu Jul 29 19:38:44 2010
@@ -1,5 +1,6 @@
 /*
- *  dump_editor.c: The svn_delta_editor_t editor used by svnrdump
+ *  dump_editor.c: The svn_delta_editor_t editor used by svnrdump to
+ *  dump revisions.
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -28,23 +29,54 @@
 #include "svn_props.h"
 #include "svn_dirent_uri.h"
 
-#include "svnrdump.h"
 #include "dump_editor.h"
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
-/* Make a directory baton to represent the directory at path (relative
-   to the edit_baton).
+/* The baton used by the dump editor. */
+struct dump_edit_baton {
+  /* The output stream we write the dumpfile to */
+  svn_stream_t *stream;
+
+  /* Pool for per-edit-session allocations */
+  apr_pool_t *pool;
+
+  /* Properties which were modified during change_file_prop
+   * or change_dir_prop. */
+  apr_hash_t *props;
+
+  /* Properties which were deleted during change_file_prop
+   * or change_dir_prop. */
+  apr_hash_t *deleted_props;
+
+  /* Temporary buffer to write property hashes to in human-readable
+   * form. ### Is this really needed? */
+  svn_stringbuf_t *propstring;
+   
+  /* Temporary file to write delta to along with its checksum. */
+  char *delta_abspath;
+
+  /* The checksum of the file the delta is being applied to */
+  const char *base_checksum;
+
+  /* Flags to trigger dumping props and text */
+  svn_boolean_t dump_props;
+  svn_boolean_t dump_text;
+  svn_boolean_t dump_props_pending;
+};
 
-   COPYFROM_PATH/COPYFROM_REV are the path/revision against which this
-   directory should be compared for changes. If the copyfrom
-   information is valid, the directory will be compared
-   against its copy source.
-
-   PARENT_DIR_BATON 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.  */
+/* Make a directory baton to represent the directory at path (relative
+ * to the edit_baton).
+ *
+ * COPYFROM_PATH/COPYFROM_REV are the path/revision against which this
+ * directory should be compared for changes. If the copyfrom
+ * information is valid, the directory will be compared against its
+ * copy source.
+ *
+ * PARENT_DIR_BATON 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.  */
 static struct dir_baton *
 make_dir_baton(const char *path,
                const char *copyfrom_path,
@@ -59,23 +91,25 @@ make_dir_baton(const char *path,
   struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
   const char *abspath;
 
-  /* A path relative to nothing?  I don't think so. */
+  /* Disallow a path relative to nothing. */
   SVN_ERR_ASSERT_NO_RETURN(!path || pb);
 
   /* Construct the full path of this node. */
   if (pb)
-    abspath = svn_dirent_join_many(pool, "/", path);
+    abspath = svn_uri_join("/", path, pool);
   else
-    abspath = apr_pstrdup(pool, "/");
+    abspath = "/";
 
   /* Remove leading slashes from copyfrom paths. */
   if (copyfrom_path && strcmp(copyfrom_path, "/"))
-    copyfrom_path = ((*copyfrom_path == '/') ? copyfrom_path + 1 : copyfrom_path);
+    copyfrom_path = ((*copyfrom_path == '/') ?
+                     copyfrom_path + 1 : copyfrom_path);
 
   new_db->eb = eb;
   new_db->parent_dir_baton = pb;
   new_db->abspath = abspath;
-  new_db->copyfrom_path = copyfrom_path ? apr_pstrdup(pool, copyfrom_path) : NULL;
+  new_db->copyfrom_path = copyfrom_path ?
+    apr_pstrdup(pool, copyfrom_path) : NULL;
   new_db->copyfrom_rev = copyfrom_rev;
   new_db->added = added;
   new_db->written_out = FALSE;
@@ -83,30 +117,98 @@ make_dir_baton(const char *path,
 
   return new_db;
 }
+
+/* Extract and dump properties stored in edit baton EB, using POOL for
+ * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
+ * Unless DUMP_DATA_TOO is set, only property headers are dumped.
+ */
+static svn_error_t *
+dump_props(struct dump_edit_baton *eb,
+           svn_boolean_t *trigger_var,
+           svn_boolean_t dump_data_too,
+           apr_pool_t *pool)
+{
+  svn_stream_t *propstream;
+
+  if (trigger_var && !*trigger_var)
+    return SVN_NO_ERROR;
+
+  svn_stringbuf_setempty(eb->propstring);
+  propstream = svn_stream_from_stringbuf(eb->propstring, eb->pool);
+  SVN_ERR(svn_hash_write_incremental(eb->props, eb->deleted_props,
+                                     propstream, "PROPS-END", pool));
+  SVN_ERR(svn_stream_close(propstream));
+  
+  /* Prop-delta: true */
+  SVN_ERR(svn_stream_printf(eb->stream, pool,
+                            SVN_REPOS_DUMPFILE_PROP_DELTA
+                            ": true\n"));
+
+  /* Prop-content-length: 193 */
+  SVN_ERR(svn_stream_printf(eb->stream, pool,
+                            SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n", eb->propstring->len));
+
+  if (dump_data_too)
+    {
+      /* Content-length: 14 */
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                                ": %" APR_SIZE_T_FMT "\n\n",
+                                eb->propstring->len));
+
+      /* The properties. */
+      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
+                               &(eb->propstring->len)));
+
+      /* No text is going to be dumped. Write a couple of newlines and
+         wait for the next node/ revision. */
+      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+
+      /* Cleanup so that data is never dumped twice. */
+      svn_hash__clear(eb->props, pool);
+      svn_hash__clear(eb->deleted_props, pool);
+      if (trigger_var)
+        *trigger_var = FALSE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /*
  * Write out a node record for PATH of type KIND under EB->FS_ROOT.
- * ACTION describes what is happening to the node (see enum svn_node_action).
- * Write record to writable EB->STREAM, using EB->BUFFER to write in chunks.
+ * ACTION describes what is happening to the node (see enum
+ * svn_node_action). Write record to writable EB->STREAM, using
+ * EB->BUFFER to write in chunks.
  *
  * 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.
+ * 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.
  */
 static svn_error_t *
 dump_node(struct dump_edit_baton *eb,
           const char *path,    /* an absolute path. */
           svn_node_kind_t kind,
           enum svn_node_action action,
+          svn_boolean_t is_copy,
           const char *copyfrom_path,
           svn_revnum_t copyfrom_rev,
           apr_pool_t *pool)
 {
-  /* Write out metadata headers for this node. */
+  /* Remove leading slashes from path and copyfrom_path */
+  if (path && strcmp(path, "/"))
+    path = ((*path == '/') ? path + 1 : path);
+  
+  if (copyfrom_path && strcmp(copyfrom_path, "/"))
+    copyfrom_path = ((*copyfrom_path == '/') ?
+                     copyfrom_path + 1 : copyfrom_path);
+
+  /* Node-path: commons/STATUS */
   SVN_ERR(svn_stream_printf(eb->stream, pool,
-          SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
-          (*path == '/') ? path + 1 : path));
+                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
 
+  /* Node-kind: file */
   if (kind == svn_node_file)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
@@ -114,118 +216,109 @@ dump_node(struct dump_edit_baton *eb,
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
 
-  /* Remove leading slashes from copyfrom paths. */
-  if (copyfrom_path && strcmp(copyfrom_path, "/"))
-    copyfrom_path = ((*copyfrom_path == '/') ? copyfrom_path + 1 : copyfrom_path);
 
-  switch (action) {
+  /* Write the appropriate Node-action header */
+  switch (action)
+    {
+    case svn_node_action_change:
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_ACTION
+                                ": change\n"));
+      break;
+
+    case svn_node_action_replace:
+      if (!is_copy)
+        {
+          /* Node-action: replace */
+          SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                    SVN_REPOS_DUMPFILE_NODE_ACTION
+                                    ": replace\n"));
+
+          eb->dump_props_pending = TRUE;
+          break;
+        }
+      /* 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_change:
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION
-                              ": change\n"));
-    break;
-
-  case svn_node_action_replace:
-    if (!eb->is_copy) {
-      /* a simple delete+add, implied by a single 'replace' action. */
       SVN_ERR(svn_stream_printf(eb->stream, pool,
                                 SVN_REPOS_DUMPFILE_NODE_ACTION
-                                ": replace\n"));
+                                ": delete\n\n"));
 
-      eb->dump_props_pending = TRUE;
+      /* Recurse: Print an additional add-with-history record. */
+      SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
+                        is_copy, copyfrom_path, copyfrom_rev, pool));
+
+      /* We can leave this routine quietly now, don't need to dump any
+         content; that was already done in the second record. */
+      eb->dump_props = FALSE;
       break;
-    }
-    /* More complex case: eb->is_copy is true, and
-       copyfrom_path/ copyfrom_rev are present: delete the original,
-       and then re-add it */
-
-    /* the path & kind headers have already been printed;  just
-       add a delete action, and end the current record.*/
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION
-                              ": delete\n\n"));
 
-    /* recurse:  print an additional add-with-history record. */
-    SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
-                      copyfrom_path, copyfrom_rev, pool));
-
-    /* we can leave this routine quietly now, don't need to dump
-       any content;  that was already done in the second record. */
-    eb->dump_props = FALSE;
-    eb->is_copy = FALSE;
-    break;
+    case svn_node_action_delete:
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_ACTION
+                                ": delete\n"));
 
-  case svn_node_action_delete:
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION
-                              ": delete\n"));
+      /* We can leave this routine quietly now, don't need to dump
+         any content. */
+      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+      eb->dump_props = FALSE;
+      break;
 
-    /* we can leave this routine quietly now, don't need to dump
-       any content. */
-    SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
-    eb->dump_props = FALSE;
-    break;
+    case svn_node_action_add:
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
 
-  case svn_node_action_add:
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+      if (!is_copy)
+        {
+          /* eb->dump_props_pending for files is handled in close_file
+             which is called immediately.  However, directories are not
+             closed until all the work inside them has been done;
+             eb->dump_props_pending for directories is handled in all the
+             functions that can possibly be called after add_directory:
+             add_directory, open_directory, delete_entry, close_directory,
+             add_file, open_file. change_dir_prop is a special case. */
+
+          eb->dump_props_pending = TRUE;
+          break;
+        }
 
-    if (!eb->is_copy) {
-      /* eb->dump_props_pending for files is handled in close_file
-         which is called immediately.  However, directories are not
-         closed until all the work inside them has been done;
-         eb->dump_props_pending for directories is handled in all the
-         functions that can possibly be called after add_directory:
-         add_directory, open_directory, delete_entry, close_directory,
-         add_file, open_file and change_dir_prop; change_dir_prop is a
-         special case ofcourse */
+      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));
+
+      /* Ugly hack: If a directory was copied from a previous revision,
+         nothing else can be done, and close_file won't be called to
+         write two blank lines. Write them here otherwise the `svnadmin
+         load` parser will fail. */
+      if (kind == svn_node_dir)
+        SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
 
-      eb->dump_props_pending = TRUE;
       break;
     }
 
-    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));
-
-    /* Dump the text only if apply_textdelta sets
-       eb->dump_text */
-
-    /* UGLY hack: If a directory was copied from a
-       previous revision, nothing else can be done, and
-       close_file won't be called to write two blank
-       lines; write them here */
-    if (kind == svn_node_dir)
-      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
-
-    eb->is_copy = FALSE;
-
-    break;
-  }
-
   /* Dump property headers */
   SVN_ERR(dump_props(eb, &(eb->dump_props), FALSE, pool));
 
   return SVN_NO_ERROR;
 }
 
-static svn_error_t *open_root(void *edit_baton,
-            svn_revnum_t base_revision,
-            apr_pool_t *pool,
-            void **root_baton)
+static svn_error_t *
+open_root(void *edit_baton,
+          svn_revnum_t base_revision,
+          apr_pool_t *pool,
+          void **root_baton)
 {
+  struct dump_edit_baton *eb = edit_baton;
   /* Allocate a special pool for the edit_baton to avoid pool
      lifetime issues */
-  struct dump_edit_baton *eb = edit_baton;
   eb->pool = svn_pool_create(pool);
-  eb->properties = apr_hash_make(eb->pool);
-  eb->del_properties = apr_hash_make(eb->pool);
+  eb->props = apr_hash_make(eb->pool);
+  eb->deleted_props = apr_hash_make(eb->pool);
   eb->propstring = svn_stringbuf_create("", eb->pool);
-  eb->is_copy = FALSE;
 
   *root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
                                edit_baton, NULL, FALSE, pool);
@@ -244,7 +337,8 @@ delete_entry(const char *path,
   /* Some pending properties to dump? */
   SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
 
-  /* remember this path needs to be deleted */
+  /* Add this path to the deleted_entries of the parent directory
+     baton. */
   apr_hash_set(pb->deleted_entries, mypath, APR_HASH_KEY_STRING, pb);
 
   return SVN_NO_ERROR;
@@ -262,6 +356,7 @@ add_directory(const char *path,
   void *val;
   struct dir_baton *new_db
     = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, TRUE, pool);
+  svn_boolean_t is_copy;
 
   /* Some pending properties to dump? */
   SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
@@ -270,14 +365,15 @@ add_directory(const char *path,
   val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
   /* Detect an add-with-history */
-  pb->eb->is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
+  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
   /* Dump the node */
   SVN_ERR(dump_node(pb->eb, path,
                     svn_node_dir,
                     val ? svn_node_action_replace : svn_node_action_add,
-                    pb->eb->is_copy ? copyfrom_path : NULL,
-                    pb->eb->is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+                    is_copy,
+                    is_copy ? copyfrom_path : NULL,
+                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
                     pool));
 
   if (val)
@@ -307,13 +403,16 @@ open_directory(const char *path,
 
   /* If the parent directory has explicit comparison path and rev,
      record the same for this one. */
-  if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev)) {
-    copyfrom_path = svn_dirent_join_many(pool, pb->copyfrom_path,
-                                         svn_relpath_basename(path, pool));
-    copyfrom_rev = pb->copyfrom_rev;
-  }
+  if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
+    {
+      copyfrom_path = svn_uri_join(pb->copyfrom_path,
+                                   svn_relpath_basename(path, pool),
+                                   pool);
+      copyfrom_rev = pb->copyfrom_rev;
+    }
 
-  new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, FALSE, pool);
+  new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
+                          FALSE, pool);
   *child_baton = new_db;
   return SVN_NO_ERROR;
 }
@@ -332,18 +431,18 @@ close_directory(void *dir_baton,
 
   /* Dump the directory entries */
   for (hi = apr_hash_first(pool, db->deleted_entries); hi;
-       hi = apr_hash_next(hi)) {
-    const void *key;
-    const char *path;
-    apr_hash_this(hi, &key, NULL, NULL);
-    path = key;
-
-    svn_pool_clear(iterpool);
-
-    SVN_ERR(dump_node(db->eb, path,
-                      svn_node_unknown, svn_node_action_delete,
-                      NULL, SVN_INVALID_REVNUM, iterpool));
-  }
+       hi = apr_hash_next(hi))
+    {
+      const void *key;
+      const char *path;
+      apr_hash_this(hi, &key, NULL, NULL);
+      path = key;
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(dump_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
+                        FALSE, NULL, SVN_INVALID_REVNUM, iterpool));
+    }
 
   svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
@@ -359,6 +458,7 @@ add_file(const char *path,
 {
   struct dir_baton *pb = parent_baton;
   void *val;
+  svn_boolean_t is_copy;
 
   /* Some pending properties to dump? */
   SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
@@ -367,21 +467,23 @@ add_file(const char *path,
   val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
   /* Detect add-with-history. */
-  pb->eb->is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
+  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
   /* Dump the node. */
   SVN_ERR(dump_node(pb->eb, path,
                     svn_node_file,
                     val ? svn_node_action_replace : svn_node_action_add,
-                    pb->eb->is_copy ? copyfrom_path : NULL,
-                    pb->eb->is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+                    is_copy,
+                    is_copy ? copyfrom_path : NULL,
+                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
                     pool));
 
   if (val)
     /* delete the path, it's now been dumped. */
     apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
 
-  /* Build a nice file baton to pass to change_file_prop and apply_textdelta */
+  /* Build a nice file baton to pass to change_file_prop and
+     apply_textdelta */
   *file_baton = pb->eb;
 
   return SVN_NO_ERROR;
@@ -406,19 +508,20 @@ open_file(const char *path,
 
   /* If the parent directory has explicit copyfrom path and rev,
      record the same for this one. */
-  if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev)) {
-    APR_ARRAY_PUSH(compose_path, const char *) = pb->copyfrom_path;
-    APR_ARRAY_PUSH(compose_path, const char *) =
-      svn_relpath_basename(path, pool);
-    copyfrom_path = svn_path_compose(compose_path, pool);
-    copyfrom_rev = pb->copyfrom_rev;
-  }
+  if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
+    {
+      APR_ARRAY_PUSH(compose_path, const char *) = pb->copyfrom_path;
+      APR_ARRAY_PUSH(compose_path, const char *) =
+        svn_relpath_basename(path, pool);
+      copyfrom_path = svn_path_compose(compose_path, pool);
+      copyfrom_rev = pb->copyfrom_rev;
+    }
 
-  SVN_ERR(dump_node(pb->eb, path,
-                    svn_node_file, svn_node_action_change,
-                    copyfrom_path, copyfrom_rev, pool));
+  SVN_ERR(dump_node(pb->eb, path, svn_node_file, svn_node_action_change,
+                    FALSE, copyfrom_path, copyfrom_rev, pool));
 
-  /* Build a nice file baton to pass to change_file_prop and apply_textdelta */
+  /* Build a nice file baton to pass to change_file_prop and
+     apply_textdelta */
   *file_baton = pb->eb;
 
   return SVN_NO_ERROR;
@@ -435,29 +538,28 @@ change_dir_prop(void *parent_baton,
   if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
     return SVN_NO_ERROR;
 
-  value ? apr_hash_set(db->eb->properties, apr_pstrdup(pool, name),
-                       APR_HASH_KEY_STRING, svn_string_dup(value, pool)) :
-    apr_hash_set(db->eb->del_properties, apr_pstrdup(pool, name),
-                 APR_HASH_KEY_STRING, (void *)0x1);
-
-  /* This function is what distinguishes between a directory that is
-     opened to merely get somewhere, vs. one that is opened because it
-     actually changed by itself  */
-  if (! db->written_out) {
-    /* If eb->dump_props_pending was set, it means that the
-       node information corresponding to add_directory has already
-       been written; just don't unset it and dump_node will dump
-       the properties before doing anything else. If it wasn't
-       set, node information hasn't been written yet: so dump the
-       node itself before dumping the props */
-
-    SVN_ERR(dump_node(db->eb, db->abspath,
-                      svn_node_dir, svn_node_action_change,
-                      db->copyfrom_path, db->copyfrom_rev, pool));
-
-    SVN_ERR(dump_props(db->eb, NULL, TRUE, pool));
-    db->written_out = TRUE;
-  }
+  if (value)
+    apr_hash_set(db->eb->props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, pool));
+  else
+    apr_hash_set(db->eb->deleted_props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, "");
+
+  if (! db->written_out)
+    {
+      /* If db->written_out is set, it means that the node information
+         corresponding to this directory has already been written: don't
+         do anything; dump_props_pending will take care of dumping the
+         props. If it not, dump the node itself before dumping the
+         props. */
+
+      SVN_ERR(dump_node(db->eb, db->abspath, svn_node_dir,
+                        svn_node_action_change, FALSE, db->copyfrom_path,
+                        db->copyfrom_rev, pool));
+
+      SVN_ERR(dump_props(db->eb, NULL, TRUE, pool));
+      db->written_out = TRUE;
+    }
   return SVN_NO_ERROR;
 }
 
@@ -472,9 +574,13 @@ change_file_prop(void *file_baton,
   if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
     return SVN_NO_ERROR;
 
-  apr_hash_set(eb->properties, apr_pstrdup(pool, name),
-               APR_HASH_KEY_STRING, value ?
-               svn_string_dup(value, pool): (void *)0x1);
+  if (value)
+    apr_hash_set(eb->props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, pool));
+  else
+    apr_hash_set(eb->deleted_props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, "");
+
   /* Dump the property headers and wait; close_file might need
      to write text headers too depending on whether
      apply_textdelta is called */
@@ -498,12 +604,9 @@ window_handler(svn_txdelta_window_t *win
     SVN_ERR(err);
 
   /* Write information about the filepath to hb->eb */
-  eb->delta_abspath = apr_pstrdup(eb->pool,
-          hb->delta_abspath);
+  eb->delta_abspath = apr_pstrdup(eb->pool, hb->delta_abspath);
 
   /* Cleanup */
-  SVN_ERR(svn_io_file_close(hb->delta_file, hb->pool));
-  SVN_ERR(svn_stream_close(hb->delta_filestream));
   svn_pool_destroy(hb->pool);
   return SVN_NO_ERROR;
 }
@@ -515,8 +618,6 @@ apply_textdelta(void *file_baton, const 
                 void **handler_baton)
 {
   struct dump_edit_baton *eb = file_baton;
-  apr_status_t apr_err;
-  const char *tempdir;
 
   /* Custom handler_baton allocated in a separate pool */
   apr_pool_t *handler_pool = svn_pool_create(pool);
@@ -525,24 +626,19 @@ apply_textdelta(void *file_baton, const 
   hb->eb = eb;
 
   /* Use a temporary file to measure the text-content-length */
-  SVN_ERR(svn_io_temp_dir(&tempdir, hb->pool));
+  SVN_ERR(svn_stream_open_unique(&(hb->delta_filestream), &hb->delta_abspath,
+                                 NULL, svn_io_file_del_none, hb->pool,
+                                 hb->pool));
 
-  hb->delta_abspath = svn_dirent_join(tempdir, "XXXXXX", hb->pool);
-  apr_err = apr_file_mktemp(&(hb->delta_file), hb->delta_abspath,
-          APR_CREATE | APR_READ | APR_WRITE | APR_EXCL,
-          hb->pool);
-  if (apr_err != APR_SUCCESS)
-    SVN_ERR(svn_error_wrap_apr(apr_err, NULL));
-
-  hb->delta_filestream = svn_stream_from_aprfile2(hb->delta_file, TRUE, hb->pool);
-
-  /* Prepare to write the delta to the temporary file */
+  /* Prepare to write the delta to the temporary file. */
   svn_txdelta_to_svndiff2(&(hb->apply_handler), &(hb->apply_baton),
                           hb->delta_filestream, 0, hb->pool);
   eb->dump_text = TRUE;
+  eb->base_checksum = apr_pstrdup(pool, base_checksum);
 
-  /* The actual writing takes place when this function has finished */
-  /* Set the handler and handler_baton */
+  /* The actual writing takes place when this function has
+     finished. Set handler and handler_baton now so for
+     window_handler() */
   *handler = window_handler;
   *handler_baton = hb;
 
@@ -559,75 +655,83 @@ close_file(void *file_baton,
   svn_stream_t *delta_filestream;
   apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
 
-  /* We didn't write the property headers because we were
-     waiting for file_prop_change; write them now */
+  /* Some pending properties to dump? */
   SVN_ERR(dump_props(eb, &(eb->dump_props_pending), FALSE, pool));
 
-  /* The prop headers have already been dumped in dump_node */
-  /* Dump the text headers */
-  if (eb->dump_text) {
-    /* text-delta header */
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-            SVN_REPOS_DUMPFILE_TEXT_DELTA
-            ": true\n"));
+  /* The prop headers have already been dumped in dump_node; now dump
+     the text headers. */
+  if (eb->dump_text)
+    {
+      /* Text-delta: true */
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_TEXT_DELTA
+                                ": true\n"));
 
-    /* Measure the length */
-    SVN_ERR(svn_io_stat(info, eb->delta_abspath, APR_FINFO_SIZE, pool));
+      SVN_ERR(svn_io_stat(info, eb->delta_abspath, APR_FINFO_SIZE, pool));
 
-    /* text-content-length header */
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-            SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
-            ": %lu\n",
-            (unsigned long)info->size));
-    /* text-content-md5 header */
-    SVN_ERR(svn_stream_printf(eb->stream, pool,
-            SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
-            ": %s\n",
-            text_checksum));
-  }
+      if (eb->base_checksum)
+        /* Text-delta-base-md5: */
+        SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                  SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
+                                  ": %s\n",
+                                  eb->base_checksum));
 
-  /* content-length header: if both text and props are absent,
-     skip this block */
+      /* Text-content-length: 39 */
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
+                                ": %lu\n",
+                                (unsigned long)info->size));
+
+      /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */      
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
+                                ": %s\n",
+                                text_checksum));
+    }
+
+  /* Content-length: 1549 */
+  /* If both text and props are absent, skip this header */
   if (eb->dump_props || eb->dump_props_pending)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
-            SVN_REPOS_DUMPFILE_CONTENT_LENGTH
-            ": %ld\n\n",
-            (unsigned long)info->size + eb->propstring->len));
+                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                              ": %ld\n\n",
+                              (unsigned long)info->size + eb->propstring->len));
   else if (eb->dump_text)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
-            SVN_REPOS_DUMPFILE_CONTENT_LENGTH
-            ": %ld\n\n",
-            (unsigned long)info->size));
+                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                              ": %ld\n\n",
+                              (unsigned long)info->size));
 
   /* Dump the props; the propstring should have already been
      written in dump_node or above */
-  if (eb->dump_props || eb->dump_props_pending) {
-    SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
-           &(eb->propstring->len)));
-
-    /* Cleanup */
-    eb->dump_props = eb->dump_props_pending = FALSE;
-    svn_hash__clear(eb->properties, pool);
-    svn_hash__clear(eb->del_properties, pool);
-  }
+  if (eb->dump_props || eb->dump_props_pending)
+    {
+      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
+                               &(eb->propstring->len)));
+
+      /* Cleanup */
+      eb->dump_props = eb->dump_props_pending = FALSE;
+      svn_hash__clear(eb->props, pool);
+      svn_hash__clear(eb->deleted_props, pool);
+    }
 
   /* Dump the text */
-  if (eb->dump_text) {
-
-    /* Open the temporary file, map it to a stream, copy
-       the stream to eb->stream, close and delete the
-       file */
-    SVN_ERR(svn_io_file_open(&delta_file, eb->delta_abspath,APR_READ,
-           0600,pool));
-    delta_filestream = svn_stream_from_aprfile2(delta_file, TRUE, pool);
-    SVN_ERR(svn_stream_copy3(delta_filestream, eb->stream, NULL, NULL, pool));
-
-    /* Cleanup */
-    SVN_ERR(svn_io_file_close(delta_file, pool));
-    SVN_ERR(svn_stream_close(delta_filestream));
-    SVN_ERR(svn_io_remove_file2(eb->delta_abspath, TRUE, pool));
-    eb->dump_text = FALSE;
-  }
+  if (eb->dump_text)
+    {
+      /* Open the temporary file, map it to a stream, copy
+         the stream to eb->stream, close and delete the
+         file */
+      SVN_ERR(svn_io_file_open(&delta_file, eb->delta_abspath, APR_READ,
+                               APR_OS_DEFAULT, pool));
+      delta_filestream = svn_stream_from_aprfile2(delta_file, TRUE, pool);
+      SVN_ERR(svn_stream_copy3(delta_filestream, eb->stream, NULL, NULL, pool));
+
+      /* Cleanup */
+      SVN_ERR(svn_io_file_close(delta_file, pool));
+      SVN_ERR(svn_stream_close(delta_filestream));
+      SVN_ERR(svn_io_remove_file2(eb->delta_abspath, TRUE, pool));
+      eb->dump_text = FALSE;
+    }
 
   SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
 
@@ -646,15 +750,16 @@ close_edit(void *edit_baton, apr_pool_t 
 svn_error_t *
 get_dump_editor(const svn_delta_editor_t **editor,
                 void **edit_baton,
+                svn_stream_t *stream,
                 apr_pool_t *pool)
 {
   struct dump_edit_baton *eb;
   svn_delta_editor_t *de;
 
   eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
-  SVN_ERR(svn_stream_for_stdout(&(eb->stream), pool));
-  de = svn_delta_default_editor(pool);
+  eb->stream = stream;
 
+  de = svn_delta_default_editor(pool);
   de->open_root = open_root;
   de->delete_entry = delete_entry;
   de->add_directory = add_directory;
@@ -668,7 +773,7 @@ get_dump_editor(const svn_delta_editor_t
   de->close_file = close_file;
   de->close_edit = close_edit;
 
-  /* Set the edit_baton and editor */
+  /* Set the edit_baton and editor. */
   *edit_baton = eb;
   *editor = de;
 

Modified: subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.h
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.h?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.h (original)
+++ subversion/branches/issue-2779-dev/subversion/svnrdump/dump_editor.h Thu Jul 29 19:38:44 2010
@@ -21,46 +21,19 @@
  * @endcopyright
  *
  * @file dump_editor.h
- * @brief The svn_delta_editor_t editor used by svnrdump
+ * @brief The svn_delta_editor_t editor used by svnrdump to dump
+ * revisions.
  */
 
 #ifndef DUMP_EDITOR_H_
 #define DUMP_EDITOR_H_
 
 /**
- * The baton used by the dump editor.
- */
-struct dump_edit_baton {
-  /* The output stream we write the dumpfile to */
-  svn_stream_t *stream;
-
-  /* pool is for per-edit-session allocations */
-  apr_pool_t *pool;
-
-  /* Store the properties that changed */
-  apr_hash_t *properties;
-  apr_hash_t *del_properties; /* Value is undefined */
-  /* write_hash_to_stringbuf writes properties and del_properties to
-     propstring so they can be printed directly to the stream */
-  svn_stringbuf_t *propstring;
-
-  /* Was a copy command issued? */
-  svn_boolean_t is_copy;
-
-  /* Temporary file to write delta to along with its checksum */
-  char *delta_abspath;
-
-  /* Flags to trigger dumping props and text */
-  svn_boolean_t dump_props;
-  svn_boolean_t dump_text;
-  svn_boolean_t dump_props_pending;
-};
-
-/**
  * A directory baton used by all directory-related callback functions
  * in the dump editor.
  */
-struct dir_baton {
+struct dir_baton
+{
   struct dump_edit_baton *eb;
   struct dir_baton *parent_dir_baton;
 
@@ -97,8 +70,7 @@ struct handler_baton
   apr_pool_t *pool;
 
   /* Information about the path of the temporary file used */
-  char *delta_abspath;
-  apr_file_t *delta_file;
+  const char *delta_abspath;
   svn_stream_t *delta_filestream;
 
   /* Global edit baton */
@@ -107,11 +79,12 @@ struct handler_baton
 
 /**
  * Get a dump editor @a editor along with a @a edit_baton allocated in
- * @a pool.
+ * @a pool. The editor will write output to @a stream.
  */
 svn_error_t *
 get_dump_editor(const svn_delta_editor_t **editor,
                 void **edit_baton,
+                svn_stream_t *stream,
                 apr_pool_t *pool);
 
 #endif

Modified: subversion/branches/issue-2779-dev/subversion/svnrdump/svnrdump.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/svnrdump/svnrdump.c?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/svnrdump/svnrdump.c (original)
+++ subversion/branches/issue-2779-dev/subversion/svnrdump/svnrdump.c Thu Jul 29 19:38:44 2010
@@ -25,13 +25,90 @@
 #include "svn_pools.h"
 #include "svn_cmdline.h"
 #include "svn_client.h"
+#include "svn_hash.h"
 #include "svn_ra.h"
 #include "svn_repos.h"
 #include "svn_path.h"
+#include "svn_utf.h"
 #include "svn_private_config.h"
+#include "svn_string.h"
+#include "svn_props.h"
 
-#include "svnrdump.h"
 #include "dump_editor.h"
+#include "load_editor.h"
+
+static svn_opt_subcommand_t dump_cmd, load_cmd;
+
+enum svn_svnrdump__longopt_t
+  {
+    opt_config_dir = SVN_OPT_FIRST_LONGOPT_ID,
+    opt_auth_username,
+    opt_auth_password,
+    opt_non_interactive,
+    opt_auth_nocache,
+    opt_version,
+  };
+
+static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] =
+  {
+    { "dump", dump_cmd, { "d" },
+      N_("usage: svnrdump dump URL [-r LOWER[:UPPER]]\n\n"
+         "Dump revisions LOWER to UPPER of repository at remote URL "
+         "to stdout in a 'dumpfile' portable format.\n"
+         "If omitted, LOWER defaults to zero and UPPER to the latest "
+         "latest revision.\n"),
+      { 0 } },
+    { "load", load_cmd, { "l" },
+      N_("usage: svnrdump load URL\n\n"
+         "Load a 'dumpfile' given on stdin to a repository "
+         "at remote URL.\n"
+         "## This feature is not yet available.\n"),
+      { 0 } },
+    { "help", 0, { "?", "h" },
+      N_("usage: svnrdump help [SUBCOMMAND...]\n\n"
+         "Describe the usage of this program or its subcommands.\n"),
+      { 0 } },
+    { NULL, NULL, { 0 }, NULL, { 0 } }
+  };
+
+static const apr_getopt_option_t svnrdump__options[] =
+  {
+    {"revision",     'r', 1, N_("REV1[:REV2] range of revisions to dump")},
+    {"quiet",         'q', 0, N_("no progress (only errors) to stderr")},
+    {"config-dir",    opt_config_dir, 1, N_("read user configuration files from"
+                                            " directory ARG") },
+    {"username",      opt_auth_username, 1, N_("specify a username ARG")},
+    {"password",      opt_auth_password, 1, N_("specify a password ARG")},
+    {"non-interactive", opt_non_interactive, 0, N_("do no interactive"
+                                                   " prompting")},
+    {"no-auth-cache", opt_auth_nocache, 0, N_("do not cache authentication"
+                                              " tokens")},
+  
+    {"help",          'h', 0, N_("display this help")},
+    {"version",       opt_version, 0, N_("show program version information")},
+
+    {0,                  0,   0, 0}
+  };
+
+/* Baton for the RA replay session. */
+struct replay_baton {
+  /* The editor producing diffs. */
+  const svn_delta_editor_t *editor;
+
+  /* Baton for the editor. */
+  void *edit_baton;
+
+  /* Whether to be quiet. */
+  svn_boolean_t quiet;
+};
+
+typedef struct {
+  svn_ra_session_t *session;
+  const char *url;
+  svn_revnum_t start_revision;
+  svn_revnum_t end_revision;
+  svn_boolean_t quiet;
+} opt_baton_t;
 
 static svn_error_t *
 replay_revstart(svn_revnum_t revision,
@@ -42,39 +119,40 @@ replay_revstart(svn_revnum_t revision,
                 apr_pool_t *pool)
 {
   struct replay_baton *rb = replay_baton;
-  /* Editing this revision has just started; dump the revprops
-     before invoking the editor callbacks */
-  svn_stringbuf_t *propstring = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *propstring;
   svn_stream_t *stdout_stream;
+  svn_stream_t *revprop_stream;
 
-  /* Create an stdout stream */
   svn_stream_for_stdout(&stdout_stream, pool);
 
-        /* Print revision number and prepare the propstring */
+  /* Revision-number: 19 */
   SVN_ERR(svn_stream_printf(stdout_stream, pool,
-          SVN_REPOS_DUMPFILE_REVISION_NUMBER
-          ": %ld\n", revision));
-  write_hash_to_stringbuf(rev_props, FALSE, &propstring, pool);
-  svn_stringbuf_appendbytes(propstring, "PROPS-END\n", 10);
+                            SVN_REPOS_DUMPFILE_REVISION_NUMBER
+                            ": %ld\n", revision));
+  propstring = svn_stringbuf_create_ensure(0, pool);
+  revprop_stream = svn_stream_from_stringbuf(propstring, pool);
+  SVN_ERR(svn_hash_write2(rev_props, revprop_stream, "PROPS-END", pool));
+  SVN_ERR(svn_stream_close(revprop_stream));
 
-  /* prop-content-length header */
+  /* Prop-content-length: 13 */
   SVN_ERR(svn_stream_printf(stdout_stream, pool,
-          SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
-          ": %" APR_SIZE_T_FMT "\n", propstring->len));
+                            SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n", propstring->len));
 
-  /* content-length header */
+  /* Content-length: 29 */
   SVN_ERR(svn_stream_printf(stdout_stream, pool,
-          SVN_REPOS_DUMPFILE_CONTENT_LENGTH
-          ": %" APR_SIZE_T_FMT "\n\n", propstring->len));
+                            SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n\n", propstring->len));
 
-  /* Print the revprops now */
+  /* Property data. */
   SVN_ERR(svn_stream_write(stdout_stream, propstring->data,
-         &(propstring->len)));
+                           &(propstring->len)));
 
-  svn_stream_close(stdout_stream);
+  SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n"));
+  SVN_ERR(svn_stream_close(stdout_stream));
 
   /* Extract editor and editor_baton from the replay_baton and
-     set them so that the editor callbacks can use them */
+     set them so that the editor callbacks can use them. */
   *editor = rb->editor;
   *edit_baton = rb->edit_baton;
 
@@ -89,121 +167,408 @@ replay_revend(svn_revnum_t revision,
               apr_hash_t *rev_props,
               apr_pool_t *pool)
 {
-  /* Editor has finished for this revision and close_edit has
-     been called; do nothing: just continue to the next
-     revision */
+  /* No resources left to free. */
   struct replay_baton *rb = replay_baton;
-  if (rb->verbose)
+  if (! rb->quiet)
     svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu\n", revision);
   return SVN_NO_ERROR;
 }
 
+/* Return in *SESSION a new RA session to URL.
+ * Allocate *SESSION and related data structures in POOL.
+ * Use CONFIG_DIR and pass USERNAME, PASSWORD, CONFIG_DIR and
+ * NO_AUTH_CACHE to initialize the authorization baton.*/
 static svn_error_t *
-open_connection(svn_ra_session_t *session, const char *url, apr_pool_t *pool)
+open_connection(svn_ra_session_t **session,
+                const char *url,
+                svn_boolean_t non_interactive,
+                const char *username,
+                const char *password,
+                const char *config_dir,
+                svn_boolean_t no_auth_cache,
+                apr_pool_t *pool)
 {
   svn_client_ctx_t *ctx = NULL;
-  SVN_ERR(svn_config_ensure (NULL, pool));
-  SVN_ERR(svn_client_create_context (&ctx, pool));
+  svn_config_t *cfg_config;
+
   SVN_ERR(svn_ra_initialize(pool));
 
-  SVN_ERR(svn_config_get_config(&(ctx->config), NULL, pool));
+  SVN_ERR(svn_config_ensure(config_dir, pool));
+  SVN_ERR(svn_client_create_context(&ctx, pool));
+
+  SVN_ERR(svn_config_get_config(&(ctx->config), config_dir, pool));
+
+  cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
+                            APR_HASH_KEY_STRING);
 
   /* Default authentication providers for non-interactive use */
-  SVN_ERR(svn_cmdline_create_auth_baton(&(ctx->auth_baton), TRUE,
-                NULL, NULL, NULL, FALSE,
-                FALSE, NULL, NULL, NULL,
-                pool));
-  SVN_ERR(svn_client_open_ra_session(&session, url, ctx, pool));
+  SVN_ERR(svn_cmdline_create_auth_baton(&(ctx->auth_baton), non_interactive,
+                                        username, password, config_dir,
+                                        no_auth_cache, FALSE, cfg_config,
+                                        ctx->cancel_func, ctx->cancel_baton,
+                                        pool));
+  SVN_ERR(svn_client_open_ra_session(session, url, ctx, pool));
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
-replay_range(svn_ra_session_t *session, svn_revnum_t start_revision,
-	     svn_revnum_t end_revision, apr_pool_t *pool,
-	     svn_boolean_t verbose)
+replay_revisions(svn_ra_session_t *session, const char *url,
+                 svn_revnum_t start_revision, svn_revnum_t end_revision,
+                 svn_boolean_t quiet, apr_pool_t *pool)
 {
   const svn_delta_editor_t *dump_editor;
   struct replay_baton *replay_baton;
   void *dump_baton;
+  const char *uuid;
+  svn_stream_t *stdout_stream;
 
-  SVN_ERR(get_dump_editor(&dump_editor,
-                          &dump_baton, pool));
+  SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
+
+  SVN_ERR(get_dump_editor(&dump_editor, &dump_baton, stdout_stream, pool));
 
   replay_baton = apr_pcalloc(pool, sizeof(*replay_baton));
   replay_baton->editor = dump_editor;
   replay_baton->edit_baton = dump_baton;
-  replay_baton->verbose = verbose;
-  SVN_ERR(svn_cmdline_printf(pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n",
-           SVN_REPOS_DUMPFILE_FORMAT_VERSION));
+  replay_baton->quiet = quiet;
+
+  /* Write the magic header and UUID */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
+                            SVN_REPOS_DUMPFILE_FORMAT_VERSION));
+  SVN_ERR(svn_ra_get_uuid2(session, &uuid, pool));
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid));
+
+  /* Fake revision 0 if necessary */
+  if (start_revision == 0)
+    {
+      apr_hash_t *prophash;
+      svn_stringbuf_t *propstring;
+      svn_stream_t *propstream;
+      SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                                SVN_REPOS_DUMPFILE_REVISION_NUMBER
+                                ": %ld\n", start_revision));
+
+      prophash = apr_hash_make(pool);
+      propstring = svn_stringbuf_create("", pool);
+
+      SVN_ERR(svn_ra_rev_proplist(session, start_revision,
+                                  &prophash, pool));
+
+      propstream = svn_stream_from_stringbuf(propstring, pool);
+      SVN_ERR(svn_hash_write2(prophash, propstream, "PROPS-END", pool));
+      SVN_ERR(svn_stream_close(propstream));
+
+      /* Property-content-length: 14; Content-length: 14 */
+      SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                                SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                                ": %" APR_SIZE_T_FMT "\n",
+                                propstring->len));
+      SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                                SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                                ": %" APR_SIZE_T_FMT "\n\n",
+                                propstring->len));
+      /* The properties */
+      SVN_ERR(svn_stream_write(stdout_stream, propstring->data,
+                               &(propstring->len)));
+      SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n"));
+      if (! quiet)
+        svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu\n", start_revision);
+
+      start_revision++;
+    }
+
   SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
                               0, TRUE, replay_revstart, replay_revend,
                               replay_baton, pool));
+  SVN_ERR(svn_stream_close(stdout_stream));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+load_revisions(svn_ra_session_t *session, const char *url,
+               svn_boolean_t quiet, apr_pool_t *pool)
+{
+  const svn_delta_editor_t *load_editor;
+  void *load_baton;
+  apr_file_t *stdin_file;
+  svn_stream_t *stdin_stream;
+  struct operation *root_operation;
+
+  apr_file_open_stdin(&stdin_file, pool);
+  stdin_stream = svn_stream_from_aprfile2(stdin_file, FALSE, pool);
+
+  SVN_ERR(get_load_editor(&load_editor, &load_baton, &root_operation,
+                          stdin_stream, session, pool));
+  SVN_ERR(drive_load_editor(root_operation, load_editor, pool));
+
+  svn_stream_close(stdin_stream);
+
+  return SVN_NO_ERROR;
+}
+
+
+static const char *
+ensure_appname(const char *progname, apr_pool_t *pool)
+{
+  if (!progname)
+    return "svnrdump";
+  
+  progname = svn_dirent_internal_style(progname, pool);
+  return svn_dirent_basename(progname, NULL);
+}
+
+static svn_error_t *
+usage(const char *progname, apr_pool_t *pool)
+{
+  progname = ensure_appname(progname, pool);
+
+  SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+                              _("Type '%s help' for usage.\n"),
+                              progname));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+version(const char *progname, apr_pool_t *pool)
+{
+  const char *ra_desc_start
+    = _("The following repository access (RA) modules are available:\n\n");
+
+  svn_stringbuf_t *version_footer = svn_stringbuf_create(ra_desc_start,
+                                                         pool);
+
+  SVN_ERR(svn_ra_print_modules(version_footer, pool));
+
+  progname = ensure_appname(progname, pool);
+
+  return svn_opt_print_help3(NULL, progname, TRUE, FALSE,
+                             version_footer->data, NULL, NULL,
+                             NULL, NULL, NULL, pool);
+}
+
+
+/** A statement macro, similar to @c SVN_ERR, but returns an integer.
+ *
+ * Evaluate @a expr. If it yields an error, handle that error and
+ * return @c EXIT_FAILURE.
+ */
+#define SVNRDUMP_ERR(expr)                                              \
+  do                                                                    \
+    {                                                                   \
+      svn_error_t *svn_err__temp = (expr);                              \
+      if (svn_err__temp)                                                \
+        {                                                               \
+          svn_handle_error2(svn_err__temp, stderr, FALSE, "svnrdump: "); \
+          svn_error_clear(svn_err__temp);                               \
+          return EXIT_FAILURE;                                          \
+        }                                                               \
+    }                                                                   \
+  while (0)
+
+static svn_error_t *
+dump_cmd(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  opt_baton_t *opt_baton = baton;
+  SVN_ERR(replay_revisions(opt_baton->session, opt_baton->url,
+                           opt_baton->start_revision, opt_baton->end_revision,
+                           opt_baton->quiet, pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+load_cmd(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  opt_baton_t *opt_baton = baton;
+  SVN_ERR(load_revisions(opt_baton->session, opt_baton->url,
+                         opt_baton->quiet, pool));  
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
-usage(FILE *out_stream)
+help_cmd(apr_getopt_t *os, void *baton, apr_pool_t *pool)
 {
-  fprintf(out_stream,
-    _("usage: svnrdump URL [-r LOWER[:UPPER]]\n\n"
-    "Dump the contents of repository at remote URL to stdout in a 'dumpfile'\n"
-    "portable format.  Dump revisions LOWER rev through UPPER rev.\n"
-    "LOWER defaults to 1 and UPPER defaults to the highest possible revision\n"
-    "if omitted.\n"));
+  const char *header =
+    _("general usage: svnrdump SUBCOMMAND URL [-r LOWER[:UPPER]]\n"
+      "Type 'svnrdump help <subcommand>' for help on a specific subcommand.\n\n"
+      "Available subcommands:\n");
+
+  SVN_ERR(svn_opt_print_help3(os, "svnrdump", FALSE, FALSE, NULL, header,
+                              svnrdump__cmd_table, svnrdump__options, NULL,
+                              NULL, pool));
+
   return SVN_NO_ERROR;
 }
 
 int
 main(int argc, const char **argv)
 {
-  int i;
-  const char *url = NULL;
+  const svn_opt_subcommand_desc2_t *subcommand = NULL;
+  opt_baton_t *opt_baton;
   char *revision_cut = NULL;
-  svn_revnum_t start_revision = svn_opt_revision_unspecified;
-  svn_revnum_t end_revision = svn_opt_revision_unspecified;
-  svn_boolean_t verbose = FALSE;
+  svn_revnum_t latest_revision = svn_opt_revision_unspecified;
   apr_pool_t *pool = NULL;
-  svn_ra_session_t *session = NULL;
+  const char *config_dir = NULL;
+  const char *username = NULL;
+  const char *password = NULL;
+  svn_boolean_t no_auth_cache = FALSE;
+  svn_boolean_t non_interactive = FALSE;
+  apr_getopt_t *os;
+  const char *first_arg;
 
   if (svn_cmdline_init ("svnrdump", stderr) != EXIT_SUCCESS)
     return EXIT_FAILURE;
 
   pool = svn_pool_create(NULL);
+  opt_baton = apr_pcalloc(pool, sizeof(*opt_baton));
+  opt_baton->start_revision = svn_opt_revision_unspecified;
+  opt_baton->end_revision = svn_opt_revision_unspecified;
+  opt_baton->url = NULL;
+
+  SVNRDUMP_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
+
+  os->interleave = TRUE; /* Options and arguments can be interleaved */
+
+  while (1)
+    {
+      int opt;
+      const char *opt_arg;
+      apr_status_t status = apr_getopt_long(os, svnrdump__options, &opt,
+                                            &opt_arg);
+
+      if (APR_STATUS_IS_EOF(status))
+        break;
+      if (status != APR_SUCCESS)
+        {
+          SVNRDUMP_ERR(usage(argv[0], pool));
+          exit(EXIT_FAILURE);
+        }
+
+      switch(opt)
+        {
+        case 'r':
+          {
+            revision_cut = strchr(opt_arg, ':');
+            if (revision_cut)
+              {
+                opt_baton->start_revision = (svn_revnum_t)strtoul(opt_arg,
+                                                                  &revision_cut, 10);
+                opt_baton->end_revision = (svn_revnum_t)strtoul(revision_cut + 1,
+                                                                NULL, 10);
+              }
+            else
+              opt_baton->start_revision = (svn_revnum_t)strtoul(opt_arg, NULL, 10);
+          }
+          break;
+        case 'q':
+          opt_baton->quiet = TRUE;
+          break;
+        case opt_config_dir:
+          config_dir = opt_arg;
+          break;
+        case opt_version:
+          SVNRDUMP_ERR(version(argv[0], pool));
+          exit(EXIT_SUCCESS);
+          break;
+        case 'h':
+          SVNRDUMP_ERR(help_cmd(os, opt_baton, pool));
+          exit(EXIT_SUCCESS);
+          break;
+        case opt_auth_username:
+          SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool));
+          break;
+        case opt_auth_password:
+          SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool));
+          break;
+        case opt_auth_nocache:
+          no_auth_cache = TRUE;
+          break;
+        case opt_non_interactive:
+          non_interactive = TRUE;
+          break;
+        }
+    }
+
+  if (os->ind >= os->argc)
+    {
+      svn_error_clear
+                (svn_cmdline_fprintf(stderr, pool,
+                                     _("Subcommand argument required\n")));
+      SVNRDUMP_ERR(help_cmd(NULL, NULL, pool));
+      svn_pool_destroy(pool);
+      exit(EXIT_FAILURE);
+    }
+
+  first_arg = os->argv[os->ind++];
+
+  subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table,
+                                                 first_arg);
+
+  if (subcommand == NULL)
+    {
+      const char *first_arg_utf8;
+      svn_error_t *err = svn_utf_cstring_to_utf8(&first_arg_utf8,
+                                                 first_arg, pool);
+      if (err)
+        return svn_cmdline_handle_exit_error(err, pool, "svnrdump: ");
+      svn_error_clear(svn_cmdline_fprintf(stderr, pool,
+                                          _("Unknown command: '%s'\n"),
+                                          first_arg_utf8));
+      SVNRDUMP_ERR(help_cmd(NULL, NULL, pool));
+      svn_pool_destroy(pool);
+      exit(EXIT_FAILURE);
+    }
+
+  if (subcommand && strcmp(subcommand->name, "help") == 0)
+    {
+      SVNRDUMP_ERR(help_cmd(os, opt_baton, pool));
+      exit(EXIT_SUCCESS);
+    }
+
+  /* Only continue if the only not option argument is a url */
+  if ((os->ind != os->argc-1)
+      || !svn_path_is_url(os->argv[os->ind]))
+    {
+      SVNRDUMP_ERR(usage(argv[0], pool));
+      exit(EXIT_FAILURE);
+    }
+
+  SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&(opt_baton->url), os->argv[os->ind], pool));
+
+  opt_baton->url = svn_uri_canonicalize(os->argv[os->ind], pool);
+
+  SVNRDUMP_ERR(open_connection(&(opt_baton->session),
+                               opt_baton->url,
+                               non_interactive,
+                               username,
+                               password,
+                               config_dir,
+                               no_auth_cache,
+                               pool));
+
+  /* Have sane opt_baton->start_revision and end_revision defaults if unspecified */
+  SVNRDUMP_ERR(svn_ra_get_latest_revnum(opt_baton->session, &latest_revision, pool));
+  if (opt_baton->start_revision == svn_opt_revision_unspecified)
+    opt_baton->start_revision = 0;
+  if (opt_baton->end_revision == svn_opt_revision_unspecified)
+    opt_baton->end_revision = latest_revision;
+  if (opt_baton->end_revision > latest_revision)
+    {
+      SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
+                                      _("Revision %ld does not exist.\n"),
+                                      opt_baton->end_revision));
+      exit(EXIT_FAILURE);
+    }
+  if (opt_baton->end_revision < opt_baton->start_revision)
+    {
+      SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
+                                      _("LOWER cannot be greater "
+                                        "than UPPER.\n")));
+      exit(EXIT_FAILURE);
+    }
 
-  for (i = 1; i < argc; i++) {
-    if (!strncmp("-r", argv[i], 2)) {
-      revision_cut = strchr(argv[i] + 2, ':');
-      if (revision_cut) {
-        start_revision = (svn_revnum_t) strtoul(argv[i] + 2, &revision_cut, 10);
-        end_revision = (svn_revnum_t) strtoul(revision_cut + 1, NULL, 10);
-      }
-      else
-        start_revision = (svn_revnum_t) strtoul(argv[i] + 2, NULL, 10);
-    } else if (!strcmp("-v", argv[i]) || !strcmp("--verbose", argv[i])) {
-      verbose = TRUE;
-    } else if (!strcmp("help", argv[i]) || !strcmp("--help", argv[i])) {
-      SVN_INT_ERR(usage(stdout));
-      return EXIT_SUCCESS;
-    } else if (*argv[i] == '-' || url) {
-      SVN_INT_ERR(usage(stderr));
-      return EXIT_FAILURE;
-    } else
-      url = argv[i];
-  }
-
-  if (!url || !svn_path_is_url(url)) {
-    usage(stderr);
-    return EXIT_FAILURE;
-  }
-  SVN_INT_ERR(open_connection(session, url, pool));
-
-  /* Have sane start_revision and end_revision defaults if unspecified */
-  if (start_revision == svn_opt_revision_unspecified)
-    start_revision = 1;
-  if (end_revision == svn_opt_revision_unspecified)
-    SVN_INT_ERR(svn_ra_get_latest_revnum(session, &end_revision, pool));
-
-  SVN_INT_ERR(replay_range(session, start_revision, end_revision, pool, verbose));
+  /* Dispatch the subcommand */
+  SVNRDUMP_ERR((*subcommand->cmd_func)(os, opt_baton, pool));
 
   svn_pool_destroy(pool);
 

Propchange: subversion/branches/issue-2779-dev/subversion/svnrdump/svnrdump.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/issue-2779-dev/subversion/tests/cmdline/basic_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/tests/cmdline/basic_tests.py?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/tests/cmdline/basic_tests.py (original)
+++ subversion/branches/issue-2779-dev/subversion/tests/cmdline/basic_tests.py Thu Jul 29 19:38:44 2010
@@ -704,8 +704,11 @@ def basic_cleanup(sbox):
 
   svntest.actions.run_and_verify_status(wc_dir, expected_output)
 
-  # corrupted/non-existing temporary directory should be restored
-  svntest.actions.remove_admin_tmp_dir(B_path)
+  # corrupted/non-existing temporary directory should be restored while
+  # we are not at single-db (where this tmp dir will be gone)
+  tmp_path = os.path.join(B_path, svntest.main.get_admin_name(), 'tmp')
+  if os.path.exists(tmp_path):
+    svntest.main.safe_rmtree(tmp_path)
 
   # Run cleanup (### todo: cleanup doesn't currently print anything)
   svntest.actions.run_and_verify_svn("Cleanup command", None, [],
@@ -1146,9 +1149,9 @@ def basic_delete(sbox):
 
   # check versioned dir is not removed
   if not verify_dir_deleted(F_path):
-    print("Removed versioned dir")
-    ### we should raise a less generic error here. which?
-    raise svntest.Failure
+    # If we are not running in single-db, this is an error
+    if os.path.isdir(os.path.join(F_path, '../' + svntest.main.get_admin_name())):
+      raise svntest.Failure("Removed administrative area")
 
   # check unversioned and added dirs has been removed
   if verify_dir_deleted(Q_path):
@@ -1210,8 +1213,8 @@ def basic_checkout_deleted(sbox):
 
 #----------------------------------------------------------------------
 
-# Issue 846, changing a deleted file to an added directory is not
-# supported.
+# Issue 846, changing a deleted file to an added directory was not
+# supported before WC-NG. But we can handle it.
 
 def basic_node_kind_change(sbox):
   "attempt to change node kind"
@@ -1229,25 +1232,24 @@ def basic_node_kind_change(sbox):
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
   # Try and fail to create a directory (file scheduled for deletion)
-  svntest.actions.run_and_verify_svn('Cannot change node kind',
-                                     None, svntest.verify.AnyOutput,
-                                     'mkdir', gamma_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', gamma_path)
 
-  # Status is unchanged
+  # Status is replaced
+  expected_status.tweak('A/D/gamma', status='R ')
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
   # Commit file deletion
   expected_output = wc.State(wc_dir, {
-    'A/D/gamma' : Item(verb='Deleting'),
+    'A/D/gamma' : Item(verb='Replacing'),
     })
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
-  expected_status.remove('A/D/gamma')
+  expected_status.tweak('A/D/gamma', status='  ', wc_rev='2')
   svntest.actions.run_and_verify_commit(wc_dir,
                                         expected_output, expected_status,
                                         None, wc_dir)
 
   # Try and fail to create a directory (file deleted)
-  svntest.actions.run_and_verify_svn('Cannot change node kind',
+  svntest.actions.run_and_verify_svn(None,
                                      None, svntest.verify.AnyOutput,
                                      'mkdir', gamma_path)
 
@@ -1258,11 +1260,12 @@ def basic_node_kind_change(sbox):
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
   # mkdir should succeed
+  svntest.actions.run_and_verify_svn(None, None, [], 'rm', gamma_path)
   svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', gamma_path)
 
   expected_status.tweak(wc_rev=2)
   expected_status.add({
-    'A/D/gamma' : Item(status='A ', wc_rev=0),
+    'A/D/gamma' : Item(status='R ', wc_rev=2),
     })
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
@@ -2539,7 +2542,7 @@ test_list = [ None,
               basic_switch,
               basic_delete,
               basic_checkout_deleted,
-              basic_node_kind_change,
+              Wimp('Needs single-db', basic_node_kind_change),
               basic_import,
               basic_cat,
               basic_ls,

Modified: subversion/branches/issue-2779-dev/subversion/tests/cmdline/checkout_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/tests/cmdline/checkout_tests.py?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/tests/cmdline/checkout_tests.py (original)
+++ subversion/branches/issue-2779-dev/subversion/tests/cmdline/checkout_tests.py Thu Jul 29 19:38:44 2010
@@ -151,13 +151,88 @@ def make_local_tree(sbox, mod_files=Fals
 #----------------------------------------------------------------------
 
 def checkout_with_obstructions(sbox):
-  """co with obstructions should fail without --force"""
+  """co with obstructions conflicts without --force"""
 
   make_local_tree(sbox, False, False)
 
-  svntest.actions.run_and_verify_svn("No error where some expected",
-                                     None, svntest.verify.AnyOutput,
-                                     "co", sbox.repo_url, sbox.wc_dir)
+  #svntest.factory.make(sbox,
+  #       """# Checkout with unversioned obstructions lying around.
+  #          svn co url wc_dir
+  #          svn status""")
+  #svntest.factory.make(sbox,
+  #       """# Now see to it that we can recover from the obstructions.
+  #          rm -rf A iota
+  #          svn up""")
+  #exit(0)
+
+  wc_dir = sbox.wc_dir
+  url = sbox.repo_url
+
+  # Checkout with unversioned obstructions causes tree conflicts.
+  # svn co url wc_dir
+  expected_output = svntest.wc.State(wc_dir, {
+    'iota'              : Item(status='  ', treeconflict='C'),
+    'A'                 : Item(status='  ', treeconflict='C'),
+  })
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('A/B', 'A/B/E', 'A/B/E/beta', 'A/B/E/alpha', 'A/B/F',
+    'A/B/lambda', 'A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/pi', 'A/D/G/tau',
+    'A/D/H', 'A/D/H/psi', 'A/D/H/omega', 'A/D/H/chi', 'A/D/gamma', 'A/C')
+
+  actions.run_and_verify_checkout2(False, url, wc_dir, expected_output,
+    expected_disk, None, None, None, None)
+
+  # svn status
+  expected_status = actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A', 'iota', status='? ', wc_rev=None,
+    treeconflict='C')
+  expected_status.tweak('A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/pi', 'A/D/G/tau',
+    'A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi', 'A/D/gamma', 'A/B',
+    'A/B/E', 'A/B/E/beta', 'A/B/E/alpha', 'A/B/F', 'A/B/lambda', 'A/C',
+    wc_rev=None)
+  expected_status.tweak('A/mu', status='? ', wc_rev=None)
+
+  actions.run_and_verify_unquiet_status(wc_dir, expected_status)
+
+
+  # Now see to it that we can recover from the obstructions.
+  # rm -rf A iota
+  svntest.main.safe_rmtree( os.path.join(wc_dir, 'A') )
+  os.remove( os.path.join(wc_dir, 'iota') )
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {
+    'A'                 : Item(status='A '),
+    'A/D'               : Item(status='A '),
+    'A/D/gamma'         : Item(status='A '),
+    'A/D/G'             : Item(status='A '),
+    'A/D/G/rho'         : Item(status='A '),
+    'A/D/G/pi'          : Item(status='A '),
+    'A/D/G/tau'         : Item(status='A '),
+    'A/D/H'             : Item(status='A '),
+    'A/D/H/chi'         : Item(status='A '),
+    'A/D/H/omega'       : Item(status='A '),
+    'A/D/H/psi'         : Item(status='A '),
+    'A/B'               : Item(status='A '),
+    'A/B/E'             : Item(status='A '),
+    'A/B/E/beta'        : Item(status='A '),
+    'A/B/E/alpha'       : Item(status='A '),
+    'A/B/F'             : Item(status='A '),
+    'A/B/lambda'        : Item(status='A '),
+    'A/C'               : Item(status='A '),
+    'A/mu'              : Item(status='A '),
+    'iota'              : Item(status='A '),
+  })
+
+  expected_disk = svntest.main.greek_state.copy()
+
+  expected_status = actions.get_virginal_state(wc_dir, 1)
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+
 
 #----------------------------------------------------------------------
 
@@ -209,23 +284,83 @@ def forced_checkout_of_file_with_dir_obs
 #----------------------------------------------------------------------
 
 def forced_checkout_of_dir_with_file_obstructions(sbox):
-  """forced co fails if a file obstructs a dir"""
+  """forced co flags conflict if a file obstructs a dir"""
 
   make_local_tree(sbox, False, False)
 
-  # Make the "other" working copy
-  other_wc = sbox.add_wc_path('other')
-  os.mkdir(other_wc)
-  svntest.main.file_append(os.path.join(other_wc, "A"), "The file A")
+  #svntest.factory.make(sbox,"""
+  #          mkdir wc_dir_other
+  #          echo "The file A" > wc_dir_other/A
+  #          svn co --force url wc_dir_other
+  #          """)
+  #svntest.factory.make(sbox,"""
+  #          # Now see to it that we can recover from the obstructions.
+  #          rm wc_dir_other/A
+  #          svn up wc_dir_other""")
+  #exit(0)
 
-  # Checkout the standard greek repos into a directory that has a file named
-  # "A" obstructing the dir "A" in the repos.  This should fail.
-  exit_code, sout, serr = svntest.actions.run_and_verify_svn(
-    "Expected error during co", None, svntest.verify.AnyOutput,
-    "co", "--force", sbox.repo_url, other_wc)
+  url = sbox.repo_url
+  wc_dir_other = sbox.add_wc_path('other')
+
+  other_A = os.path.join(wc_dir_other, 'A')
+
+  # mkdir wc_dir_other
+  os.makedirs(wc_dir_other)
+
+  # echo "The file A" > wc_dir_other/A
+  svntest.main.file_write(other_A, 'The file A\n')
+
+  # svn co --force url wc_dir_other
+  expected_output = svntest.wc.State(wc_dir_other, {
+    'iota'              : Item(status='A '),
+    'A'                 : Item(status='  ', treeconflict='C'),
+  })
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('A/B', 'A/B/E', 'A/B/E/beta', 'A/B/E/alpha', 'A/B/F',
+    'A/B/lambda', 'A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/pi', 'A/D/G/tau',
+    'A/D/H', 'A/D/H/psi', 'A/D/H/omega', 'A/D/H/chi', 'A/D/gamma', 'A/mu',
+    'A/C')
+  expected_disk.tweak('A', contents='The file A\n')
+
+  actions.run_and_verify_checkout(url, wc_dir_other, expected_output,
+    expected_disk, None, None, None, None, '--force')
+
+
+  # Now see to it that we can recover from the obstructions.
+  # rm wc_dir_other/A
+  os.remove(other_A)
+
+  # svn up wc_dir_other
+  expected_output = svntest.wc.State(wc_dir_other, {
+    'A'                 : Item(status='A '),
+    'A/mu'              : Item(status='A '),
+    'A/D'               : Item(status='A '),
+    'A/D/G'             : Item(status='A '),
+    'A/D/G/tau'         : Item(status='A '),
+    'A/D/G/pi'          : Item(status='A '),
+    'A/D/G/rho'         : Item(status='A '),
+    'A/D/H'             : Item(status='A '),
+    'A/D/H/psi'         : Item(status='A '),
+    'A/D/H/omega'       : Item(status='A '),
+    'A/D/H/chi'         : Item(status='A '),
+    'A/D/gamma'         : Item(status='A '),
+    'A/C'               : Item(status='A '),
+    'A/B'               : Item(status='A '),
+    'A/B/E'             : Item(status='A '),
+    'A/B/E/beta'        : Item(status='A '),
+    'A/B/E/alpha'       : Item(status='A '),
+    'A/B/F'             : Item(status='A '),
+    'A/B/lambda'        : Item(status='A '),
+  })
+
+  expected_disk = svntest.main.greek_state.copy()
+
+  expected_status = actions.get_virginal_state(wc_dir_other, 1)
+
+  actions.run_and_verify_update(wc_dir_other, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir_other)
 
-  test_stderr(".*Failed to add directory.*a non-directory object of the " \
-              "same name already exists", serr)
 
 #----------------------------------------------------------------------
 

Modified: subversion/branches/issue-2779-dev/subversion/tests/cmdline/commit_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/tests/cmdline/commit_tests.py?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/tests/cmdline/commit_tests.py (original)
+++ subversion/branches/issue-2779-dev/subversion/tests/cmdline/commit_tests.py Thu Jul 29 19:38:44 2010
@@ -86,11 +86,15 @@ def make_standard_slew_of_changes(wc_dir
   svntest.main.run_svn(None, 'add', 'Q')
 
   # Remove two directories
-  svntest.main.run_svn(None, 'rm', os.path.join('A', 'B', 'E'))
+  A_B_E = os.path.join('A', 'B', 'E')
+  svntest.main.run_svn(None, 'rm', A_B_E)
   svntest.main.run_svn(None, 'rm', os.path.join('A', 'C'))
 
   # Replace one of the removed directories
-  svntest.main.run_svn(None, 'add', os.path.join('A', 'B', 'E'))
+  # But first recreate if it doesn't exist (single-db)
+  if not os.path.exists(A_B_E):
+    os.mkdir(A_B_E)
+  svntest.main.run_svn(None, 'add', A_B_E)
 
   # Make property mods to two directories
   svntest.main.run_svn(None, 'propset', 'foo', 'bar', os.curdir)
@@ -452,16 +456,21 @@ def nested_dir_replacements(sbox):
   sbox.build()
   wc_dir = sbox.wc_dir
 
+  A_D = os.path.join(wc_dir, 'A', 'D')
+
   # Delete and re-add A/D (a replacement), and A/D/H (another replace).
-  svntest.main.run_svn(None, 'rm', os.path.join(wc_dir, 'A', 'D'))
-  svntest.main.run_svn(None, 'add', '--depth=empty',
-                       os.path.join(wc_dir, 'A', 'D'))
-  svntest.main.run_svn(None, 'add', '--depth=empty',
-                       os.path.join(wc_dir, 'A', 'D', 'H'))
+  svntest.main.run_svn(None, 'rm', A_D)
+
+  # Recreate directories for single-db
+  if not os.path.exists(A_D):
+    os.mkdir(A_D)
+    os.mkdir(os.path.join(A_D, 'H'))
+  svntest.main.run_svn(None, 'add', '--depth=empty', A_D)
+  svntest.main.run_svn(None, 'add', '--depth=empty', os.path.join(A_D, 'H'))
 
   # For kicks, add new file A/D/bloo.
-  svntest.main.file_append(os.path.join(wc_dir, 'A', 'D', 'bloo'), "hi")
-  svntest.main.run_svn(None, 'add', os.path.join(wc_dir, 'A', 'D', 'bloo'))
+  svntest.main.file_append(os.path.join(A_D, 'bloo'), "hi")
+  svntest.main.run_svn(None, 'add', os.path.join(A_D, 'bloo'))
 
   # Verify pre-commit status:
   #
@@ -1497,8 +1506,10 @@ def commit_multiple_wc_multiple_repos(sb
 
   # Commit should fail, since WCs come from different repositories.
   # The exact error message depends on whether or not the tests are
-  # run below a 1.7 working copy
-  error_re = ".*(is not a|Are all targets part of the same) working copy.*"
+  # run below an existing working copy
+  error_re = ( ".*(is not a working copy" +
+                 "|Are all targets part of the same working copy" +
+                 "|was not found).*" )
   svntest.actions.run_and_verify_svn("Expected output on stderr doesn't match",
                                      [], error_re,
                                      'commit', '-m', 'log',
@@ -1946,6 +1957,9 @@ def mods_in_schedule_delete(sbox):
   C_path = os.path.join(wc_dir, 'A', 'C')
   svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
                                      'rm', C_path)
+
+  if not os.path.exists(C_path):
+    os.mkdir(C_path)
   foo_path = os.path.join(C_path, 'foo')
   foo_contents = 'zig\nzag\n'
   svntest.main.file_append(foo_path, foo_contents)
@@ -2659,15 +2673,16 @@ def start_commit_detect_capabilities(sbo
 def commit_url(sbox):
   "'svn commit SOME_URL' should error"
   sbox.build()
-  wc_dir = sbox.wc_dir
-  repos_url = sbox.repo_url
+  url = sbox.repo_url
 
   # Commit directly to a URL
+  expected_error = ("svn: '" + url + 
+                    "' is a URL, but URLs cannot be commit targets")
   svntest.actions.run_and_verify_commit(None,
                                         None,
                                         None,
-                                        "Must give local path",
-                                        repos_url)
+                                        expected_error,
+                                        url)
 
 # Test for issue #3198
 def commit_added_missing(sbox):

Modified: subversion/branches/issue-2779-dev/subversion/tests/cmdline/copy_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/tests/cmdline/copy_tests.py?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/tests/cmdline/copy_tests.py (original)
+++ subversion/branches/issue-2779-dev/subversion/tests/cmdline/copy_tests.py Thu Jul 29 19:38:44 2010
@@ -36,6 +36,7 @@ from svntest.main import SVN_PROP_MERGEI
 Skip = svntest.testcase.Skip
 SkipUnless = svntest.testcase.SkipUnless
 XFail = svntest.testcase.XFail
+Wimp = svntest.testcase.Wimp
 Item = svntest.wc.StateItem
 
 
@@ -2460,16 +2461,18 @@ def move_dir_back_and_forth(sbox):
 
   # Move the moved dir: D_moved back to its starting
   # location at A/D.
-  exit_code, out, err = svntest.actions.run_and_verify_svn(
-    None, None, svntest.verify.AnyOutput,
-    'mv', D_move_path, D_path)
 
-  for line in err:
-    if re.match('.*Cannot copy to .*as it is scheduled for deletion',
-                line, ):
-      return
-  raise svntest.Failure("mv failed but not in the expected way")
+  if svntest.main.wc_is_singledb(wc_dir):
+    # In single-db target is gone on-disk after it was moved away, so this
+    # move works ok
+    expected_err = []
+  else:
+    # In !SINGLE_DB the target of the copy exists on-dir, so svn tries
+    # to move the file below the deleted directory
+    expected_err = '.*Cannot copy to .*as it is scheduled for deletion'
 
+  svntest.actions.run_and_verify_svn(None, None, expected_err,
+                                     'mv', D_move_path, D_path)
 
 def copy_move_added_paths(sbox):
   "copy and move added paths without commits"
@@ -4767,6 +4770,69 @@ def move_added_nodes(sbox):
   expected_status.add({'X/Z' : Item(status='A ', wc_rev='0')})
   svntest.actions.run_and_verify_status(sbox.wc_dir, expected_status)
 
+def locate_wrong_origin(sbox):
+  "update editor locates invalid file source"
+
+  sbox.build()
+
+  iota = os.path.join(sbox.wc_dir, 'iota')
+  gamma = os.path.join(sbox.wc_dir, 'A/D/gamma')
+
+  D1 = os.path.join(sbox.wc_dir, 'D1')
+  D2 = os.path.join(sbox.wc_dir, 'D2')
+
+  main.run_svn(None, 'mkdir', D1, D2)
+  main.run_svn(None, 'cp', iota, os.path.join(D1, 'iota'))
+  main.run_svn(None, 'cp', gamma, os.path.join(D2, 'iota'))
+
+  main.run_svn(None, 'ci', sbox.wc_dir, '-m', 'Add 2*iotas in r2')
+  main.run_svn(None, 'rm', D1)
+
+  main.run_svn(None, 'ci', sbox.wc_dir, '-m', 'Why?')
+
+  main.run_svn(None, 'cp', D2, D1)
+  main.run_svn(None, 'ci', sbox.wc_dir, '-m', 'Replace one iota')
+
+  # <= 1.6 needs a new checkout here to reproduce, but not since r961831.
+  # so we just perform an update
+  main.run_svn(None, 'up', sbox.wc_dir)
+
+  main.run_svn(None, 'cp', sbox.repo_url + '/D1/iota@2',
+               sbox.repo_url + '/iobeta', '-m', 'Copy iota')
+
+
+  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 4)
+  expected_status.add({
+    'D1'                : Item(status='  ', wc_rev='4'),
+    'D1/iota'           : Item(status='  ', wc_rev='4'),
+    'D2'                : Item(status='  ', wc_rev='4'),
+    'D2/iota'           : Item(status='  ', wc_rev='4'),
+  })
+  svntest.actions.run_and_verify_status(sbox.wc_dir, expected_status)
+
+  # The next update receives an add_file('/D1/iota', 2), which it then tries
+  # to locate in the local working copy. It finds a '/D1/iota' in the expected
+  # place, with a last-changed revision of 2 and a local revision of HEAD-1
+  #
+  # locate_copyfrom identifies this file as correct because 
+  #     * last-mod <= 2 and
+  #     * 2 <= REV
+  #
+  # Luckily close_file() receives an expected_checksum which makes us fail, or
+  # we would have a completely broken working copy
+
+  # So this gives a Checksum mismatch error.
+  main.run_svn(None, 'up', sbox.wc_dir)
+
+# This test currently fails, but should work ok once we move to single DB
+def copy_over_deleted_dir(sbox):
+  "copy a directory over a deleted directory"
+  sbox.build(read_only = True)
+
+  main.run_svn(None, 'rm', os.path.join(sbox.wc_dir, 'A/B'))
+  main.run_svn(None, 'cp', os.path.join(sbox.wc_dir, 'A/D'),
+               os.path.join(sbox.wc_dir, 'A/B'))
+
 
 ########################################################################
 # Run the tests
@@ -4842,7 +4908,7 @@ test_list = [ None,
               copy_make_parents_wc_repo,
               copy_make_parents_repo_repo,
               URI_encoded_repos_to_wc,
-              XFail(allow_unversioned_parent_for_copy_src),
+              Wimp('Needs single db', allow_unversioned_parent_for_copy_src),
               replaced_local_source_for_incoming_copy,
               unneeded_parents,
               double_parents_with_url,
@@ -4861,7 +4927,11 @@ test_list = [ None,
               copy_dir_with_space,
               changed_data_should_match_checkout,
               XFail(changed_dir_data_should_match_checkout),
-              XFail(move_added_nodes),
+              Wimp('Needs single db', move_added_nodes),
+              # Serf needs a different testcase for this issue
+              XFail(Skip(locate_wrong_origin,
+                         svntest.main.is_ra_type_dav_serf)),
+              Wimp('Needs single db', copy_over_deleted_dir),
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/issue-2779-dev/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-2779-dev/subversion/tests/cmdline/diff_tests.py?rev=980551&r1=980550&r2=980551&view=diff
==============================================================================
--- subversion/branches/issue-2779-dev/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/branches/issue-2779-dev/subversion/tests/cmdline/diff_tests.py Thu Jul 29 19:38:44 2010
@@ -3367,6 +3367,143 @@ def diff_git_format_url_url(sbox):
                                      '--old', repo_url + '@1', '--new',
                                      repo_url + '@2')
 
+def diff_prop_missing_context(sbox):
+  "diff for property has missing context"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  iota_path = os.path.join(wc_dir, 'iota')
+  prop_val = "".join([
+       "line 1\n",
+       "line 2\n",
+       "line 3\n",
+       "line 4\n",
+       "line 5\n",
+       "line 6\n",
+       "line 7\n",
+     ])
+  svntest.main.run_svn(None,
+                       "propset", "prop", prop_val, iota_path)
+
+  expected_output = svntest.wc.State(wc_dir, {
+      'iota'    : Item(verb='Sending'),
+      })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('iota', wc_rev=2)
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        expected_status, None, wc_dir)
+
+  prop_val = "".join([
+               "line 3\n",
+               "line 4\n",
+               "line 5\n",
+               "line 6\n",
+             ])
+  svntest.main.run_svn(None,
+                       "propset", "prop", prop_val, iota_path)
+  expected_output = [
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 2)\n",
+    "+++ iota\t(working copy)\n",
+    "\n",
+    "Property changes on: iota\n",
+    "___________________________________________________________________\n",
+    "Modified: prop\n",
+    "## -1,8 +1,5 ##\n",
+    "-line 1\n",
+    "-line 2\n",
+    " line 3\n",
+    " line 4\n",
+    " line 5\n",
+    " line 6\n",
+    "-line 7\n",
+  ]
+
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'diff', iota_path)
+
+def diff_prop_multiple_hunks(sbox):
+  "diff for property with multiple hunks"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  iota_path = os.path.join(wc_dir, 'iota')
+  prop_val = "".join([
+       "line 1\n",
+       "line 2\n",
+       "line 3\n",
+       "line 4\n",
+       "line 5\n",
+       "line 6\n",
+       "line 7\n",
+       "line 8\n",
+       "line 9\n",
+       "line 10\n",
+       "line 11\n",
+       "line 12\n",
+       "line 13\n",
+     ])
+  svntest.main.run_svn(None,
+                       "propset", "prop", prop_val, iota_path)
+
+  expected_output = svntest.wc.State(wc_dir, {
+      'iota'    : Item(verb='Sending'),
+      })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('iota', wc_rev=2)
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        expected_status, None, wc_dir)
+
+  prop_val = "".join([
+               "line 1\n",
+               "line 2\n",
+               "line 3\n",
+               "Add a line here\n",
+               "line 4\n",
+               "line 5\n",
+               "line 6\n",
+               "line 7\n",
+               "line 8\n",
+               "line 9\n",
+               "line 10\n",
+               "And add a line here\n",
+               "line 11\n",
+               "line 12\n",
+               "line 13\n",
+             ])
+  svntest.main.run_svn(None,
+                       "propset", "prop", prop_val, iota_path)
+  expected_output = [
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 2)\n",
+    "+++ iota\t(working copy)\n",
+    "\n",
+    "Property changes on: iota\n",
+    "___________________________________________________________________\n",
+    "Modified: prop\n",
+    "## -1,6 +1,7 ##\n",
+    " line 1\n",
+    " line 2\n",
+    " line 3\n",
+    "+Add a line here\n",
+    " line 4\n",
+    " line 5\n",
+    " line 6\n",
+    "## -8,6 +9,7 ##\n",
+    " line 8\n",
+    " line 9\n",
+    " line 10\n",
+    "+And add a line here\n",
+    " line 11\n",
+    " line 12\n",
+    " line 13\n",
+  ]
+
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'diff', iota_path)
+
 ########################################################################
 #Run the tests
 
@@ -3426,6 +3563,8 @@ test_list = [ None,
               diff_git_format_wc_wc,
               diff_git_format_url_wc,
               diff_git_format_url_url,
+              XFail(diff_prop_missing_context),
+              XFail(diff_prop_multiple_hunks),
               ]
 
 if __name__ == '__main__':