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 2012/12/20 23:30:03 UTC

svn commit: r1424736 - /subversion/branches/issue-4116-dev/subversion/svnrdump/dump_editor.c

Author: cmpilato
Date: Thu Dec 20 22:30:03 2012
New Revision: 1424736

URL: http://svn.apache.org/viewvc?rev=1424736&view=rev
Log:
On the 'issue-4116-dev' branch:  Check in a whopper of a change to
svnrdump's dump_editor which, unfortunately, doesn't really work all
the time.

(I'm working on this branch because I've made so many incremental
changes without committing that this is the nasty result.  Now I'm
losing track of what does and doesn't work.  Ugh.)

* subversion/svnrdump/dump_editor.c
  (struct dir_baton): Add some fields: 'props', 'deleted_props',
    'dump_props' and 'dump_newlines'.
  (struct file_baton): Add some fields: 'pool', 'props',
    'deleted_props', 'is_copy', 'copyfrom_path', 'copyfrom_rev',
    action, 'dump_text', and 'dump_props'.
  (struct dump_edit_baton): Remove 'props', 'deleted_props',
    'propstring', 'dump_text', 'dump_props', and 'dump_newlines'.  Add
    'pending_kind' and 'pending_baton'.
  (make_dir_baton): Use stricter type for 'parent_dir_baton' (renaming
    to 'pb').  Also, init the new 'props' and 'deleted_props' fields.
  (make_file_baton): New helper function.
  (do_dump_props): Lose 'dump_data_too' parameter and handling -- just
    let the NULL-ness of the return variable indicate whether data
    should be dumped or return in that variable.
  (dump_node): Lose the 'kind' parameter in favor of directory and
    file baton parameters (both of which may be NULL -- the deletion
    case), but at least one of which must be.  Rework this function in
    light of the new locations of the dump_props flags, etc.
  (dump_pending): Now consult the edit baton's 'pending_baton' and
    'pending_kind' variables to know if something is pending and of
    what kind it is.  This allows the handling of pending file batons
    too, now.
  (open_root): Don't init property hashes and such -- make_dir_baton()
    does that now.  Update calls to dump_node().  When converting an
    open_root() into an add of the top-level directory, stash the dir
    baton in the pending area.
  (deleted_entry): Update call to dump_pending(), and rework a comment
    for clarity.
  (add_directory): Call dump_pending() before making the new baton.
    Update call to dump_node().  Stash the dir baton in the pending
    area.
  (open_directory): Update call to dump_pending().
  (close_directory): Try to figure out what to do in light of the fact
    that the handling of this directory could have been interrupted at
    any point between its open and its closure.  Updates call(s) to
    dump_node().
  (add_file, open_file): Use make_file_baton() to create the file
    baton.  Update call to dump_pending().  Don't actually dump the
    node here -- rather, squirrel away the necessary bits to do so
    later (in close_file()) in the file baton.
  (change_dir_prop): Handle any pending batons, and track changes in
    locations of property hashes, flags, etc.
  (change_file_prop): Track changes in locations of property hashes,
    flags, etc.
  (apply_textdelta): Minor debugging/comment tweaks, and track the new
    location of the 'dump_text' flag.
  (close_file): Actually dump the file node, now that we know all that
    we need to know about it.
  (svn_rdump__get_dump_editor): Initialize the new 'pending_kind'
    baton member to NULL.

Modified:
    subversion/branches/issue-4116-dev/subversion/svnrdump/dump_editor.c

Modified: subversion/branches/issue-4116-dev/subversion/svnrdump/dump_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4116-dev/subversion/svnrdump/dump_editor.c?rev=1424736&r1=1424735&r2=1424736&view=diff
==============================================================================
--- subversion/branches/issue-4116-dev/subversion/svnrdump/dump_editor.c (original)
+++ subversion/branches/issue-4116-dev/subversion/svnrdump/dump_editor.c Thu Dec 20 22:30:03 2012
@@ -50,7 +50,8 @@ struct dir_baton
   struct dump_edit_baton *eb;
   struct dir_baton *parent_dir_baton;
 
-  apr_pool_t *pool; /* Directory pool */
+  /* Pool for per-directory allocations */
+  apr_pool_t *pool;
 
   /* is this directory a new addition to this revision? */
   svn_boolean_t added;
@@ -65,11 +66,21 @@ struct dir_baton
   const char *copyfrom_path; /* a relpath */
   svn_revnum_t copyfrom_rev;
 
+  /* Properties which were modified during change_dir_prop. */
+  apr_hash_t *props;
+
+  /* Properties which were deleted during change_dir_prop. */
+  apr_hash_t *deleted_props;
+
   /* Hash of paths that need to be deleted, though some -might- be
      replaced.  Maps const char * paths to this dir_baton. Note that
      they're full paths, because that's what the editor driver gives
      us, although they're all really within this directory. */
   apr_hash_t *deleted_entries;
+
+  /* Flags to trigger dumping props and record termination newlines. */
+  svn_boolean_t dump_props;
+  svn_boolean_t dump_newlines;
 };
 
 /* A file baton used by all file-related callback functions in the dump
@@ -79,11 +90,32 @@ struct file_baton
   struct dump_edit_baton *eb;
   struct dir_baton *parent_dir_baton;
 
+  /* Pool for per-file allocations */
+  apr_pool_t *pool;
+
   /* the path to this file */
   const char *repos_relpath; /* a relpath */
 
+  /* Properties which were modified during change_file_prop. */
+  apr_hash_t *props;
+
+  /* Properties which were deleted during change_file_prop. */
+  apr_hash_t *deleted_props;
+
   /* The checksum of the file the delta is being applied to */
   const char *base_checksum;
+
+  /* Copy state and source information (if any). */
+  svn_boolean_t is_copy;
+  const char *copyfrom_path;
+  svn_revnum_t copyfrom_rev;
+
+  /* The action associate with this node. */
+  enum svn_node_action action;
+  
+  /* Flags to trigger dumping props and text. */
+  svn_boolean_t dump_text;
+  svn_boolean_t dump_props;
 };
 
 /* A handler baton to be used in window_handler().  */
@@ -112,31 +144,20 @@ struct dump_edit_baton {
   /* Pool for per-revision 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 used for textdelta application along with its
      absolute path; these two variables should be allocated in the
      per-edit-session pool */
   const char *delta_abspath;
   apr_file_t *delta_file;
 
-  /* Flags to trigger dumping props and text */
-  svn_boolean_t dump_text;
-  svn_boolean_t dump_props;
-  svn_boolean_t dump_newlines;
-
   /* The revision we're currently dumping. */
   svn_revnum_t current_revision;
+
+  /* The kind (file or directory) and baton of the item 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;
 };
 
 /* Make a directory baton to represent the directory at PATH (relative
@@ -147,21 +168,20 @@ struct dump_edit_baton {
  * 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.  */
+ * 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.  */
 static struct dir_baton *
 make_dir_baton(const char *path,
                const char *copyfrom_path,
                svn_revnum_t copyfrom_rev,
                void *edit_baton,
-               void *parent_dir_baton,
+               struct dir_baton *pb,
                svn_boolean_t added,
                apr_pool_t *pool)
 {
   struct dump_edit_baton *eb = edit_baton;
-  struct dir_baton *pb = parent_dir_baton;
   struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
   const char *repos_relpath;
 
@@ -186,11 +206,38 @@ make_dir_baton(const char *path,
   new_db->copyfrom_rev = copyfrom_rev;
   new_db->added = added;
   new_db->written_out = FALSE;
+  new_db->props = apr_hash_make(pool);
+  new_db->deleted_props = apr_hash_make(pool);
   new_db->deleted_entries = apr_hash_make(pool);
 
   return new_db;
 }
 
+/* Make a file baton to represent the directory at PATH (relative to
+ * PB->eb).  PB is the directory baton of this directory's parent, or
+ * NULL if this is the top-level directory of the edit.  Perform all
+ * allocations in POOL.  */
+static struct file_baton *
+make_file_baton(const char *path,
+                struct dir_baton *pb,
+                apr_pool_t *pool)
+{
+  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);
+  new_fb->deleted_props = apr_hash_make(pool);
+  new_fb->is_copy = FALSE;
+  new_fb->copyfrom_path = NULL;
+  new_fb->copyfrom_rev = SVN_INVALID_REVNUM;
+  new_fb->action = svn_node_action_change;
+
+  return new_fb;
+}
+
 /* Return in *HEADER and *CONTENT the headers and content for PROPS. */
 static svn_error_t *
 get_props_content(svn_stringbuf_t **header,
@@ -227,9 +274,13 @@ get_props_content(svn_stringbuf_t **head
   return SVN_NO_ERROR;
 }
 
-/* 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.
+/* Extract and dump properties stored in PROPS and property deletions
+ * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to
+ * FALSE.
+ *
+ * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing
+ * the content block of the property changes; otherwise, dump that to
+ * the stream, too.
  */
 static svn_error_t *
 do_dump_props(svn_stringbuf_t **propstring,
@@ -237,7 +288,6 @@ do_dump_props(svn_stringbuf_t **propstri
               apr_hash_t *props,
               apr_hash_t *deleted_props,
               svn_boolean_t *trigger_var,
-              svn_boolean_t dump_data_too,
               apr_pool_t *result_pool,
               apr_pool_t *scratch_pool)
 {
@@ -250,14 +300,14 @@ do_dump_props(svn_stringbuf_t **propstri
 
   SVN_ERR(get_props_content(&header, &content, props, deleted_props,
                             result_pool, scratch_pool));
-
-  /* This is a wacky side-effect of this function. */
-  *propstring = content;
-
   len = header->len;
   SVN_ERR(svn_stream_write(stream, header->data, &len));
 
-  if (dump_data_too)
+  if (propstring)
+    {
+      *propstring = content;
+    }
+  else
     {
       /* Content-length: 14 */
       SVN_ERR(svn_stream_printf(stream, scratch_pool,
@@ -309,7 +359,8 @@ do_dump_newlines(struct dump_edit_baton 
 static svn_error_t *
 dump_node(struct dump_edit_baton *eb,
           const char *repos_relpath,
-          svn_node_kind_t kind,
+          struct dir_baton *db,
+          struct file_baton *fb,
           enum svn_node_action action,
           svn_boolean_t is_copy,
           const char *copyfrom_path,
@@ -320,6 +371,7 @@ dump_node(struct dump_edit_baton *eb,
 
   assert(svn_relpath_is_canonical(repos_relpath));
   assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path));
+  assert(! (db && fb));
 
   /* Add the edit root relpath prefix if necessary. */
   if (eb->update_anchor_relpath)
@@ -332,10 +384,10 @@ dump_node(struct dump_edit_baton *eb,
                             node_relpath));
 
   /* Node-kind: "file" | "dir" */
-  if (kind == svn_node_file)
+  if (fb)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
-  else if (kind == svn_node_dir)
+  else if (db)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
 
@@ -358,9 +410,9 @@ dump_node(struct dump_edit_baton *eb,
         {
           /* Delete the original, and then re-add the replacement as a
              copy using recursive calls into this function. */
-          SVN_ERR(dump_node(eb, repos_relpath, kind, svn_node_action_delete,
+          SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete,
                             FALSE, NULL, SVN_INVALID_REVNUM, pool));
-          SVN_ERR(dump_node(eb, repos_relpath, kind, svn_node_action_add,
+          SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add,
                             is_copy, copyfrom_path, copyfrom_rev, pool));
         }
       else
@@ -372,7 +424,10 @@ dump_node(struct dump_edit_baton *eb,
 
           /* Wait for a change_*_prop to be called before dumping
              anything */
-          eb->dump_props = TRUE;
+          if (fb)
+            fb->dump_props = TRUE;
+          else if (db)
+            db->dump_props = TRUE;
         }
       break;
 
@@ -410,16 +465,16 @@ dump_node(struct dump_edit_baton *eb,
              set DUMP_NEWLINES here to print the newlines unless
              change_dir_prop() is called next otherwise the `svnadmin load`
              parser will fail.  */
-          if (kind == svn_node_dir)
-            eb->dump_newlines = TRUE;
+          if (db)
+            db->dump_newlines = TRUE;
         }
       else
         {
-          /* eb->dump_props (for files) is handled in close_file()
+          /* fb->dump_props (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 (for directories)
+             inside them has been done; db->dump_props (for directories)
              is handled (via dump_pending()) in all the functions that
              can possibly be called after add_directory():
 
@@ -431,7 +486,10 @@ dump_node(struct dump_edit_baton *eb,
                - open_file()
 
              change_dir_prop() is a special case. */
-          eb->dump_props = TRUE;
+          if (fb)
+            fb->dump_props = TRUE;
+          else if (db)
+            db->dump_props = TRUE;
         }
 
       break;
@@ -483,21 +541,45 @@ dump_mkdir(struct dump_edit_baton *eb,
 /* Dump pending items from the specified node, to allow starting the dump
    of a child node */
 static svn_error_t *
-dump_pending(struct dir_baton *pb,
+dump_pending(struct dump_edit_baton *eb,
              apr_pool_t *scratch_pool)
 {
-  /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(&pb->eb->propstring, pb->eb->stream,
-                        pb->eb->props, pb->eb->deleted_props,
-                        &(pb->eb->dump_props), TRUE,
-                        pb->pool, scratch_pool));
+  if (! eb->pending_baton)
+    return SVN_NO_ERROR;
+
+  if (eb->pending_kind == svn_node_dir)
+    {
+      struct dir_baton *db = eb->pending_baton;
+
+      /* Some pending properties to dump? */
+      SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props,
+                            &(db->dump_props), db->pool, scratch_pool));
+      
+      /* Some pending newlines to dump? */
+      SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool));
+    }
+  else if (eb->pending_kind == svn_node_file)
+    {
+      struct file_baton *fb = eb->pending_baton;
+
+      /* Some pending properties to dump? */
+      SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props,
+                            &(fb->dump_props), fb->pool, scratch_pool));
+    }
+  else
+    abort();
 
-  /* Some pending newlines to dump? */
-  SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), scratch_pool));
+  /* Anything that was pending is pending no longer. */
+  eb->pending_baton = NULL;
+  eb->pending_kind = svn_node_none;
 
   return SVN_NO_ERROR;
 }
 
+
+
+/*** Editor Function Implementations ***/
+
 static svn_error_t *
 open_root(void *edit_baton,
           svn_revnum_t base_revision,
@@ -510,10 +592,6 @@ open_root(void *edit_baton,
   /* Clear the per-revision pool after each revision */
   svn_pool_clear(eb->pool);
 
-  eb->props = apr_hash_make(eb->pool);
-  eb->deleted_props = apr_hash_make(eb->pool);
-  eb->propstring = svn_stringbuf_create_empty(eb->pool);
-
   LDR_DBG(("open_root %p\n", *root_baton));
 
   if (eb->update_anchor_relpath)
@@ -549,10 +627,15 @@ open_root(void *edit_baton,
                  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, svn_node_dir,
-                                svn_node_action_add, FALSE, NULL, SVN_INVALID_REVNUM,
-                                pool));
+              SVN_ERR(dump_node(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. */
               new_db->written_out = TRUE;
+              eb->pending_baton = new_db;
+              eb->pending_kind = svn_node_dir;
             }
         }
       svn_pool_destroy(iterpool);
@@ -565,7 +648,6 @@ open_root(void *edit_baton,
     }
 
   *root_baton = new_db;
-    
   return SVN_NO_ERROR;
 }
 
@@ -579,10 +661,12 @@ delete_entry(const char *path,
 
   LDR_DBG(("delete_entry %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(pb->eb, pool));
 
-  /* Add this path to the deleted_entries of the parent directory
-     baton. */
+  /* We don't dump this deletion immediate.  Rather, we add this path
+     to the deleted_entries of the parent directory baton.  That way,
+     we can tell (later) an addition from a replacement.  All the real
+     deletions get handled in close_directory().  */
   apr_hash_set(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path),
                APR_HASH_KEY_STRING, pb);
 
@@ -604,11 +688,11 @@ add_directory(const char *path,
 
   LDR_DBG(("add_directory %s\n", path));
 
+  SVN_ERR(dump_pending(pb->eb, pool));
+
   new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb,
                           pb, TRUE, pb->eb->pool);
 
-  SVN_ERR(dump_pending(pb, pool));
-
   /* This might be a replacement -- is the path already deleted? */
   val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
@@ -616,8 +700,7 @@ add_directory(const char *path,
   is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
   /* Dump the node */
-  SVN_ERR(dump_node(pb->eb, new_db->repos_relpath,
-                    svn_node_dir,
+  SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL,
                     val ? svn_node_action_replace : svn_node_action_add,
                     is_copy,
                     is_copy ? new_db->copyfrom_path : NULL,
@@ -628,7 +711,11 @@ add_directory(const char *path,
     /* Delete the path, it's now been dumped */
     apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
 
+  /* Remember that we've started, but not yet finished handling this
+     directory. */
   new_db->written_out = TRUE;
+  pb->eb->pending_baton = new_db;
+  pb->eb->pending_kind = svn_node_dir;
 
   *child_baton = new_db;
   return SVN_NO_ERROR;
@@ -648,7 +735,7 @@ open_directory(const char *path,
 
   LDR_DBG(("open_directory %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(pb->eb, pool));
 
   /* If the parent directory has explicit comparison path and rev,
      record the same for this one. */
@@ -662,6 +749,7 @@ open_directory(const char *path,
 
   new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
                           FALSE, pb->eb->pool);
+
   *child_baton = new_db;
   return SVN_NO_ERROR;
 }
@@ -672,10 +760,37 @@ close_directory(void *dir_baton,
 {
   struct dir_baton *db = dir_baton;
   apr_hash_index_t *hi;
+  svn_boolean_t this_pending;
 
   LDR_DBG(("close_directory %p\n", dir_baton));
 
-  SVN_ERR(dump_pending(db, pool));
+  /* Remember if this directory is the one currently pending. */
+  this_pending = (db->eb->pending_kind && (db->eb->pending_baton == db));
+
+  SVN_ERR(dump_pending(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
+     that would be the case is if this directory was added/replaced.
+
+     Otherwise, if stuff for this directory has already been written
+     out (at some point in the past, prior to our handling other
+     nodes), we might need to generate a second "change" record just
+     to carry the information we've since learned about the
+     directory. */ 
+  if (! this_pending)
+    {
+      if (db->written_out)
+        {
+          LDR_DBG(("*** directory add -> change %s", db->repos_relpath));
+          SVN_ERR(dump_node(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));
+    }
 
   /* Dump the deleted directory entries */
   for (hi = apr_hash_first(pool, db->deleted_entries); hi;
@@ -683,11 +798,13 @@ close_directory(void *dir_baton,
     {
       const char *path = svn__apr_hash_index_key(hi);
 
-      SVN_ERR(dump_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
+      SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete,
                         FALSE, NULL, SVN_INVALID_REVNUM, pool));
     }
 
+  /* ### should be unnecessary */
   SVN_ERR(svn_hash__clear(db->deleted_entries, pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -700,42 +817,33 @@ add_file(const char *path,
          void **file_baton)
 {
   struct dir_baton *pb = parent_baton;
-  struct file_baton *fb = apr_pcalloc(pool, sizeof(*fb));
+  struct file_baton *fb;
   void *val;
-  svn_boolean_t is_copy;
-
-  fb->eb = pb->eb;
-  fb->parent_dir_baton = pb;
-  fb->repos_relpath = svn_relpath_canonicalize(path, pool);
 
   LDR_DBG(("add_file %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(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 = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
   /* Detect add-with-history. */
-  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
-
-  /* Dump the node. */
-  SVN_ERR(dump_node(pb->eb, fb->repos_relpath,
-                    svn_node_file,
-                    val ? svn_node_action_replace : svn_node_action_add,
-                    is_copy,
-                    is_copy ? svn_relpath_canonicalize(copyfrom_path, pool)
-                            : NULL,
-                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
-                    pool));
+  if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev))
+    {    
+      fb->copyfrom_path = svn_relpath_canonicalize(copyfrom_path, fb->pool);
+      fb->copyfrom_rev = copyfrom_rev;
+      fb->is_copy = TRUE;
+    }
+  fb->action = val ? svn_node_action_replace : svn_node_action_add;
 
+  /* Delete the path, it's now been dumped. */
   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 */
   *file_baton = fb;
-
   return SVN_NO_ERROR;
 }
 
@@ -747,36 +855,26 @@ open_file(const char *path,
           void **file_baton)
 {
   struct dir_baton *pb = parent_baton;
-  struct file_baton *fb = apr_pcalloc(pool, sizeof(*fb));
-  const char *copyfrom_path = NULL;
-  svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
-
-  fb->eb = pb->eb;
-  fb->parent_dir_baton = pb;
-  fb->repos_relpath = svn_relpath_canonicalize(path, pool);
+  struct file_baton *fb;
 
   LDR_DBG(("open_file %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(pb->eb, pool));
+
+  /* Make the file baton. */
+  fb = make_file_baton(path, pb, pool);
 
   /* If the parent directory has explicit copyfrom path and rev,
      record the same for this one. */
   if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
     {
-      copyfrom_path = svn_relpath_join(pb->copyfrom_path,
-                                       svn_relpath_basename(path, NULL),
-                                       pb->eb->pool);
-      copyfrom_rev = pb->copyfrom_rev;
+      fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path,
+                                           svn_relpath_basename(path, NULL),
+                                           pb->eb->pool);
+      fb->copyfrom_rev = pb->copyfrom_rev;
     }
 
-  SVN_ERR(dump_node(pb->eb, fb->repos_relpath, 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 */
   *file_baton = fb;
-
   return SVN_NO_ERROR;
 }
 
@@ -790,34 +888,22 @@ change_dir_prop(void *parent_baton,
 
   LDR_DBG(("change_dir_prop %p\n", parent_baton));
 
+  SVN_ERR(dump_pending(db->eb, pool));
+
   if (svn_property_kind2(name) != svn_prop_regular_kind)
     return SVN_NO_ERROR;
 
   if (value)
-    apr_hash_set(db->eb->props, apr_pstrdup(db->eb->pool, name),
-                 APR_HASH_KEY_STRING, svn_string_dup(value, db->eb->pool));
+    apr_hash_set(db->props, apr_pstrdup(db->pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, db->pool));
   else
-    apr_hash_set(db->eb->deleted_props, apr_pstrdup(db->eb->pool, name),
+    apr_hash_set(db->deleted_props, apr_pstrdup(db->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; do_dump_props() 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->repos_relpath, svn_node_dir,
-                        svn_node_action_change, FALSE, db->copyfrom_path,
-                        db->copyfrom_rev, pool));
-      db->written_out = TRUE;
-    }
-
   /* Make sure we eventually output the props, and disable printing
      a couple of extra newlines */
-  db->eb->dump_newlines = FALSE;
-  db->eb->dump_props = TRUE;
+  db->dump_newlines = FALSE;
+  db->dump_props = TRUE;
 
   return SVN_NO_ERROR;
 }
@@ -829,7 +915,6 @@ change_file_prop(void *file_baton,
                  apr_pool_t *pool)
 {
   struct file_baton *fb = file_baton;
-  struct dump_edit_baton *eb = fb->eb;
 
   LDR_DBG(("change_file_prop %p\n", file_baton));
 
@@ -837,16 +922,16 @@ change_file_prop(void *file_baton,
     return SVN_NO_ERROR;
 
   if (value)
-    apr_hash_set(eb->props, apr_pstrdup(eb->pool, name),
-                 APR_HASH_KEY_STRING, svn_string_dup(value, eb->pool));
+    apr_hash_set(fb->props, apr_pstrdup(fb->pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, fb->pool));
   else
-    apr_hash_set(eb->deleted_props, apr_pstrdup(eb->pool, name),
+    apr_hash_set(fb->deleted_props, apr_pstrdup(fb->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 */
-  eb->dump_props = TRUE;
+  fb->dump_props = TRUE;
 
   return SVN_NO_ERROR;
 }
@@ -875,15 +960,14 @@ apply_textdelta(void *file_baton, const 
 {
   struct file_baton *fb = file_baton;
   struct dump_edit_baton *eb = fb->eb;
-
-  /* Custom handler_baton allocated in a separate pool */
   struct handler_baton *hb;
   svn_stream_t *delta_filestream;
 
-  hb = apr_pcalloc(eb->pool, sizeof(*hb));
-
   LDR_DBG(("apply_textdelta %p\n", file_baton));
 
+  /* This is custom handler_baton, allocated from a separate pool.  */
+  hb = apr_pcalloc(eb->pool, sizeof(*hb));
+
   /* Use a temporary file to measure the Text-content-length */
   delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool);
 
@@ -892,7 +976,8 @@ apply_textdelta(void *file_baton, const 
                           delta_filestream, 0,
                           SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
 
-  eb->dump_text = TRUE;
+  /* Record that there's text to be dumped, and its base checksum. */
+  fb->dump_text = TRUE;
   fb->base_checksum = apr_pstrdup(eb->pool, base_checksum);
 
   /* The actual writing takes place when this function has
@@ -912,17 +997,25 @@ 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;
+  
   LDR_DBG(("close_file %p\n", file_baton));
 
-  /* Some pending properties to dump? Dump just the headers- dump the
-     props only after dumping the text headers too (if present) */
-  SVN_ERR(do_dump_props(&eb->propstring, eb->stream,
-                        eb->props, eb->deleted_props,
-                        &(eb->dump_props), FALSE, pool, pool));
+  SVN_ERR(dump_pending(eb, pool));
+
+  /* Dump the node. */
+  SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb,
+                    fb->action, fb->is_copy, fb->copyfrom_path,
+                    fb->copyfrom_rev, pool));
+
+  /* Some pending properties to dump?  We'll dump just the headers for
+     now, then dump the actual propchange content only after dumping
+     the text headers too (if present). */
+  SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props,
+                        &(fb->dump_props), pool, pool));
 
   /* Dump the text headers */
-  if (eb->dump_text)
+  if (fb->dump_text)
     {
       apr_status_t err;
 
@@ -957,31 +1050,31 @@ close_file(void *file_baton,
 
   /* Content-length: 1549 */
   /* If both text and props are absent, skip this header */
-  if (eb->dump_props)
+  if (fb->dump_props)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_CONTENT_LENGTH
                               ": %ld\n\n",
-                              (unsigned long)info->size + eb->propstring->len));
-  else if (eb->dump_text)
+                              (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 props now */
-  if (eb->dump_props)
+  if (fb->dump_props)
     {
-      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
-                               &(eb->propstring->len)));
+      SVN_ERR(svn_stream_write(eb->stream, propstring->data,
+                               &(propstring->len)));
 
       /* Cleanup */
-      eb->dump_props = FALSE;
-      SVN_ERR(svn_hash__clear(eb->props, eb->pool));
-      SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
+      fb->dump_props = FALSE;
+      SVN_ERR(svn_hash__clear(fb->props, fb->pool));
+      SVN_ERR(svn_hash__clear(fb->deleted_props, fb->pool));
     }
 
   /* Dump the text */
-  if (eb->dump_text)
+  if (fb->dump_text)
     {
       /* Seek to the beginning of the delta file, map it to a stream,
          and copy the stream to eb->stream. Then close the stream and
@@ -998,7 +1091,6 @@ close_file(void *file_baton,
       /* Cleanup */
       SVN_ERR(svn_stream_close(delta_filestream));
       SVN_ERR(svn_io_file_trunc(eb->delta_file, 0, pool));
-      eb->dump_text = FALSE;
     }
 
   /* Write a couple of blank lines for matching output with `svnadmin
@@ -1143,6 +1235,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;
 
   /* Create a special per-revision pool */
   eb->pool = svn_pool_create(pool);