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/05 05:41:48 UTC

[PATCH] svn_fs_commit_txn

I examined nodes table and revisions table by db_dump and
this works fine.  There will also be test cases for this.

* dag.c (make_node_immutable): New helper function for
  stabilize_node.
* dag.c (stabilize_node): New helper function for
  svn_fs__dag_commit_txn.
* dag.c (svn_fs__dag_commit_txn): New function.
* dag.h (svn_fs__dag_commit_txn): Add svn_revnum_t argument
  so that svn_fs_commit_txn can get new revision number.
* rev-table.c (svn_fs__youngest_rev): New function.
* rev-table.c (txn_body_youngest_rev): Call
  svn_fs__youngest_rev.
* rev-table.h (svn_fs__youngest_rev): New function declaration.
* txn.c (struct commit_txn_args): New struct.
* txn.c (txn_body_commit_txn): New helper function for
  svn_fs_commit_txn.
* txn.c (svn_fs_commit_txn): Fill in this function.

Index: subversion/libsvn_fs/dag.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/dag.c,v
retrieving revision 1.70
diff -u -r1.70 dag.c
--- subversion/libsvn_fs/dag.c	2001/03/03 21:10:13	1.70
+++ subversion/libsvn_fs/dag.c	2001/03/05 03:59:27
@@ -574,6 +572,122 @@
 }
 
 
+static svn_error_t *
+make_node_immutable (dag_node_t *node, trail_t *trail)
+{
+  skel_t *node_rev;
+  skel_t *header;
+  skel_t *flag, *prev = NULL;
+
+  /* Go get a fresh NODE-REVISION for this node. */
+  SVN_ERR (get_node_revision (&node_rev, node, trail));
+  /* The node "header" is the first element of a node-revision skel,
+     itself a list. */
+  header = node_rev->children;
+  
+  /* Flag is the 3rd element of the header. */
+  for (flag = header->children->next->next; flag; flag = flag->next)
+    {
+      if (! flag->is_atom && svn_fs__matches_atom (flag->children, "mutable"))
+        {
+          /* We found it.  */
+          if (prev)
+            prev->next = flag->next;
+          else
+            header->children->next->next = 0;
+
+          SVN_ERR (set_node_revision (node, node_rev, trail));
+          return SVN_NO_ERROR;
+        }
+      prev = flag;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+stabilize_node (dag_node_t *node, trail_t *trail)
+{
+  if (svn_fs__dag_is_directory (node))
+    {
+      skel_t *entries;
+      skel_t *entry;
+
+      SVN_ERR (svn_fs__dag_dir_entries (&entries, node, trail));
+
+      /* Each entry looks like (NAME ID).  */
+      for (entry = entries->children; entry; entry = entry->next)
+        {
+          dag_node_t *child;
+          skel_t *id_skel = entry->children->next;
+          svn_boolean_t is_mutable;
+          
+          SVN_ERR (svn_fs__dag_get_node (&child, node->fs,
+                                         svn_fs_parse_id (id_skel->data,
+                                                          id_skel->len,
+                                                          trail->pool),
+                                         trail));
+          SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, child, trail));
+
+          if (is_mutable)
+            SVN_ERR (stabilize_node (child, trail));
+        }
+    }
+  else if (svn_fs__dag_is_file (node)
+           || svn_fs__dag_is_copy (node))
+    ;
+  else
+    abort ();
+
+  SVN_ERR (make_node_immutable (node, trail));
+  SVN_ERR (svn_fs__stable_node (node->fs, node->id, trail));
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_fs__dag_commit_txn (svn_revnum_t *new_rev,
+                        svn_fs_t *fs,
+                        const char *svn_txn,
+                        trail_t *trail)
+{
+  dag_node_t *root;
+  svn_boolean_t is_mutable;
+
+  SVN_ERR (svn_fs__dag_txn_root (&root, fs, svn_txn, trail));
+  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, root, trail));
+
+  /* Nothing is changed in this transaction.  */
+  if (! is_mutable)
+    return SVN_NO_ERROR;
+
+  /* Make all mutable node immutable and stable.  */
+  SVN_ERR (stabilize_node (root, trail));
+
+  {
+    /* Add rew revision entry to `revisions' table.  */
+    skel_t *new_revision_skel;
+    svn_string_t *id_string = svn_fs_unparse_id (root->id, trail->pool);
+
+    new_revision_skel = svn_fs__make_empty_list (trail->pool);
+    svn_fs__prepend (svn_fs__make_empty_list (trail->pool),
+                     new_revision_skel);
+    svn_fs__prepend (svn_fs__mem_atom (id_string->data,
+                                       id_string->len, trail->pool),
+                     new_revision_skel);
+    svn_fs__prepend (svn_fs__str_atom ((char *) "revision", trail->pool),
+                     new_revision_skel);
+    SVN_ERR (svn_fs__put_rev (new_rev, fs, new_revision_skel, trail));
+  }
+  /* Delete transaction from `transactions' table.  */
+  SVN_ERR (svn_fs__delete_txn (fs, svn_txn, trail));
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Helper function for svn_fs__dag_clone_child.
    Given a PARENT directory, and the NAME of an entry in that
    directory, update the PARENT's ENTRY list item for the NAMEd entry
Index: subversion/libsvn_fs/dag.h
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/dag.h,v
retrieving revision 1.31
diff -u -r1.31 dag.h
--- subversion/libsvn_fs/dag.h	2001/03/02 20:32:36	1.31
+++ subversion/libsvn_fs/dag.h	2001/03/05 03:59:27
@@ -158,7 +158,8 @@
                                      trail_t *trail);
 
 
-/* Commit the transaction SVN_TXN in FS, as part of TRAIL.  This entails:
+/* Commit the transaction SVN_TXN in FS, as part of TRAIL.  Store the
+   new revision number in NEW_REV.  This entails:
    - marking the tree of mutable nodes at SVN_TXN's root as immutable,
      and marking all their contents as stable
    - creating a new revision, with SVN_TXN's root as its root directory
@@ -171,7 +172,8 @@
    Do any necessary temporary allocation in a subpool of TRAIL->pool.
    Consume temporary space at most proportional to the maximum depth
    of SVN_TXN's tree of mutable nodes.  */
-svn_error_t *svn_fs__dag_commit_txn (svn_fs_t *fs,
+svn_error_t *svn_fs__dag_commit_txn (svn_revnum_t *new_rev,
+                                     svn_fs_t *fs,
                                      const char *svn_txn,
                                      trail_t *trail);
 
Index: subversion/libsvn_fs/rev-table.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/rev-table.c,v
retrieving revision 1.8
diff -u -r1.8 rev-table.c
--- subversion/libsvn_fs/rev-table.c	2001/03/03 10:18:01	1.8
+++ subversion/libsvn_fs/rev-table.c	2001/03/05 03:59:27
@@ -161,25 +161,16 @@
 /* Getting the youngest revision.  */
 
 
-struct youngest_rev_args {
-  svn_revnum_t *youngest_p;
-  svn_fs_t *fs;
-};
-
-
-static svn_error_t *
-txn_body_youngest_rev (void *baton,
-                       trail_t *trail)
+svn_error_t *
+svn_fs__youngest_rev (svn_revnum_t *youngest_p,
+                      svn_fs_t *fs,
+                      trail_t *trail)
 {
-  struct youngest_rev_args *args = baton;
-
   int db_err;
   DBC *cursor = 0;
   DBT key, value;
   db_recno_t recno;
 
-  svn_fs_t *fs = args->fs;
-
   /* Create a database cursor.  */
   SVN_ERR (DB_WRAP (fs, "getting youngest revision (creating cursor)",
                     fs->revisions->cursor (fs->revisions, trail->db_txn,
@@ -218,7 +209,24 @@
   /* Turn the record number into a Subversion revision number.
      Revisions are numbered starting with zero; Berkeley DB record
      numbers begin with one.  */
-  *args->youngest_p = recno - 1;
+  *youngest_p = recno - 1;
+
+  return SVN_NO_ERROR;
+}
+
+struct youngest_rev_args {
+  svn_revnum_t *youngest_p;
+  svn_fs_t *fs;
+};
+
+
+static svn_error_t *
+txn_body_youngest_rev (void *baton,
+                       trail_t *trail)
+{
+  struct youngest_rev_args *args = baton;
+  SVN_ERR (svn_fs__youngest_rev (args->youngest_p, args->fs, trail));
+
   return SVN_NO_ERROR;
 }
 
Index: subversion/libsvn_fs/rev-table.h
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/rev-table.h,v
retrieving revision 1.5
diff -u -r1.5 rev-table.h
--- subversion/libsvn_fs/rev-table.h	2001/03/03 10:18:01	1.5
+++ subversion/libsvn_fs/rev-table.h	2001/03/05 03:59:27
@@ -63,6 +63,13 @@
                                    trail_t *trail);
 
 
+/* Set *YOUNGEST_P to the number of the youngest revision in filesystem FS,
+   as part of TRAIL.  Use TRAIL->pool for all temporary allocation. */
+svn_error_t *svn_fs__youngest_rev (svn_revnum_t *youngest_p,
+                                   svn_fs_t *fs,
+                                   trail_t *trail);
+
+
 #endif /* SVN_LIBSVN_FS_REV_TABLE_H */
 
 
Index: subversion/libsvn_fs/txn.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_fs/txn.c,v
retrieving revision 1.29
diff -u -r1.29 txn.c
--- subversion/libsvn_fs/txn.c	2001/03/03 17:25:38	1.29
+++ subversion/libsvn_fs/txn.c	2001/03/05 05:34:51
@@ -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"
 
 
@@ -146,12 +148,50 @@
 }
 
 
+struct commit_txn_args {
+  svn_revnum_t *new_rev;
+  svn_fs_txn_t *txn;
+};
+
+
+static svn_error_t *
+txn_body_commit_txn (void *baton, trail_t *trail)
+{
+  struct commit_txn_args *args = baton;
+
+  svn_fs_txn_t *txn = args->txn;
+  svn_revnum_t youngest_rev;
+  svn_fs_id_t *ignored, *base_root_id, *current_root_id;
+
+  /* Is there already a new revision?  */
+  SVN_ERR (svn_fs__get_txn (&ignored, &base_root_id, txn->fs,
+                            txn->id, trail));
+  SVN_ERR (svn_fs__youngest_rev (&youngest_rev, txn->fs, trail));
+  SVN_ERR (svn_fs__rev_get_root (&current_root_id, txn->fs,
+                                 youngest_rev, trail));
+  if (! svn_fs_id_eq (base_root_id, current_root_id))
+    return svn_error_createf (SVN_ERR_FS_CONFLICT, 0, 0, trail->pool,
+                              "Trying to commit old transaction, ID `%s'",
+                              txn->id);
+  
+  SVN_ERR (svn_fs__dag_commit_txn (args->new_rev, txn->fs, txn->id, trail));
+
+  return SVN_NO_ERROR;
+}
+
+
 svn_error_t *
 svn_fs_commit_txn (svn_revnum_t *new_rev, 
                    svn_fs_txn_t *txn)
 {
+  struct commit_txn_args args;
+
   *new_rev = SVN_INVALID_REVNUM;
-  abort();
+  args.new_rev = new_rev;
+  args.txn = txn;
+
+  SVN_ERR (svn_fs__retry_txn (txn->fs, txn_body_commit_txn, &args, txn->pool));
+
   return SVN_NO_ERROR;
 }
 


-- 
Yoshiki Hayashi

Re: [PATCH] svn_fs_commit_txn

Posted by Karl Fogel <kf...@galois.collab.net>.
Karl Fogel <kf...@galois.collab.net> writes:
> Yoshiki Hayashi <yo...@xemacs.org> writes:
> > Yeah, I've read it.  My interpreteation is that svn_fs_merge
> > should be called by an editor when svn_fs_commit_txn returns
> > SVN_ERR_FS_CONFLICT error.  If svn_fs_commit_txn
> > automatically calls svn_fs_merge, there's no way to return
> > conflict_p to the caller of svn_fs_commit_txn.
> 
> Yes, there is -- svn_fs_commit_txn can just return the appropriate
> error, as its doc string says.

Sorry, Yoshiki.  I misread your first paragraph, and gave an
irrelevant (and needlessly obnoxious, sorry) response above.  You
weren't asking about the error, but about the conflict_p thing.  

Probably svn_fs_commit_txn should return conflict_p too, just passing
up whatever it got from svn_fs_merge.  We should make failures provide
as much useful information as possible.

-Karl

> There are two kinds of "conflicts" here.  Some conflicts cannot be
> resolved by the repository.  For example, if two users made changes to
> the same file, then someone is going to have to resolve that
> conflict.  The repository cannot do it.
> 
> But other conflicts can be resolved by the repository -- for example,
> when two users add different new files to the same directory.  Say
> that first Jane adds foo.c, and then Bill adds bar.c, to directory D,
> and both started with the *same* base revision of D.  It's true that
> when bar.c gets committed, D has already changed since base (because D
> has received foo.c), but this can and should be automatically resolved
> by the filesystem, through the appropriate merge.  This is the sort of
> situation svn_fs_merge handles gracefully.
> 
> Here's a question that might help you: If the caller of
> svn_fs_commit_txn were also responsible for calling svn_fs_merge, then
> why does svn_fs_commit_txn *return* the new revision number by
> reference?  In the scenario you're describing, the caller would know
> that number already, and wouldn't need to be told it.
> 
> This is why I'm saying that callers of svn_fs.h just call
> svn_fs_commit_txn and let it do all the hard work.  I understand it's
> confusing that svn_fs_merge is also a public interface, but I think
> Jim just put it there because of an intuition that it might be useful
> to expose it.  Nevertheless, svn_fs_commit_txn should call
> svn_fs_merge internally.
> 
> -Karl
> 
> > do
> >   commit_retval = svn_fs_commit_txn
> >     if commit_retval equals SVN_ERR_FS_CONFLICT
> >       do
> >         retval = svn_fs_merge (merge differences between
> >                                base and latest fs revision)
> >         if retval equals SVN_ERR_FS_CONFLICT
> >           do something to resolve conflict (possibly with user interaction?)
> >           or abort the svn transaction
> >       while retval equals SVN_ERR_FS_CONFLICT
> > while commit_retval equals SVN_ERR_FS_CONFLICT
> > 
> > is how I see resolving conflict works.
> > 
> > -- 
> > Yoshiki Hayashi

Re: [PATCH] svn_fs_commit_txn

Posted by Karl Fogel <kf...@galois.collab.net>.
Yoshiki Hayashi <yo...@xemacs.org> writes:
> > Yoshiki, have you read the section "Merge rules" in
> > libsvn_fs/structure, or looked at the interface to svn_fs_merge() in
> > svn_fs.h?
> 
> Yeah, I've read it.  My interpreteation is that svn_fs_merge
> should be called by an editor when svn_fs_commit_txn returns
> SVN_ERR_FS_CONFLICT error.  If svn_fs_commit_txn
> automatically calls svn_fs_merge, there's no way to return
> conflict_p to the caller of svn_fs_commit_txn.

Yes, there is -- svn_fs_commit_txn can just return the appropriate
error, as its doc string says.

There are two kinds of "conflicts" here.  Some conflicts cannot be
resolved by the repository.  For example, if two users made changes to
the same file, then someone is going to have to resolve that
conflict.  The repository cannot do it.

But other conflicts can be resolved by the repository -- for example,
when two users add different new files to the same directory.  Say
that first Jane adds foo.c, and then Bill adds bar.c, to directory D,
and both started with the *same* base revision of D.  It's true that
when bar.c gets committed, D has already changed since base (because D
has received foo.c), but this can and should be automatically resolved
by the filesystem, through the appropriate merge.  This is the sort of
situation svn_fs_merge handles gracefully.

Here's a question that might help you: If the caller of
svn_fs_commit_txn were also responsible for calling svn_fs_merge, then
why does svn_fs_commit_txn *return* the new revision number by
reference?  In the scenario you're describing, the caller would know
that number already, and wouldn't need to be told it.

This is why I'm saying that callers of svn_fs.h just call
svn_fs_commit_txn and let it do all the hard work.  I understand it's
confusing that svn_fs_merge is also a public interface, but I think
Jim just put it there because of an intuition that it might be useful
to expose it.  Nevertheless, svn_fs_commit_txn should call
svn_fs_merge internally.

-Karl

> do
>   commit_retval = svn_fs_commit_txn
>     if commit_retval equals SVN_ERR_FS_CONFLICT
>       do
>         retval = svn_fs_merge (merge differences between
>                                base and latest fs revision)
>         if retval equals SVN_ERR_FS_CONFLICT
>           do something to resolve conflict (possibly with user interaction?)
>           or abort the svn transaction
>       while retval equals SVN_ERR_FS_CONFLICT
> while commit_retval equals SVN_ERR_FS_CONFLICT
> 
> is how I see resolving conflict works.
> 
> -- 
> Yoshiki Hayashi

Re: [PATCH] svn_fs_commit_txn

Posted by Karl Fogel <kf...@galois.collab.net>.
Jim Blandy <ji...@savonarola.red-bean.com> writes:
> Please don't take the current prototypes for svn_fs_commit_txn and
> svn_fs_merge as holy writ.  Perhaps svn_fs_commit_txn should simply be
> extended to hand back the same information that svn_fs_merge would, as
> generated by the internal merge attempt.
> 
> To be honest, I have no idea even what svn_fs_merge should return; I'd
> be happy to see its interface changed to return something clean and
> sufficient and well-defined.  I just couldn't see what that should be.
> 
> For example: the current interface for svn_fs_merge just returns the
> name of a conflicting file; should it return a list of all conflict
> points?  Should there be some way to say what you want?

Yah; will feel free to tweak as necessary, thanks.

-K

> > Yoshiki Hayashi <yo...@xemacs.org> writes:
> > 
> > > Karl Fogel <kf...@galois.ch.collab.net> writes:
> > > 
> > > > Yoshiki, have you read the section "Merge rules" in
> > > > libsvn_fs/structure, or looked at the interface to svn_fs_merge() in
> > > > svn_fs.h?
> > > 
> > > Yeah, I've read it.  My interpreteation is that svn_fs_merge
> > > should be called by an editor when svn_fs_commit_txn returns
> > > SVN_ERR_FS_CONFLICT error.  If svn_fs_commit_txn
> > > automatically calls svn_fs_merge, there's no way to return
> > > conflict_p to the caller of svn_fs_commit_txn.
> > > 
> > > do
> > >   commit_retval = svn_fs_commit_txn
> > >     if commit_retval equals SVN_ERR_FS_CONFLICT
> > >       do
> > >         retval = svn_fs_merge (merge differences between
> > >                                base and latest fs revision)
> > >         if retval equals SVN_ERR_FS_CONFLICT
> > >           do something to resolve conflict (possibly with user interaction?)
> > >           or abort the svn transaction
> > >       while retval equals SVN_ERR_FS_CONFLICT
> > > while commit_retval equals SVN_ERR_FS_CONFLICT
> > > 
> > > is how I see resolving conflict works.

Re: [PATCH] svn_fs_commit_txn

Posted by Jim Blandy <ji...@savonarola.red-bean.com>.
Please don't take the current prototypes for svn_fs_commit_txn and
svn_fs_merge as holy writ.  Perhaps svn_fs_commit_txn should simply be
extended to hand back the same information that svn_fs_merge would, as
generated by the internal merge attempt.

To be honest, I have no idea even what svn_fs_merge should return; I'd
be happy to see its interface changed to return something clean and
sufficient and well-defined.  I just couldn't see what that should be.

For example: the current interface for svn_fs_merge just returns the
name of a conflicting file; should it return a list of all conflict
points?  Should there be some way to say what you want?



> Yoshiki Hayashi <yo...@xemacs.org> writes:
> 
> > Karl Fogel <kf...@galois.ch.collab.net> writes:
> > 
> > > Yoshiki, have you read the section "Merge rules" in
> > > libsvn_fs/structure, or looked at the interface to svn_fs_merge() in
> > > svn_fs.h?
> > 
> > Yeah, I've read it.  My interpreteation is that svn_fs_merge
> > should be called by an editor when svn_fs_commit_txn returns
> > SVN_ERR_FS_CONFLICT error.  If svn_fs_commit_txn
> > automatically calls svn_fs_merge, there's no way to return
> > conflict_p to the caller of svn_fs_commit_txn.
> > 
> > do
> >   commit_retval = svn_fs_commit_txn
> >     if commit_retval equals SVN_ERR_FS_CONFLICT
> >       do
> >         retval = svn_fs_merge (merge differences between
> >                                base and latest fs revision)
> >         if retval equals SVN_ERR_FS_CONFLICT
> >           do something to resolve conflict (possibly with user interaction?)
> >           or abort the svn transaction
> >       while retval equals SVN_ERR_FS_CONFLICT
> > while commit_retval equals SVN_ERR_FS_CONFLICT
> > 
> > is how I see resolving conflict works.

Re: [PATCH] svn_fs_commit_txn

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

> Yoshiki, have you read the section "Merge rules" in
> libsvn_fs/structure, or looked at the interface to svn_fs_merge() in
> svn_fs.h?

Yeah, I've read it.  My interpreteation is that svn_fs_merge
should be called by an editor when svn_fs_commit_txn returns
SVN_ERR_FS_CONFLICT error.  If svn_fs_commit_txn
automatically calls svn_fs_merge, there's no way to return
conflict_p to the caller of svn_fs_commit_txn.

do
  commit_retval = svn_fs_commit_txn
    if commit_retval equals SVN_ERR_FS_CONFLICT
      do
        retval = svn_fs_merge (merge differences between
                               base and latest fs revision)
        if retval equals SVN_ERR_FS_CONFLICT
          do something to resolve conflict (possibly with user interaction?)
          or abort the svn transaction
      while retval equals SVN_ERR_FS_CONFLICT
while commit_retval equals SVN_ERR_FS_CONFLICT

is how I see resolving conflict works.

-- 
Yoshiki Hayashi

Re: [PATCH] svn_fs_commit_txn

Posted by Karl Fogel <kf...@galois.collab.net>.
Yoshiki, have you read the section "Merge rules" in
libsvn_fs/structure, or looked at the interface to svn_fs_merge() in
svn_fs.h?

I think committing a transaction is a lot more complex than the patch
below implements.  We can't just error out if a new revision has been
committed since the base revision against which we're trying to
commit.  Subversion wouldn't be very usable then. :-)

I'm starting work on this right now, just wanted to send out this
feedback early.

Best,
-Karl

Yoshiki Hayashi <yo...@xemacs.org> writes:
> I examined nodes table and revisions table by db_dump and
> this works fine.  There will also be test cases for this.
> 
> * dag.c (make_node_immutable): New helper function for
>   stabilize_node.
> * dag.c (stabilize_node): New helper function for
>   svn_fs__dag_commit_txn.
> * dag.c (svn_fs__dag_commit_txn): New function.
> * dag.h (svn_fs__dag_commit_txn): Add svn_revnum_t argument
>   so that svn_fs_commit_txn can get new revision number.
> * rev-table.c (svn_fs__youngest_rev): New function.
> * rev-table.c (txn_body_youngest_rev): Call
>   svn_fs__youngest_rev.
> * rev-table.h (svn_fs__youngest_rev): New function declaration.
> * txn.c (struct commit_txn_args): New struct.
> * txn.c (txn_body_commit_txn): New helper function for
>   svn_fs_commit_txn.
> * txn.c (svn_fs_commit_txn): Fill in this function.
> 
> Index: subversion/libsvn_fs/dag.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/dag.c,v
> retrieving revision 1.70
> diff -u -r1.70 dag.c
> --- subversion/libsvn_fs/dag.c	2001/03/03 21:10:13	1.70
> +++ subversion/libsvn_fs/dag.c	2001/03/05 03:59:27
> @@ -574,6 +572,122 @@
>  }
>  
>  
> +static svn_error_t *
> +make_node_immutable (dag_node_t *node, trail_t *trail)
> +{
> +  skel_t *node_rev;
> +  skel_t *header;
> +  skel_t *flag, *prev = NULL;
> +
> +  /* Go get a fresh NODE-REVISION for this node. */
> +  SVN_ERR (get_node_revision (&node_rev, node, trail));
> +  /* The node "header" is the first element of a node-revision skel,
> +     itself a list. */
> +  header = node_rev->children;
> +  
> +  /* Flag is the 3rd element of the header. */
> +  for (flag = header->children->next->next; flag; flag = flag->next)
> +    {
> +      if (! flag->is_atom && svn_fs__matches_atom (flag->children, "mutable"))
> +        {
> +          /* We found it.  */
> +          if (prev)
> +            prev->next = flag->next;
> +          else
> +            header->children->next->next = 0;
> +
> +          SVN_ERR (set_node_revision (node, node_rev, trail));
> +          return SVN_NO_ERROR;
> +        }
> +      prev = flag;
> +    }
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +static svn_error_t *
> +stabilize_node (dag_node_t *node, trail_t *trail)
> +{
> +  if (svn_fs__dag_is_directory (node))
> +    {
> +      skel_t *entries;
> +      skel_t *entry;
> +
> +      SVN_ERR (svn_fs__dag_dir_entries (&entries, node, trail));
> +
> +      /* Each entry looks like (NAME ID).  */
> +      for (entry = entries->children; entry; entry = entry->next)
> +        {
> +          dag_node_t *child;
> +          skel_t *id_skel = entry->children->next;
> +          svn_boolean_t is_mutable;
> +          
> +          SVN_ERR (svn_fs__dag_get_node (&child, node->fs,
> +                                         svn_fs_parse_id (id_skel->data,
> +                                                          id_skel->len,
> +                                                          trail->pool),
> +                                         trail));
> +          SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, child, trail));
> +
> +          if (is_mutable)
> +            SVN_ERR (stabilize_node (child, trail));
> +        }
> +    }
> +  else if (svn_fs__dag_is_file (node)
> +           || svn_fs__dag_is_copy (node))
> +    ;
> +  else
> +    abort ();
> +
> +  SVN_ERR (make_node_immutable (node, trail));
> +  SVN_ERR (svn_fs__stable_node (node->fs, node->id, trail));
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +
> +svn_error_t *
> +svn_fs__dag_commit_txn (svn_revnum_t *new_rev,
> +                        svn_fs_t *fs,
> +                        const char *svn_txn,
> +                        trail_t *trail)
> +{
> +  dag_node_t *root;
> +  svn_boolean_t is_mutable;
> +
> +  SVN_ERR (svn_fs__dag_txn_root (&root, fs, svn_txn, trail));
> +  SVN_ERR (svn_fs__dag_check_mutable (&is_mutable, root, trail));
> +
> +  /* Nothing is changed in this transaction.  */
> +  if (! is_mutable)
> +    return SVN_NO_ERROR;
> +
> +  /* Make all mutable node immutable and stable.  */
> +  SVN_ERR (stabilize_node (root, trail));
> +
> +  {
> +    /* Add rew revision entry to `revisions' table.  */
> +    skel_t *new_revision_skel;
> +    svn_string_t *id_string = svn_fs_unparse_id (root->id, trail->pool);
> +
> +    new_revision_skel = svn_fs__make_empty_list (trail->pool);
> +    svn_fs__prepend (svn_fs__make_empty_list (trail->pool),
> +                     new_revision_skel);
> +    svn_fs__prepend (svn_fs__mem_atom (id_string->data,
> +                                       id_string->len, trail->pool),
> +                     new_revision_skel);
> +    svn_fs__prepend (svn_fs__str_atom ((char *) "revision", trail->pool),
> +                     new_revision_skel);
> +    SVN_ERR (svn_fs__put_rev (new_rev, fs, new_revision_skel, trail));
> +  }
> +  /* Delete transaction from `transactions' table.  */
> +  SVN_ERR (svn_fs__delete_txn (fs, svn_txn, trail));
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +
>  /* Helper function for svn_fs__dag_clone_child.
>     Given a PARENT directory, and the NAME of an entry in that
>     directory, update the PARENT's ENTRY list item for the NAMEd entry
> Index: subversion/libsvn_fs/dag.h
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/dag.h,v
> retrieving revision 1.31
> diff -u -r1.31 dag.h
> --- subversion/libsvn_fs/dag.h	2001/03/02 20:32:36	1.31
> +++ subversion/libsvn_fs/dag.h	2001/03/05 03:59:27
> @@ -158,7 +158,8 @@
>                                       trail_t *trail);
>  
>  
> -/* Commit the transaction SVN_TXN in FS, as part of TRAIL.  This entails:
> +/* Commit the transaction SVN_TXN in FS, as part of TRAIL.  Store the
> +   new revision number in NEW_REV.  This entails:
>     - marking the tree of mutable nodes at SVN_TXN's root as immutable,
>       and marking all their contents as stable
>     - creating a new revision, with SVN_TXN's root as its root directory
> @@ -171,7 +172,8 @@
>     Do any necessary temporary allocation in a subpool of TRAIL->pool.
>     Consume temporary space at most proportional to the maximum depth
>     of SVN_TXN's tree of mutable nodes.  */
> -svn_error_t *svn_fs__dag_commit_txn (svn_fs_t *fs,
> +svn_error_t *svn_fs__dag_commit_txn (svn_revnum_t *new_rev,
> +                                     svn_fs_t *fs,
>                                       const char *svn_txn,
>                                       trail_t *trail);
>  
> Index: subversion/libsvn_fs/rev-table.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/rev-table.c,v
> retrieving revision 1.8
> diff -u -r1.8 rev-table.c
> --- subversion/libsvn_fs/rev-table.c	2001/03/03 10:18:01	1.8
> +++ subversion/libsvn_fs/rev-table.c	2001/03/05 03:59:27
> @@ -161,25 +161,16 @@
>  /* Getting the youngest revision.  */
>  
>  
> -struct youngest_rev_args {
> -  svn_revnum_t *youngest_p;
> -  svn_fs_t *fs;
> -};
> -
> -
> -static svn_error_t *
> -txn_body_youngest_rev (void *baton,
> -                       trail_t *trail)
> +svn_error_t *
> +svn_fs__youngest_rev (svn_revnum_t *youngest_p,
> +                      svn_fs_t *fs,
> +                      trail_t *trail)
>  {
> -  struct youngest_rev_args *args = baton;
> -
>    int db_err;
>    DBC *cursor = 0;
>    DBT key, value;
>    db_recno_t recno;
>  
> -  svn_fs_t *fs = args->fs;
> -
>    /* Create a database cursor.  */
>    SVN_ERR (DB_WRAP (fs, "getting youngest revision (creating cursor)",
>                      fs->revisions->cursor (fs->revisions, trail->db_txn,
> @@ -218,7 +209,24 @@
>    /* Turn the record number into a Subversion revision number.
>       Revisions are numbered starting with zero; Berkeley DB record
>       numbers begin with one.  */
> -  *args->youngest_p = recno - 1;
> +  *youngest_p = recno - 1;
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +struct youngest_rev_args {
> +  svn_revnum_t *youngest_p;
> +  svn_fs_t *fs;
> +};
> +
> +
> +static svn_error_t *
> +txn_body_youngest_rev (void *baton,
> +                       trail_t *trail)
> +{
> +  struct youngest_rev_args *args = baton;
> +  SVN_ERR (svn_fs__youngest_rev (args->youngest_p, args->fs, trail));
> +
>    return SVN_NO_ERROR;
>  }
>  
> Index: subversion/libsvn_fs/rev-table.h
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/rev-table.h,v
> retrieving revision 1.5
> diff -u -r1.5 rev-table.h
> --- subversion/libsvn_fs/rev-table.h	2001/03/03 10:18:01	1.5
> +++ subversion/libsvn_fs/rev-table.h	2001/03/05 03:59:27
> @@ -63,6 +63,13 @@
>                                     trail_t *trail);
>  
>  
> +/* Set *YOUNGEST_P to the number of the youngest revision in filesystem FS,
> +   as part of TRAIL.  Use TRAIL->pool for all temporary allocation. */
> +svn_error_t *svn_fs__youngest_rev (svn_revnum_t *youngest_p,
> +                                   svn_fs_t *fs,
> +                                   trail_t *trail);
> +
> +
>  #endif /* SVN_LIBSVN_FS_REV_TABLE_H */
>  
>  
> Index: subversion/libsvn_fs/txn.c
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_fs/txn.c,v
> retrieving revision 1.29
> diff -u -r1.29 txn.c
> --- subversion/libsvn_fs/txn.c	2001/03/03 17:25:38	1.29
> +++ subversion/libsvn_fs/txn.c	2001/03/05 05:34:51
> @@ -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"
>  
>  
> @@ -146,12 +148,50 @@
>  }
>  
>  
> +struct commit_txn_args {
> +  svn_revnum_t *new_rev;
> +  svn_fs_txn_t *txn;
> +};
> +
> +
> +static svn_error_t *
> +txn_body_commit_txn (void *baton, trail_t *trail)
> +{
> +  struct commit_txn_args *args = baton;
> +
> +  svn_fs_txn_t *txn = args->txn;
> +  svn_revnum_t youngest_rev;
> +  svn_fs_id_t *ignored, *base_root_id, *current_root_id;
> +
> +  /* Is there already a new revision?  */
> +  SVN_ERR (svn_fs__get_txn (&ignored, &base_root_id, txn->fs,
> +                            txn->id, trail));
> +  SVN_ERR (svn_fs__youngest_rev (&youngest_rev, txn->fs, trail));
> +  SVN_ERR (svn_fs__rev_get_root (&current_root_id, txn->fs,
> +                                 youngest_rev, trail));
> +  if (! svn_fs_id_eq (base_root_id, current_root_id))
> +    return svn_error_createf (SVN_ERR_FS_CONFLICT, 0, 0, trail->pool,
> +                              "Trying to commit old transaction, ID `%s'",
> +                              txn->id);
> +  
> +  SVN_ERR (svn_fs__dag_commit_txn (args->new_rev, txn->fs, txn->id, trail));
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +
>  svn_error_t *
>  svn_fs_commit_txn (svn_revnum_t *new_rev, 
>                     svn_fs_txn_t *txn)
>  {
> +  struct commit_txn_args args;
> +
>    *new_rev = SVN_INVALID_REVNUM;
> -  abort();
> +  args.new_rev = new_rev;
> +  args.txn = txn;
> +
> +  SVN_ERR (svn_fs__retry_txn (txn->fs, txn_body_commit_txn, &args, txn->pool));
> +
>    return SVN_NO_ERROR;
>  }
>  
> 
> 
> -- 
> Yoshiki Hayashi