You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/10/16 00:57:09 UTC

svn commit: r1532583 [4/10] - in /subversion/branches/fsfs-improvements: ./ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ notes/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/or...

Modified: subversion/branches/fsfs-improvements/subversion/include/svn_repos.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/include/svn_repos.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/include/svn_repos.h (original)
+++ subversion/branches/fsfs-improvements/subversion/include/svn_repos.h Tue Oct 15 22:57:03 2013
@@ -1794,6 +1794,9 @@ svn_repos_node_location_segments(svn_rep
  * filesystem, as limited by @a paths. In the latter case those revisions
  * are skipped and @a receiver is not invoked.
  *
+ * @a move_behavior defines which changes are being reported as moves.
+ * See #svn_move_behavior_t for the various options.
+ *
  * If @a revprops is NULL, retrieve all revision properties; else, retrieve
  * only the revision properties named by the (const char *) array elements
  * (i.e. retrieve none if the array is empty).
@@ -1818,8 +1821,33 @@ svn_repos_node_location_segments(svn_rep
  *
  * Use @a pool for temporary allocations.
  *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_repos_get_logs5(svn_repos_t *repos,
+                    const apr_array_header_t *paths,
+                    svn_revnum_t start,
+                    svn_revnum_t end,
+                    int limit,
+                    svn_boolean_t discover_changed_paths,
+                    svn_boolean_t strict_node_history,
+                    svn_boolean_t include_merged_revisions,
+                    svn_move_behavior_t move_behavior,
+                    const apr_array_header_t *revprops,
+                    svn_repos_authz_func_t authz_read_func,
+                    void *authz_read_baton,
+                    svn_log_entry_receiver_t receiver,
+                    void *receiver_baton,
+                    apr_pool_t *pool);
+
+/**
+ * Same as svn_repos_get_logs5(), but with @a move_behavior being set to
+ * #svn_fs_move_behavior_no_moves.
+ *
  * @since New in 1.5.
+ * @deprecated Provided for backward compatibility with the 1.8 API.
  */
+SVN_DEPRECATED
 svn_error_t *
 svn_repos_get_logs4(svn_repos_t *repos,
                     const apr_array_header_t *paths,

Modified: subversion/branches/fsfs-improvements/subversion/include/svn_types.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/include/svn_types.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/include/svn_types.h (original)
+++ subversion/branches/fsfs-improvements/subversion/include/svn_types.h Tue Oct 15 22:57:03 2013
@@ -64,6 +64,26 @@ extern "C" {
 #endif
 
 
+/** Macro used to mark experimental functions.
+ *
+ * @since New in 1.9.
+ */
+#ifndef SVN_EXPERIMENTAL
+# if !defined(SWIGPERL) && !defined(SWIGPYTHON) && !defined(SWIGRUBY)
+#  if !defined(__clang__) && defined(__GNUC__) \
+      && (__GNUC__ >= 4 || (__GNUC__==3 && __GNUC_MINOR__>=1))
+#   define SVN_EXPERIMENTAL __attribute__((warning("experimental function used")))
+#  elif defined(_MSC_VER) && _MSC_VER >= 1300
+#   define SVN_EXPERIMENTAL __declspec(deprecated("experimental function used"))
+#  else
+#   define SVN_EXPERIMENTAL
+#  endif
+# else
+#  define SVN_EXPERIMENTAL
+# endif
+#endif
+
+
 /** Indicate whether the current platform supports unaligned data access.
  *
  * On the majority of machines running SVN (x86 / x64), unaligned access
@@ -219,6 +239,16 @@ svn__apr_hash_index_val(const apr_hash_i
                       || ((s) == APR_OS_START_SYSERR + ERROR_INVALID_NAME))
 #endif
 
+/** On Windows, APR_STATUS_IS_EPIPE does not include ERROR_NO_DATA error.
+ * So we include it.*/
+/* ### These fixes should go into APR. */
+#ifndef WIN32
+#define SVN__APR_STATUS_IS_EPIPE(s)  APR_STATUS_IS_EPIPE(s)
+#else
+#define SVN__APR_STATUS_IS_EPIPE(s)  (APR_STATUS_IS_EPIPE(s) \
+                      || ((s) == APR_OS_START_SYSERR + ERROR_NO_DATA))
+#endif
+
 /** @} */
 
 
@@ -752,7 +782,7 @@ svn_commit_info_dup(const svn_commit_inf
  */
 typedef struct svn_log_changed_path2_t
 {
-  /** 'A'dd, 'D'elete, 'R'eplace, 'M'odify */
+  /** 'A'dd, 'D'elete, 'R'eplace, 'M'odify, mo'V'ed, move-replac'E'd */
   char action;
 
   /** Source path of copy (if any). */
@@ -991,6 +1021,28 @@ typedef svn_error_t *(*svn_log_message_r
   const char *message,
   apr_pool_t *pool);
 
+/**
+ * This enumeration contains the various options how SVN shall report
+ * and process explicit MOVes as well as ADD+DEL pairs.
+ *
+ * @since New in 1.9.
+ */
+typedef enum svn_move_behavior_t
+{
+  /* report all moves as ADD with history.
+     This also provides backward compatibility with 1.8 clients. */
+  svn_move_behavior_no_moves = 0,
+
+  /* report all changes, including moves, as they were reported.
+     This is option with the least overhead. */
+  svn_move_behavior_explicit_moves,
+
+  /* in addition to explicit moves, try to find matching DEL + ADD pairs
+     and report the ADD in those as moves as well.  Which of the eligible
+     DEL + ADD pairs will be detected is implementation-dependent. */
+  svn_move_behavior_auto_moves
+} svn_move_behavior_t;
+
 
 
 /** Callback function type for commits.

Modified: subversion/branches/fsfs-improvements/subversion/include/svn_version.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/include/svn_version.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/include/svn_version.h (original)
+++ subversion/branches/fsfs-improvements/subversion/include/svn_version.h Tue Oct 15 22:57:03 2013
@@ -28,15 +28,13 @@
 #define SVN_VERSION_H
 
 /* Hack to prevent the resource compiler from including
-   apr_general.h.  It doesn't resolve the include paths
-   correctly and blows up without this.
- */
-#ifndef APR_STRINGIFY
+   apr and other headers. */
+#ifndef SVN_WIN32_RESOURCE_COMPILATION
 #include <apr_general.h>
-#endif
 #include <apr_tables.h>
 
 #include "svn_types.h"
+#endif
 
 #ifdef __cplusplus
 extern "C" {

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_client/copy.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_client/copy.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_client/copy.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_client/copy.c Tue Oct 15 22:57:03 2013
@@ -1945,6 +1945,9 @@ try_copy(svn_boolean_t *timestamp_sleep,
   svn_boolean_t srcs_are_urls, dst_is_url;
   int i;
 
+  /* Assert instead of crashing if the sources list is empty. */
+  SVN_ERR_ASSERT(sources->nelts > 0);
+
   /* Are either of our paths URLs?  Just check the first src_path.  If
      there are more than one, we'll check for homogeneity among them
      down below. */

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_client/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_client/deprecated.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_client/deprecated.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_client/deprecated.c Tue Oct 15 22:57:03 2013
@@ -1498,6 +1498,28 @@ svn_client_ls(apr_hash_t **dirents,
 }
 
 /*** From log.c ***/
+
+svn_error_t *
+svn_client_log5(const apr_array_header_t *targets,
+                const svn_opt_revision_t *peg_revision,
+                const apr_array_header_t *revision_ranges,
+                int limit,
+                svn_boolean_t discover_changed_paths,
+                svn_boolean_t strict_node_history,
+                svn_boolean_t include_merged_revisions,
+                const apr_array_header_t *revprops,
+                svn_log_entry_receiver_t receiver,
+                void *receiver_baton,
+                svn_client_ctx_t *ctx,
+                apr_pool_t *pool)
+{
+  return svn_client_log6(targets, peg_revision, revision_ranges, limit,
+                         discover_changed_paths, strict_node_history,
+                         include_merged_revisions,
+                         svn_move_behavior_no_moves, revprops, receiver,
+                         receiver_baton, ctx, pool);
+}
+
 svn_error_t *
 svn_client_log4(const apr_array_header_t *targets,
                 const svn_opt_revision_t *peg_revision,

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_client/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_client/externals.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_client/externals.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_client/externals.c Tue Oct 15 22:57:03 2013
@@ -738,7 +738,7 @@ handle_external_item_change(svn_client_c
   switch (ext_kind)
     {
       case svn_node_dir:
-        SVN_ERR(switch_dir_external(local_abspath, new_url,
+        SVN_ERR(switch_dir_external(local_abspath, new_loc->url,
                                     &(new_item->peg_revision),
                                     &(new_item->revision),
                                     parent_dir_abspath,

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_client/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_client/log.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_client/log.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_client/log.c Tue Oct 15 22:57:03 2013
@@ -610,6 +610,7 @@ run_ra_get_log(apr_array_header_t *revis
                svn_boolean_t discover_changed_paths,
                svn_boolean_t strict_node_history,
                svn_boolean_t include_merged_revisions,
+               svn_move_behavior_t move_behavior,
                const apr_array_header_t *revprops,
                svn_log_entry_receiver_t real_receiver,
                void *real_receiver_baton,
@@ -768,7 +769,7 @@ run_ra_get_log(apr_array_header_t *revis
           passed_receiver_baton = &lb;
         }
 
-      SVN_ERR(svn_ra_get_log2(ra_session,
+      SVN_ERR(svn_ra_get_log3(ra_session,
                               paths,
                               range->range_start,
                               range->range_end,
@@ -776,6 +777,7 @@ run_ra_get_log(apr_array_header_t *revis
                               discover_changed_paths,
                               strict_node_history,
                               include_merged_revisions,
+                              move_behavior,
                               passed_receiver_revprops,
                               passed_receiver,
                               passed_receiver_baton,
@@ -798,13 +800,14 @@ run_ra_get_log(apr_array_header_t *revis
 /*** Public Interface. ***/
 
 svn_error_t *
-svn_client_log5(const apr_array_header_t *targets,
+svn_client_log6(const apr_array_header_t *targets,
                 const svn_opt_revision_t *peg_revision,
                 const apr_array_header_t *opt_rev_ranges,
                 int limit,
                 svn_boolean_t discover_changed_paths,
                 svn_boolean_t strict_node_history,
                 svn_boolean_t include_merged_revisions,
+                svn_move_behavior_t move_behavior,
                 const apr_array_header_t *revprops,
                 svn_log_entry_receiver_t real_receiver,
                 void *real_receiver_baton,
@@ -890,8 +893,8 @@ svn_client_log5(const apr_array_header_t
   SVN_ERR(run_ra_get_log(revision_ranges, relative_targets, log_segments,
                          actual_loc, ra_session, targets, limit,
                          discover_changed_paths, strict_node_history,
-                         include_merged_revisions, revprops, real_receiver,
-                         real_receiver_baton, ctx, pool));
+                         include_merged_revisions, move_behavior, revprops,
+                         real_receiver, real_receiver_baton, ctx, pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_client/merge.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_client/merge.c Tue Oct 15 22:57:03 2013
@@ -10715,7 +10715,7 @@ log_find_operative_revs(void *baton,
 
           suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev,
                                              source_rel_path);
-          if (suffix)
+          if (suffix && suffix[0] != '\0')
             {
               missing_path = apr_pstrmemdup(pool, path,
                                             strlen(path) - strlen(suffix) - 1);
@@ -12301,7 +12301,16 @@ find_base_on_target(svn_client__pathrev_
   return SVN_NO_ERROR;
 }
 
-/* The body of client_find_automatic_merge(), which see.
+/* Find the last point at which the branch at S_T->source was completely
+ * merged to the branch at S_T->target or vice-versa.
+ *
+ * Fill in S_T->source_branch and S_T->target_branch and S_T->yca.
+ * Set *BASE_P to the merge base.  Set *IS_REINTEGRATE_LIKE to true if
+ * an automatic merge from source to target would be a reintegration
+ * merge: that is, if the last automatic merge was in the opposite
+ * direction; or to false otherwise.
+ *
+ * If there is no youngest common ancestor, throw an error.
  */
 static svn_error_t *
 find_automatic_merge(svn_client__pathrev_t **base_p,
@@ -12371,6 +12380,9 @@ find_automatic_merge(svn_client__pathrev
  * Like find_automatic_merge() except that the target is
  * specified by @a target_path_or_url at @a target_revision, which must
  * refer to a repository location, instead of by a WC path argument.
+ *
+ * Set *MERGE_P to a new structure with all fields filled in except the
+ * 'allow_*' flags.
  */
 static svn_error_t *
 find_automatic_merge_no_wc(automatic_merge_t **merge_p,
@@ -12446,6 +12458,8 @@ client_find_automatic_merge(automatic_me
   source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
   automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
 
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
+
   /* "Open" the target WC.  Check the target WC for mixed-rev, local mods and
    * switched subtrees yet to faster exit and notify user before contacting
    * with server.  After we find out what kind of merge is required, then if a
@@ -12477,6 +12491,7 @@ client_find_automatic_merge(automatic_me
                                ctx, result_pool, scratch_pool));
   merge->yca = s_t->yca;
   merge->right = s_t->source;
+  merge->target = &s_t->target->loc;
   merge->allow_mixed_rev = allow_mixed_rev;
   merge->allow_local_mods = allow_local_mods;
   merge->allow_switched_subtrees = allow_switched_subtrees;
@@ -12667,12 +12682,18 @@ svn_client_get_merging_summary(svn_boole
                  && (target_revision->kind == svn_opt_revision_unspecified
                      || target_revision->kind == svn_opt_revision_working);
   if (target_is_wc)
-    SVN_ERR(client_find_automatic_merge(
-              &merge,
-              source_path_or_url, source_revision,
-              target_path_or_url,
-              TRUE, TRUE, TRUE,  /* allow_* */
-              ctx, scratch_pool, scratch_pool));
+    {
+      const char *target_abspath;
+
+      SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_path_or_url,
+                                      scratch_pool));
+      SVN_ERR(client_find_automatic_merge(
+                &merge,
+                source_path_or_url, source_revision,
+                target_abspath,
+                TRUE, TRUE, TRUE,  /* allow_* */
+                ctx, scratch_pool, scratch_pool));
+    }
   else
     SVN_ERR(find_automatic_merge_no_wc(
               &merge,

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_client/mergeinfo.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_client/mergeinfo.c Tue Oct 15 22:57:03 2013
@@ -1405,17 +1405,21 @@ filter_log_entry_with_rangelist(void *ba
              obviously back.  If it was added or replaced it's still around
              possibly it was replaced one or more times, but it's back now.
              Regardless, LOG_ENTRY->REVISION is *not* an eligible revision! */
-          if (ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
+          if (nearest_ancestor_mergeinfo &&
+              ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
               && (change->action != 'M'))
             {
               svn_rangelist_t *rangelist =
                   svn_hash_gets(nearest_ancestor_mergeinfo, path);
-              svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
-                rangelist, rangelist->nelts - 1, svn_merge_range_t *);
+              if (rangelist)
+                {
+                  svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
+                    rangelist, rangelist->nelts - 1, svn_merge_range_t *);
 
-              if (youngest_range
-                  && (youngest_range->end > log_entry->revision))
-                continue;
+                  if (youngest_range
+                      && (youngest_range->end > log_entry->revision))
+                    continue;
+                }
             }
 
           if (nearest_ancestor_mergeinfo)

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_client/ra.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_client/ra.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_client/ra.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_client/ra.c Tue Oct 15 22:57:03 2013
@@ -350,6 +350,10 @@ svn_client__open_ra_session_internal(svn
   cbtable->get_client_string = get_client_string;
   if (base_dir_abspath)
     cbtable->get_wc_contents = get_wc_contents;
+  cbtable->check_tunnel_func = ctx->check_tunnel_func;
+  cbtable->open_tunnel_func = ctx->open_tunnel_func;
+  cbtable->close_tunnel_func = ctx->close_tunnel_func;
+  cbtable->tunnel_baton = ctx->tunnel_baton;
 
   cb->commit_items = commit_items;
   cb->ctx = ctx;

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_delta/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_delta/compat.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_delta/compat.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_delta/compat.c Tue Oct 15 22:57:03 2013
@@ -1330,17 +1330,6 @@ move_cb(void *baton,
   return SVN_NO_ERROR;
 }
 
-/* This implements svn_editor_cb_rotate_t */
-static svn_error_t *
-rotate_cb(void *baton,
-          const apr_array_header_t *relpaths,
-          const apr_array_header_t *revisions,
-          apr_pool_t *scratch_pool)
-{
-  SVN__NOT_IMPLEMENTED();
-}
-
-
 static int
 count_components(const char *relpath)
 {
@@ -1888,7 +1877,6 @@ svn_delta__editor_from_delta(svn_editor_
       delete_cb,
       copy_cb,
       move_cb,
-      rotate_cb,
       complete_cb,
       abort_cb
     };

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_delta/editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_delta/editor.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_delta/editor.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_delta/editor.c Tue Oct 15 22:57:03 2013
@@ -391,16 +391,6 @@ svn_editor_setcb_move(svn_editor_t *edit
 
 
 svn_error_t *
-svn_editor_setcb_rotate(svn_editor_t *editor,
-                        svn_editor_cb_rotate_t callback,
-                        apr_pool_t *scratch_pool)
-{
-  editor->funcs.cb_rotate = callback;
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
 svn_editor_setcb_complete(svn_editor_t *editor,
                           svn_editor_cb_complete_t callback,
                           apr_pool_t *scratch_pool)
@@ -437,7 +427,6 @@ svn_editor_setcb_many(svn_editor_t *edit
   COPY_CALLBACK(cb_delete);
   COPY_CALLBACK(cb_copy);
   COPY_CALLBACK(cb_move);
-  COPY_CALLBACK(cb_rotate);
   COPY_CALLBACK(cb_complete);
   COPY_CALLBACK(cb_abort);
 
@@ -862,56 +851,6 @@ svn_editor_move(svn_editor_t *editor,
 
 
 svn_error_t *
-svn_editor_rotate(svn_editor_t *editor,
-                  const apr_array_header_t *relpaths,
-                  const apr_array_header_t *revisions)
-{
-  svn_error_t *err = SVN_NO_ERROR;
-
-  SHOULD_NOT_BE_FINISHED(editor);
-#ifdef ENABLE_ORDERING_CHECK
-  {
-    int i;
-    for (i = 0; i < relpaths->nelts; i++)
-      {
-        const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
-
-        SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
-        SHOULD_NOT_BE_COMPLETED(editor, relpath);
-        VERIFY_PARENT_MAY_EXIST(editor, relpath);
-        CHILD_DELETIONS_ALLOWED(editor, relpath);
-      }
-  }
-#endif
-
-  SVN_ERR(check_cancel(editor));
-
-  if (editor->funcs.cb_rotate)
-    {
-      START_CALLBACK(editor);
-      err = editor->funcs.cb_rotate(editor->baton, relpaths, revisions,
-                                    editor->scratch_pool);
-      END_CALLBACK(editor);
-    }
-
-#ifdef ENABLE_ORDERING_CHECK
-  {
-    int i;
-    for (i = 0; i < relpaths->nelts; i++)
-      {
-        const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
-        MARK_ALLOW_ALTER(editor, relpath);
-        MARK_PARENT_STABLE(editor, relpath);
-      }
-  }
-#endif
-
-  svn_pool_clear(editor->scratch_pool);
-  return svn_error_trace(err);
-}
-
-
-svn_error_t *
 svn_editor_complete(svn_editor_t *editor)
 {
   svn_error_t *err = SVN_NO_ERROR;

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs/editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs/editor.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs/editor.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs/editor.c Tue Oct 15 22:57:03 2013
@@ -177,7 +177,7 @@ can_modify(svn_fs_root_t *txn_root,
   SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath,
                                   scratch_pool));
 
-  /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination)
+  /* Uncommitted nodes (eg. a descendent of a copy/move destination)
      have no (committed) revision number. Let the caller go ahead and
      modify these nodes.
 
@@ -195,7 +195,7 @@ can_modify(svn_fs_root_t *txn_root,
      have supplied a valid revision number [that they expect to change].
      The checks further below will determine the out-of-dateness of the
      specified revision.  */
-  /* ### ugh. descendents of copy/move/rotate destinations carry along
+  /* ### ugh. descendents of copy/move destinations carry along
      ### their original immutable state and (thus) a valid CREATED_REV.
      ### but they are logically uncommitted, so the caller will pass
      ### SVN_INVALID_REVNUM. (technically, the caller could provide
@@ -205,7 +205,7 @@ can_modify(svn_fs_root_t *txn_root,
      ### for now, we will assume the caller knows what they are doing
      ### and an invalid revision implies such a descendent. in the
      ### future, we could examine the ancestor chain looking for a
-     ### copy/move/rotate-here node and allow the modification (and the
+     ### copy/move-here node and allow the modification (and the
      ### converse: if no such ancestor, the caller must specify the
      ### correct/intended revision to modify).
   */
@@ -299,7 +299,7 @@ can_create(svn_fs_root_t *txn_root,
      ### test the ancestor to determine if it has been *-here in this
      ### txn, or just a simple modification.  */
 
-  /* Are any of the parents copied/moved/rotated-here?  */
+  /* Are any of the parents copied/moved-here?  */
   for (cur_fspath = fspath;
        strlen(cur_fspath) > 1;  /* not the root  */
        cur_fspath = svn_fspath__dirname(cur_fspath, scratch_pool))
@@ -633,19 +633,6 @@ move_cb(void *baton,
 }
 
 
-/* This implements svn_editor_cb_rotate_t */
-static svn_error_t *
-rotate_cb(void *baton,
-          const apr_array_header_t *relpaths,
-          const apr_array_header_t *revisions,
-          apr_pool_t *scratch_pool)
-{
-  struct edit_baton *eb = baton;
-
-  UNUSED(eb); SVN__NOT_IMPLEMENTED();
-}
-
-
 /* This implements svn_editor_cb_complete_t */
 static svn_error_t *
 complete_cb(void *baton,
@@ -714,7 +701,6 @@ make_editor(svn_editor_t **editor,
     delete_cb,
     copy_cb,
     move_cb,
-    rotate_cb,
     complete_cb,
     abort_cb
   };

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.c Tue Oct 15 22:57:03 2013
@@ -24,6 +24,7 @@
 
 #include <string.h>
 #include <apr.h>
+#include <apr_atomic.h>
 #include <apr_hash.h>
 #include <apr_md5.h>
 #include <apr_thread_mutex.h>
@@ -41,6 +42,7 @@
 #include "svn_xml.h"
 #include "svn_pools.h"
 #include "svn_string.h"
+#include "svn_sorts.h"
 
 #include "private/svn_fs_private.h"
 #include "private/svn_fs_util.h"
@@ -70,6 +72,7 @@ struct fs_type_defn {
   const char *fs_type;
   const char *fsap_name;
   fs_init_func_t initfunc;
+  fs_library_vtable_t *vtable;
   struct fs_type_defn *next;
 };
 
@@ -81,7 +84,9 @@ static struct fs_type_defn base_defn =
 #else
     NULL,
 #endif
-    NULL
+    NULL,
+    NULL /* End of static list: this needs to be reset to NULL if the
+            common_pool used when setting it has been cleared. */
   };
 
 static struct fs_type_defn fsx_defn =
@@ -92,6 +97,7 @@ static struct fs_type_defn fsx_defn =
 #else
     NULL,
 #endif
+    NULL,
     &base_defn
   };
 
@@ -103,6 +109,7 @@ static struct fs_type_defn fsfs_defn =
 #else
     NULL,
 #endif
+    NULL,
     &fsx_defn
   };
 
@@ -158,13 +165,20 @@ load_module(fs_init_func_t *initfunc, co
 /* Fetch a library vtable by a pointer into the library definitions array. */
 static svn_error_t *
 get_library_vtable_direct(fs_library_vtable_t **vtable,
-                          const struct fs_type_defn *fst,
+                          struct fs_type_defn *fst,
                           apr_pool_t *pool)
 {
   fs_init_func_t initfunc = NULL;
   const svn_version_t *my_version = svn_fs_version();
   const svn_version_t *fs_version;
 
+  /* most times, we get lucky */
+  *vtable = apr_atomic_casptr((volatile void **)&fst->vtable, NULL, NULL);
+  if (*vtable)
+    return SVN_NO_ERROR;
+
+  /* o.k. the first access needs to actually load the module, find the
+     vtable and check for version compatibility. */
   initfunc = fst->initfunc;
   if (! initfunc)
     SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
@@ -201,6 +215,10 @@ get_library_vtable_direct(fs_library_vta
                              my_version->patch, my_version->tag,
                              fs_version->major, fs_version->minor,
                              fs_version->patch, fs_version->tag);
+
+  /* the vtable will not change.  Remember it */
+  apr_atomic_casptr((volatile void **)&fst->vtable, *vtable, NULL);
+
   return SVN_NO_ERROR;
 }
 
@@ -364,6 +382,7 @@ svn_fs_initialize(apr_pool_t *pool)
     return SVN_NO_ERROR;
 
   common_pool = svn_pool_create(pool);
+  base_defn.next = NULL;
   SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
 
   /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
@@ -467,8 +486,7 @@ svn_fs_create(svn_fs_t **fs_p, const cha
   /* Perform the actual creation. */
   *fs_p = fs_new(fs_config, pool);
 
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->create(*fs_p, path, pool, common_pool));
+  SVN_ERR(vtable->create(*fs_p, path, common_pool_lock, pool, common_pool));
   SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
 
   return SVN_NO_ERROR;
@@ -482,8 +500,7 @@ svn_fs_open(svn_fs_t **fs_p, const char 
 
   SVN_ERR(fs_library_vtable(&vtable, path, pool));
   *fs_p = fs_new(fs_config, pool);
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->open_fs(*fs_p, path, pool, common_pool));
+  SVN_ERR(vtable->open_fs(*fs_p, path, common_pool_lock, pool, common_pool));
   SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
 
   return SVN_NO_ERROR;
@@ -503,11 +520,11 @@ svn_fs_upgrade2(const char *path,
   SVN_ERR(fs_library_vtable(&vtable, path, pool));
   fs = fs_new(NULL, pool);
 
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->upgrade_fs(fs, path,
-                                          notify_func, notify_baton,
-                                          cancel_func, cancel_baton,
-                                          pool, common_pool));
+  SVN_ERR(vtable->upgrade_fs(fs, path,
+                             notify_func, notify_baton,
+                             cancel_func, cancel_baton,
+                             common_pool_lock,
+                             pool, common_pool));
   return SVN_NO_ERROR;
 }
 
@@ -534,11 +551,11 @@ svn_fs_verify(const char *path,
   SVN_ERR(fs_library_vtable(&vtable, path, pool));
   fs = fs_new(fs_config, pool);
 
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->verify_fs(fs, path, start, end,
-                                         notify_func, notify_baton,
-                                         cancel_func, cancel_baton,
-                                         pool, common_pool));
+  SVN_ERR(vtable->verify_fs(fs, path, start, end,
+                            notify_func, notify_baton,
+                            cancel_func, cancel_baton,
+                            common_pool_lock,
+                            pool, common_pool));
   return SVN_NO_ERROR;
 }
 
@@ -649,10 +666,9 @@ svn_fs_pack(const char *path,
   SVN_ERR(fs_library_vtable(&vtable, path, pool));
   fs = fs_new(NULL, pool);
 
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->pack_fs(fs, path, notify_func, notify_baton,
-                                       cancel_func, cancel_baton, pool,
-                                       common_pool));
+  SVN_ERR(vtable->pack_fs(fs, path, notify_func, notify_baton,
+                          cancel_func, cancel_baton, common_pool_lock,
+                          pool, common_pool));
   return SVN_NO_ERROR;
 }
 
@@ -667,9 +683,8 @@ svn_fs_recover(const char *path,
   SVN_ERR(fs_library_vtable(&vtable, path, pool));
   fs = fs_new(NULL, pool);
 
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->open_fs_for_recovery(fs, path, pool,
-                                                    common_pool));
+  SVN_ERR(vtable->open_fs_for_recovery(fs, path, common_pool_lock,
+                                       pool, common_pool));
   return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
                                          pool));
 }
@@ -710,8 +725,7 @@ svn_fs_create_berkeley(svn_fs_t *fs, con
   SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
 
   /* Perform the actual creation. */
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->create(fs, path, fs->pool, common_pool));
+  SVN_ERR(vtable->create(fs, path, common_pool_lock, fs->pool, common_pool));
   SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
 
   return SVN_NO_ERROR;
@@ -723,8 +737,7 @@ svn_fs_open_berkeley(svn_fs_t *fs, const
   fs_library_vtable_t *vtable;
 
   SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
-  SVN_MUTEX__WITH_LOCK(common_pool_lock,
-                       vtable->open_fs(fs, path, fs->pool, common_pool));
+  SVN_ERR(vtable->open_fs(fs, path, common_pool_lock, fs->pool, common_pool));
   SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
 
   return SVN_NO_ERROR;
@@ -976,11 +989,177 @@ svn_fs_revision_root_revision(svn_fs_roo
   return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
 }
 
+/* Return TRUE, if CHANGE deleted the node previously found at its target
+   path. */
+static svn_boolean_t
+is_deletion(svn_fs_path_change2_t *change)
+{
+  return change->change_kind == svn_fs_path_change_movereplace
+      || change->change_kind == svn_fs_path_change_replace
+      || change->change_kind == svn_fs_path_change_delete;
+}
+
+/* Change all moves in CHANGES to ADD.  Use POOL for temporary allocations.
+ */
+static void
+turn_moves_into_copies(apr_hash_t *changes,
+                       apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      const char *key;
+      apr_ssize_t klen;
+      svn_fs_path_change2_t *change;
+      apr_hash_this(hi, (const void **)&key, &klen, (void**)&change);
+
+      switch (change->change_kind)
+        {
+          case svn_fs_path_change_move:
+            change->change_kind = svn_fs_path_change_add;
+            break;
+
+          case svn_fs_path_change_movereplace:
+            change->change_kind = svn_fs_path_change_replace;
+            break;
+
+          default:
+            break;
+        }
+    }
+}
+
+/* Replace ADDs with MOVes, if they are unique, have a matching deletion
+ * and if the copy-from revision is REVISION-1.  Use POOL for temporary
+ * allocations.
+ */
+static void
+turn_unique_copies_into_moves(apr_hash_t *changes,
+                              svn_revnum_t revision,
+                              apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_hash_t *unique_copy_sources;
+  const char **sources;
+  int i;
+
+  /* find all copy-from paths (ADD and MOV alike) */
+
+  svn_boolean_t any_deletion = FALSE;
+  apr_array_header_t *copy_sources
+    = apr_array_make(pool, apr_hash_count(changes), sizeof(const char*));
+
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      svn_fs_path_change2_t *change;
+      apr_hash_this(hi, NULL, NULL, (void**)&change);
+
+      if (change->copyfrom_path && change->copyfrom_rev == revision-1)
+        APR_ARRAY_PUSH(copy_sources, const char *)
+          = change->copyfrom_path;
+
+      any_deletion |= is_deletion(change);
+    }
+
+  /* no suitable copy-from or no deletion -> no moves */
+
+  if (!copy_sources->nelts || !any_deletion)
+    return;
+
+  /* identify copy-from paths that have been mentioned exactly once */
+
+  sources = (const char **)copy_sources->elts;
+  qsort(sources, copy_sources->nelts, copy_sources->elt_size,
+        (int (*)(const void *, const void *))svn_sort_compare_paths);
+
+  unique_copy_sources = apr_hash_make(pool);
+  for (i = 0; i < copy_sources->nelts; ++i)
+    if (   (i == 0 || strcmp(sources[i-1], sources[i]))
+        && (i == copy_sources->nelts-1 || strcmp(sources[i+1], sources[i])))
+      {
+        apr_hash_set(unique_copy_sources, sources[i],
+                     APR_HASH_KEY_STRING, sources[i]);
+      }
+
+  /* no unique copy-from paths -> no moves */
+
+  if (!apr_hash_count(unique_copy_sources))
+    return;
+
+  /* Replace all additions, replacements with a unique copy-from path,
+     the correct copy-from rev and a matching deletion in this revision,
+     with moves and move-replacements, respectively. */
+
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      const char *key;
+      apr_ssize_t klen;
+      svn_fs_path_change2_t *change, *copy_from_change;
+
+      apr_hash_this(hi, (const void **)&key, &klen, (void**)&change);
+      if (   change->copyfrom_rev != revision-1
+          || !change->copyfrom_path
+          || !apr_hash_get(unique_copy_sources, change->copyfrom_path,
+                           APR_HASH_KEY_STRING))
+        continue;
+
+      copy_from_change = apr_hash_get(changes, change->copyfrom_path,
+                                      APR_HASH_KEY_STRING);
+      if (!copy_from_change || !is_deletion(copy_from_change))
+        continue;
+
+      /* There is a deletion of the ADD's copy-from path in *REVISION*.
+         This can either be the same as in REVISION-1 (o.k.) or must have
+         been replaced by some other node.  However, that would imply that
+         it still got deleted as part of the replacement, i.e. both cases
+         are o.k. */
+
+      switch (change->change_kind)
+        {
+          case svn_fs_path_change_add:
+            change->change_kind = svn_fs_path_change_move;
+            break;
+
+          case svn_fs_path_change_replace:
+            change->change_kind = svn_fs_path_change_movereplace;
+            break;
+
+          default:
+            break;
+        }
+    }
+}
+
+svn_error_t *
+svn_fs_paths_changed3(apr_hash_t **changed_paths_p,
+                      svn_fs_root_t *root,
+                      svn_move_behavior_t move_behavior,
+                      apr_pool_t *pool)
+{
+  SVN_ERR(root->vtable->paths_changed(changed_paths_p, root, pool));
+  switch(move_behavior)
+    {
+      case svn_move_behavior_no_moves:
+        turn_moves_into_copies(*changed_paths_p, pool);
+        break;
+
+      case svn_move_behavior_auto_moves:
+        turn_unique_copies_into_moves(*changed_paths_p, root->rev, pool);
+        break;
+
+      default:
+        break;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
                       apr_pool_t *pool)
 {
-  return root->vtable->paths_changed(changed_paths_p, root, pool);
+  return svn_fs_paths_changed3(changed_paths_p, root,
+                               svn_move_behavior_no_moves, pool);
 }
 
 svn_error_t *
@@ -990,7 +1169,8 @@ svn_fs_paths_changed(apr_hash_t **change
   apr_hash_t *changed_paths_new_structs;
   apr_hash_index_t *hi;
 
-  SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
+  SVN_ERR(svn_fs_paths_changed3(&changed_paths_new_structs, root,
+                                svn_move_behavior_no_moves, pool));
   *changed_paths_p = apr_hash_make(pool);
   for (hi = apr_hash_first(pool, changed_paths_new_structs);
        hi;
@@ -1224,6 +1404,15 @@ svn_fs_revision_link(svn_fs_root_t *from
 }
 
 svn_error_t *
+svn_fs_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)
+{
+  SVN_ERR(svn_fs__path_valid(to_path, pool));
+  return svn_error_trace(to_root->vtable->move(from_root, from_path,
+                                               to_root, to_path, pool));
+}
+
+svn_error_t *
 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
                    const char *path, apr_pool_t *pool)
 {
@@ -1623,7 +1812,7 @@ svn_error_t *
 svn_fs_print_modules(svn_stringbuf_t *output,
                      apr_pool_t *pool)
 {
-  const struct fs_type_defn *defn = fs_modules;
+  struct fs_type_defn *defn = fs_modules;
   fs_library_vtable_t *vtable;
   apr_pool_t *iterpool = svn_pool_create(pool);
 

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs/fs-loader.h Tue Oct 15 22:57:03 2013
@@ -27,6 +27,7 @@
 
 #include "svn_types.h"
 #include "svn_fs.h"
+#include "private/svn_mutex.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -72,17 +73,22 @@ typedef struct fs_library_vtable_t
      this statement, now that the minor version has increased. */
   const svn_version_t *(*get_version)(void);
 
-  /* The open_fs/create/open_fs_for_recovery/upgrade_fs functions are
-     serialized so that they may use the common_pool parameter to
-     allocate fs-global objects such as the bdb env cache. */
-  svn_error_t *(*create)(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+  /* The open_fs/create/open_fs_for_recovery/upgrade_fs functions must
+     use the common_pool_lock to serialize the access to the common_pool
+     parameter for allocating fs-global objects such as an env cache. */
+  svn_error_t *(*create)(svn_fs_t *fs, const char *path,
+                         svn_mutex__t *common_pool_lock,
+                         apr_pool_t *pool,
                          apr_pool_t *common_pool);
-  svn_error_t *(*open_fs)(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+  svn_error_t *(*open_fs)(svn_fs_t *fs, const char *path,
+                          svn_mutex__t *common_pool_lock,
+                          apr_pool_t *pool,
                           apr_pool_t *common_pool);
   /* open_for_recovery() is like open(), but used to fill in an fs pointer
      that will be passed to recover().  We assume that the open() method
      might not be immediately appropriate for recovery. */
   svn_error_t *(*open_fs_for_recovery)(svn_fs_t *fs, const char *path,
+                                       svn_mutex__t *common_pool_lock,
                                        apr_pool_t *pool,
                                        apr_pool_t *common_pool);
   svn_error_t *(*upgrade_fs)(svn_fs_t *fs,
@@ -91,6 +97,7 @@ typedef struct fs_library_vtable_t
                              void *notify_baton,
                              svn_cancel_func_t cancel_func,
                              void *cancel_baton,
+                             svn_mutex__t *common_pool_lock,
                              apr_pool_t *pool,
                              apr_pool_t *common_pool);
   svn_error_t *(*verify_fs)(svn_fs_t *fs, const char *path,
@@ -100,6 +107,7 @@ typedef struct fs_library_vtable_t
                             void *notify_baton,
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
+                            svn_mutex__t *common_pool_lock,
                             apr_pool_t *pool,
                             apr_pool_t *common_pool);
   svn_error_t *(*delete_fs)(const char *path, apr_pool_t *pool);
@@ -115,6 +123,7 @@ typedef struct fs_library_vtable_t
   svn_error_t *(*pack_fs)(svn_fs_t *fs, const char *path,
                           svn_fs_pack_notify_t notify_func, void *notify_baton,
                           svn_cancel_func_t cancel_func, void *cancel_baton,
+                          svn_mutex__t *common_pool_lock,
                           apr_pool_t *pool, apr_pool_t *common_pool);
 
   /* Provider-specific functions should go here, even if they could go
@@ -301,6 +310,16 @@ typedef struct root_vtable_t
                                     apr_pool_t *pool);
   svn_error_t *(*delete_node)(svn_fs_root_t *root, const char *path,
                               apr_pool_t *pool);
+  svn_error_t *(*copy)(svn_fs_root_t *from_root, const char *from_path,
+                       svn_fs_root_t *to_root, const char *to_path,
+                       apr_pool_t *pool);
+  svn_error_t *(*revision_link)(svn_fs_root_t *from_root,
+                                svn_fs_root_t *to_root,
+                                const char *path,
+                                apr_pool_t *pool);
+  svn_error_t *(*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);
   svn_error_t *(*copied_from)(svn_revnum_t *rev_p, const char **path_p,
                               svn_fs_root_t *root, const char *path,
                               apr_pool_t *pool);
@@ -331,13 +350,6 @@ typedef struct root_vtable_t
                                     apr_pool_t *pool);
   svn_error_t *(*make_dir)(svn_fs_root_t *root, const char *path,
                            apr_pool_t *pool);
-  svn_error_t *(*copy)(svn_fs_root_t *from_root, const char *from_path,
-                       svn_fs_root_t *to_root, const char *to_path,
-                       apr_pool_t *pool);
-  svn_error_t *(*revision_link)(svn_fs_root_t *from_root,
-                                svn_fs_root_t *to_root,
-                                const char *path,
-                                apr_pool_t *pool);
 
   /* Files */
   svn_error_t *(*file_length)(svn_filesize_t *length_p, svn_fs_root_t *root,

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/fs.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/fs.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/fs.c Tue Oct 15 22:57:03 2013
@@ -729,7 +729,10 @@ populate_opened_fs(svn_fs_t *fs, apr_poo
 }
 
 static svn_error_t *
-base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+base_create(svn_fs_t *fs,
+            const char *path,
+            svn_mutex__t *common_pool_lock,
+            apr_pool_t *pool,
             apr_pool_t *common_pool)
 {
   int format = SVN_FS_BASE__FORMAT_NUMBER;
@@ -805,7 +808,10 @@ check_format(int format)
 }
 
 static svn_error_t *
-base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+base_open(svn_fs_t *fs,
+          const char *path,
+          svn_mutex__t *common_pool_lock,
+          apr_pool_t *pool,
           apr_pool_t *common_pool)
 {
   int format;
@@ -888,7 +894,10 @@ bdb_recover(const char *path, svn_boolea
 }
 
 static svn_error_t *
-base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+base_open_for_recovery(svn_fs_t *fs,
+                       const char *path,
+                       svn_mutex__t *common_pool_lock,
+                       apr_pool_t *pool,
                        apr_pool_t *common_pool)
 {
   /* Just stash the path in the fs pointer - it's all we really need. */
@@ -904,6 +913,7 @@ base_upgrade(svn_fs_t *fs,
              void *notify_baton,
              svn_cancel_func_t cancel_func,
              void *cancel_baton,
+             svn_mutex__t *common_pool_lock,
              apr_pool_t *pool,
              apr_pool_t *common_pool)
 {
@@ -946,7 +956,7 @@ base_upgrade(svn_fs_t *fs,
          But it's better to use the existing encapsulation of "opening
          the filesystem" rather than duplicating (or worse, partially
          duplicating) that logic here.  */
-      SVN_ERR(base_open(fs, path, subpool, common_pool));
+      SVN_ERR(base_open(fs, path, common_pool_lock, subpool, common_pool));
 
       /* Fetch the youngest rev, and record it */
       SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool));
@@ -968,6 +978,7 @@ base_verify(svn_fs_t *fs, const char *pa
             void *notify_baton,
             svn_cancel_func_t cancel_func,
             void *cancel_baton,
+            svn_mutex__t *common_pool_lock,
             apr_pool_t *pool,
             apr_pool_t *common_pool)
 {
@@ -992,6 +1003,7 @@ base_bdb_pack(svn_fs_t *fs,
               void *notify_baton,
               svn_cancel_func_t cancel,
               void *cancel_baton,
+              svn_mutex__t *common_pool_lock,
               apr_pool_t *pool,
               apr_pool_t *common_pool)
 {

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/tree.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/tree.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_base/tree.c Tue Oct 15 22:57:03 2013
@@ -3277,6 +3277,18 @@ base_revision_link(svn_fs_root_t *from_r
 }
 
 
+static svn_error_t *
+base_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)
+{
+  /* BDB supports MOVes only as backward compatible ADD-with-history */
+  return base_copy(from_root, from_path, to_root, to_path, pool);
+}
+
+
 struct copied_from_args
 {
   svn_fs_root_t *root;      /* Root for the node whose ancestry we seek. */
@@ -5395,6 +5407,9 @@ static root_vtable_t root_vtable = {
   base_node_origin_rev,
   base_node_created_path,
   base_delete_node,
+  base_copy,
+  base_revision_link,
+  base_move,
   base_copied_from,
   base_closest_copy,
   base_node_prop,
@@ -5404,8 +5419,6 @@ static root_vtable_t root_vtable = {
   base_dir_entries,
   base_dir_optimal_order,
   base_make_dir,
-  base_copy,
-  base_revision_link,
   base_file_length,
   base_file_checksum,
   base_file_contents,

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.c Tue Oct 15 22:57:03 2013
@@ -271,7 +271,7 @@ get_node_revision_body(node_revision_t *
     {
       /* noderevs in rev / pack files can be cached */
       const svn_fs_fs__id_part_t *rev_item = svn_fs_fs__id_rev_offset(id);
-      pair_cache_key_t key;
+      pair_cache_key_t key = { 0 };
       key.revision = rev_item->revision;
       key.second = rev_item->number;
 
@@ -1751,12 +1751,12 @@ get_dir_contents(apr_hash_t *entries,
       apr_size_t len = noderev->data_rep->expanded_size
                      ? (apr_size_t)noderev->data_rep->expanded_size
                      : (apr_size_t)noderev->data_rep->size;
-      svn_stringbuf_t *text = svn_stringbuf_create_ensure(len, text_pool);
-      text->len = len;
+      svn_stringbuf_t *text;
 
       /* The representation is immutable.  Read it normally. */
-      SVN_ERR(svn_fs_fs__get_contents(&contents, fs, noderev->data_rep, text_pool));
-      SVN_ERR(svn_stream_read(contents, text->data, &text->len));
+      SVN_ERR(svn_fs_fs__get_contents(&contents, fs, noderev->data_rep,
+                                      text_pool));
+      SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, text_pool));
       SVN_ERR(svn_stream_close(contents));
 
       /* de-serialize hash */

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.h (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.h Tue Oct 15 22:57:03 2013
@@ -138,4 +138,4 @@ svn_fs_fs__get_changes(apr_array_header_
                        svn_revnum_t rev,
                        apr_pool_t *pool);
 
-#endif
\ No newline at end of file
+#endif

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/cached_data.h
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.c Tue Oct 15 22:57:03 2013
@@ -229,9 +229,12 @@ initialize_fs_struct(svn_fs_t *fs)
 /* This implements the fs_library_vtable_t.create() API.  Create a new
    fsfs-backed Subversion filesystem at path PATH and link it into
    *FS.  Perform temporary allocations in POOL, and fs-global allocations
-   in COMMON_POOL. */
+   in COMMON_POOL.  The latter must be serialized using COMMON_POOL_LOCK. */
 static svn_error_t *
-fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+fs_create(svn_fs_t *fs,
+          const char *path,
+          svn_mutex__t *common_pool_lock,
+          apr_pool_t *pool,
           apr_pool_t *common_pool)
 {
   SVN_ERR(svn_fs__check_fs(fs, FALSE));
@@ -241,7 +244,10 @@ fs_create(svn_fs_t *fs, const char *path
   SVN_ERR(svn_fs_fs__create(fs, path, pool));
 
   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
-  return fs_serialized_init(fs, common_pool, pool);
+  SVN_MUTEX__WITH_LOCK(common_pool_lock,
+                       fs_serialized_init(fs, common_pool, pool));
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -251,17 +257,26 @@ fs_create(svn_fs_t *fs, const char *path
 /* This implements the fs_library_vtable_t.open() API.  Open an FSFS
    Subversion filesystem located at PATH, set *FS to point to the
    correct vtable for the filesystem.  Use POOL for any temporary
-   allocations, and COMMON_POOL for fs-global allocations. */
+   allocations, and COMMON_POOL for fs-global allocations.
+   The latter must be serialized using COMMON_POOL_LOCK. */
 static svn_error_t *
-fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+fs_open(svn_fs_t *fs,
+        const char *path,
+        svn_mutex__t *common_pool_lock,
+        apr_pool_t *pool,
         apr_pool_t *common_pool)
 {
+  SVN_ERR(svn_fs__check_fs(fs, FALSE));
+
   SVN_ERR(initialize_fs_struct(fs));
 
   SVN_ERR(svn_fs_fs__open(fs, path, pool));
 
   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
-  return fs_serialized_init(fs, common_pool, pool);
+  SVN_MUTEX__WITH_LOCK(common_pool_lock,
+                       fs_serialized_init(fs, common_pool, pool));
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -270,7 +285,9 @@ fs_open(svn_fs_t *fs, const char *path, 
 static svn_error_t *
 fs_open_for_recovery(svn_fs_t *fs,
                      const char *path,
-                     apr_pool_t *pool, apr_pool_t *common_pool)
+                     svn_mutex__t *common_pool_lock,
+                     apr_pool_t *pool,
+                     apr_pool_t *common_pool)
 {
   /* Recovery for FSFS is currently limited to recreating the 'current'
      file from the latest revision. */
@@ -288,7 +305,7 @@ fs_open_for_recovery(svn_fs_t *fs,
                                      "0 1 1\n", pool));
 
   /* Now open the filesystem properly by calling the vtable method directly. */
-  return fs_open(fs, path, pool, common_pool);
+  return fs_open(fs, path, common_pool_lock, pool, common_pool);
 }
 
 
@@ -301,14 +318,11 @@ fs_upgrade(svn_fs_t *fs,
            void *notify_baton,
            svn_cancel_func_t cancel_func,
            void *cancel_baton,
+           svn_mutex__t *common_pool_lock,
            apr_pool_t *pool,
            apr_pool_t *common_pool)
 {
-  SVN_ERR(svn_fs__check_fs(fs, FALSE));
-  SVN_ERR(initialize_fs_struct(fs));
-  SVN_ERR(svn_fs_fs__open(fs, path, pool));
-  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
-  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
+  SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
   return svn_fs_fs__upgrade(fs, notify_func, notify_baton,
                             cancel_func, cancel_baton, pool);
 }
@@ -321,14 +335,11 @@ fs_verify(svn_fs_t *fs, const char *path
           void *notify_baton,
           svn_cancel_func_t cancel_func,
           void *cancel_baton,
+          svn_mutex__t *common_pool_lock,
           apr_pool_t *pool,
           apr_pool_t *common_pool)
 {
-  SVN_ERR(svn_fs__check_fs(fs, FALSE));
-  SVN_ERR(initialize_fs_struct(fs));
-  SVN_ERR(svn_fs_fs__open(fs, path, pool));
-  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
-  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
+  SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
   return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
                            cancel_func, cancel_baton, pool);
 }
@@ -340,14 +351,11 @@ fs_pack(svn_fs_t *fs,
         void *notify_baton,
         svn_cancel_func_t cancel_func,
         void *cancel_baton,
+        svn_mutex__t *common_pool_lock,
         apr_pool_t *pool,
         apr_pool_t *common_pool)
 {
-  SVN_ERR(svn_fs__check_fs(fs, FALSE));
-  SVN_ERR(initialize_fs_struct(fs));
-  SVN_ERR(svn_fs_fs__open(fs, path, pool));
-  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
-  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
+  SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
   return svn_fs_fs__pack(fs, notify_func, notify_baton,
                          cancel_func, cancel_baton, pool);
 }

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs.h Tue Oct 15 22:57:03 2013
@@ -148,6 +148,9 @@ extern "C" {
 /* The minimum format number that supports packed revprops. */
 #define SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT 6
 
+/* Minimum format number that will record moves */
+#define SVN_FS_FS__MIN_MOVE_SUPPORT_FORMAT 7
+
 /* The minimum format number that supports a configuration file (fsfs.conf) */
 #define SVN_FS_FS__MIN_CONFIG_FILE 4
 

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/hotcopy.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/hotcopy.h
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/low_level.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/low_level.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/low_level.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/low_level.c Tue Oct 15 22:57:03 2013
@@ -50,6 +50,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"
@@ -239,6 +241,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,
@@ -350,9 +360,11 @@ write_change_entry(svn_stream_t *stream,
                    svn_boolean_t include_node_kind,
                    apr_pool_t *pool)
 {
-  const char *idstr, *buf;
+  const char *idstr;
   const char *change_string = NULL;
   const char *kind_string = "";
+  svn_stringbuf_t *buf;
+  apr_size_t len;
 
   switch (change->change_kind)
     {
@@ -371,6 +383,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"),
@@ -391,22 +409,24 @@ write_change_entry(svn_stream_t *stream,
                                  ? SVN_FS_FS__KIND_DIR
                                   : SVN_FS_FS__KIND_FILE);
     }
-  buf = apr_psprintf(pool, "%s %s%s %s %s %s\n",
-                     idstr, change_string, kind_string,
-                     change->text_mod ? FLAG_TRUE : FLAG_FALSE,
-                     change->prop_mod ? FLAG_TRUE : FLAG_FALSE,
-                     path);
-
-  SVN_ERR(svn_stream_puts(stream, buf));
+  buf = svn_stringbuf_createf(pool, "%s %s%s %s %s %s\n",
+                              idstr, change_string, kind_string,
+                              change->text_mod ? FLAG_TRUE : FLAG_FALSE,
+                              change->prop_mod ? FLAG_TRUE : FLAG_FALSE,
+                              path);
 
   if (SVN_IS_VALID_REVNUM(change->copyfrom_rev))
     {
-      buf = apr_psprintf(pool, "%ld %s", change->copyfrom_rev,
-                         change->copyfrom_path);
-      SVN_ERR(svn_stream_puts(stream, buf));
+      svn_stringbuf_appendcstr(buf, apr_psprintf(pool, "%ld %s",
+                                                 change->copyfrom_rev,
+                                                 change->copyfrom_path));
     }
 
-  return svn_error_trace(svn_stream_puts(stream, "\n"));
+   svn_stringbuf_appendbyte(buf, '\n');
+
+   /* Write all change info in one write call. */
+   len = buf->len;
+   return svn_error_trace(svn_stream_write(stream, buf->data, &len));
 }
 
 svn_error_t *

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/low_level.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/low_level.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/pack.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/pack.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/recovery.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/recovery.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/revprops.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/revprops.h
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.c Tue Oct 15 22:57:03 2013
@@ -598,6 +598,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_fs__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
@@ -636,11 +663,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,
@@ -648,7 +677,8 @@ fold_change(apr_hash_t *changes,
 
       /* Sanity check: an add can't follow anything except
          a delete or reset.  */
-      if ((info->change_kind == svn_fs_path_change_add)
+      if ((   (info->change_kind == svn_fs_path_change_add)
+           || (info->change_kind == svn_fs_path_change_move))
           && (old_change->change_kind != svn_fs_path_change_delete)
           && (old_change->change_kind != svn_fs_path_change_reset))
         return svn_error_create
@@ -665,7 +695,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
@@ -687,22 +718,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_fs__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:
@@ -768,7 +793,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;
 
@@ -1518,9 +1544,10 @@ svn_fs_fs__add_change(svn_fs_t *fs,
   svn_fs_path_change2_t *change;
   apr_hash_t *changes = apr_hash_make(pool);
 
+  /* Not using APR_BUFFERED to append change in one atomic write operation. */
   SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, pool),
-                           APR_APPEND | APR_WRITE | APR_CREATE
-                           | APR_BUFFERED, APR_OS_DEFAULT, pool));
+                           APR_APPEND | APR_WRITE | APR_CREATE,
+                           APR_OS_DEFAULT, pool));
 
   change = svn_fs__path_change_create_internal(id, change_kind, pool);
   change->text_mod = text_mod;
@@ -2661,23 +2688,34 @@ 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 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_fs__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_ERR(svn_fs_fs__get_file_offset(&offset, file, pool));
 
-  SVN_ERR(svn_fs_fs__txn_changes_fetch(&changed_paths, fs, txn_id, pool));
+  /* all moves specify the "copy-from-rev" as REV-1 */
+  if (svn_fs_fs__supports_move(fs))
+    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->change_kind == svn_fs_path_change_movereplace))
+          change->copyfrom_rev = new_rev - 1;
+      }
 
   SVN_ERR(svn_fs_fs__write_changes(svn_stream_from_aprfile2(file, TRUE, pool),
                                    fs, changed_paths, TRUE, pool));
@@ -2836,6 +2874,169 @@ 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.
+   Allocate the hashed strings in POOL. */
+static svn_error_t *
+check_for_duplicate_move_source(apr_hash_t *source_paths,
+                                change_t *change,
+                                apr_pool_t *pool)
+{
+  if (   change->info.change_kind == svn_fs_path_change_move
+      || change->info.change_kind == svn_fs_path_change_movereplace)
+    if (change->info.copyfrom_path)
+      {
+        apr_size_t len = strlen(change->info.copyfrom_path);
+        if (apr_hash_get(source_paths, change->info.copyfrom_path, len))
+          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,
+                     apr_pstrmemdup(pool, change->info.copyfrom_path, len),
+                     len,
+                     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_fs__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,
+                          pool));
+
+  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],
+                                                pool));
+    }
+
+  /* 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;
@@ -2869,6 +3070,7 @@ commit_body(void *baton, apr_pool_t *poo
   svn_prop_t prop;
   const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(cb->txn);
   svn_stringbuf_t *trailer;
+  apr_hash_t *changed_paths;
 
   /* Get the current youngest revision. */
   SVN_ERR(svn_fs_fs__youngest_rev(&old_rev, cb->fs, pool));
@@ -2885,6 +3087,16 @@ 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_fs__txn_changes_fetch(&changed_paths, cb->fs, txn_id,
+                                       pool));
+
+  /* ensure that no change in this txn or any txn committed since the start
+   * of this txn violates our move semantics */
+  if (svn_fs_fs__supports_move(cb->fs))
+    SVN_ERR(verify_moves(cb->fs, txn_id, old_rev, changed_paths, pool));
+
   /* Get the next node_id and copy_id to use. */
   if (ffd->format < SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
     SVN_ERR(get_next_revision_ids(&start_node_id, &start_copy_id, cb->fs,
@@ -2907,7 +3119,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, changed_paths, new_rev,
+                                        pool));
 
   /* Write the final line. */
   trailer = svn_fs_fs__unparse_revision_trailer

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.h (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.h Tue Oct 15 22:57:03 2013
@@ -280,4 +280,4 @@ svn_fs_fs__begin_txn(svn_fs_txn_t **txn_
                      apr_uint32_t flags,
                      apr_pool_t *pool);
 
-#endif
\ No newline at end of file
+#endif

Propchange: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/transaction.h
------------------------------------------------------------------------------
    svn:eol-style = native