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/02/28 10:27:12 UTC

[PATCH] svn_fs_abort_txn and bug fixes

All of these changes are necessary to implement
svn_fs_abort_txn().  This was harder than I thought because
of some bugs. :-)

* dag.c (struct dag_node_t): Fix indentation.
* dag.c (svn_fs__dag_dir_entries): Fill in this function.
* dag.c (svn_fs__dag_clone_root): Update root node ID in
  transaction table.
* dag.c (svn_fs__dag_delete): Delete node from nodes table
  when node is mutable.
* dag.h (svn_fs__dag_delete): Doc fix.
* nodes-table.c (svn_fs__delete_node): New function.
* nodes-table.h (svn_fs__delete_node): New function declaration.
* txn-table.c (put_txn):  Fix the order of prepend.
  Transaction skel is (transaction ROOT-ID BASE-ROOT-ID).
* txn.c (delete_nodes): New function.
* txn.c (txn_body_abort_txn): New function.
* txn.c (svn_fs_abort_txn): New function.

Index: dag.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/dag.c,v
retrieving revision 1.59
diff -u -r1.59 dag.c
--- dag.c	2001/02/28 07:53:31	1.59
+++ dag.c	2001/02/28 10:11:30
@@ -65,7 +65,7 @@
      this yourself, but you're probably better off just calling
      `get_node_revision' and `set_node_revision', which take care of
      things for you.  */
- skel_t *node_revision;
+  skel_t *node_revision;
 
 };
 
@@ -367,14 +367,37 @@
 }
 
 
+
+svn_error_t *
+svn_fs__dag_dir_entries (skel_t **entries_p,
+                         dag_node_t *node,
+                         trail_t *trail)
+{
+  skel_t *node_rev, *entries, *entry;
+
+  if (! svn_fs__dag_is_directory (node))
+    return svn_error_create
+      (SVN_ERR_FS_NOT_DIRECTORY, 0, NULL, trail->pool,
+       "Attempted to get entry from *non*-directory node.");
+
+  /* Go get a fresh NODE-REVISION for this node. */
+  SVN_ERR (get_node_revision (&node_rev, node, trail));
+
+  /* Directory entries start at the second element of a node-revision
+     skel, itself is a list.  */
+  entries = node_rev->children->next->children;
 
-svn_error_t *svn_fs__dag_dir_entries (skel_t **entries_p,
-                                      dag_node_t *node,
-                                      trail_t *trail)
-{
-  abort();
-  /* NOTREACHED */
-  return NULL;
+  /* Check entries are well-formed.  */
+  for (entry = entries; entry; entry = entry->next)
+    {
+      /* ENTRY must be a list which has two elements.  */
+      if (svn_fs__list_length (entry) != 2)
+        return svn_error_create (SVN_ERR_FS_CORRUPT, 0, NULL, trail->pool,
+                                 "Malformed directory entry.");
+    }
+
+  *entries_p = entries;
+  return SVN_NO_ERROR;
 }
 
 
@@ -674,6 +697,10 @@
   /* One way or another, root_id now identifies a cloned root node. */
   SVN_ERR (create_node (root_p, fs, root_id, trail));
 
+  /* Update root_id in transactions table.  */
+  SVN_ERR (svn_fs__set_txn_root (fs, svn_txn, root_id, trail));
+
+
   /* ... And when it is grown
    *      Then my own little clone
    *        Will be of the opposite sex!
@@ -726,10 +753,9 @@
   new_dirent_list = svn_fs__copy_skel (node_rev->children->next, 
                                        trail->pool);
 
-  entry = new_dirent_list->children;
   old_entry = NULL;
 
-  while (entry)
+  for (entry = new_dirent_list->children; entry; entry = entry->next)
     {
       if (svn_fs__matches_atom (entry->children, name))
         {
@@ -747,10 +773,10 @@
                                               id,
                                               trail));
 
-          if (svn_fs__matches_atom (entry_content->children->children,
-                                    "dir"))
+          if (has_mutable_flag (entry_content))
             {
-              if (has_mutable_flag (entry_content))
+              if (svn_fs__matches_atom (entry_content->children->children,
+                                        "dir"))
                 {
                   int len = 
                     svn_fs__list_length (entry_content->children->next);
@@ -761,15 +787,21 @@
                        "Attempted to delete *non-empty* directory `%s'.",
                        name);                        
                 }
-            }
 
-          /* Just "lose" this entry by setting the *previous* entry's
-             next ptr to the current entry's next ptr. */          
-          if (! old_entry)
-            /* Base case:  the very *first* entry matched. */
-            new_dirent_list->children = entry->next;
+              /* We are aborting a transaction.  Mutable nodes must be
+                 removed from `nodes' table.  */
+              SVN_ERR (svn_fs__delete_node (parent->fs, id, trail));
+            }
           else
-            old_entry->next = entry->next;
+            {
+              /* Just "lose" this entry by setting the *previous* entry's
+                 next ptr to the current entry's next ptr. */          
+              if (! old_entry)
+                /* Base case:  the very *first* entry matched. */
+                new_dirent_list->children = entry->next;
+              else
+                old_entry->next = entry->next;
+            }
 
           deleted = TRUE;
           break;
@@ -777,7 +809,6 @@
 
       /* No match, move to next entry. */
       old_entry = entry;
-      entry = entry->next;
     }
     
   if (! deleted)
Index: dag.h
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/dag.h,v
retrieving revision 1.30
diff -u -r1.30 dag.h
--- dag.h	2001/02/27 22:03:46	1.30
+++ dag.h	2001/02/28 10:11:30
@@ -262,7 +262,8 @@
 /* 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, it must be empty.  */
+   node being deleted is mutable, it will vanish from the earth.  If
+   it is a mutable directory, it must be empty.  */
 svn_error_t *svn_fs__dag_delete (dag_node_t *parent,
                                  const char *name,
                                  trail_t *trail);
Index: nodes-table.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/nodes-table.c,v
retrieving revision 1.9
diff -u -r1.9 nodes-table.c
--- nodes-table.c	2001/02/28 02:19:55	1.9
+++ nodes-table.c	2001/02/28 10:11:30
@@ -337,6 +337,27 @@
 
 
 
+/* Removing revisions.  */
+
+
+svn_error_t *
+svn_fs__delete_node (svn_fs_t *fs,
+                     const svn_fs_id_t *id,
+                     trail_t *trail)
+{
+  DBT key;
+
+  SVN_ERR (DB_WRAP (fs, "deleting entry from `nodes' table",
+                    fs->nodes->del (fs->nodes,
+                                    trail->db_txn,
+                                    svn_fs__id_to_dbt (&key, id, trail->pool),
+                                    0)));
+
+  return SVN_NO_ERROR;
+}
+
+
+
 /* Choosing node revision ID's.  */
 
 
Index: nodes-table.h
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/nodes-table.h,v
retrieving revision 1.5
diff -u -r1.5 nodes-table.h
--- nodes-table.h	2001/02/13 16:17:24	1.5
+++ nodes-table.h	2001/02/28 10:11:30
@@ -52,6 +52,12 @@
                               trail_t *trail);
 
 
+/* Remove the node ID from `nodes' table of FS, as part of TRAIL.  */
+svn_error_t *svn_fs__delete_node (svn_fs_t *fs,
+                                  const svn_fs_id_t *id,
+                                  trail_t *trail);
+
+
 /* Check FS's `nodes' table to find an unused node number, and set
    *ID_P to the ID of the first revision of an entirely new node in
    FS, as part of TRAIL.  Allocate the new ID, and do all temporary
Index: txn-table.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/txn-table.c,v
retrieving revision 1.16
diff -u -r1.16 txn-table.c
--- txn-table.c	2001/02/27 15:16:58	1.16
+++ txn-table.c	2001/02/28 10:11:30
@@ -80,12 +80,12 @@
   skel_t *txn_skel = svn_fs__make_empty_list (pool);
   DBT key, value;
 
-  svn_fs__prepend (svn_fs__mem_atom (unparsed_root_id->data,
-                                     unparsed_root_id->len,
-                                     pool),
-                   txn_skel);
   svn_fs__prepend (svn_fs__mem_atom (unparsed_base_root_id->data,
                                      unparsed_base_root_id->len,
+                                     pool),
+                   txn_skel);
+  svn_fs__prepend (svn_fs__mem_atom (unparsed_root_id->data,
+                                     unparsed_root_id->len,
                                      pool),
                    txn_skel);
   svn_fs__prepend (svn_fs__str_atom ((char *) "transaction", pool), txn_skel);
Index: txn.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/txn.c,v
retrieving revision 1.28
diff -u -r1.28 txn.c
--- txn.c	2001/02/24 09:13:31	1.28
+++ txn.c	2001/02/28 10:11:31
@@ -22,8 +22,10 @@
 #include "txn.h"
 #include "err.h"
 #include "trail.h"
+#include "nodes-table.h"
 #include "rev-table.h"
 #include "txn-table.h"
+#include "dag.h"
 #include "tree.h"
 
 
@@ -152,6 +154,112 @@
 {
   *new_rev = SVN_INVALID_REVNUM;
   abort();
+  return SVN_NO_ERROR;
+}
+
+
+/* Helper function for svn_fs_abort_txn.  Remove all mutable nodes
+   recursively.  NODE must be a mutable directory.  */
+static svn_error_t *
+delete_nodes (svn_fs_t *fs,
+              dag_node_t *node,
+              trail_t *trail)
+{
+  svn_boolean_t is_mutable;
+
+  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, node, trail));
+
+  if (is_mutable && svn_fs__dag_is_directory (node))
+    {
+      skel_t *entries, *entry;
+      SVN_ERR (svn_fs__dag_dir_entries (&entries, node, trail));
+          
+      for (entry = entries; entry; entry = entry->next)
+        {
+          dag_node_t *child;
+          skel_t *name_skel;
+          char *name;
+
+          /* ENTRY is guaranteed to be well-formed.  (NAME ID).  */
+          name_skel = entry->children;
+          name = apr_palloc (trail->pool, name_skel->len + 1);
+          memcpy (name, name_skel->data, name_skel->len);
+          name[name_skel->len] = '\0';
+          SVN_ERR (svn_fs__dag_open (&child, node,
+                                     name,
+                                     trail));
+    
+          if (svn_fs__dag_is_file (child)
+              || svn_fs__dag_is_copy (child))
+            {
+              SVN_ERR (svn_fs__dag_delete (node, name, trail));
+            }
+          else if (svn_fs__dag_is_directory)
+            {
+              svn_boolean_t child_is_mutable;
+
+              SVN_ERR (svn_fs__dag_check_mutable (&child_is_mutable,
+                                                  node, trail));
+              /* Recurse if this is mutable directory.  */
+              if (child_is_mutable)
+                SVN_ERR (delete_nodes (fs, child, trail));
+
+              /* Delete this directory regardless of mutable flag.  */
+              SVN_ERR (svn_fs__dag_delete (node, name, trail));
+            }
+          else
+            abort ();
+        }
+    }
+  else
+    /* NODE must be a mutable directory or we have logic error.  */
+    abort ();
+      
+  return SVN_NO_ERROR;
+}
+
+
+
+static svn_error_t *
+txn_body_abort_txn (void *baton,
+                    trail_t *trail)
+{
+  svn_fs_txn_t *txn = baton;
+
+  dag_node_t *root_node;
+  svn_boolean_t is_mutable;
+
+  SVN_ERR (svn_fs__dag_txn_root (&root_node, txn->fs, txn->id, trail));
+
+  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, root_node, trail));
+
+  /* It's possible no cloning is done.  */
+  if (is_mutable)
+    {
+      /* Remove all mutable node from `nodes' table.  */
+      SVN_ERR (delete_nodes (txn->fs, root_node, trail));
+
+      /* Remove root node itself */
+      SVN_ERR (svn_fs__delete_node (txn->fs,
+                                    svn_fs__dag_get_id (root_node),
+                                    trail));
+    }
+
+  /* Delete transaction from `transactions' table.  */
+  SVN_ERR (svn_fs__delete_txn (txn->fs, txn->id, trail));
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_fs_abort_txn (svn_fs_txn_t *txn)
+{
+  /* Remove all mutable nodes reachable from transaction's root.  */
+  SVN_ERR (svn_fs__retry_txn (txn->fs, txn_body_abort_txn, txn, txn->pool));
+
+  SVN_ERR (svn_fs_close_txn (txn));
+
   return SVN_NO_ERROR;
 }
 


-- 
Yoshiki Hayashi

Re: [PATCH] svn_fs_abort_txn and bug fixes

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

> Hi, Yoshiki!  Regarding this and the other related patch, thanks very
> much for sending them, and my apologies for being slow about
> reviewing.  Had meant to do it today, but some other things came up.
> Will be doing tomorrow or at worst Friday, though -- obviously, these
> are important functions. :-)

No apologies are necessary.  I'm enjoing writing these
functions and a few days of delay doesn't bother me.
Besides, I'm away at m17n2001 next two days so you'll have a
plenty of time before I come back. :-)
http://www.m17n.org/m17n2001/

BTW, I saw Jim at m17n99.  The sad thing is, I didn't have
many chances to chat with those good guys because I wasn't
good at speaking English. :-(

-- 
Yoshiki Hayashi

Re: [PATCH] svn_fs_abort_txn and bug fixes

Posted by Karl Fogel <kf...@galois.collab.net>.
Hi, Yoshiki!  Regarding this and the other related patch, thanks very
much for sending them, and my apologies for being slow about
reviewing.  Had meant to do it today, but some other things came up.
Will be doing tomorrow or at worst Friday, though -- obviously, these
are important functions. :-)

Thanks again,
-Karl

Yoshiki Hayashi <yo...@xemacs.org> writes:
> All of these changes are necessary to implement
> svn_fs_abort_txn().  This was harder than I thought because
> of some bugs. :-)
> 
> * dag.c (struct dag_node_t): Fix indentation.
> * dag.c (svn_fs__dag_dir_entries): Fill in this function.
> * dag.c (svn_fs__dag_clone_root): Update root node ID in
>   transaction table.
> * dag.c (svn_fs__dag_delete): Delete node from nodes table
>   when node is mutable.
> * dag.h (svn_fs__dag_delete): Doc fix.
> * nodes-table.c (svn_fs__delete_node): New function.
> * nodes-table.h (svn_fs__delete_node): New function declaration.
> * txn-table.c (put_txn):  Fix the order of prepend.
>   Transaction skel is (transaction ROOT-ID BASE-ROOT-ID).
> * txn.c (delete_nodes): New function.
> * txn.c (txn_body_abort_txn): New function.
> * txn.c (svn_fs_abort_txn): New function.
> 
> Index: dag.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/dag.c,v
> retrieving revision 1.59
> diff -u -r1.59 dag.c
> --- dag.c	2001/02/28 07:53:31	1.59
> +++ dag.c	2001/02/28 10:11:30
> @@ -65,7 +65,7 @@
>       this yourself, but you're probably better off just calling
>       `get_node_revision' and `set_node_revision', which take care of
>       things for you.  */
> - skel_t *node_revision;
> +  skel_t *node_revision;
>  
>  };
>  
> @@ -367,14 +367,37 @@
>  }
>  
>  
> +
> +svn_error_t *
> +svn_fs__dag_dir_entries (skel_t **entries_p,
> +                         dag_node_t *node,
> +                         trail_t *trail)
> +{
> +  skel_t *node_rev, *entries, *entry;
> +
> +  if (! svn_fs__dag_is_directory (node))
> +    return svn_error_create
> +      (SVN_ERR_FS_NOT_DIRECTORY, 0, NULL, trail->pool,
> +       "Attempted to get entry from *non*-directory node.");
> +
> +  /* Go get a fresh NODE-REVISION for this node. */
> +  SVN_ERR (get_node_revision (&node_rev, node, trail));
> +
> +  /* Directory entries start at the second element of a node-revision
> +     skel, itself is a list.  */
> +  entries = node_rev->children->next->children;
>  
> -svn_error_t *svn_fs__dag_dir_entries (skel_t **entries_p,
> -                                      dag_node_t *node,
> -                                      trail_t *trail)
> -{
> -  abort();
> -  /* NOTREACHED */
> -  return NULL;
> +  /* Check entries are well-formed.  */
> +  for (entry = entries; entry; entry = entry->next)
> +    {
> +      /* ENTRY must be a list which has two elements.  */
> +      if (svn_fs__list_length (entry) != 2)
> +        return svn_error_create (SVN_ERR_FS_CORRUPT, 0, NULL, trail->pool,
> +                                 "Malformed directory entry.");
> +    }
> +
> +  *entries_p = entries;
> +  return SVN_NO_ERROR;
>  }
>  
>  
> @@ -674,6 +697,10 @@
>    /* One way or another, root_id now identifies a cloned root node. */
>    SVN_ERR (create_node (root_p, fs, root_id, trail));
>  
> +  /* Update root_id in transactions table.  */
> +  SVN_ERR (svn_fs__set_txn_root (fs, svn_txn, root_id, trail));
> +
> +
>    /* ... And when it is grown
>     *      Then my own little clone
>     *        Will be of the opposite sex!
> @@ -726,10 +753,9 @@
>    new_dirent_list = svn_fs__copy_skel (node_rev->children->next, 
>                                         trail->pool);
>  
> -  entry = new_dirent_list->children;
>    old_entry = NULL;
>  
> -  while (entry)
> +  for (entry = new_dirent_list->children; entry; entry = entry->next)
>      {
>        if (svn_fs__matches_atom (entry->children, name))
>          {
> @@ -747,10 +773,10 @@
>                                                id,
>                                                trail));
>  
> -          if (svn_fs__matches_atom (entry_content->children->children,
> -                                    "dir"))
> +          if (has_mutable_flag (entry_content))
>              {
> -              if (has_mutable_flag (entry_content))
> +              if (svn_fs__matches_atom (entry_content->children->children,
> +                                        "dir"))
>                  {
>                    int len = 
>                      svn_fs__list_length (entry_content->children->next);
> @@ -761,15 +787,21 @@
>                         "Attempted to delete *non-empty* directory `%s'.",
>                         name);                        
>                  }
> -            }
>  
> -          /* Just "lose" this entry by setting the *previous* entry's
> -             next ptr to the current entry's next ptr. */          
> -          if (! old_entry)
> -            /* Base case:  the very *first* entry matched. */
> -            new_dirent_list->children = entry->next;
> +              /* We are aborting a transaction.  Mutable nodes must be
> +                 removed from `nodes' table.  */
> +              SVN_ERR (svn_fs__delete_node (parent->fs, id, trail));
> +            }
>            else
> -            old_entry->next = entry->next;
> +            {
> +              /* Just "lose" this entry by setting the *previous* entry's
> +                 next ptr to the current entry's next ptr. */          
> +              if (! old_entry)
> +                /* Base case:  the very *first* entry matched. */
> +                new_dirent_list->children = entry->next;
> +              else
> +                old_entry->next = entry->next;
> +            }
>  
>            deleted = TRUE;
>            break;
> @@ -777,7 +809,6 @@
>  
>        /* No match, move to next entry. */
>        old_entry = entry;
> -      entry = entry->next;
>      }
>      
>    if (! deleted)
> Index: dag.h
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/dag.h,v
> retrieving revision 1.30
> diff -u -r1.30 dag.h
> --- dag.h	2001/02/27 22:03:46	1.30
> +++ dag.h	2001/02/28 10:11:30
> @@ -262,7 +262,8 @@
>  /* 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, it must be empty.  */
> +   node being deleted is mutable, it will vanish from the earth.  If
> +   it is a mutable directory, it must be empty.  */
>  svn_error_t *svn_fs__dag_delete (dag_node_t *parent,
>                                   const char *name,
>                                   trail_t *trail);
> Index: nodes-table.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/nodes-table.c,v
> retrieving revision 1.9
> diff -u -r1.9 nodes-table.c
> --- nodes-table.c	2001/02/28 02:19:55	1.9
> +++ nodes-table.c	2001/02/28 10:11:30
> @@ -337,6 +337,27 @@
>  
>  
>  
> +/* Removing revisions.  */
> +
> +
> +svn_error_t *
> +svn_fs__delete_node (svn_fs_t *fs,
> +                     const svn_fs_id_t *id,
> +                     trail_t *trail)
> +{
> +  DBT key;
> +
> +  SVN_ERR (DB_WRAP (fs, "deleting entry from `nodes' table",
> +                    fs->nodes->del (fs->nodes,
> +                                    trail->db_txn,
> +                                    svn_fs__id_to_dbt (&key, id, trail->pool),
> +                                    0)));
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +
>  /* Choosing node revision ID's.  */
>  
>  
> Index: nodes-table.h
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/nodes-table.h,v
> retrieving revision 1.5
> diff -u -r1.5 nodes-table.h
> --- nodes-table.h	2001/02/13 16:17:24	1.5
> +++ nodes-table.h	2001/02/28 10:11:30
> @@ -52,6 +52,12 @@
>                                trail_t *trail);
>  
>  
> +/* Remove the node ID from `nodes' table of FS, as part of TRAIL.  */
> +svn_error_t *svn_fs__delete_node (svn_fs_t *fs,
> +                                  const svn_fs_id_t *id,
> +                                  trail_t *trail);
> +
> +
>  /* Check FS's `nodes' table to find an unused node number, and set
>     *ID_P to the ID of the first revision of an entirely new node in
>     FS, as part of TRAIL.  Allocate the new ID, and do all temporary
> Index: txn-table.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/txn-table.c,v
> retrieving revision 1.16
> diff -u -r1.16 txn-table.c
> --- txn-table.c	2001/02/27 15:16:58	1.16
> +++ txn-table.c	2001/02/28 10:11:30
> @@ -80,12 +80,12 @@
>    skel_t *txn_skel = svn_fs__make_empty_list (pool);
>    DBT key, value;
>  
> -  svn_fs__prepend (svn_fs__mem_atom (unparsed_root_id->data,
> -                                     unparsed_root_id->len,
> -                                     pool),
> -                   txn_skel);
>    svn_fs__prepend (svn_fs__mem_atom (unparsed_base_root_id->data,
>                                       unparsed_base_root_id->len,
> +                                     pool),
> +                   txn_skel);
> +  svn_fs__prepend (svn_fs__mem_atom (unparsed_root_id->data,
> +                                     unparsed_root_id->len,
>                                       pool),
>                     txn_skel);
>    svn_fs__prepend (svn_fs__str_atom ((char *) "transaction", pool), txn_skel);
> Index: txn.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/txn.c,v
> retrieving revision 1.28
> diff -u -r1.28 txn.c
> --- txn.c	2001/02/24 09:13:31	1.28
> +++ txn.c	2001/02/28 10:11:31
> @@ -22,8 +22,10 @@
>  #include "txn.h"
>  #include "err.h"
>  #include "trail.h"
> +#include "nodes-table.h"
>  #include "rev-table.h"
>  #include "txn-table.h"
> +#include "dag.h"
>  #include "tree.h"
>  
>  
> @@ -152,6 +154,112 @@
>  {
>    *new_rev = SVN_INVALID_REVNUM;
>    abort();
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +/* Helper function for svn_fs_abort_txn.  Remove all mutable nodes
> +   recursively.  NODE must be a mutable directory.  */
> +static svn_error_t *
> +delete_nodes (svn_fs_t *fs,
> +              dag_node_t *node,
> +              trail_t *trail)
> +{
> +  svn_boolean_t is_mutable;
> +
> +  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, node, trail));
> +
> +  if (is_mutable && svn_fs__dag_is_directory (node))
> +    {
> +      skel_t *entries, *entry;
> +      SVN_ERR (svn_fs__dag_dir_entries (&entries, node, trail));
> +          
> +      for (entry = entries; entry; entry = entry->next)
> +        {
> +          dag_node_t *child;
> +          skel_t *name_skel;
> +          char *name;
> +
> +          /* ENTRY is guaranteed to be well-formed.  (NAME ID).  */
> +          name_skel = entry->children;
> +          name = apr_palloc (trail->pool, name_skel->len + 1);
> +          memcpy (name, name_skel->data, name_skel->len);
> +          name[name_skel->len] = '\0';
> +          SVN_ERR (svn_fs__dag_open (&child, node,
> +                                     name,
> +                                     trail));
> +    
> +          if (svn_fs__dag_is_file (child)
> +              || svn_fs__dag_is_copy (child))
> +            {
> +              SVN_ERR (svn_fs__dag_delete (node, name, trail));
> +            }
> +          else if (svn_fs__dag_is_directory)
> +            {
> +              svn_boolean_t child_is_mutable;
> +
> +              SVN_ERR (svn_fs__dag_check_mutable (&child_is_mutable,
> +                                                  node, trail));
> +              /* Recurse if this is mutable directory.  */
> +              if (child_is_mutable)
> +                SVN_ERR (delete_nodes (fs, child, trail));
> +
> +              /* Delete this directory regardless of mutable flag.  */
> +              SVN_ERR (svn_fs__dag_delete (node, name, trail));
> +            }
> +          else
> +            abort ();
> +        }
> +    }
> +  else
> +    /* NODE must be a mutable directory or we have logic error.  */
> +    abort ();
> +      
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +
> +static svn_error_t *
> +txn_body_abort_txn (void *baton,
> +                    trail_t *trail)
> +{
> +  svn_fs_txn_t *txn = baton;
> +
> +  dag_node_t *root_node;
> +  svn_boolean_t is_mutable;
> +
> +  SVN_ERR (svn_fs__dag_txn_root (&root_node, txn->fs, txn->id, trail));
> +
> +  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, root_node, trail));
> +
> +  /* It's possible no cloning is done.  */
> +  if (is_mutable)
> +    {
> +      /* Remove all mutable node from `nodes' table.  */
> +      SVN_ERR (delete_nodes (txn->fs, root_node, trail));
> +
> +      /* Remove root node itself */
> +      SVN_ERR (svn_fs__delete_node (txn->fs,
> +                                    svn_fs__dag_get_id (root_node),
> +                                    trail));
> +    }
> +
> +  /* Delete transaction from `transactions' table.  */
> +  SVN_ERR (svn_fs__delete_txn (txn->fs, txn->id, trail));
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +svn_error_t *
> +svn_fs_abort_txn (svn_fs_txn_t *txn)
> +{
> +  /* Remove all mutable nodes reachable from transaction's root.  */
> +  SVN_ERR (svn_fs__retry_txn (txn->fs, txn_body_abort_txn, txn, txn->pool));
> +
> +  SVN_ERR (svn_fs_close_txn (txn));
> +
>    return SVN_NO_ERROR;
>  }
>  
> 
> 
> -- 
> Yoshiki Hayashi