You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by Yoshiki Hayashi <yo...@xemacs.org> on 2001/03/19 05:42:21 UTC

svn_fs_delete_tree bug fix.

Current implementation of svn_fs_delete_tree can't delete a
non-empty mutable directory.  Here's the fix.  Essentialy,
this patch adds new function svn_fs__dag_delete_tree which
can delete a non-empty mutable directory.  Other parts are
just moving bits around.  This patch also adds a new check
to svn_fs_delete_tree and svn_fs_delete to see svn_fs_root_t
is a transaction root.  Unless someone else adds the same
check to functions like svn_fs_change_node_prop, I'll do it.

* svn_error.h (SVN_ERR_FS_NOT_TXN_ROOT): New error.

* dag.c (svn_fs__dag_delete_tree): New function.
* dag.c (delete_node): New function.
* dag.c (svn_fs__dag_delete_mutable_node): Renamed from
  delete_from_id in txn.c.  All callers are changed.
* dag.h (svn_fs__dag_delete_tree): New declaration.
* dag.h (svn_fs__dag_delete_mutable_node): New declaration.
* tree.c (struct delete_args): Remove require_empty_dir arg.
* tree.c (txn_body_delete): Check root object is a transaction
  root.
* tree.c (svn_fs_delete): Remove require_empty_dir arg.
* tree.c (txn_body_delete_tree): New function.
* tree.c (svn_fs_delete_tree):
* txn.c (delete_from_id): Moved to dag.c and renamed to
  svn_fs__dag_delete_mutable_node.

* subversion/tests/libsvn_fs/fs-test.c (delete_tree): New test.

Index: subversion/include/svn_error.h
===================================================================
RCS file: /cvs/subversion/subversion/include/svn_error.h,v
retrieving revision 1.86
diff -u -r1.86 svn_error.h
--- subversion/include/svn_error.h	2001/03/17 07:16:27	1.86
+++ subversion/include/svn_error.h	2001/03/19 05:40:21
@@ -173,6 +173,9 @@
      or create another node named /.  */
   SVN_ERR_FS_ROOT_DIR,
 
+  /* The root object given is not transaction root.  */
+  SVN_ERR_FS_NOT_TXN_ROOT,
+
   /* The transaction could not be committed, because of a conflict with
      a prior change.  */
   SVN_ERR_FS_CONFLICT,
Index: subversion/libsvn_fs/dag.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/dag.c,v
retrieving revision 1.87
diff -u -r1.87 dag.c
--- subversion/libsvn_fs/dag.c	2001/03/16 21:53:12	1.87
+++ subversion/libsvn_fs/dag.c	2001/03/19 05:40:21
@@ -1063,10 +1063,15 @@
 }
 
 
-svn_error_t *
-svn_fs__dag_delete (dag_node_t *parent,
-                    const char *name,
-                    trail_t *trail)
+/* Delete the directory entry named NAME from PARENT, as part of
+   TRAIL.  PARENT must be mutable.  NAME must be a single path
+   component.  If REQUIRE_EMPTY is true and the node being deleted is
+   mutable, it must be empty.  */
+static svn_error_t *
+delete_node (dag_node_t *parent,
+             const char *name,
+             svn_boolean_t require_empty,
+             trail_t *trail)
 {
   skel_t *node_rev, *new_dirent_list, *prev_entry, *entry;
   int deleted = FALSE;
@@ -1156,7 +1161,8 @@
                   SVN_ERR (svn_fs__get_node_revision (&content, parent->fs,
                                                       id, trail));
                   
-                  if (svn_fs__list_length (content->children->next) != 0)
+                  if (svn_fs__list_length (content->children->next) != 0
+                      && require_empty)
                     {
                       return
                         svn_error_createf
@@ -1167,7 +1173,7 @@
                 }
 
               /* Delete the mutable node from the database. */
-              SVN_ERR (svn_fs__delete_node_revision (parent->fs, id, trail));
+              SVN_ERR (svn_fs__dag_delete_mutable_node (parent->fs, id, trail));
             }
 
           /* Just "lose" this entry by setting the *previous* entry's
@@ -1197,6 +1203,62 @@
                                       node_rev,
                                       trail));
 
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_fs__dag_delete (dag_node_t *parent,
+                    const char *name,
+                    trail_t *trail)
+{
+  return delete_node (parent, name, TRUE, trail);
+}
+
+
+svn_error_t *
+svn_fs__dag_delete_tree (dag_node_t *parent,
+                         const char *name,
+                         trail_t *trail)
+{
+  return delete_node (parent, name, FALSE, trail);
+}
+
+
+svn_error_t *
+svn_fs__dag_delete_mutable_node (svn_fs_t *fs,
+                                 svn_fs_id_t *id,
+                                 trail_t *trail)
+{
+  svn_boolean_t is_mutable;
+  dag_node_t *node;
+
+  SVN_ERR (svn_fs__dag_get_node (&node, fs, id, trail));
+
+  /* If immutable, do nothing and return immediately. */
+  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, node, trail));
+  if (! is_mutable)
+    return SVN_NO_ERROR;
+
+  /* Else it's mutable.  Recurse on directories... */
+  if (svn_fs__dag_is_directory (node))
+    {
+      skel_t *entries, *entry;
+      SVN_ERR (svn_fs__dag_dir_entries_skel (&entries, node, trail));
+          
+      for (entry = entries->children; entry; entry = entry->next)
+        {
+          skel_t *id_skel = entry->children->next;
+          svn_fs_id_t *this_id
+            = svn_fs_parse_id (id_skel->data, id_skel->len, trail->pool);
+
+          SVN_ERR (svn_fs__dag_delete_mutable_node (fs, this_id, trail));
+        }
+    }
+
+  /* ... then delete the node itself. */
+  SVN_ERR (svn_fs__delete_node_revision (fs, id, trail));
+  
   return SVN_NO_ERROR;
 }
 
Index: subversion/libsvn_fs/dag.h
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/dag.h,v
retrieving revision 1.40
diff -u -r1.40 dag.h
--- subversion/libsvn_fs/dag.h	2001/03/16 03:38:49	1.40
+++ subversion/libsvn_fs/dag.h	2001/03/19 05:40:22
@@ -310,6 +310,25 @@
                                  trail_t *trail);
 
 
+/* Delete the directory entry named NAME from PARENT, as part of
+   TRAIL.  PARENT must be mutable.  NAME must be a single path
+   component; it cannot be a slash-separated directory path.  If the
+   node being deleted is a mutable directory, remove all reachable
+   mutable nodes from there.  */
+svn_error_t *svn_fs__dag_delete_tree (dag_node_t *parent,
+                                      const char *name,
+                                      trail_t *trail);
+
+
+/* Delete all mutable node revisions reachable from node ID, including
+   ID itself, from FS's `nodes' table, as part of TRAIL.  ID may refer
+   to a file or directory, which may be mutable or immutable.  */
+svn_error_t *
+svn_fs__dag_delete_mutable_node (svn_fs_t *fs,
+                                 svn_fs_id_t *id,
+                                 trail_t *trail);
+
+
 /* Create a new mutable directory named NAME in PARENT, as part of
    TRAIL.  Set *CHILD_P to a reference to the new node, allocated in
    TRAIL->pool.  The new directory has no contents, and no properties.
Index: subversion/libsvn_fs/tree.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/tree.c,v
retrieving revision 1.51
diff -u -r1.51 tree.c
--- subversion/libsvn_fs/tree.c	2001/03/16 20:07:57	1.51
+++ subversion/libsvn_fs/tree.c	2001/03/19 05:40:22
@@ -1760,7 +1760,6 @@
 {
   svn_fs_root_t *root;
   const char *path;
-  svn_boolean_t require_empty_dir;
 };
 
 
@@ -1771,11 +1770,15 @@
   struct delete_args *args = baton;
   svn_fs_root_t *root = args->root;
   const char *path = args->path;
-  svn_boolean_t require_empty_dir = args->require_empty_dir;
   parent_path_t *parent_path;
 
   SVN_ERR (open_path (&parent_path, root, path, 0, trail));
 
+  if (root->kind != transaction_root)
+    return svn_error_create
+      (SVN_ERR_FS_NOT_TXN_ROOT, 0, NULL, trail->pool,
+       "root object must be a transaction root");
+
   /* We can't remove the root of the filesystem.  */
   if (! parent_path->parent)
     return svn_error_create (SVN_ERR_FS_ROOT_DIR, 0, NULL, trail->pool,
@@ -1785,9 +1788,8 @@
   SVN_ERR (make_path_mutable (root, parent_path->parent, path, trail));
 
   /* If this is a directory, we have to check to see if it is required
-     by our caller to be empty before deleting it. */
-  if (svn_fs__dag_is_directory (parent_path->node)
-      && require_empty_dir)
+         by our caller to be empty before deleting it. */
+  if (svn_fs__dag_is_directory (parent_path->node))
     {
       skel_t *entries;
 
@@ -1821,11 +1823,42 @@
 
   args.root = root;
   args.path = path;
-  args.require_empty_dir = TRUE;
   return svn_fs__retry_txn (root->fs, txn_body_delete, &args, pool);
 }
 
 
+static svn_error_t *
+txn_body_delete_tree (void *baton,
+                      trail_t *trail)
+{
+  struct delete_args *args = baton;
+  svn_fs_root_t *root = args->root;
+  const char *path = args->path;
+  parent_path_t *parent_path;
+
+  SVN_ERR (open_path (&parent_path, root, path, 0, trail));
+
+  if (root->kind != transaction_root)
+    return svn_error_create
+      (SVN_ERR_FS_NOT_TXN_ROOT, 0, NULL, trail->pool,
+       "root object must be a transaction root");
+
+  /* We can't remove the root of the filesystem.  */
+  if (! parent_path->parent)
+    return svn_error_create (SVN_ERR_FS_ROOT_DIR, 0, NULL, trail->pool,
+                             "the root directory cannot be deleted");
+
+  /* Make the parent directory mutable.  */
+  SVN_ERR (make_path_mutable (root, parent_path->parent, path, trail));
+
+  SVN_ERR (svn_fs__dag_delete_tree (parent_path->parent->node,
+                                    parent_path->entry,
+                                    trail));
+
+  return SVN_NO_ERROR;
+}
+
+
 svn_error_t *
 svn_fs_delete_tree (svn_fs_root_t *root,
                     const char *path,
@@ -1835,8 +1868,7 @@
 
   args.root = root;
   args.path = path;
-  args.require_empty_dir = FALSE;
-  return svn_fs__retry_txn (root->fs, txn_body_delete, &args, pool);
+  return svn_fs__retry_txn (root->fs, txn_body_delete_tree, &args, pool);
 }
 
 
Index: subversion/libsvn_fs/txn.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/txn.c,v
retrieving revision 1.37
diff -u -r1.37 txn.c
--- subversion/libsvn_fs/txn.c	2001/03/14 00:21:28	1.37
+++ subversion/libsvn_fs/txn.c	2001/03/19 05:40:22
@@ -165,46 +165,7 @@
 };
 
 
-/* Delete all mutable node revisions reachable from node ID, including
-   ID itself, from filesystem FS, as part of TRAIL.  ID may refer to
-   a file or directory, which may be mutable or immutable.  */
 static svn_error_t *
-delete_from_id (svn_fs_t *fs, svn_fs_id_t *id, trail_t *trail)
-{
-  svn_boolean_t is_mutable;
-  dag_node_t *node;
-
-  SVN_ERR (svn_fs__dag_get_node (&node, fs, id, trail));
-
-  /* If immutable, do nothing and return immediately. */
-  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, node, trail));
-  if (! is_mutable)
-    return SVN_NO_ERROR;
-
-  /* Else it's mutable.  Recurse on directories... */
-  if (svn_fs__dag_is_directory (node))
-    {
-      skel_t *entries, *entry;
-      SVN_ERR (svn_fs__dag_dir_entries_skel (&entries, node, trail));
-          
-      for (entry = entries->children; entry; entry = entry->next)
-        {
-          skel_t *id_skel = entry->children->next;
-          svn_fs_id_t *this_id
-            = svn_fs_parse_id (id_skel->data, id_skel->len, trail->pool);
-
-          SVN_ERR (delete_from_id (fs, this_id, trail));
-        }
-    }
-
-  /* ... then delete the node itself. */
-  SVN_ERR (svn_fs__delete_node_revision (fs, id, trail));
-  
-  return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
 txn_body_abort_txn (void *baton, trail_t *trail)
 {
   struct abort_txn_args *args = baton;
@@ -214,7 +175,7 @@
 
   SVN_ERR (svn_fs_txn_name (&txn_name, txn, txn->pool));
   SVN_ERR (svn_fs__get_txn (&root_id, &ignored_id, txn->fs, txn_name, trail));
-  SVN_ERR (delete_from_id (txn->fs, root_id, trail));
+  SVN_ERR (svn_fs__dag_delete_mutable_node (txn->fs, root_id, trail));
   SVN_ERR (svn_fs__delete_txn (txn->fs, txn->id, trail));
 
   return SVN_NO_ERROR;
Index: subversion/tests/libsvn_fs/fs-test.c
===================================================================
RCS file: /cvs/subversion/subversion/tests/libsvn_fs/fs-test.c,v
retrieving revision 1.57
diff -u -r1.57 fs-test.c
--- subversion/tests/libsvn_fs/fs-test.c	2001/03/16 20:08:02	1.57
+++ subversion/tests/libsvn_fs/fs-test.c	2001/03/19 05:40:22
@@ -3026,6 +3026,408 @@
   }
   /* Close the filesystem. */
   SVN_ERR (svn_fs_close_fs (fs));
+  return SVN_NO_ERROR;
+}
+
+
+/* Test svn_fs_delete_tree.
+
+   NOTE: This function tests internal filesystem interfaces, not just
+   the public filesystem interface.  */
+static svn_error_t *
+delete_tree (const char **msg)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root;
+  svn_revnum_t new_rev;
+
+  *msg = "delete nodes tree";
+
+  /* This function tests 5 cases:
+   *
+   * 1. Delete mutable file.
+   * 2. Delete mutable directory.
+   * 3. Delete mutable directory with immutable nodes.
+   * 4. Delete immutable file.
+   * 5. Delete immutable directory.
+   */
+
+  /* Prepare a txn to receive the greek tree. */
+  SVN_ERR (create_fs_and_repos (&fs, "test-repo-del-tree"));
+  SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, pool));
+  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
+
+  /* Create the greek tree. */
+  SVN_ERR (greek_tree_under_root (txn_root));
+
+  /* 1 */
+  {
+    svn_fs_id_t *iota_id;
+    tree_test_entry_t expected_entries[] = {
+      /* path, is_dir, contents */
+      { "A",           1, "" },
+      { "A/mu",        0, "This is the file 'mu'.\n" },
+      { "A/B",         1, "" },
+      { "A/B/lambda",  0, "This is the file 'lambda'.\n" },
+      { "A/B/E",       1, "" },
+      { "A/B/E/alpha", 0, "This is the file 'alpha'.\n" },
+      { "A/B/E/beta",  0, "This is the file 'beta'.\n" },
+      { "A/B/F",       1, "" },
+      { "A/C",         1, "" },
+      { "A/D",         1, "" },
+      { "A/D/gamma",   0, "This is the file 'gamma'.\n" },
+      { "A/D/G",       1, "" },
+      { "A/D/G/pi",    0, "This is the file 'pi'.\n" },
+      { "A/D/G/rho",   0, "This is the file 'rho'.\n" },
+      { "A/D/G/tau",   0, "This is the file 'tau'.\n" },
+      { "A/D/H",       1, "" },
+      { "A/D/H/chi",   0, "This is the file 'chi'.\n" },
+      { "A/D/H/psi",   0, "This is the file 'psi'.\n" },
+      { "A/D/H/omega", 0, "This is the file 'omega'.\n" }
+    };
+    /* Check nodes revision ID is gone.  */
+    SVN_ERR (svn_fs_node_id (&iota_id, txn_root, "iota", pool));
+    SVN_ERR (check_entry_present (txn_root, "", "iota"));
+    SVN_ERR (check_id_present (fs, iota_id));
+
+    SVN_ERR (svn_fs_delete_tree (txn_root, "iota", pool));
+    SVN_ERR (check_entry_absent (txn_root, "", "iota"));
+    SVN_ERR (check_id_absent (fs, iota_id));
+
+    /* Validate the tree.  */
+    SVN_ERR (validate_tree (txn_root, expected_entries, 19));
+  }
+  /* Abort transaction.  */
+  SVN_ERR (svn_fs_abort_txn (txn));
+
+  /* Prepare a txn to receive the greek tree. */
+  SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, pool));
+  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
+
+  /* Create the greek tree. */
+  SVN_ERR (greek_tree_under_root (txn_root));
+
+  /* 2 */
+  {
+    svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
+      *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
+      *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
+
+    tree_test_entry_t expected_entries[] = {
+      /* path, is_dir, contents */
+      { "iota",        0, "This is the file 'iota'.\n" }
+    };
+
+    /* Check nodes revision ID is gone.  */
+    SVN_ERR (svn_fs_node_id (&A_id, txn_root, "/A", pool));
+    SVN_ERR (check_entry_present (txn_root, "", "A"));
+    SVN_ERR (svn_fs_node_id (&mu_id, txn_root, "/A/mu", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "mu"));
+    SVN_ERR (svn_fs_node_id (&B_id, txn_root, "/A/B", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "B"));
+    SVN_ERR (svn_fs_node_id (&lambda_id, txn_root, "/A/B/lambda", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "lambda"));
+    SVN_ERR (svn_fs_node_id (&E_id, txn_root, "/A/B/E", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "E"));
+    SVN_ERR (svn_fs_node_id (&alpha_id, txn_root, "/A/B/E/alpha", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B/E", "alpha"));
+    SVN_ERR (svn_fs_node_id (&beta_id, txn_root, "/A/B/E/beta", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B/E", "beta"));
+    SVN_ERR (svn_fs_node_id (&F_id, txn_root, "/A/B/F", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "F"));
+    SVN_ERR (svn_fs_node_id (&C_id, txn_root, "/A/C", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "C"));
+    SVN_ERR (svn_fs_node_id (&D_id, txn_root, "/A/D", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "D"));
+    SVN_ERR (svn_fs_node_id (&gamma_id, txn_root, "/A/D/gamma", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "gamma"));
+    SVN_ERR (svn_fs_node_id (&H_id, txn_root, "/A/D/H", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "H"));
+    SVN_ERR (svn_fs_node_id (&chi_id, txn_root, "/A/D/H/chi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "chi"));
+    SVN_ERR (svn_fs_node_id (&psi_id, txn_root, "/A/D/H/psi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "psi"));
+    SVN_ERR (svn_fs_node_id (&omega_id, txn_root, "/A/D/H/omega", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "omega"));
+    SVN_ERR (svn_fs_node_id (&G_id, txn_root, "/A/D/G", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "G"));
+    SVN_ERR (svn_fs_node_id (&pi_id, txn_root, "/A/D/G/pi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "pi"));
+    SVN_ERR (svn_fs_node_id (&rho_id, txn_root, "/A/D/G/rho", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "rho"));
+    SVN_ERR (svn_fs_node_id (&tau_id, txn_root, "/A/D/G/tau", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "tau"));
+
+    SVN_ERR (svn_fs_delete_tree (txn_root, "A", pool));
+
+    SVN_ERR (check_entry_absent (txn_root, "", "A"));
+    SVN_ERR (check_id_absent (fs, A_id));
+    SVN_ERR (check_id_absent (fs, mu_id));
+    SVN_ERR (check_id_absent (fs, B_id));
+    SVN_ERR (check_id_absent (fs, lambda_id));
+    SVN_ERR (check_id_absent (fs, E_id));
+    SVN_ERR (check_id_absent (fs, alpha_id));
+    SVN_ERR (check_id_absent (fs, beta_id));
+    SVN_ERR (check_id_absent (fs, F_id));
+    SVN_ERR (check_id_absent (fs, C_id));
+    SVN_ERR (check_id_absent (fs, D_id));
+    SVN_ERR (check_id_absent (fs, gamma_id));
+    SVN_ERR (check_id_absent (fs, H_id));
+    SVN_ERR (check_id_absent (fs, chi_id));
+    SVN_ERR (check_id_absent (fs, psi_id));
+    SVN_ERR (check_id_absent (fs, omega_id));
+    SVN_ERR (check_id_absent (fs, G_id));
+    SVN_ERR (check_id_absent (fs, pi_id));
+    SVN_ERR (check_id_absent (fs, rho_id));
+    SVN_ERR (check_id_absent (fs, tau_id));
+
+    /* Validate the tree.  */
+    SVN_ERR (validate_tree (txn_root, expected_entries, 1));
+  }
+
+  /* Abort transaction.  */
+  SVN_ERR (svn_fs_abort_txn (txn));
+
+  /* Prepare a txn to receive the greek tree. */
+  SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, pool));
+  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
+
+  /* Create the greek tree. */
+  SVN_ERR (greek_tree_under_root (txn_root));
+
+  /* Commit the greek tree. */
+  {
+    const char *conflict;
+    SVN_ERR (svn_fs_commit_txn (&conflict, &new_rev, txn));
+  }
+  SVN_ERR (svn_fs_close_txn (txn));
+
+  /* Create new transaction. */
+  SVN_ERR (svn_fs_begin_txn (&txn, fs, new_rev, pool));
+  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
+
+  /* 3 */
+  {
+    svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
+      *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
+      *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id, *sigma_id;
+
+    tree_test_entry_t expected_entries[] = {
+      /* path, is_dir, contents */
+      { "iota",        0, "This is the file 'iota'.\n" }
+    };
+
+    /* Create A/D/G/sigma.  This makes all component of A/D/G
+       mutable.  */
+    SVN_ERR (svn_fs_make_file (txn_root, "A/D/G/sigma", pool));
+    SVN_ERR (set_file_contents (txn_root, "A/D/G/sigma",
+                                "This is another file 'sigma'.\n"));
+
+    /* Check mutable nodes revision ID is removed and immutable ones
+       still exist.  */
+    SVN_ERR (svn_fs_node_id (&A_id, txn_root, "/A", pool));
+    SVN_ERR (check_entry_present (txn_root, "", "A"));
+    SVN_ERR (svn_fs_node_id (&mu_id, txn_root, "/A/mu", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "mu"));
+    SVN_ERR (svn_fs_node_id (&B_id, txn_root, "/A/B", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "B"));
+    SVN_ERR (svn_fs_node_id (&lambda_id, txn_root, "/A/B/lambda", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "lambda"));
+    SVN_ERR (svn_fs_node_id (&E_id, txn_root, "/A/B/E", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "E"));
+    SVN_ERR (svn_fs_node_id (&alpha_id, txn_root, "/A/B/E/alpha", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B/E", "alpha"));
+    SVN_ERR (svn_fs_node_id (&beta_id, txn_root, "/A/B/E/beta", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B/E", "beta"));
+    SVN_ERR (svn_fs_node_id (&F_id, txn_root, "/A/B/F", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "F"));
+    SVN_ERR (svn_fs_node_id (&C_id, txn_root, "/A/C", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "C"));
+    SVN_ERR (svn_fs_node_id (&D_id, txn_root, "/A/D", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "D"));
+    SVN_ERR (svn_fs_node_id (&gamma_id, txn_root, "/A/D/gamma", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "gamma"));
+    SVN_ERR (svn_fs_node_id (&H_id, txn_root, "/A/D/H", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "H"));
+    SVN_ERR (svn_fs_node_id (&chi_id, txn_root, "/A/D/H/chi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "chi"));
+    SVN_ERR (svn_fs_node_id (&psi_id, txn_root, "/A/D/H/psi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "psi"));
+    SVN_ERR (svn_fs_node_id (&omega_id, txn_root, "/A/D/H/omega", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "omega"));
+    SVN_ERR (svn_fs_node_id (&G_id, txn_root, "/A/D/G", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "G"));
+    SVN_ERR (svn_fs_node_id (&pi_id, txn_root, "/A/D/G/pi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "pi"));
+    SVN_ERR (svn_fs_node_id (&rho_id, txn_root, "/A/D/G/rho", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "rho"));
+    SVN_ERR (svn_fs_node_id (&tau_id, txn_root, "/A/D/G/tau", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "tau"));
+    SVN_ERR (svn_fs_node_id (&sigma_id, txn_root, "/A/D/G/sigma", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "sigma"));
+
+    SVN_ERR (svn_fs_delete_tree (txn_root, "A", pool));
+
+    SVN_ERR (check_entry_absent (txn_root, "", "A"));
+    SVN_ERR (check_id_absent (fs, A_id));
+    SVN_ERR (check_id_present (fs, mu_id));
+    SVN_ERR (check_id_present (fs, B_id));
+    SVN_ERR (check_id_present (fs, lambda_id));
+    SVN_ERR (check_id_present (fs, E_id));
+    SVN_ERR (check_id_present (fs, alpha_id));
+    SVN_ERR (check_id_present (fs, beta_id));
+    SVN_ERR (check_id_present (fs, F_id));
+    SVN_ERR (check_id_present (fs, C_id));
+    SVN_ERR (check_id_absent (fs, D_id));
+    SVN_ERR (check_id_present (fs, gamma_id));
+    SVN_ERR (check_id_present (fs, H_id));
+    SVN_ERR (check_id_present (fs, chi_id));
+    SVN_ERR (check_id_present (fs, psi_id));
+    SVN_ERR (check_id_present (fs, omega_id));
+    SVN_ERR (check_id_absent (fs, G_id));
+    SVN_ERR (check_id_present (fs, pi_id));
+    SVN_ERR (check_id_present (fs, rho_id));
+    SVN_ERR (check_id_present (fs, tau_id));
+    SVN_ERR (check_id_absent (fs, sigma_id));
+
+    /* Validate the tree.  */
+    SVN_ERR (validate_tree (txn_root, expected_entries, 1));
+  }
+
+  /* Abort transaction.  */
+  SVN_ERR (svn_fs_abort_txn (txn));
+
+  /* Create new transaction. */
+  SVN_ERR (svn_fs_begin_txn (&txn, fs, new_rev, pool));
+  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
+
+  /* 4 */
+  {
+    svn_fs_id_t *iota_id;
+    tree_test_entry_t expected_entries[] = {
+      /* path, is_dir, contents */
+      { "A",           1, "" },
+      { "A/mu",        0, "This is the file 'mu'.\n" },
+      { "A/B",         1, "" },
+      { "A/B/lambda",  0, "This is the file 'lambda'.\n" },
+      { "A/B/E",       1, "" },
+      { "A/B/E/alpha", 0, "This is the file 'alpha'.\n" },
+      { "A/B/E/beta",  0, "This is the file 'beta'.\n" },
+      { "A/B/F",       1, "" },
+      { "A/C",         1, "" },
+      { "A/D",         1, "" },
+      { "A/D/gamma",   0, "This is the file 'gamma'.\n" },
+      { "A/D/G",       1, "" },
+      { "A/D/G/pi",    0, "This is the file 'pi'.\n" },
+      { "A/D/G/rho",   0, "This is the file 'rho'.\n" },
+      { "A/D/G/tau",   0, "This is the file 'tau'.\n" },
+      { "A/D/H",       1, "" },
+      { "A/D/H/chi",   0, "This is the file 'chi'.\n" },
+      { "A/D/H/psi",   0, "This is the file 'psi'.\n" },
+      { "A/D/H/omega", 0, "This is the file 'omega'.\n" }
+    };
+    /* Check nodes revision ID is present.  */
+    SVN_ERR (svn_fs_node_id (&iota_id, txn_root, "iota", pool));
+    SVN_ERR (check_entry_present (txn_root, "", "iota"));
+    SVN_ERR (check_id_present (fs, iota_id));
+
+    SVN_ERR (svn_fs_delete_tree (txn_root, "iota", pool));
+    SVN_ERR (check_entry_absent (txn_root, "", "iota"));
+    SVN_ERR (check_id_present (fs, iota_id));
+
+    /* Validate the tree.  */
+    SVN_ERR (validate_tree (txn_root, expected_entries, 19));
+  }
+
+  /* Abort transaction.  */
+  SVN_ERR (svn_fs_abort_txn (txn));
+
+  /* Create new transaction. */
+  SVN_ERR (svn_fs_begin_txn (&txn, fs, new_rev, pool));
+  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
+
+  /* 5 */
+  {
+    svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
+      *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
+      *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
+
+    tree_test_entry_t expected_entries[] = {
+      /* path, is_dir, contents */
+      { "iota",        0, "This is the file 'iota'.\n" }
+    };
+
+    /* Check nodes revision ID is present.  */
+    SVN_ERR (svn_fs_node_id (&A_id, txn_root, "/A", pool));
+    SVN_ERR (check_entry_present (txn_root, "", "A"));
+    SVN_ERR (svn_fs_node_id (&mu_id, txn_root, "/A/mu", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "mu"));
+    SVN_ERR (svn_fs_node_id (&B_id, txn_root, "/A/B", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "B"));
+    SVN_ERR (svn_fs_node_id (&lambda_id, txn_root, "/A/B/lambda", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "lambda"));
+    SVN_ERR (svn_fs_node_id (&E_id, txn_root, "/A/B/E", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "E"));
+    SVN_ERR (svn_fs_node_id (&alpha_id, txn_root, "/A/B/E/alpha", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B/E", "alpha"));
+    SVN_ERR (svn_fs_node_id (&beta_id, txn_root, "/A/B/E/beta", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B/E", "beta"));
+    SVN_ERR (svn_fs_node_id (&F_id, txn_root, "/A/B/F", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/B", "F"));
+    SVN_ERR (svn_fs_node_id (&C_id, txn_root, "/A/C", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "C"));
+    SVN_ERR (svn_fs_node_id (&D_id, txn_root, "/A/D", pool));
+    SVN_ERR (check_entry_present (txn_root, "A", "D"));
+    SVN_ERR (svn_fs_node_id (&gamma_id, txn_root, "/A/D/gamma", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "gamma"));
+    SVN_ERR (svn_fs_node_id (&H_id, txn_root, "/A/D/H", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "H"));
+    SVN_ERR (svn_fs_node_id (&chi_id, txn_root, "/A/D/H/chi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "chi"));
+    SVN_ERR (svn_fs_node_id (&psi_id, txn_root, "/A/D/H/psi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "psi"));
+    SVN_ERR (svn_fs_node_id (&omega_id, txn_root, "/A/D/H/omega", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/H", "omega"));
+    SVN_ERR (svn_fs_node_id (&G_id, txn_root, "/A/D/G", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D", "G"));
+    SVN_ERR (svn_fs_node_id (&pi_id, txn_root, "/A/D/G/pi", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "pi"));
+    SVN_ERR (svn_fs_node_id (&rho_id, txn_root, "/A/D/G/rho", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "rho"));
+    SVN_ERR (svn_fs_node_id (&tau_id, txn_root, "/A/D/G/tau", pool));
+    SVN_ERR (check_entry_present (txn_root, "A/D/G", "tau"));
+
+    SVN_ERR (svn_fs_delete_tree (txn_root, "A", pool));
+
+    SVN_ERR (check_entry_absent (txn_root, "", "A"));
+    SVN_ERR (check_id_present (fs, A_id));
+    SVN_ERR (check_id_present (fs, mu_id));
+    SVN_ERR (check_id_present (fs, B_id));
+    SVN_ERR (check_id_present (fs, lambda_id));
+    SVN_ERR (check_id_present (fs, E_id));
+    SVN_ERR (check_id_present (fs, alpha_id));
+    SVN_ERR (check_id_present (fs, beta_id));
+    SVN_ERR (check_id_present (fs, F_id));
+    SVN_ERR (check_id_present (fs, C_id));
+    SVN_ERR (check_id_present (fs, D_id));
+    SVN_ERR (check_id_present (fs, gamma_id));
+    SVN_ERR (check_id_present (fs, H_id));
+    SVN_ERR (check_id_present (fs, chi_id));
+    SVN_ERR (check_id_present (fs, psi_id));
+    SVN_ERR (check_id_present (fs, omega_id));
+    SVN_ERR (check_id_present (fs, G_id));
+    SVN_ERR (check_id_present (fs, pi_id));
+    SVN_ERR (check_id_present (fs, rho_id));
+    SVN_ERR (check_id_present (fs, tau_id));
+
+    /* Validate the tree.  */
+    SVN_ERR (validate_tree (txn_root, expected_entries, 1));
+  }
+
+  /* Close the transaction and fs. */
+  SVN_ERR (svn_fs_close_txn (txn));
+  SVN_ERR (svn_fs_close_fs (fs));
 
   return SVN_NO_ERROR;
 }
@@ -3058,6 +3460,7 @@
   basic_commit,
   copy_test,
   merging_commit,
+  delete_tree,
   0
 };
 


-- 
Yoshiki Hayashi

Re: svn_fs_delete_tree bug fix.

Posted by Yoshiki Hayashi <yo...@xemacs.org>.
Karl Fogel <kf...@galois.ch.collab.net> writes:

> > Unless someone else adds the same
> > check to functions like svn_fs_change_node_prop, I'll do it.
> 
> Good idea.  Do you have the patch already?  If not, I (or someone) can
> just add them, let us know.

No, I don't.  Please feel free to add them yourself.

-- 
Yoshiki Hayashi

Re: svn_fs_delete_tree bug fix.

Posted by Karl Fogel <kf...@galois.collab.net>.
Yoshiki Hayashi <yo...@xemacs.org> writes:
> Current implementation of svn_fs_delete_tree can't delete a
> non-empty mutable directory.  Here's the fix.  Essentialy,
> this patch adds new function svn_fs__dag_delete_tree which
> can delete a non-empty mutable directory.  Other parts are
> just moving bits around.  This patch also adds a new check
> to svn_fs_delete_tree and svn_fs_delete to see svn_fs_root_t
> is a transaction root.  

Thanks for finding this!  I've checked in a fix which is basically
your patch, with some more code factorization, plus more test code to
test expected failures as well as expected successes.

> Unless someone else adds the same
> check to functions like svn_fs_change_node_prop, I'll do it.

Good idea.  Do you have the patch already?  If not, I (or someone) can
just add them, let us know.

Thanks,
-K

> * svn_error.h (SVN_ERR_FS_NOT_TXN_ROOT): New error.
> 
> * dag.c (svn_fs__dag_delete_tree): New function.
> * dag.c (delete_node): New function.
> * dag.c (svn_fs__dag_delete_mutable_node): Renamed from
>   delete_from_id in txn.c.  All callers are changed.
> * dag.h (svn_fs__dag_delete_tree): New declaration.
> * dag.h (svn_fs__dag_delete_mutable_node): New declaration.
> * tree.c (struct delete_args): Remove require_empty_dir arg.
> * tree.c (txn_body_delete): Check root object is a transaction
>   root.
> * tree.c (svn_fs_delete): Remove require_empty_dir arg.
> * tree.c (txn_body_delete_tree): New function.
> * tree.c (svn_fs_delete_tree):
> * txn.c (delete_from_id): Moved to dag.c and renamed to
>   svn_fs__dag_delete_mutable_node.
> 
> * subversion/tests/libsvn_fs/fs-test.c (delete_tree): New test.
> 
> Index: subversion/include/svn_error.h
> ===================================================================
> RCS file: /cvs/subversion/subversion/include/svn_error.h,v
> retrieving revision 1.86
> diff -u -r1.86 svn_error.h
> --- subversion/include/svn_error.h	2001/03/17 07:16:27	1.86
> +++ subversion/include/svn_error.h	2001/03/19 05:40:21
> @@ -173,6 +173,9 @@
>       or create another node named /.  */
>    SVN_ERR_FS_ROOT_DIR,
>  
> +  /* The root object given is not transaction root.  */
> +  SVN_ERR_FS_NOT_TXN_ROOT,
> +
>    /* The transaction could not be committed, because of a conflict with
>       a prior change.  */
>    SVN_ERR_FS_CONFLICT,
> Index: subversion/libsvn_fs/dag.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/dag.c,v
> retrieving revision 1.87
> diff -u -r1.87 dag.c
> --- subversion/libsvn_fs/dag.c	2001/03/16 21:53:12	1.87
> +++ subversion/libsvn_fs/dag.c	2001/03/19 05:40:21
> @@ -1063,10 +1063,15 @@
>  }
>  
>  
> -svn_error_t *
> -svn_fs__dag_delete (dag_node_t *parent,
> -                    const char *name,
> -                    trail_t *trail)
> +/* Delete the directory entry named NAME from PARENT, as part of
> +   TRAIL.  PARENT must be mutable.  NAME must be a single path
> +   component.  If REQUIRE_EMPTY is true and the node being deleted is
> +   mutable, it must be empty.  */
> +static svn_error_t *
> +delete_node (dag_node_t *parent,
> +             const char *name,
> +             svn_boolean_t require_empty,
> +             trail_t *trail)
>  {
>    skel_t *node_rev, *new_dirent_list, *prev_entry, *entry;
>    int deleted = FALSE;
> @@ -1156,7 +1161,8 @@
>                    SVN_ERR (svn_fs__get_node_revision (&content, parent->fs,
>                                                        id, trail));
>                    
> -                  if (svn_fs__list_length (content->children->next) != 0)
> +                  if (svn_fs__list_length (content->children->next) != 0
> +                      && require_empty)
>                      {
>                        return
>                          svn_error_createf
> @@ -1167,7 +1173,7 @@
>                  }
>  
>                /* Delete the mutable node from the database. */
> -              SVN_ERR (svn_fs__delete_node_revision (parent->fs, id, trail));
> +              SVN_ERR (svn_fs__dag_delete_mutable_node (parent->fs, id, trail));
>              }
>  
>            /* Just "lose" this entry by setting the *previous* entry's
> @@ -1197,6 +1203,62 @@
>                                        node_rev,
>                                        trail));
>  
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +svn_error_t *
> +svn_fs__dag_delete (dag_node_t *parent,
> +                    const char *name,
> +                    trail_t *trail)
> +{
> +  return delete_node (parent, name, TRUE, trail);
> +}
> +
> +
> +svn_error_t *
> +svn_fs__dag_delete_tree (dag_node_t *parent,
> +                         const char *name,
> +                         trail_t *trail)
> +{
> +  return delete_node (parent, name, FALSE, trail);
> +}
> +
> +
> +svn_error_t *
> +svn_fs__dag_delete_mutable_node (svn_fs_t *fs,
> +                                 svn_fs_id_t *id,
> +                                 trail_t *trail)
> +{
> +  svn_boolean_t is_mutable;
> +  dag_node_t *node;
> +
> +  SVN_ERR (svn_fs__dag_get_node (&node, fs, id, trail));
> +
> +  /* If immutable, do nothing and return immediately. */
> +  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, node, trail));
> +  if (! is_mutable)
> +    return SVN_NO_ERROR;
> +
> +  /* Else it's mutable.  Recurse on directories... */
> +  if (svn_fs__dag_is_directory (node))
> +    {
> +      skel_t *entries, *entry;
> +      SVN_ERR (svn_fs__dag_dir_entries_skel (&entries, node, trail));
> +          
> +      for (entry = entries->children; entry; entry = entry->next)
> +        {
> +          skel_t *id_skel = entry->children->next;
> +          svn_fs_id_t *this_id
> +            = svn_fs_parse_id (id_skel->data, id_skel->len, trail->pool);
> +
> +          SVN_ERR (svn_fs__dag_delete_mutable_node (fs, this_id, trail));
> +        }
> +    }
> +
> +  /* ... then delete the node itself. */
> +  SVN_ERR (svn_fs__delete_node_revision (fs, id, trail));
> +  
>    return SVN_NO_ERROR;
>  }
>  
> Index: subversion/libsvn_fs/dag.h
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/dag.h,v
> retrieving revision 1.40
> diff -u -r1.40 dag.h
> --- subversion/libsvn_fs/dag.h	2001/03/16 03:38:49	1.40
> +++ subversion/libsvn_fs/dag.h	2001/03/19 05:40:22
> @@ -310,6 +310,25 @@
>                                   trail_t *trail);
>  
>  
> +/* Delete the directory entry named NAME from PARENT, as part of
> +   TRAIL.  PARENT must be mutable.  NAME must be a single path
> +   component; it cannot be a slash-separated directory path.  If the
> +   node being deleted is a mutable directory, remove all reachable
> +   mutable nodes from there.  */
> +svn_error_t *svn_fs__dag_delete_tree (dag_node_t *parent,
> +                                      const char *name,
> +                                      trail_t *trail);
> +
> +
> +/* Delete all mutable node revisions reachable from node ID, including
> +   ID itself, from FS's `nodes' table, as part of TRAIL.  ID may refer
> +   to a file or directory, which may be mutable or immutable.  */
> +svn_error_t *
> +svn_fs__dag_delete_mutable_node (svn_fs_t *fs,
> +                                 svn_fs_id_t *id,
> +                                 trail_t *trail);
> +
> +
>  /* Create a new mutable directory named NAME in PARENT, as part of
>     TRAIL.  Set *CHILD_P to a reference to the new node, allocated in
>     TRAIL->pool.  The new directory has no contents, and no properties.
> Index: subversion/libsvn_fs/tree.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/tree.c,v
> retrieving revision 1.51
> diff -u -r1.51 tree.c
> --- subversion/libsvn_fs/tree.c	2001/03/16 20:07:57	1.51
> +++ subversion/libsvn_fs/tree.c	2001/03/19 05:40:22
> @@ -1760,7 +1760,6 @@
>  {
>    svn_fs_root_t *root;
>    const char *path;
> -  svn_boolean_t require_empty_dir;
>  };
>  
>  
> @@ -1771,11 +1770,15 @@
>    struct delete_args *args = baton;
>    svn_fs_root_t *root = args->root;
>    const char *path = args->path;
> -  svn_boolean_t require_empty_dir = args->require_empty_dir;
>    parent_path_t *parent_path;
>  
>    SVN_ERR (open_path (&parent_path, root, path, 0, trail));
>  
> +  if (root->kind != transaction_root)
> +    return svn_error_create
> +      (SVN_ERR_FS_NOT_TXN_ROOT, 0, NULL, trail->pool,
> +       "root object must be a transaction root");
> +
>    /* We can't remove the root of the filesystem.  */
>    if (! parent_path->parent)
>      return svn_error_create (SVN_ERR_FS_ROOT_DIR, 0, NULL, trail->pool,
> @@ -1785,9 +1788,8 @@
>    SVN_ERR (make_path_mutable (root, parent_path->parent, path, trail));
>  
>    /* If this is a directory, we have to check to see if it is required
> -     by our caller to be empty before deleting it. */
> -  if (svn_fs__dag_is_directory (parent_path->node)
> -      && require_empty_dir)
> +         by our caller to be empty before deleting it. */
> +  if (svn_fs__dag_is_directory (parent_path->node))
>      {
>        skel_t *entries;
>  
> @@ -1821,11 +1823,42 @@
>  
>    args.root = root;
>    args.path = path;
> -  args.require_empty_dir = TRUE;
>    return svn_fs__retry_txn (root->fs, txn_body_delete, &args, pool);
>  }
>  
>  
> +static svn_error_t *
> +txn_body_delete_tree (void *baton,
> +                      trail_t *trail)
> +{
> +  struct delete_args *args = baton;
> +  svn_fs_root_t *root = args->root;
> +  const char *path = args->path;
> +  parent_path_t *parent_path;
> +
> +  SVN_ERR (open_path (&parent_path, root, path, 0, trail));
> +
> +  if (root->kind != transaction_root)
> +    return svn_error_create
> +      (SVN_ERR_FS_NOT_TXN_ROOT, 0, NULL, trail->pool,
> +       "root object must be a transaction root");
> +
> +  /* We can't remove the root of the filesystem.  */
> +  if (! parent_path->parent)
> +    return svn_error_create (SVN_ERR_FS_ROOT_DIR, 0, NULL, trail->pool,
> +                             "the root directory cannot be deleted");
> +
> +  /* Make the parent directory mutable.  */
> +  SVN_ERR (make_path_mutable (root, parent_path->parent, path, trail));
> +
> +  SVN_ERR (svn_fs__dag_delete_tree (parent_path->parent->node,
> +                                    parent_path->entry,
> +                                    trail));
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +
>  svn_error_t *
>  svn_fs_delete_tree (svn_fs_root_t *root,
>                      const char *path,
> @@ -1835,8 +1868,7 @@
>  
>    args.root = root;
>    args.path = path;
> -  args.require_empty_dir = FALSE;
> -  return svn_fs__retry_txn (root->fs, txn_body_delete, &args, pool);
> +  return svn_fs__retry_txn (root->fs, txn_body_delete_tree, &args, pool);
>  }
>  
>  
> Index: subversion/libsvn_fs/txn.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/txn.c,v
> retrieving revision 1.37
> diff -u -r1.37 txn.c
> --- subversion/libsvn_fs/txn.c	2001/03/14 00:21:28	1.37
> +++ subversion/libsvn_fs/txn.c	2001/03/19 05:40:22
> @@ -165,46 +165,7 @@
>  };
>  
>  
> -/* Delete all mutable node revisions reachable from node ID, including
> -   ID itself, from filesystem FS, as part of TRAIL.  ID may refer to
> -   a file or directory, which may be mutable or immutable.  */
>  static svn_error_t *
> -delete_from_id (svn_fs_t *fs, svn_fs_id_t *id, trail_t *trail)
> -{
> -  svn_boolean_t is_mutable;
> -  dag_node_t *node;
> -
> -  SVN_ERR (svn_fs__dag_get_node (&node, fs, id, trail));
> -
> -  /* If immutable, do nothing and return immediately. */
> -  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, node, trail));
> -  if (! is_mutable)
> -    return SVN_NO_ERROR;
> -
> -  /* Else it's mutable.  Recurse on directories... */
> -  if (svn_fs__dag_is_directory (node))
> -    {
> -      skel_t *entries, *entry;
> -      SVN_ERR (svn_fs__dag_dir_entries_skel (&entries, node, trail));
> -          
> -      for (entry = entries->children; entry; entry = entry->next)
> -        {
> -          skel_t *id_skel = entry->children->next;
> -          svn_fs_id_t *this_id
> -            = svn_fs_parse_id (id_skel->data, id_skel->len, trail->pool);
> -
> -          SVN_ERR (delete_from_id (fs, this_id, trail));
> -        }
> -    }
> -
> -  /* ... then delete the node itself. */
> -  SVN_ERR (svn_fs__delete_node_revision (fs, id, trail));
> -  
> -  return SVN_NO_ERROR;
> -}
> -
> -
> -static svn_error_t *
>  txn_body_abort_txn (void *baton, trail_t *trail)
>  {
>    struct abort_txn_args *args = baton;
> @@ -214,7 +175,7 @@
>  
>    SVN_ERR (svn_fs_txn_name (&txn_name, txn, txn->pool));
>    SVN_ERR (svn_fs__get_txn (&root_id, &ignored_id, txn->fs, txn_name, trail));
> -  SVN_ERR (delete_from_id (txn->fs, root_id, trail));
> +  SVN_ERR (svn_fs__dag_delete_mutable_node (txn->fs, root_id, trail));
>    SVN_ERR (svn_fs__delete_txn (txn->fs, txn->id, trail));
>  
>    return SVN_NO_ERROR;
> Index: subversion/tests/libsvn_fs/fs-test.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/tests/libsvn_fs/fs-test.c,v
> retrieving revision 1.57
> diff -u -r1.57 fs-test.c
> --- subversion/tests/libsvn_fs/fs-test.c	2001/03/16 20:08:02	1.57
> +++ subversion/tests/libsvn_fs/fs-test.c	2001/03/19 05:40:22
> @@ -3026,6 +3026,408 @@
>    }
>    /* Close the filesystem. */
>    SVN_ERR (svn_fs_close_fs (fs));
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +/* Test svn_fs_delete_tree.
> +
> +   NOTE: This function tests internal filesystem interfaces, not just
> +   the public filesystem interface.  */
> +static svn_error_t *
> +delete_tree (const char **msg)
> +{
> +  svn_fs_t *fs;
> +  svn_fs_txn_t *txn;
> +  svn_fs_root_t *txn_root;
> +  svn_revnum_t new_rev;
> +
> +  *msg = "delete nodes tree";
> +
> +  /* This function tests 5 cases:
> +   *
> +   * 1. Delete mutable file.
> +   * 2. Delete mutable directory.
> +   * 3. Delete mutable directory with immutable nodes.
> +   * 4. Delete immutable file.
> +   * 5. Delete immutable directory.
> +   */
> +
> +  /* Prepare a txn to receive the greek tree. */
> +  SVN_ERR (create_fs_and_repos (&fs, "test-repo-del-tree"));
> +  SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, pool));
> +  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
> +
> +  /* Create the greek tree. */
> +  SVN_ERR (greek_tree_under_root (txn_root));
> +
> +  /* 1 */
> +  {
> +    svn_fs_id_t *iota_id;
> +    tree_test_entry_t expected_entries[] = {
> +      /* path, is_dir, contents */
> +      { "A",           1, "" },
> +      { "A/mu",        0, "This is the file 'mu'.\n" },
> +      { "A/B",         1, "" },
> +      { "A/B/lambda",  0, "This is the file 'lambda'.\n" },
> +      { "A/B/E",       1, "" },
> +      { "A/B/E/alpha", 0, "This is the file 'alpha'.\n" },
> +      { "A/B/E/beta",  0, "This is the file 'beta'.\n" },
> +      { "A/B/F",       1, "" },
> +      { "A/C",         1, "" },
> +      { "A/D",         1, "" },
> +      { "A/D/gamma",   0, "This is the file 'gamma'.\n" },
> +      { "A/D/G",       1, "" },
> +      { "A/D/G/pi",    0, "This is the file 'pi'.\n" },
> +      { "A/D/G/rho",   0, "This is the file 'rho'.\n" },
> +      { "A/D/G/tau",   0, "This is the file 'tau'.\n" },
> +      { "A/D/H",       1, "" },
> +      { "A/D/H/chi",   0, "This is the file 'chi'.\n" },
> +      { "A/D/H/psi",   0, "This is the file 'psi'.\n" },
> +      { "A/D/H/omega", 0, "This is the file 'omega'.\n" }
> +    };
> +    /* Check nodes revision ID is gone.  */
> +    SVN_ERR (svn_fs_node_id (&iota_id, txn_root, "iota", pool));
> +    SVN_ERR (check_entry_present (txn_root, "", "iota"));
> +    SVN_ERR (check_id_present (fs, iota_id));
> +
> +    SVN_ERR (svn_fs_delete_tree (txn_root, "iota", pool));
> +    SVN_ERR (check_entry_absent (txn_root, "", "iota"));
> +    SVN_ERR (check_id_absent (fs, iota_id));
> +
> +    /* Validate the tree.  */
> +    SVN_ERR (validate_tree (txn_root, expected_entries, 19));
> +  }
> +  /* Abort transaction.  */
> +  SVN_ERR (svn_fs_abort_txn (txn));
> +
> +  /* Prepare a txn to receive the greek tree. */
> +  SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, pool));
> +  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
> +
> +  /* Create the greek tree. */
> +  SVN_ERR (greek_tree_under_root (txn_root));
> +
> +  /* 2 */
> +  {
> +    svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
> +      *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
> +      *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
> +
> +    tree_test_entry_t expected_entries[] = {
> +      /* path, is_dir, contents */
> +      { "iota",        0, "This is the file 'iota'.\n" }
> +    };
> +
> +    /* Check nodes revision ID is gone.  */
> +    SVN_ERR (svn_fs_node_id (&A_id, txn_root, "/A", pool));
> +    SVN_ERR (check_entry_present (txn_root, "", "A"));
> +    SVN_ERR (svn_fs_node_id (&mu_id, txn_root, "/A/mu", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "mu"));
> +    SVN_ERR (svn_fs_node_id (&B_id, txn_root, "/A/B", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "B"));
> +    SVN_ERR (svn_fs_node_id (&lambda_id, txn_root, "/A/B/lambda", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "lambda"));
> +    SVN_ERR (svn_fs_node_id (&E_id, txn_root, "/A/B/E", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "E"));
> +    SVN_ERR (svn_fs_node_id (&alpha_id, txn_root, "/A/B/E/alpha", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B/E", "alpha"));
> +    SVN_ERR (svn_fs_node_id (&beta_id, txn_root, "/A/B/E/beta", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B/E", "beta"));
> +    SVN_ERR (svn_fs_node_id (&F_id, txn_root, "/A/B/F", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "F"));
> +    SVN_ERR (svn_fs_node_id (&C_id, txn_root, "/A/C", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "C"));
> +    SVN_ERR (svn_fs_node_id (&D_id, txn_root, "/A/D", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "D"));
> +    SVN_ERR (svn_fs_node_id (&gamma_id, txn_root, "/A/D/gamma", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "gamma"));
> +    SVN_ERR (svn_fs_node_id (&H_id, txn_root, "/A/D/H", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "H"));
> +    SVN_ERR (svn_fs_node_id (&chi_id, txn_root, "/A/D/H/chi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "chi"));
> +    SVN_ERR (svn_fs_node_id (&psi_id, txn_root, "/A/D/H/psi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "psi"));
> +    SVN_ERR (svn_fs_node_id (&omega_id, txn_root, "/A/D/H/omega", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "omega"));
> +    SVN_ERR (svn_fs_node_id (&G_id, txn_root, "/A/D/G", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "G"));
> +    SVN_ERR (svn_fs_node_id (&pi_id, txn_root, "/A/D/G/pi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "pi"));
> +    SVN_ERR (svn_fs_node_id (&rho_id, txn_root, "/A/D/G/rho", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "rho"));
> +    SVN_ERR (svn_fs_node_id (&tau_id, txn_root, "/A/D/G/tau", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "tau"));
> +
> +    SVN_ERR (svn_fs_delete_tree (txn_root, "A", pool));
> +
> +    SVN_ERR (check_entry_absent (txn_root, "", "A"));
> +    SVN_ERR (check_id_absent (fs, A_id));
> +    SVN_ERR (check_id_absent (fs, mu_id));
> +    SVN_ERR (check_id_absent (fs, B_id));
> +    SVN_ERR (check_id_absent (fs, lambda_id));
> +    SVN_ERR (check_id_absent (fs, E_id));
> +    SVN_ERR (check_id_absent (fs, alpha_id));
> +    SVN_ERR (check_id_absent (fs, beta_id));
> +    SVN_ERR (check_id_absent (fs, F_id));
> +    SVN_ERR (check_id_absent (fs, C_id));
> +    SVN_ERR (check_id_absent (fs, D_id));
> +    SVN_ERR (check_id_absent (fs, gamma_id));
> +    SVN_ERR (check_id_absent (fs, H_id));
> +    SVN_ERR (check_id_absent (fs, chi_id));
> +    SVN_ERR (check_id_absent (fs, psi_id));
> +    SVN_ERR (check_id_absent (fs, omega_id));
> +    SVN_ERR (check_id_absent (fs, G_id));
> +    SVN_ERR (check_id_absent (fs, pi_id));
> +    SVN_ERR (check_id_absent (fs, rho_id));
> +    SVN_ERR (check_id_absent (fs, tau_id));
> +
> +    /* Validate the tree.  */
> +    SVN_ERR (validate_tree (txn_root, expected_entries, 1));
> +  }
> +
> +  /* Abort transaction.  */
> +  SVN_ERR (svn_fs_abort_txn (txn));
> +
> +  /* Prepare a txn to receive the greek tree. */
> +  SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, pool));
> +  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
> +
> +  /* Create the greek tree. */
> +  SVN_ERR (greek_tree_under_root (txn_root));
> +
> +  /* Commit the greek tree. */
> +  {
> +    const char *conflict;
> +    SVN_ERR (svn_fs_commit_txn (&conflict, &new_rev, txn));
> +  }
> +  SVN_ERR (svn_fs_close_txn (txn));
> +
> +  /* Create new transaction. */
> +  SVN_ERR (svn_fs_begin_txn (&txn, fs, new_rev, pool));
> +  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
> +
> +  /* 3 */
> +  {
> +    svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
> +      *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
> +      *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id, *sigma_id;
> +
> +    tree_test_entry_t expected_entries[] = {
> +      /* path, is_dir, contents */
> +      { "iota",        0, "This is the file 'iota'.\n" }
> +    };
> +
> +    /* Create A/D/G/sigma.  This makes all component of A/D/G
> +       mutable.  */
> +    SVN_ERR (svn_fs_make_file (txn_root, "A/D/G/sigma", pool));
> +    SVN_ERR (set_file_contents (txn_root, "A/D/G/sigma",
> +                                "This is another file 'sigma'.\n"));
> +
> +    /* Check mutable nodes revision ID is removed and immutable ones
> +       still exist.  */
> +    SVN_ERR (svn_fs_node_id (&A_id, txn_root, "/A", pool));
> +    SVN_ERR (check_entry_present (txn_root, "", "A"));
> +    SVN_ERR (svn_fs_node_id (&mu_id, txn_root, "/A/mu", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "mu"));
> +    SVN_ERR (svn_fs_node_id (&B_id, txn_root, "/A/B", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "B"));
> +    SVN_ERR (svn_fs_node_id (&lambda_id, txn_root, "/A/B/lambda", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "lambda"));
> +    SVN_ERR (svn_fs_node_id (&E_id, txn_root, "/A/B/E", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "E"));
> +    SVN_ERR (svn_fs_node_id (&alpha_id, txn_root, "/A/B/E/alpha", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B/E", "alpha"));
> +    SVN_ERR (svn_fs_node_id (&beta_id, txn_root, "/A/B/E/beta", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B/E", "beta"));
> +    SVN_ERR (svn_fs_node_id (&F_id, txn_root, "/A/B/F", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "F"));
> +    SVN_ERR (svn_fs_node_id (&C_id, txn_root, "/A/C", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "C"));
> +    SVN_ERR (svn_fs_node_id (&D_id, txn_root, "/A/D", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "D"));
> +    SVN_ERR (svn_fs_node_id (&gamma_id, txn_root, "/A/D/gamma", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "gamma"));
> +    SVN_ERR (svn_fs_node_id (&H_id, txn_root, "/A/D/H", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "H"));
> +    SVN_ERR (svn_fs_node_id (&chi_id, txn_root, "/A/D/H/chi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "chi"));
> +    SVN_ERR (svn_fs_node_id (&psi_id, txn_root, "/A/D/H/psi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "psi"));
> +    SVN_ERR (svn_fs_node_id (&omega_id, txn_root, "/A/D/H/omega", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "omega"));
> +    SVN_ERR (svn_fs_node_id (&G_id, txn_root, "/A/D/G", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "G"));
> +    SVN_ERR (svn_fs_node_id (&pi_id, txn_root, "/A/D/G/pi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "pi"));
> +    SVN_ERR (svn_fs_node_id (&rho_id, txn_root, "/A/D/G/rho", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "rho"));
> +    SVN_ERR (svn_fs_node_id (&tau_id, txn_root, "/A/D/G/tau", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "tau"));
> +    SVN_ERR (svn_fs_node_id (&sigma_id, txn_root, "/A/D/G/sigma", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "sigma"));
> +
> +    SVN_ERR (svn_fs_delete_tree (txn_root, "A", pool));
> +
> +    SVN_ERR (check_entry_absent (txn_root, "", "A"));
> +    SVN_ERR (check_id_absent (fs, A_id));
> +    SVN_ERR (check_id_present (fs, mu_id));
> +    SVN_ERR (check_id_present (fs, B_id));
> +    SVN_ERR (check_id_present (fs, lambda_id));
> +    SVN_ERR (check_id_present (fs, E_id));
> +    SVN_ERR (check_id_present (fs, alpha_id));
> +    SVN_ERR (check_id_present (fs, beta_id));
> +    SVN_ERR (check_id_present (fs, F_id));
> +    SVN_ERR (check_id_present (fs, C_id));
> +    SVN_ERR (check_id_absent (fs, D_id));
> +    SVN_ERR (check_id_present (fs, gamma_id));
> +    SVN_ERR (check_id_present (fs, H_id));
> +    SVN_ERR (check_id_present (fs, chi_id));
> +    SVN_ERR (check_id_present (fs, psi_id));
> +    SVN_ERR (check_id_present (fs, omega_id));
> +    SVN_ERR (check_id_absent (fs, G_id));
> +    SVN_ERR (check_id_present (fs, pi_id));
> +    SVN_ERR (check_id_present (fs, rho_id));
> +    SVN_ERR (check_id_present (fs, tau_id));
> +    SVN_ERR (check_id_absent (fs, sigma_id));
> +
> +    /* Validate the tree.  */
> +    SVN_ERR (validate_tree (txn_root, expected_entries, 1));
> +  }
> +
> +  /* Abort transaction.  */
> +  SVN_ERR (svn_fs_abort_txn (txn));
> +
> +  /* Create new transaction. */
> +  SVN_ERR (svn_fs_begin_txn (&txn, fs, new_rev, pool));
> +  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
> +
> +  /* 4 */
> +  {
> +    svn_fs_id_t *iota_id;
> +    tree_test_entry_t expected_entries[] = {
> +      /* path, is_dir, contents */
> +      { "A",           1, "" },
> +      { "A/mu",        0, "This is the file 'mu'.\n" },
> +      { "A/B",         1, "" },
> +      { "A/B/lambda",  0, "This is the file 'lambda'.\n" },
> +      { "A/B/E",       1, "" },
> +      { "A/B/E/alpha", 0, "This is the file 'alpha'.\n" },
> +      { "A/B/E/beta",  0, "This is the file 'beta'.\n" },
> +      { "A/B/F",       1, "" },
> +      { "A/C",         1, "" },
> +      { "A/D",         1, "" },
> +      { "A/D/gamma",   0, "This is the file 'gamma'.\n" },
> +      { "A/D/G",       1, "" },
> +      { "A/D/G/pi",    0, "This is the file 'pi'.\n" },
> +      { "A/D/G/rho",   0, "This is the file 'rho'.\n" },
> +      { "A/D/G/tau",   0, "This is the file 'tau'.\n" },
> +      { "A/D/H",       1, "" },
> +      { "A/D/H/chi",   0, "This is the file 'chi'.\n" },
> +      { "A/D/H/psi",   0, "This is the file 'psi'.\n" },
> +      { "A/D/H/omega", 0, "This is the file 'omega'.\n" }
> +    };
> +    /* Check nodes revision ID is present.  */
> +    SVN_ERR (svn_fs_node_id (&iota_id, txn_root, "iota", pool));
> +    SVN_ERR (check_entry_present (txn_root, "", "iota"));
> +    SVN_ERR (check_id_present (fs, iota_id));
> +
> +    SVN_ERR (svn_fs_delete_tree (txn_root, "iota", pool));
> +    SVN_ERR (check_entry_absent (txn_root, "", "iota"));
> +    SVN_ERR (check_id_present (fs, iota_id));
> +
> +    /* Validate the tree.  */
> +    SVN_ERR (validate_tree (txn_root, expected_entries, 19));
> +  }
> +
> +  /* Abort transaction.  */
> +  SVN_ERR (svn_fs_abort_txn (txn));
> +
> +  /* Create new transaction. */
> +  SVN_ERR (svn_fs_begin_txn (&txn, fs, new_rev, pool));
> +  SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
> +
> +  /* 5 */
> +  {
> +    svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
> +      *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
> +      *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
> +
> +    tree_test_entry_t expected_entries[] = {
> +      /* path, is_dir, contents */
> +      { "iota",        0, "This is the file 'iota'.\n" }
> +    };
> +
> +    /* Check nodes revision ID is present.  */
> +    SVN_ERR (svn_fs_node_id (&A_id, txn_root, "/A", pool));
> +    SVN_ERR (check_entry_present (txn_root, "", "A"));
> +    SVN_ERR (svn_fs_node_id (&mu_id, txn_root, "/A/mu", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "mu"));
> +    SVN_ERR (svn_fs_node_id (&B_id, txn_root, "/A/B", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "B"));
> +    SVN_ERR (svn_fs_node_id (&lambda_id, txn_root, "/A/B/lambda", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "lambda"));
> +    SVN_ERR (svn_fs_node_id (&E_id, txn_root, "/A/B/E", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "E"));
> +    SVN_ERR (svn_fs_node_id (&alpha_id, txn_root, "/A/B/E/alpha", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B/E", "alpha"));
> +    SVN_ERR (svn_fs_node_id (&beta_id, txn_root, "/A/B/E/beta", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B/E", "beta"));
> +    SVN_ERR (svn_fs_node_id (&F_id, txn_root, "/A/B/F", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/B", "F"));
> +    SVN_ERR (svn_fs_node_id (&C_id, txn_root, "/A/C", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "C"));
> +    SVN_ERR (svn_fs_node_id (&D_id, txn_root, "/A/D", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A", "D"));
> +    SVN_ERR (svn_fs_node_id (&gamma_id, txn_root, "/A/D/gamma", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "gamma"));
> +    SVN_ERR (svn_fs_node_id (&H_id, txn_root, "/A/D/H", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "H"));
> +    SVN_ERR (svn_fs_node_id (&chi_id, txn_root, "/A/D/H/chi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "chi"));
> +    SVN_ERR (svn_fs_node_id (&psi_id, txn_root, "/A/D/H/psi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "psi"));
> +    SVN_ERR (svn_fs_node_id (&omega_id, txn_root, "/A/D/H/omega", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/H", "omega"));
> +    SVN_ERR (svn_fs_node_id (&G_id, txn_root, "/A/D/G", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D", "G"));
> +    SVN_ERR (svn_fs_node_id (&pi_id, txn_root, "/A/D/G/pi", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "pi"));
> +    SVN_ERR (svn_fs_node_id (&rho_id, txn_root, "/A/D/G/rho", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "rho"));
> +    SVN_ERR (svn_fs_node_id (&tau_id, txn_root, "/A/D/G/tau", pool));
> +    SVN_ERR (check_entry_present (txn_root, "A/D/G", "tau"));
> +
> +    SVN_ERR (svn_fs_delete_tree (txn_root, "A", pool));
> +
> +    SVN_ERR (check_entry_absent (txn_root, "", "A"));
> +    SVN_ERR (check_id_present (fs, A_id));
> +    SVN_ERR (check_id_present (fs, mu_id));
> +    SVN_ERR (check_id_present (fs, B_id));
> +    SVN_ERR (check_id_present (fs, lambda_id));
> +    SVN_ERR (check_id_present (fs, E_id));
> +    SVN_ERR (check_id_present (fs, alpha_id));
> +    SVN_ERR (check_id_present (fs, beta_id));
> +    SVN_ERR (check_id_present (fs, F_id));
> +    SVN_ERR (check_id_present (fs, C_id));
> +    SVN_ERR (check_id_present (fs, D_id));
> +    SVN_ERR (check_id_present (fs, gamma_id));
> +    SVN_ERR (check_id_present (fs, H_id));
> +    SVN_ERR (check_id_present (fs, chi_id));
> +    SVN_ERR (check_id_present (fs, psi_id));
> +    SVN_ERR (check_id_present (fs, omega_id));
> +    SVN_ERR (check_id_present (fs, G_id));
> +    SVN_ERR (check_id_present (fs, pi_id));
> +    SVN_ERR (check_id_present (fs, rho_id));
> +    SVN_ERR (check_id_present (fs, tau_id));
> +
> +    /* Validate the tree.  */
> +    SVN_ERR (validate_tree (txn_root, expected_entries, 1));
> +  }
> +
> +  /* Close the transaction and fs. */
> +  SVN_ERR (svn_fs_close_txn (txn));
> +  SVN_ERR (svn_fs_close_fs (fs));
>  
>    return SVN_NO_ERROR;
>  }
> @@ -3058,6 +3460,7 @@
>    basic_commit,
>    copy_test,
>    merging_commit,
> +  delete_tree,
>    0
>  };
>  
> 
> 
> -- 
> Yoshiki Hayashi