subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stef...@apache.org
Subject svn commit: r1525429 - in /subversion/trunk/subversion/libsvn_fs_x: changes.c low_level.c transaction.c tree.c
Date Sun, 22 Sep 2013 20:17:26 GMT
Author: stefan2
Date: Sun Sep 22 20:17:26 2013
New Revision: 1525429

URL: http://svn.apache.org/r1525429
Log:
Add MOVe support to FSX.  This is mainly duplicating changes from FSFS.

* subversion/libsvn_fs_x/changes.c
  (CHANGE_KIND_MOVE,
   CHANGE_KIND_MOVEREPLACE): pro forma declaration of the new change kinds

* subversion/libsvn_fs_x/low_level.c
  (ACTION_MOVE,
   ACTION_MOVEREPLACE): declare new change type strings
  (read_change,
   write_change_entry): write / parse the new change types

* subversion/libsvn_fs_x/transaction.c
  (replace_change):  factored out from ...
  (fold_change): ... this one; handle moves similar to adds
  (process_changes): handle move-replaces similar to replaces
  (write_final_changed_path_info): update move source revs to Rev-1;
                     make changed_paths an input parameter
  (check_for_duplicate_move_source,
   verify_moves): new move verification code
  (commit_body):  verify moves when finalizing the commit;
                  update function all

* subversion/libsvn_fs_x/tree.c
  (enum copy_type_t): declare new parameter type
  (copy_helper): support moves just like ADDs; add extra param checks
  (x_copy,
   x_revision_link): update callers
  (x_move): add new function for MOVes

Modified:
    subversion/trunk/subversion/libsvn_fs_x/changes.c
    subversion/trunk/subversion/libsvn_fs_x/low_level.c
    subversion/trunk/subversion/libsvn_fs_x/transaction.c
    subversion/trunk/subversion/libsvn_fs_x/tree.c

Modified: subversion/trunk/subversion/libsvn_fs_x/changes.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/changes.c?rev=1525429&r1=1525428&r2=1525429&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/changes.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/changes.c Sun Sep 22 20:17:26 2013
@@ -60,6 +60,8 @@
 #define CHANGE_KIND_DELETE  0x00040
 #define CHANGE_KIND_REPLACE 0x00060
 #define CHANGE_KIND_RESET   0x00080
+#define CHANGE_KIND_MOVE    0x000A0
+#define CHANGE_KIND_MOVEREPLACE 0x000C0
 
 /* Our internal representation of a change */
 typedef struct binary_change_t

Modified: subversion/trunk/subversion/libsvn_fs_x/low_level.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/low_level.c?rev=1525429&r1=1525428&r2=1525429&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/low_level.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/low_level.c Sun Sep 22 20:17:26 2013
@@ -53,6 +53,8 @@
 #define ACTION_DELETE      "delete"
 #define ACTION_REPLACE     "replace"
 #define ACTION_RESET       "reset"
+#define ACTION_MOVE        "move"
+#define ACTION_MOVEREPLACE "movereplace"
 
 /* True and False flags. */
 #define FLAG_TRUE          "true"
@@ -828,6 +830,14 @@ read_change(change_t **change_p,
     {
       info->change_kind = svn_fs_path_change_reset;
     }
+  else if (strcmp(str, ACTION_MOVE) == 0)
+    {
+      info->change_kind = svn_fs_path_change_move;
+    }
+  else if (strcmp(str, ACTION_MOVEREPLACE) == 0)
+    {
+      info->change_kind = svn_fs_path_change_movereplace;
+    }
   else
     {
       return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -961,6 +971,12 @@ write_change_entry(svn_stream_t *stream,
     case svn_fs_path_change_reset:
       change_string = ACTION_RESET;
       break;
+    case svn_fs_path_change_move:
+      change_string = ACTION_MOVE;
+      break;
+    case svn_fs_path_change_movereplace:
+      change_string = ACTION_MOVEREPLACE;
+      break;
     default:
       return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                                _("Invalid change type %d"),

Modified: subversion/trunk/subversion/libsvn_fs_x/transaction.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/transaction.c?rev=1525429&r1=1525428&r2=1525429&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/transaction.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/transaction.c Sun Sep 22 20:17:26 2013
@@ -646,6 +646,33 @@ unparse_dir_entries(apr_hash_t **str_ent
   return SVN_NO_ERROR;
 }
 
+/* Copy the contents of NEW_CHANGE into OLD_CHANGE assuming that both
+   belong to the same path.  Allocate copies in POOL.
+ */
+static void
+replace_change(svn_fs_path_change2_t *old_change,
+               const svn_fs_path_change2_t *new_change,
+               apr_pool_t *pool)
+{
+  /* An add at this point must be following a previous delete,
+      so treat it just like a replace. */
+  old_change->node_kind = new_change->node_kind;
+  old_change->node_rev_id = svn_fs_x__id_copy(new_change->node_rev_id, pool);
+  old_change->text_mod = new_change->text_mod;
+  old_change->prop_mod = new_change->prop_mod;
+  if (new_change->copyfrom_rev == SVN_INVALID_REVNUM)
+    {
+      old_change->copyfrom_rev = SVN_INVALID_REVNUM;
+      old_change->copyfrom_path = NULL;
+    }
+  else
+    {
+      old_change->copyfrom_rev = new_change->copyfrom_rev;
+      old_change->copyfrom_path = apr_pstrdup(pool,
+                                              new_change->copyfrom_path);
+    }
+}
+
 /* Merge the internal-use-only CHANGE into a hash of public-FS
    svn_fs_path_change2_t CHANGES, collapsing multiple changes into a
    single summarical (is that real word?) change per path.  Also keep
@@ -683,11 +710,13 @@ fold_change(apr_hash_t *changes,
            _("Invalid change ordering: new node revision ID "
              "without delete"));
 
-      /* Sanity check: an add, replacement, or reset must be the first
+      /* Sanity check: an add, replacement, move, or reset must be the first
          thing to follow a deletion. */
       if ((old_change->change_kind == svn_fs_path_change_delete)
           && (! ((info->change_kind == svn_fs_path_change_replace)
                  || (info->change_kind == svn_fs_path_change_reset)
+                 || (info->change_kind == svn_fs_path_change_movereplace)
+                 || (info->change_kind == svn_fs_path_change_move)
                  || (info->change_kind == svn_fs_path_change_add))))
         return svn_error_create
           (SVN_ERR_FS_CORRUPT, NULL,
@@ -712,7 +741,8 @@ fold_change(apr_hash_t *changes,
           break;
 
         case svn_fs_path_change_delete:
-          if (old_change->change_kind == svn_fs_path_change_add)
+          if ((old_change->change_kind == svn_fs_path_change_add)
+              || (old_change->change_kind == svn_fs_path_change_move))
             {
               /* If the path was introduced in this transaction via an
                  add, and we are deleting it, just remove the path
@@ -734,22 +764,16 @@ fold_change(apr_hash_t *changes,
         case svn_fs_path_change_replace:
           /* An add at this point must be following a previous delete,
              so treat it just like a replace. */
+          replace_change(old_change, info, pool);
           old_change->change_kind = svn_fs_path_change_replace;
-          old_change->node_rev_id = svn_fs_x__id_copy(info->node_rev_id,
-                                                      pool);
-          old_change->text_mod = info->text_mod;
-          old_change->prop_mod = info->prop_mod;
-          if (info->copyfrom_rev == SVN_INVALID_REVNUM)
-            {
-              old_change->copyfrom_rev = SVN_INVALID_REVNUM;
-              old_change->copyfrom_path = NULL;
-            }
-          else
-            {
-              old_change->copyfrom_rev = info->copyfrom_rev;
-              old_change->copyfrom_path = apr_pstrdup(pool,
-                                                      info->copyfrom_path);
-            }
+          break;
+
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
+          /* A move at this point must be following a previous delete,
+             so treat it just like a replacing move. */
+          replace_change(old_change, info, pool);
+          old_change->change_kind = svn_fs_path_change_movereplace;
           break;
 
         case svn_fs_path_change_modify:
@@ -816,7 +840,8 @@ process_changes(apr_hash_t *changed_path
       */
 
       if ((change->info.change_kind == svn_fs_path_change_delete)
-           || (change->info.change_kind == svn_fs_path_change_replace))
+           || (change->info.change_kind == svn_fs_path_change_replace)
+           || (change->info.change_kind == svn_fs_path_change_movereplace))
         {
           apr_hash_index_t *hi;
 
@@ -2655,25 +2680,37 @@ write_final_rev(const svn_fs_id_t **new_
   return SVN_NO_ERROR;
 }
 
-/* Write the changed path info from transaction TXN_ID in filesystem
-   FS to the permanent rev-file FILE.  *OFFSET_P is set the to offset
-   in the file of the beginning of this information.  Perform
-   temporary allocations in POOL. */
+/* Write the changed path info CHANGED_PATHS of transaction TXN_ID to the
+   permanent rev-file FILE representing NEW_REV in filesystem FS.  *OFFSET_P
+   is set the to offset in the file of the beginning of this information.
+   Perform temporary allocations in POOL. */
 static svn_error_t *
 write_final_changed_path_info(apr_off_t *offset_p,
                               apr_file_t *file,
                               svn_fs_t *fs,
                               const svn_fs_x__id_part_t *txn_id,
+                              apr_hash_t *changed_paths,
+                              svn_revnum_t new_rev,
                               apr_pool_t *pool)
 {
-  apr_hash_t *changed_paths;
   apr_off_t offset;
+  apr_hash_index_t *hi;
   svn_fs_x__p2l_entry_t entry;
   svn_fs_x__id_part_t rev_item
     = {SVN_INVALID_REVNUM, SVN_FS_X__ITEM_INDEX_CHANGES};
 
   SVN_ERR(svn_fs_x__get_file_offset(&offset, file, pool));
-  SVN_ERR(svn_fs_x__txn_changes_fetch(&changed_paths, fs, txn_id, pool));
+
+  /* all moves specify the "copy-from-rev" as REV-1 */
+  for (hi = apr_hash_first(pool, changed_paths); hi; hi = apr_hash_next(hi))
+    {
+      svn_fs_path_change2_t *change;
+      apr_hash_this(hi, NULL, NULL, (void **)&change);
+
+      if (change->change_kind == svn_fs_path_change_move)
+        change->copyfrom_rev = new_rev - 1;
+    }
+
   SVN_ERR(svn_fs_x__write_changes(svn_stream_from_aprfile2(file, TRUE, pool),
                                   fs, changed_paths, TRUE, pool));
 
@@ -2814,6 +2851,163 @@ verify_locks(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+/* If CHANGE is move, verify that there is no other move with the same
+   copy-from path in SOURCE_PATHS already (parent or sub-node moves are fine).
+   Add the source path to SOURCE_PATHS after successful verification. */
+static svn_error_t *
+check_for_duplicate_move_source(apr_hash_t *source_paths,
+                                change_t *change)
+{
+  if (   change->info.change_kind == svn_fs_path_change_move
+      || change->info.change_kind == svn_fs_path_change_movereplace)
+    if (change->info.copyfrom_path)
+      {
+        if (apr_hash_get(source_paths, change->info.copyfrom_path,
+                         APR_HASH_KEY_STRING))
+          return svn_error_createf(SVN_ERR_FS_AMBIGUOUS_MOVE, NULL,
+                      _("Path '%s' has been moved to more than one target"),
+                                   change->info.copyfrom_path);
+
+        apr_hash_set(source_paths, change->info.copyfrom_path,
+                     APR_HASH_KEY_STRING, change->info.copyfrom_path);
+      }
+
+  return SVN_NO_ERROR;
+}
+
+/* Verify that the moves we are about to commit with TXN_ID in FS are unique
+   and the respective copy sources have been deleted.  OLD_REV is the last
+   committed revision.  CHANGED_PATHS is the list of changes paths in this
+   txn.  Use POOL for temporary allocations. */
+static svn_error_t *
+verify_moves(svn_fs_t *fs,
+             const svn_fs_x__id_part_t *txn_id,
+             svn_revnum_t old_rev,
+             apr_hash_t *changed_paths,
+             apr_pool_t *pool)
+{
+  apr_hash_t *source_paths = apr_hash_make(pool);
+  svn_revnum_t revision;
+  apr_pool_t *iter_pool = svn_pool_create(pool);
+  apr_hash_index_t *hi;
+  int i;
+  apr_array_header_t *moves
+    = apr_array_make(pool, 16, sizeof(svn_sort__item_t));
+  apr_array_header_t *deletions
+    = apr_array_make(pool, 16, sizeof(const char *));
+
+  /* extract moves and deletions from the current txn's change list */
+
+  for (hi = apr_hash_first(pool, changed_paths); hi; hi = apr_hash_next(hi))
+    {
+      const char *path;
+      apr_ssize_t len;
+      change_t *change;
+      apr_hash_this(hi, (const void**)&path, &len, (void**)&change);
+
+      if (   change->info.copyfrom_path
+          && (   change->info.change_kind == svn_fs_path_change_move
+              || change->info.change_kind == svn_fs_path_change_movereplace))
+        {
+          svn_sort__item_t *item = apr_array_push(moves);
+          item->key = path;
+          item->klen = len;
+          item->value = change;
+        }
+
+      if (   change->info.change_kind == svn_fs_path_change_delete
+          || change->info.change_kind == svn_fs_path_change_replace
+          || change->info.change_kind == svn_fs_path_change_movereplace)
+        APR_ARRAY_PUSH(deletions, const char *) = path;
+    }
+
+  /* no moves? -> done here */
+
+  if (moves->nelts == 0)
+    return SVN_NO_ERROR;
+
+  /* correct the deletions that refer to moved paths and make them refer to
+     the paths in OLD_REV */
+
+  qsort(moves->elts, moves->nelts, moves->elt_size,
+        svn_sort_compare_paths);
+
+  for (i = 0; i < deletions->nelts; ++i)
+    {
+      const char *deleted_path = APR_ARRAY_IDX(deletions, i, const char*);
+      int closest_move_idx
+        = svn_sort__bsearch_lower_bound(deleted_path, moves,
+                                        svn_sort_compare_paths);
+
+      if (closest_move_idx < moves->nelts)
+        {
+          svn_sort__item_t *closest_move_item
+            = &APR_ARRAY_IDX(moves, closest_move_idx, svn_sort__item_t);
+          const char *relpath
+            = svn_dirent_skip_ancestor(closest_move_item->key,
+                                       deleted_path);
+          if (relpath)
+            {
+              change_t *closed_move = closest_move_item->value;
+              APR_ARRAY_IDX(deletions, i, const char*)
+                = svn_dirent_join(closed_move->info.copyfrom_path, relpath,
+                                  pool);
+            }
+        }
+    }
+
+  qsort(deletions->elts, deletions->nelts, deletions->elt_size,
+        svn_sort_compare_paths);
+
+  /* The _same_ source paths must never occur more than once in any move 
+     since our base revision. */
+
+  for (i = 0; moves->nelts; ++i)
+    SVN_ERR(check_for_duplicate_move_source (source_paths,
+                          APR_ARRAY_IDX(moves, i, svn_sort__item_t).value));
+
+  for (revision = txn_id->revision + 1; revision <= old_rev; ++revision)
+    {
+      apr_array_header_t *changes;
+      change_t **changes_p;
+
+      svn_pool_clear(iter_pool);
+      svn_fs_fs__get_changes(&changes, fs, revision, iter_pool);
+
+      changes_p = (change_t **)&changes->elts;
+      for (i = 0; i < changes->nelts; ++i)
+        SVN_ERR(check_for_duplicate_move_source(source_paths, changes_p[i]));
+    }
+
+  /* The move source paths must been deleted in this txn. */
+
+  for (i = 0; i < moves->nelts; ++i)
+    {
+      change_t *change = APR_ARRAY_IDX(moves, i, svn_sort__item_t).value;
+
+      /* there must be a deletion of move's copy-from path
+         (or any of its parents) */
+
+      int closest_deletion_idx
+        = svn_sort__bsearch_lower_bound(change->info.copyfrom_path, deletions,
+                                        svn_sort_compare_paths);
+      if (closest_deletion_idx < deletions->nelts)
+        {
+          const char *closest_deleted_path
+            = APR_ARRAY_IDX(deletions, closest_deletion_idx, const char *);
+          if (!svn_dirent_is_ancestor(closest_deleted_path,
+                                      change->info.copyfrom_path))
+            return svn_error_createf(SVN_ERR_FS_INCOMPLETE_MOVE, NULL,
+                        _("Path '%s' has been moved without being deleted"),
+                                     change->info.copyfrom_path);
+        }
+    }
+
+  svn_pool_destroy(iter_pool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Baton used for commit_body below. */
 struct commit_baton {
   svn_revnum_t *new_rev_p;
@@ -2846,6 +3040,7 @@ commit_body(void *baton, apr_pool_t *poo
   apr_array_header_t *txnprop_list;
   svn_prop_t prop;
   const svn_fs_x__id_part_t *txn_id = svn_fs_x__txn_get_id(cb->txn);
+  apr_hash_t *changed_paths;
 
   /* Get the current youngest revision. */
   SVN_ERR(svn_fs_x__youngest_rev(&old_rev, cb->fs, pool));
@@ -2862,6 +3057,13 @@ commit_body(void *baton, apr_pool_t *poo
      discovered locks. */
   SVN_ERR(verify_locks(cb->fs, txn_id, pool));
 
+  /* we need the changes list for verification as well as for writing it
+     to the final rev file */
+  SVN_ERR(svn_fs_x__txn_changes_fetch(&changed_paths, cb->fs, txn_id,
+                                      pool));
+
+  SVN_ERR(verify_moves(cb->fs, txn_id, old_rev, changed_paths, pool));
+
   /* We are going to be one better than this puny old revision. */
   new_rev = old_rev + 1;
 
@@ -2879,7 +3081,8 @@ commit_body(void *baton, apr_pool_t *poo
 
   /* Write the changed-path information. */
   SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file,
-                                        cb->fs, txn_id, pool));
+                                        cb->fs, txn_id, changed_paths,
+                                        new_rev, pool));
 
   SVN_ERR(svn_io_file_flush_to_disk(proto_file, pool));
   SVN_ERR(svn_io_file_close(proto_file, pool));

Modified: subversion/trunk/subversion/libsvn_fs_x/tree.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/tree.c?rev=1525429&r1=1525428&r2=1525429&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/tree.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/tree.c Sun Sep 22 20:17:26 2013
@@ -2330,15 +2330,30 @@ x_same_p(svn_boolean_t *same_p,
   return SVN_NO_ERROR;
 }
 
+/* Type to select the various behavioral modes of copy_helper.
+ */
+typedef enum copy_type_t
+{
+  /* add without history */
+  copy_type_plain_add,
+
+  /* add with history */
+  copy_type_add_with_history,
+
+  /* move (always with history) */
+  copy_type_move
+} copy_type_t;
+
 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
-   TO_ROOT.  If PRESERVE_HISTORY is set, then the copy is recorded in
-   the copies table.  Perform temporary allocations in POOL. */
+   TO_ROOT.  COPY_TYPE determines whether then the copy is recorded in
+   the copies table and whether it is being marked as a move.
+   Perform temporary allocations in POOL. */
 static svn_error_t *
 copy_helper(svn_fs_root_t *from_root,
             const char *from_path,
             svn_fs_root_t *to_root,
             const char *to_path,
-            svn_boolean_t preserve_history,
+            copy_type_t copy_type,
             apr_pool_t *pool)
 {
   dag_node_t *from_node;
@@ -2355,11 +2370,22 @@ copy_helper(svn_fs_root_t *from_root,
        _("Cannot copy between two different filesystems ('%s' and '%s')"),
        from_root->fs->path, to_root->fs->path);
 
+  /* more things that we can't do ATM */
   if (from_root->is_txn_root)
     return svn_error_create
       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
        _("Copy from mutable tree not currently supported"));
 
+  if (! to_root->is_txn_root)
+    return svn_error_create
+      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+       _("Copy immutable tree not supported"));
+
+  if (copy_type == copy_type_move && from_root->rev != txn_id->revision)
+    return svn_error_create
+      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+       _("Move from non-HEAD revision not currently supported"));
+
   /* Get the NODE for FROM_PATH in FROM_ROOT.*/
   SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool));
 
@@ -2396,13 +2422,19 @@ copy_helper(svn_fs_root_t *from_root,
          operation is a replacement, not an addition. */
       if (to_parent_path->node)
         {
-          kind = svn_fs_path_change_replace;
+          kind = copy_type == copy_type_move
+               ? svn_fs_path_change_movereplace
+               : svn_fs_path_change_replace;
+
           SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_start,
                                                     to_parent_path->node));
         }
       else
         {
-          kind = svn_fs_path_change_add;
+          kind = copy_type == copy_type_move
+               ? svn_fs_path_change_move
+               : svn_fs_path_change_add;
+
           mergeinfo_start = 0;
         }
 
@@ -2418,12 +2450,12 @@ copy_helper(svn_fs_root_t *from_root,
       SVN_ERR(svn_fs_x__dag_copy(to_parent_path->parent->node,
                                  to_parent_path->entry,
                                  from_node,
-                                 preserve_history,
+                                 copy_type != copy_type_plain_add,
                                  from_root->rev,
                                  from_canonpath,
                                  txn_id, pool));
 
-      if (kind == svn_fs_path_change_replace)
+      if (kind != svn_fs_path_change_add)
         SVN_ERR(dag_node_cache_invalidate(to_root,
                                           parent_path_path(to_parent_path,
                                                            pool), pool));
@@ -2477,7 +2509,7 @@ x_copy(svn_fs_root_t *from_root,
                                      to_root,
                                      svn_fs__canonicalize_abspath(to_path,
                                                                   pool),
-                                     TRUE, pool));
+                                     copy_type_add_with_history, pool));
 }
 
 
@@ -2495,7 +2527,27 @@ x_revision_link(svn_fs_root_t *from_root
 
   path = svn_fs__canonicalize_abspath(path, pool);
   return svn_error_trace(copy_helper(from_root, path, to_root, path,
-                                     FALSE, pool));
+                                     copy_type_plain_add, pool));
+}
+
+
+/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT and mark
+   it as a Move.  If FROM_PATH is a directory, copy it recursively. 
+   Temporary allocations are from POOL.*/
+static svn_error_t *
+x_move(svn_fs_root_t *from_root,
+       const char *from_path,
+       svn_fs_root_t *to_root,
+       const char *to_path,
+       apr_pool_t *pool)
+{
+  return svn_error_trace(copy_helper(from_root,
+                                     svn_fs__canonicalize_abspath(from_path,
+                                                                  pool),
+                                     to_root,
+                                     svn_fs__canonicalize_abspath(to_path,
+                                                                  pool),
+                                     copy_type_move, pool));
 }
 
 



Mime
View raw message