You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by gb...@apache.org on 2013/09/26 15:47:26 UTC

svn commit: r1526487 [8/10] - in /subversion/branches/invoke-diff-cmd-feature: ./ build/ac-macros/ build/generator/ build/generator/templates/ contrib/client-side/emacs/ notes/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apach...

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_local/split_url.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_local/split_url.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_local/split_url.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_local/split_url.c Thu Sep 26 13:47:21 2013
@@ -39,6 +39,7 @@ svn_ra_local__split_URL(svn_repos_t **re
   const char *repos_dirent;
   const char *repos_root_dirent;
   svn_stringbuf_t *urlbuf;
+  apr_size_t root_end;
 
   SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dirent, URL, pool));
 
@@ -65,10 +66,17 @@ svn_ra_local__split_URL(svn_repos_t **re
                    "/",
                    svn_dirent_skip_ancestor(repos_root_dirent, repos_dirent),
                    (const char *)NULL); */
-  *fs_path = &repos_dirent[strlen(repos_root_dirent)];
-
-  if (**fs_path == '\0')
+  root_end = strlen(repos_root_dirent);
+  if (! repos_dirent[root_end])
     *fs_path = "/";
+  else if (repos_dirent[root_end] == '/')
+    *fs_path = &repos_dirent[root_end];
+  else
+    {
+      /* On Windows "C:/" is the parent directory of "C:/dir" */
+      *fs_path = &repos_dirent[root_end-1];
+      SVN_ERR_ASSERT((*fs_path)[0] == '/');
+    }
 
   /* Remove the path components after the root dirent from the original URL,
      to get a URL to the repository root.

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/commit.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/commit.c Thu Sep 26 13:47:21 2013
@@ -1933,7 +1933,18 @@ add_file(const char *path,
 
       if (handler->sline.code != 404)
         {
-          return svn_error_createf(SVN_ERR_RA_DAV_ALREADY_EXISTS, NULL,
+          if (handler->sline.code != 200)
+            {
+              svn_error_t *err;
+
+              err = svn_ra_serf__error_on_status(handler->sline,
+                                                 handler->path,
+                                                 handler->location);
+
+              SVN_ERR(err);
+            }
+
+          return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
                                    _("File '%s' already exists"), path);
         }
     }
@@ -2168,8 +2179,8 @@ close_file(void *file_baton,
     {
       proppatch_context_t *proppatch;
 
-      proppatch = apr_pcalloc(ctx->pool, sizeof(*proppatch));
-      proppatch->pool = ctx->pool;
+      proppatch = apr_pcalloc(scratch_pool, sizeof(*proppatch));
+      proppatch->pool = scratch_pool;
       proppatch->relpath = ctx->relpath;
       proppatch->path = ctx->url;
       proppatch->commit = ctx->commit;
@@ -2177,7 +2188,7 @@ close_file(void *file_baton,
       proppatch->removed_props = ctx->removed_props;
       proppatch->base_revision = ctx->base_revision;
 
-      SVN_ERR(proppatch_resource(proppatch, ctx->commit, ctx->pool));
+      SVN_ERR(proppatch_resource(proppatch, ctx->commit, scratch_pool));
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/log.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/log.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/log.c Thu Sep 26 13:47:21 2013
@@ -76,6 +76,7 @@ typedef struct log_context_t {
   svn_boolean_t 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;
   int nest_level; /* used to track mergeinfo nesting levels */
   int count; /* only incremented when nest_level == 0 */
@@ -451,6 +452,14 @@ create_log_body(serf_bucket_t **body_bkt
                                    alloc);
     }
 
+  if (log_ctx->move_behavior != svn_move_behavior_no_moves)
+    {
+      svn_ra_serf__add_tag_buckets(buckets,
+                                   "S:move-behavior",
+                                   apr_ltoa(pool, log_ctx->move_behavior),
+                                   alloc);
+    }
+
   if (log_ctx->revprops)
     {
       int i;
@@ -507,6 +516,7 @@ svn_ra_serf__get_log(svn_ra_session_t *r
                      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 receiver,
                      void *receiver_baton,
@@ -532,6 +542,7 @@ svn_ra_serf__get_log(svn_ra_session_t *r
   log_ctx->changed_paths = discover_changed_paths;
   log_ctx->strict_node_history = strict_node_history;
   log_ctx->include_merged_revisions = include_merged_revisions;
+  log_ctx->move_behavior = move_behavior;
   log_ctx->revprops = revprops;
   log_ctx->nest_level = 0;
 

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/ra_serf.h?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/ra_serf.h Thu Sep 26 13:47:21 2013
@@ -429,6 +429,10 @@ typedef struct svn_ra_serf__handler_t {
      enabled. */
   svn_boolean_t custom_accept_encoding;
 
+  /* If TRUE then default DAV: capabilities request headers is not configured
+     for request. */
+  svn_boolean_t no_dav_headers;
+
   /* Has the request/response been completed?  */
   svn_boolean_t done;
 
@@ -1494,6 +1498,7 @@ svn_ra_serf__get_log(svn_ra_session_t *s
                      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 receiver,
                      void *receiver_baton,

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/update.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/update.c Thu Sep 26 13:47:21 2013
@@ -1574,6 +1574,7 @@ fetch_file(report_context_t *ctx, report
           handler->session = ctx->sess;
 
           handler->custom_accept_encoding = TRUE;
+          handler->no_dav_headers = TRUE;
           handler->header_delegate = headers_fetch;
           handler->header_delegate_baton = fetch_ctx;
 
@@ -3705,6 +3706,7 @@ svn_ra_serf__get_file(svn_ra_session_t *
           handler->session = session;
 
           handler->custom_accept_encoding = TRUE;
+          handler->no_dav_headers = TRUE;
           handler->header_delegate = headers_fetch;
           handler->header_delegate_baton = stream_ctx;
 

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/util.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_serf/util.c Thu Sep 26 13:47:21 2013
@@ -705,6 +705,9 @@ apr_status_t svn_ra_serf__handle_client_
  *
  * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header.
  *
+ * If DAV_HEADERS is non-zero, it will add standard DAV capabilites headers
+ * to request.
+ *
  * REQUEST_POOL should live for the duration of the request. Serf will
  * construct this and provide it to the request_setup callback, so we
  * should just use that one.
@@ -717,6 +720,7 @@ setup_serf_req(serf_request_t *request,
                const char *method, const char *url,
                serf_bucket_t *body_bkt, const char *content_type,
                const char *accept_encoding,
+               svn_boolean_t dav_headers,
                apr_pool_t *request_pool,
                apr_pool_t *scratch_pool)
 {
@@ -783,12 +787,15 @@ setup_serf_req(serf_request_t *request,
       serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding);
     }
 
-  /* These headers need to be sent with every request; see issue #3255
-     ("mod_dav_svn does not pass client capabilities to start-commit
-     hooks") for why. */
-  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
-  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
-  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
+  /* These headers need to be sent with every request except GET; see
+     issue #3255 ("mod_dav_svn does not pass client capabilities to
+     start-commit hooks") for why. */
+  if (dav_headers)
+    {
+      serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
+      serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
+      serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
+    }
 
   return SVN_NO_ERROR;
 }
@@ -2241,7 +2248,8 @@ setup_request(serf_request_t *request,
   SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt,
                          handler->session, handler->method, handler->path,
                          body_bkt, handler->body_type, accept_encoding,
-                         request_pool, scratch_pool));
+                         !handler->no_dav_headers, request_pool,
+                         scratch_pool));
 
   if (handler->header_delegate)
     {
@@ -2251,7 +2259,7 @@ setup_request(serf_request_t *request,
                                        request_pool));
     }
 
-  return APR_SUCCESS;
+  return SVN_NO_ERROR;
 }
 
 /* Implements the serf_request_setup_t interface (which sets up both a

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/client.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/client.c Thu Sep 26 13:47:21 2013
@@ -1487,6 +1487,7 @@ perform_ra_svn_log(svn_error_t **outer_e
                    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 receiver,
                    void *receiver_baton,
@@ -1536,11 +1537,13 @@ perform_ra_svn_log(svn_error_t **outer_e
           else
             want_custom_revprops = TRUE;
         }
-      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
+      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)n)",
+                                      (apr_uint64_t) move_behavior));
     }
   else
     {
-      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!w())", "all-revprops"));
+      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!w()n)", "all-revprops",
+                                      (apr_uint64_t) move_behavior));
 
       want_author = TRUE;
       want_date = TRUE;
@@ -1712,6 +1715,7 @@ ra_svn_log(svn_ra_session_t *session,
            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 receiver,
            void *receiver_baton, apr_pool_t *pool)
@@ -1726,7 +1730,7 @@ ra_svn_log(svn_ra_session_t *session,
                                            discover_changed_paths,
                                            strict_node_history,
                                            include_merged_revisions,
-                                           revprops,
+                                           move_behavior, revprops,
                                            receiver, receiver_baton,
                                            pool));
   return svn_error_trace(

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/deprecated.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/protocol
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/protocol?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/protocol (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_ra_svn/protocol Thu Sep 26 13:47:21 2013
@@ -382,11 +382,13 @@ second place for auth-request point as n
                 [ end-rev:number ] changed-paths:bool strict-node:bool
                 ? limit:number
                 ? include-merged-revisions:bool
-                all-revprops | revprops ( revprop:string ... ) )
+                all-revprops | revprops ( revprop:string ... )
+                ? move-behavior:number )
     Before sending response, server sends log entries, ending with "done".
     If a client does not want to specify a limit, it should send 0 as the
     limit parameter.  rev-props excludes author, date, and log; they are
     sent separately for backwards-compatibility.
+    Move-behavior is encoded like enum svn_move_behavior_t.
     log-entry: ( ( change:changed-path-entry ... ) rev:number
                  [ author:string ] [ date:string ] [ message:string ]
                  ? has-children:bool invalid-revnum:bool

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/commit.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/commit.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/commit.c Thu Sep 26 13:47:21 2013
@@ -1198,20 +1198,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 ev2_baton *eb = baton;
-
-  SVN_ERR(svn_editor_rotate(eb->inner, relpaths, revisions));
-  return SVN_NO_ERROR;
-}
-
-
 /* This implements svn_editor_cb_complete_t */
 static svn_error_t *
 complete_cb(void *baton,
@@ -1333,7 +1319,6 @@ svn_repos__get_commit_ev2(svn_editor_t *
     delete_cb,
     copy_cb,
     move_cb,
-    rotate_cb,
     complete_cb,
     abort_cb
   };

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/deprecated.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/deprecated.c Thu Sep 26 13:47:21 2013
@@ -472,6 +472,30 @@ svn_repos_fs_get_locks(apr_hash_t **lock
 
 /*** From logs.c ***/
 svn_error_t *
+svn_repos_get_logs4(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,
+                    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)
+{
+  return svn_repos_get_logs5(repos, paths, start, end, limit,
+                             discover_changed_paths, strict_node_history,
+                             include_merged_revisions,
+                             svn_move_behavior_no_moves, revprops,
+                             authz_read_func, authz_read_baton,
+                             receiver, receiver_baton, pool);
+}
+
+svn_error_t *
 svn_repos_get_logs3(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/dump.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/dump.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/dump.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/dump.c Thu Sep 26 13:47:21 2013
@@ -38,6 +38,7 @@
 
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_fs_private.h"
+#include "private/svn_cache.h"
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
@@ -130,6 +131,13 @@ struct edit_baton
   /* reusable buffer for writing file contents */
   char buffer[SVN__STREAM_CHUNK_SIZE];
   apr_size_t bufsize;
+
+  /* map nodeID -> node kind.  May be NULL.
+     The key is the string representation of the node ID given in
+     directory entries.  If we find an entry in this cache, the
+     respective node has already been verified as readable and being
+     of the type stored as value in the cache. */
+  svn_cache__t *verified_dirents_cache;
 };
 
 struct dir_baton
@@ -960,6 +968,7 @@ get_dump_editor(const svn_delta_editor_t
                 svn_revnum_t oldest_dumped_rev,
                 svn_boolean_t use_deltas,
                 svn_boolean_t verify,
+                svn_cache__t *verified_dirents_cache,
                 apr_pool_t *pool)
 {
   /* Allocate an edit baton to be stored in every directory baton.
@@ -984,6 +993,7 @@ get_dump_editor(const svn_delta_editor_t
   eb->verify = verify;
   eb->found_old_reference = found_old_reference;
   eb->found_old_mergeinfo = found_old_mergeinfo;
+  eb->verified_dirents_cache = verified_dirents_cache;
 
   /* Set up the editor. */
   dump_editor->open_root = open_root;
@@ -1180,7 +1190,8 @@ svn_repos_dump_fs3(svn_repos_t *repos,
                               "", stream, &found_old_reference,
                               &found_old_mergeinfo, NULL,
                               notify_func, notify_baton,
-                              start_rev, use_deltas_for_rev, FALSE, subpool));
+                              start_rev, use_deltas_for_rev, FALSE,
+                              NULL, subpool));
 
       /* Drive the editor in one way or another. */
       SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, subpool));
@@ -1292,9 +1303,42 @@ verify_directory_entry(void *baton, cons
 {
   struct dir_baton *db = baton;
   svn_fs_dirent_t *dirent = (svn_fs_dirent_t *)val;
-  char *path = svn_relpath_join(db->path, (const char *)key, pool);
+  char *path;
   apr_hash_t *dirents;
   svn_filesize_t len;
+  svn_string_t *unparsed_id;
+
+  /* most directory entries will be unchanged from previous revs.
+     We should find those in the cache and they must match the
+     type defined in the DIRENT. */
+  if (db->edit_baton->verified_dirents_cache)
+    {
+      svn_node_kind_t kind;
+      svn_boolean_t found;
+      unparsed_id = svn_fs_unparse_id(dirent->id, pool);
+
+      SVN_ERR(svn_cache__get((void **)&kind, &found,
+                             db->edit_baton->verified_dirents_cache,
+                             unparsed_id->data, pool));
+
+      if (found)
+        {
+          if (kind == dirent->kind)
+            return SVN_NO_ERROR;
+          else
+            {
+              path = svn_relpath_join(db->path, (const char *)key, pool);
+
+              return
+                  svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                                    _("Unexpected node kind %d for '%s'. "
+                                      "Expected kind was %d."),
+                                    dirent->kind, path, kind);
+            }
+        }
+    }
+
+  path = svn_relpath_join(db->path, (const char *)key, pool);
 
   /* since we can't access the directory entries directly by their ID,
      we need to navigate from the FS_ROOT to them (relatively expensive
@@ -1316,6 +1360,11 @@ verify_directory_entry(void *baton, cons
                              dirent->kind, path);
   }
 
+  /* remember ID, kind pair */
+  if (db->edit_baton->verified_dirents_cache)
+    SVN_ERR(svn_cache__set(db->edit_baton->verified_dirents_cache,
+                           unparsed_id->data, &dirent->kind, pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -1359,6 +1408,7 @@ verify_one_revision(svn_fs_t *fs,
                     svn_revnum_t start_rev,
                     svn_cancel_func_t cancel_func,
                     void *cancel_baton,
+                    svn_cache__t *verified_dirents_cache,
                     apr_pool_t *scratch_pool)
 {
   const svn_delta_editor_t *dump_editor;
@@ -1377,6 +1427,7 @@ verify_one_revision(svn_fs_t *fs,
                           notify_func, notify_baton,
                           start_rev,
                           FALSE, TRUE, /* use_deltas, verify */
+                          verified_dirents_cache,
                           scratch_pool));
   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
                                             dump_editor, dump_edit_baton,
@@ -1424,6 +1475,30 @@ verify_fs2_notify_func(svn_revnum_t revi
                             notify_baton->notify, pool);
 }
 
+/* cache entry (de-)serialization support for svn_node_kind_t. */
+static svn_error_t *
+serialize_node_kind(void **data,
+                    apr_size_t *data_len,
+                    void *in,
+                    apr_pool_t *pool)
+{
+  *data_len = sizeof(svn_node_kind_t);
+  *data = in;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+deserialize_node_kind(void **out,
+                      void *data,
+                      apr_size_t data_len,
+                      apr_pool_t *pool)
+{
+  *(svn_node_kind_t *)out = *(svn_node_kind_t *)data;
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_repos_verify_fs3(svn_repos_t *repos,
                      svn_revnum_t start_rev,
@@ -1444,6 +1519,7 @@ svn_repos_verify_fs3(svn_repos_t *repos,
   struct verify_fs2_notify_func_baton_t *verify_notify_baton = NULL;
   svn_error_t *err;
   svn_boolean_t found_corruption = FALSE;
+  svn_cache__t *verified_dirents_cache = NULL;
 
   /* Determine the current youngest revision of the filesystem. */
   SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
@@ -1505,13 +1581,26 @@ svn_repos_verify_fs3(svn_repos_t *repos,
       svn_error_clear(err);
     }
 
+  if (svn_cache__get_global_membuffer_cache())
+    SVN_ERR(svn_cache__create_membuffer_cache
+                                 (&verified_dirents_cache,
+                                  svn_cache__get_global_membuffer_cache(),
+                                  serialize_node_kind,
+                                  deserialize_node_kind,
+                                  APR_HASH_KEY_STRING,
+                                  svn_uuid_generate(pool),
+                                  SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+                                  FALSE,
+                                  pool));
+
   for (rev = start_rev; rev <= end_rev; rev++)
     {
       svn_pool_clear(iterpool);
 
       /* Wrapper function to catch the possible errors. */
-      err = verify_one_revision(fs, rev, notify_func, notify_baton, start_rev,
-                                cancel_func, cancel_baton, iterpool);
+      err = verify_one_revision(fs, rev, notify_func, notify_baton,
+                                start_rev, cancel_func, cancel_baton,
+                                verified_dirents_cache, iterpool);
 
       if (err)
         {

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/log.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/log.c Thu Sep 26 13:47:21 2013
@@ -69,7 +69,8 @@ svn_repos_check_revision_access(svn_repo
 
   /* Fetch the changes associated with REVISION. */
   SVN_ERR(svn_fs_revision_root(&rev_root, fs, revision, pool));
-  SVN_ERR(svn_fs_paths_changed2(&changes, rev_root, pool));
+  SVN_ERR(svn_fs_paths_changed3(&changes, rev_root,
+                                svn_move_behavior_explicit_moves, pool));
 
   /* No changed paths?  We're done. */
   if (apr_hash_count(changes) == 0)
@@ -105,6 +106,8 @@ svn_repos_check_revision_access(svn_repo
         {
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
           {
             const char *copyfrom_path;
             svn_revnum_t copyfrom_rev;
@@ -163,7 +166,8 @@ svn_repos_check_revision_access(svn_repo
  *
  * To prevent changes from being processed over and over again, the
  * changed paths for ROOT may be passed in PREFETCHED_CHANGES.  If the
- * latter is NULL, we will request the list inside this function.
+ * latter is NULL, we will request the list inside this function using
+ * the specified MOVE_BEHAVIOR.
  *
  * If optional AUTHZ_READ_FUNC is non-NULL, then use it (with
  * AUTHZ_READ_BATON and FS) to check whether each changed-path (and
@@ -184,6 +188,7 @@ detect_changed(apr_hash_t **changed,
                svn_fs_root_t *root,
                svn_fs_t *fs,
                apr_hash_t *prefetched_changes,
+               svn_move_behavior_t move_behavior,
                svn_repos_authz_func_t authz_read_func,
                void *authz_read_baton,
                apr_pool_t *pool)
@@ -196,7 +201,7 @@ detect_changed(apr_hash_t **changed,
 
   *changed = svn_hash__make(pool);
   if (changes == NULL)
-    SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+    SVN_ERR(svn_fs_paths_changed3(&changes, root, move_behavior, pool));
 
   if (apr_hash_count(changes) == 0)
     /* No paths changed in this revision?  Uh, sure, I guess the
@@ -255,6 +260,14 @@ detect_changed(apr_hash_t **changed,
           action = 'D';
           break;
 
+        case svn_fs_path_change_move:
+          action = 'V';
+          break;
+
+        case svn_fs_path_change_movereplace:
+          action = 'E';
+          break;
+
         case svn_fs_path_change_modify:
         default:
           action = 'M';
@@ -306,7 +319,8 @@ detect_changed(apr_hash_t **changed,
         }
 
 
-      if ((action == 'A') || (action == 'R'))
+      if (   (action == 'A') || (action == 'R')
+          || (action == 'V') || (action == 'E'))
         {
           const char *copyfrom_path = change->copyfrom_path;
           svn_revnum_t copyfrom_rev = change->copyfrom_rev;
@@ -563,7 +577,9 @@ next_history_rev(const apr_array_header_
    catalogs describing how mergeinfo values on paths (which are the
    keys of those catalogs) were changed in REV.  If *PREFETCHED_CAHNGES
    already contains the changed paths for REV, use that.  Otherwise,
-   request that data and return it in *PREFETCHED_CHANGES. */
+   request that data and return it in *PREFETCHED_CHANGES.
+   MOVE_BEHAVIOR is a simple pass-through parameter that tells the FS
+   layer which changes to report as moves instead of additions. */
 /* ### TODO: This would make a *great*, useful public function,
    ### svn_repos_fs_mergeinfo_changed()!  -- cmpilato  */
 static svn_error_t *
@@ -572,6 +588,7 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
                      apr_hash_t **prefetched_changes,
                      svn_fs_t *fs,
                      svn_revnum_t rev,
+                     svn_move_behavior_t move_behavior,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 
@@ -592,7 +609,8 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
      narrow down our search. */
   SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
   if (*prefetched_changes == NULL)
-    SVN_ERR(svn_fs_paths_changed2(prefetched_changes, root, scratch_pool));
+    SVN_ERR(svn_fs_paths_changed3(prefetched_changes, root, move_behavior,
+                                  scratch_pool));
 
   /* No changed paths?  We're done. */
   if (apr_hash_count(*prefetched_changes) == 0)
@@ -638,6 +656,8 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
            was.  If not, there's no previous location to examine.  */
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
           {
             const char *copyfrom_path;
             svn_revnum_t copyfrom_rev;
@@ -782,7 +802,8 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
    *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO.
    If *PREFETCHED_CAHNGES already contains the changed paths for
    REV, use that.  Otherwise, request that data and return it in
-   *PREFETCHED_CHANGES.
+   *PREFETCHED_CHANGES.  MOVE_BEHAVIOR tells the FS layer which
+   changes to report as moves instead of additions.
    Use POOL for all allocations. */
 static svn_error_t *
 get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo,
@@ -791,6 +812,7 @@ get_combined_mergeinfo_changes(svn_merge
                                svn_fs_t *fs,
                                const apr_array_header_t *paths,
                                svn_revnum_t rev,
+                               svn_move_behavior_t move_behavior,
                                apr_pool_t *result_pool,
                                apr_pool_t *scratch_pool)
 {
@@ -820,7 +842,8 @@ get_combined_mergeinfo_changes(svn_merge
   err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog,
                              &added_mergeinfo_catalog,
                              prefetched_changes,
-                             fs, rev, scratch_pool, scratch_pool);
+                             fs, rev, move_behavior,
+                             scratch_pool, scratch_pool);
   if (err)
     {
       if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -1025,6 +1048,7 @@ fill_log_entry(svn_log_entry_t *log_entr
                svn_fs_t *fs,
                apr_hash_t *prefetched_changes,
                svn_boolean_t discover_changed_paths,
+               svn_move_behavior_t move_behavior,
                const apr_array_header_t *revprops,
                svn_repos_authz_func_t authz_read_func,
                void *authz_read_baton,
@@ -1043,7 +1067,7 @@ fill_log_entry(svn_log_entry_t *log_entr
 
       SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool));
       patherr = detect_changed(&changed_paths,
-                               newroot, fs, prefetched_changes,
+                               newroot, fs, prefetched_changes, move_behavior,
                                authz_read_func, authz_read_baton,
                                pool);
 
@@ -1160,9 +1184,9 @@ fill_log_entry(svn_log_entry_t *log_entr
    only the revision properties named by the (const char *) array elements
    (i.e. retrieve none if the array is empty).
 
-   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, and
-   NESTED_MERGES are as per the arguments of the same name to DO_LOGS.  If
-   HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
+   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, MOVE_BEHAVIOR,
+   and NESTED_MERGES are as per the arguments of the same name to DO_LOGS.
+   If HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
    already represented in LOG_TARGET_HISTORY_AS_MERGEINFO, then don't send
    the log message for REV.  If SUBTRACTIVE_MERGE is true, then REV was
    reverse merged.
@@ -1180,6 +1204,7 @@ send_log(svn_revnum_t rev,
          svn_boolean_t discover_changed_paths,
          svn_boolean_t subtractive_merge,
          svn_boolean_t handling_merged_revision,
+         svn_move_behavior_t move_behavior,
          const apr_array_header_t *revprops,
          svn_boolean_t has_children,
          svn_log_entry_receiver_t receiver,
@@ -1195,8 +1220,8 @@ send_log(svn_revnum_t rev,
   log_entry = svn_log_entry_create(pool);
   SVN_ERR(fill_log_entry(log_entry, rev, fs, prefetched_changes,
                          discover_changed_paths || handling_merged_revision,
-                         revprops, authz_read_func, authz_read_baton,
-                         pool));
+                         move_behavior, revprops, 
+                         authz_read_func, authz_read_baton, pool));
   log_entry->has_children = has_children;
   log_entry->subtractive_merge = subtractive_merge;
 
@@ -1663,6 +1688,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t handling_merged_revisions,
         svn_boolean_t subtractive_merge,
         svn_boolean_t ignore_missing_locations,
+        svn_move_behavior_t move_behavior,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
         svn_log_entry_receiver_t receiver,
@@ -1712,6 +1738,7 @@ handle_merged_revisions(svn_revnum_t rev
                         svn_mergeinfo_t deleted_mergeinfo,
                         svn_boolean_t discover_changed_paths,
                         svn_boolean_t strict_node_history,
+                        svn_move_behavior_t move_behavior,
                         const apr_array_header_t *revprops,
                         svn_log_entry_receiver_t receiver,
                         void *receiver_baton,
@@ -1754,7 +1781,7 @@ handle_merged_revisions(svn_revnum_t rev
                       pl_range->range.start, pl_range->range.end, 0,
                       discover_changed_paths, strict_node_history,
                       TRUE, pl_range->reverse_merge, TRUE, TRUE,
-                      revprops, TRUE, receiver, receiver_baton,
+                      move_behavior, revprops, TRUE, receiver, receiver_baton,
                       authz_read_func, authz_read_baton, iterpool));
     }
   svn_pool_destroy(iterpool);
@@ -1888,6 +1915,9 @@ store_search(svn_mergeinfo_t processed,
    If IGNORE_MISSING_LOCATIONS is set, don't treat requests for bogus
    repository locations as fatal -- just ignore them.
 
+   MOVE_BEHAVIOR is a simple pass-through parameter that tells the FS
+   layer which changes to report as moves instead of additions.
+
    If LOG_TARGET_HISTORY_AS_MERGEINFO is not NULL then it contains mergeinfo
    representing the history of PATHS between HIST_START and HIST_END.
 
@@ -1925,6 +1955,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t subtractive_merge,
         svn_boolean_t handling_merged_revisions,
         svn_boolean_t ignore_missing_locations,
+        svn_move_behavior_t move_behavior,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
         svn_log_entry_receiver_t receiver,
@@ -2019,8 +2050,8 @@ do_logs(svn_fs_t *fs,
                                                      &deleted_mergeinfo,
                                                      &changes,
                                                      fs, cur_paths,
-                                                     current, iterpool,
-                                                     iterpool));
+                                                     current, move_behavior,
+                                                     iterpool, iterpool));
               has_children = (apr_hash_count(added_mergeinfo) > 0
                               || apr_hash_count(deleted_mergeinfo) > 0);
             }
@@ -2034,7 +2065,7 @@ do_logs(svn_fs_t *fs,
                                log_target_history_as_mergeinfo, nested_merges,
                                discover_changed_paths,
                                subtractive_merge, handling_merged_revisions,
-                               revprops, has_children,
+                               move_behavior, revprops, has_children,
                                receiver, receiver_baton,
                                authz_read_func, authz_read_baton, iterpool));
 
@@ -2057,6 +2088,7 @@ do_logs(svn_fs_t *fs,
                     added_mergeinfo, deleted_mergeinfo,
                     discover_changed_paths,
                     strict_node_history,
+                    move_behavior,
                     revprops,
                     receiver, receiver_baton,
                     authz_read_func,
@@ -2138,7 +2170,8 @@ do_logs(svn_fs_t *fs,
           SVN_ERR(send_log(current, fs, NULL,
                            log_target_history_as_mergeinfo, nested_merges,
                            discover_changed_paths, subtractive_merge,
-                           handling_merged_revisions, revprops, has_children,
+                           handling_merged_revisions, move_behavior,
+                           revprops, has_children,
                            receiver, receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
           if (has_children)
@@ -2156,7 +2189,8 @@ do_logs(svn_fs_t *fs,
                                               added_mergeinfo,
                                               deleted_mergeinfo,
                                               discover_changed_paths,
-                                              strict_node_history, revprops,
+                                              strict_node_history,
+                                              move_behavior, revprops,
                                               receiver, receiver_baton,
                                               authz_read_func,
                                               authz_read_baton,
@@ -2258,7 +2292,7 @@ get_paths_history_as_mergeinfo(svn_merge
 }
 
 svn_error_t *
-svn_repos_get_logs4(svn_repos_t *repos,
+svn_repos_get_logs5(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,
                     svn_revnum_t end,
@@ -2266,6 +2300,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
                     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,
@@ -2374,7 +2409,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
             rev = start + i;
           SVN_ERR(send_log(rev, fs, NULL, NULL, NULL,
                            discover_changed_paths, FALSE,
-                           FALSE, revprops, FALSE, receiver,
+                           FALSE, move_behavior, revprops, FALSE, receiver,
                            receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
         }
@@ -2402,7 +2437,8 @@ svn_repos_get_logs4(svn_repos_t *repos,
 
   return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end,
                  limit, discover_changed_paths, strict_node_history,
-                 include_merged_revisions, FALSE, FALSE, FALSE, revprops,
+                 include_merged_revisions, FALSE, FALSE, FALSE,
+                 move_behavior, revprops,
                  descending_order, receiver, receiver_baton,
                  authz_read_func, authz_read_baton, pool);
 }

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/reporter.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/reporter.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/reporter.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_repos/reporter.c Thu Sep 26 13:47:21 2013
@@ -499,7 +499,7 @@ get_revision_info(report_baton_t *b,
       info->author = author ? svn_string_dup(author, b->pool) : NULL;
 
       /* Cache it */
-      apr_hash_set(b->revision_infos, &info->rev, sizeof(rev), info);
+      apr_hash_set(b->revision_infos, &info->rev, sizeof(info->rev), info);
     }
 
   *revision_info = info;
@@ -1143,6 +1143,8 @@ delta_dirs(report_baton_t *b, svn_revnum
   apr_hash_t *s_entries = NULL, *t_entries;
   apr_hash_index_t *hi;
   apr_pool_t *subpool;
+  apr_array_header_t *t_ordered_entries = NULL;
+  int i;
 
   /* Compare the property lists.  If we're starting empty, pass a NULL
      source path so that we add all the properties.
@@ -1275,9 +1277,12 @@ delta_dirs(report_baton_t *b, svn_revnum
         }
 
       /* Loop over the dirents in the target. */
-      for (hi = apr_hash_first(pool, t_entries); hi; hi = apr_hash_next(hi))
+      SVN_ERR(svn_fs_dir_optimal_order(&t_ordered_entries, b->t_root,
+                                       t_entries, pool));
+      for (i = 0; i < t_ordered_entries->nelts; ++i)
         {
-          const svn_fs_dirent_t *t_entry = svn__apr_hash_index_val(hi);
+          const svn_fs_dirent_t *t_entry
+             = APR_ARRAY_IDX(t_ordered_entries, i, svn_fs_dirent_t *);
           const svn_fs_dirent_t *s_entry;
           const char *s_fullpath, *t_fullpath, *e_fullpath;
 

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache-membuffer.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache-membuffer.c Thu Sep 26 13:47:21 2013
@@ -51,7 +51,8 @@
  * 2. A directory of cache entries. This is organized similar to CPU
  *    data caches: for every possible key, there is exactly one group
  *    of entries that may contain the header info for an item with
- *    that given key. The result is a GROUP_SIZE-way associative cache.
+ *    that given key.  The result is a GROUP_SIZE+-way associative cache
+ *    whose associativity can be dynamically increased.
  *
  * Only the start address of these two data parts are given as a native
  * pointer. All other references are expressed as offsets to these pointers.
@@ -97,6 +98,10 @@
  * about 50% of the content survives every 50% of the cache being re-written
  * with new entries. For details on the fine-tuning involved, see the
  * comments in ensure_data_insertable_l2().
+ * 
+ * Due to the randomized mapping of keys to entry groups, some groups may
+ * overflow.  In that case, there are spare groups that can be chained to
+ * an already used group to extend it.
  *
  * To limit the entry size and management overhead, not the actual item keys
  * but only their MD5-based hashes will be stored. This is reasonably safe
@@ -111,13 +116,6 @@
  * on their hash key.
  */
 
-/* A 16-way associative cache seems to be a good compromise between
- * performance (worst-case lookups) and efficiency-loss due to collisions.
- *
- * This value may be changed to any positive integer.
- */
-#define GROUP_SIZE 16
-
 /* For more efficient copy operations, let's align all data items properly.
  * Must be a power of 2.
  */
@@ -298,17 +296,18 @@ static svn_error_t* assert_equal_tags(co
 
 #define DEBUG_CACHE_MEMBUFFER_TAG tag,
 
-#define DEBUG_CACHE_MEMBUFFER_INIT_TAG                         \
-  entry_tag_t _tag;                                            \
-  entry_tag_t *tag = &_tag;                                    \
-  SVN_ERR(store_key_part(tag,                                  \
-                         cache->prefix,                        \
-                         cache->prefix_tail,                   \
-                         key,                                  \
-                         cache->key_len == APR_HASH_KEY_STRING \
-                             ? strlen((const char *) key)      \
-                             : cache->key_len,                 \
-                         cache->pool));
+#define DEBUG_CACHE_MEMBUFFER_INIT_TAG                           \
+  entry_tag_t _tag;                                              \
+  entry_tag_t *tag = &_tag;                                      \
+  if (key)                                                       \
+    SVN_ERR(store_key_part(tag,                                  \
+                           cache->prefix,                        \
+                           cache->prefix_tail,                   \
+                           key,                                  \
+                           cache->key_len == APR_HASH_KEY_STRING \
+                               ? strlen((const char *) key)      \
+                               : cache->key_len,                 \
+                           cache->pool));
 
 #else
 
@@ -336,10 +335,11 @@ typedef struct entry_t
    */
   apr_uint64_t offset;
 
-  /* Size of the serialized item data. May be 0.
+  /* Size of the serialized item data. May be 0.  The MAX_ITEM_SIZE macro
+   * above ensures that there will be no overflows.
    * Only valid for used entries.
    */
-  apr_size_t size;
+  apr_uint32_t size;
 
   /* Number of (read) hits for this entry. Will be reset upon write.
    * Only valid for used entries.
@@ -372,15 +372,60 @@ typedef struct entry_t
 #endif
 } entry_t;
 
-/* We group dictionary entries to make this GROUP-SIZE-way associative.
+/* Group header struct.
  */
-typedef struct entry_group_t
+typedef struct group_header_t
 {
   /* number of entries used [0 .. USED-1] */
   apr_uint32_t used;
 
+  /* next group in the chain or NO_INDEX for the last.
+   * For recycleable unused spare groups, this points to the next
+   * unused spare group */
+  apr_uint32_t next;
+
+  /* previously group in the chain or NO_INDEX for the first */
+  apr_uint32_t previous;
+
+  /* number of elements in the chain from start to here.
+   * >= 1 for used groups, 0 for unused spare groups */
+  apr_uint32_t chain_length;
+ 
+} group_header_t;
+
+/* The size of the group struct should be a power of two make sure it does
+ * not cross memory page boundaries.  Since we already access the cache
+ * randomly, having two page table lookups instead of one is bad.
+ */
+#define GROUP_BLOCK_SIZE 512
+
+/* A ~10-way associative cache seems to be a good compromise between
+ * performance (worst-case lookups) and efficiency-loss due to collisions.
+ *
+ * This value may be changed to any positive integer.
+ */
+#define GROUP_SIZE \
+          ((GROUP_BLOCK_SIZE - sizeof(group_header_t)) / sizeof(entry_t))
+
+/* Maximum number of groups in a chain, i.e. a cache index group can hold
+ * up to GROUP_SIZE * MAX_GROUP_CHAIN_LENGTH entries.
+ */
+#define MAX_GROUP_CHAIN_LENGTH 8
+
+/* We group dictionary entries to make this GROUP-SIZE-way associative.
+ */
+typedef struct entry_group_t
+{
+  /* group globals */
+  group_header_t header;
+  
+  /* padding and also room for future extensions */
+  char padding[GROUP_BLOCK_SIZE - sizeof(group_header_t)
+               - sizeof(entry_t) * GROUP_SIZE];
+
   /* the actual entries */
   entry_t entries[GROUP_SIZE];
+
 } entry_group_t;
 
 /* Per-cache level header structure.  Instances of this are members of
@@ -432,7 +477,8 @@ struct svn_membuffer_t
      and that all segments must / will report the same values here. */
   apr_uint32_t segment_count;
 
-  /* The dictionary, GROUP_SIZE * group_count entries long. Never NULL.
+  /* The dictionary, GROUP_SIZE * (group_count + spare_group_count)
+   * entries long.  Never NULL.
    */
   entry_group_t *directory;
 
@@ -445,6 +491,20 @@ struct svn_membuffer_t
    */
   apr_uint32_t group_count;
 
+  /* Total number of spare groups.
+   */
+  apr_uint32_t spare_group_count;
+
+  /* First recycleable spare group.
+   */
+  apr_uint32_t first_spare_group;
+
+  /* Maximum number of spare groups ever used.  I.e. group index
+   * group_count + max_spare_used is the first unused spare group
+   * if first_spare_group is NO_INDEX.
+   */
+  apr_uint32_t max_spare_used;
+
   /* Pointer to the data buffer, data_size bytes long. Never NULL.
    */
   unsigned char *data;
@@ -645,6 +705,132 @@ do {                                    
   SVN_ERR(unlock_cache(cache, (expr)));                         \
 } while (0)
 
+/* Returns 0 if the entry group identified by GROUP_INDEX in CACHE has not
+ * been initialized, yet. In that case, this group can not data. Otherwise,
+ * a non-zero value is returned.
+ */
+static APR_INLINE unsigned char
+is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char flags
+    = cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)];
+  unsigned char bit_mask
+    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
+
+  return flags & bit_mask;
+}
+
+/* Initializes the section of the directory in CACHE that contains
+ * the entry group identified by GROUP_INDEX. */
+static void
+initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char bit_mask;
+  apr_uint32_t i;
+
+  /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
+  apr_uint32_t first_index =
+      (group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY;
+  apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY;
+  if (last_index > cache->group_count)
+    last_index = cache->group_count;
+
+  for (i = first_index; i < last_index; ++i)
+    {
+      group_header_t *header = &cache->directory[i].header;
+      header->used = 0;
+      header->chain_length = 1;
+      header->next = NO_INDEX;
+      header->previous = NO_INDEX;
+    }
+
+  /* set the "initialized" bit for these groups */
+  bit_mask
+    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
+  cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
+    |= bit_mask;
+}
+
+/* Return the next available spare group from CACHE and mark it as used.
+ * May return NULL.
+ */
+static entry_group_t *
+allocate_spare_group(svn_membuffer_t *cache)
+{
+  entry_group_t *group = NULL;
+
+  /* is there some ready-to-use group? */
+  if (cache->first_spare_group != NO_INDEX)
+    {
+      group = &cache->directory[cache->first_spare_group];
+      cache->first_spare_group = group->header.next;
+    }
+
+  /* any so far untouched spares available? */
+  else if (cache->max_spare_used < cache->spare_group_count)
+    {
+      apr_uint32_t group_index = cache->group_count + cache->max_spare_used;
+      ++cache->max_spare_used;
+
+      if (!is_group_initialized(cache, group_index))
+        initialize_group(cache, group_index);
+
+      group = &cache->directory[group_index];
+    }
+
+  /* spare groups must be empty */
+  assert(!group || !group->header.used);
+  return group;
+}
+
+/* Mark previously allocated spare group GROUP in CACHE as "unused".
+ */
+static void
+free_spare_group(svn_membuffer_t *cache,
+                 entry_group_t *group)
+{
+  assert(group->header.used == 0);
+  assert(group->header.previous != NO_INDEX);
+  assert(group - cache->directory >= cache->group_count);
+
+  /* unchain */
+  cache->directory[group->header.previous].header.next = NO_INDEX;
+  group->header.chain_length = 0;
+  group->header.previous = NO_INDEX;
+
+  /* add to chain of spares */
+  group->header.next = cache->first_spare_group;
+  cache->first_spare_group = group - cache->directory;
+}
+
+/* Follow the group chain from GROUP in CACHE to its end and return the last
+ * group.  May return GROUP.
+ */
+static entry_group_t *
+last_group_in_chain(svn_membuffer_t *cache,
+                    entry_group_t *group)
+{
+  while (group->header.next != NO_INDEX)
+    group = &cache->directory[group->header.next];
+
+  return group;
+}
+
+/* Return the CHAIN_INDEX-th element in the group chain starting from group
+ * START_GROUP_INDEX in CACHE.
+ */
+static entry_group_t *
+get_group(svn_membuffer_t *cache,
+          apr_uint32_t start_group_index,
+          apr_uint32_t chain_index)
+{
+  entry_group_t *group = &cache->directory[start_group_index];
+  for (; chain_index; --chain_index)
+    group = &cache->directory[group->header.next];
+
+  return group;
+}
+
 /* Resolve a dictionary entry reference, i.e. return the entry
  * for the given IDX.
  */
@@ -766,13 +952,13 @@ drop_entry(svn_membuffer_t *cache, entry
    */
   apr_uint32_t idx = get_index(cache, entry);
   apr_uint32_t group_index = idx / GROUP_SIZE;
-  entry_group_t *group = &cache->directory[group_index];
-  apr_uint32_t last_in_group = group_index * GROUP_SIZE + group->used - 1;
-  cache_level_t *level = get_cache_level(cache, entry);
+  entry_group_t *last_group
+    = last_group_in_chain(cache, &cache->directory[group_index]);
+  apr_uint32_t last_in_group
+    = (last_group - cache->directory) * GROUP_SIZE
+    + last_group->header.used - 1;
 
-  /* Only valid to be called for used entries.
-   */
-  assert(idx <= last_in_group);
+  cache_level_t *level = get_cache_level(cache, entry);
 
   /* update global cache usage counters
    */
@@ -806,16 +992,16 @@ drop_entry(svn_membuffer_t *cache, entry
   /* unlink it from the chain of used entries
    */
   unchain_entry(cache, level, entry, idx);
-  
+
   /* Move last entry into hole (if the removed one is not the last used).
    * We need to do this since all used entries are at the beginning of
    * the group's entries array.
    */
-  if (idx < last_in_group)
+  if (idx != last_in_group)
     {
       /* copy the last used entry to the removed entry's index
        */
-      *entry = group->entries[group->used-1];
+      *entry = last_group->entries[last_group->header.used-1];
 
       /* this ENTRY may belong to a different cache level than the entry
        * we have just removed */
@@ -839,7 +1025,12 @@ drop_entry(svn_membuffer_t *cache, entry
 
   /* Update the number of used entries.
    */
-  group->used--;
+  last_group->header.used--;
+
+  /* Release the last group in the chain if it is a spare group
+   */
+  if (!last_group->header.used && last_group->header.previous != NO_INDEX)
+    free_spare_group(cache, last_group);
 }
 
 /* Insert ENTRY into the chain of used dictionary entries. The entry's
@@ -860,7 +1051,7 @@ insert_entry(svn_membuffer_t *cache, ent
    * It must also be the first unused entry in the group.
    */
   assert(entry->offset == level->current_data);
-  assert(idx == group_index * GROUP_SIZE + group->used);
+  assert(idx == group_index * GROUP_SIZE + group->header.used);
   level->current_data = ALIGN_VALUE(entry->offset + entry->size);
 
   /* update usage counters
@@ -868,7 +1059,7 @@ insert_entry(svn_membuffer_t *cache, ent
   cache->used_entries++;
   cache->data_used += entry->size;
   entry->hit_count = 0;
-  group->used++;
+  group->header.used++;
 
   /* update entry chain
    */
@@ -910,46 +1101,6 @@ let_entry_age(svn_membuffer_t *cache, en
   entry->hit_count -= hits_removed;
 }
 
-/* Returns 0 if the entry group identified by GROUP_INDEX in CACHE has not
- * been initialized, yet. In that case, this group can not data. Otherwise,
- * a non-zero value is returned.
- */
-static APR_INLINE unsigned char
-is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
-{
-  unsigned char flags
-    = cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)];
-  unsigned char bit_mask
-    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
-
-  return flags & bit_mask;
-}
-
-/* Initializes the section of the directory in CACHE that contains
- * the entry group identified by GROUP_INDEX. */
-static void
-initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
-{
-  unsigned char bit_mask;
-  apr_uint32_t i;
-
-  /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
-  apr_uint32_t first_index =
-      (group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY;
-  apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY;
-  if (last_index > cache->group_count)
-    last_index = cache->group_count;
-
-  for (i = first_index; i < last_index; ++i)
-    cache->directory[i].used = 0;
-
-  /* set the "initialized" bit for these groups */
-  bit_mask
-    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
-  cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
-    |= bit_mask;
-}
-
 /* Given the GROUP_INDEX that shall contain an entry with the hash key
  * TO_FIND, find that entry in the specified group.
  *
@@ -995,45 +1146,98 @@ find_entry(svn_membuffer_t *cache,
 
   /* try to find the matching entry
    */
-  for (i = 0; i < group->used; ++i)
-    if (   to_find[0] == group->entries[i].key[0]
-        && to_find[1] == group->entries[i].key[1])
-      {
-        /* found it
-         */
-        entry = &group->entries[i];
-        if (find_empty)
-          drop_entry(cache, entry);
-        else
-          return entry;
-      }
+  while (1)
+    {
+      for (i = 0; i < group->header.used; ++i)
+        if (   to_find[0] == group->entries[i].key[0]
+            && to_find[1] == group->entries[i].key[1])
+          {
+            /* found it
+             */
+            entry = &group->entries[i];
+            if (!find_empty)
+              return entry;
+
+            /* need to empty that entry */
+            drop_entry(cache, entry);
+            if (group->header.used == GROUP_SIZE)
+              group = last_group_in_chain(cache, group);
+            else if (group->header.chain_length == 0)
+              group = last_group_in_chain(cache,
+                                          &cache->directory[group_index]);
+
+            break;
+          }
+
+      /* end of chain? */
+      if (group->header.next == NO_INDEX)
+        break;
+
+      /* only full groups may chain */
+      assert(group->header.used == GROUP_SIZE);
+      group = &cache->directory[group->header.next];
+    }
 
   /* None found. Are we looking for a free entry?
    */
   if (find_empty)
     {
-      /* if there is no empty entry, delete the oldest entry
+      /* There is no empty entry in the chain, try chaining a spare group.
        */
-      if (group->used == GROUP_SIZE)
+      if (   group->header.used == GROUP_SIZE
+          && group->header.chain_length < MAX_GROUP_CHAIN_LENGTH)
+        {
+          entry_group_t *new_group = allocate_spare_group(cache);
+          if (new_group)
+            {
+              /* chain groups
+               */
+              new_group->header.chain_length = group->header.chain_length + 1;
+              new_group->header.previous = group - cache->directory;
+              new_group->header.next = NO_INDEX;
+              group->header.next = new_group - cache->directory;
+              group = new_group;
+            }
+        }
+
+      /* if GROUP is still filled, we need to remove a random entry */
+      if (group->header.used == GROUP_SIZE)
         {
           /* every entry gets the same chance of being removed.
-           * Otherwise, we free the first entry, fill it and remove it
-           * again on the next occasion without considering the other
-           * entries in this group.  Also, apply priorities strictly.
+           * Otherwise, we free the first entry, fill it and
+           * remove it again on the next occasion without considering
+           * the other entries in this group.
+           *
+           * We hit only one random group instead of processing all
+           * groups in the chain.
            */
-          entry = &group->entries[rand() % GROUP_SIZE];
-          for (i = 1; i < GROUP_SIZE; ++i)
-            if (   (entry->priority > group->entries[i].priority)
-                || (   entry->priority == group->entries[i].priority
-                    && entry->hit_count > group->entries[i].hit_count))
-              entry = &group->entries[i];
+          cache_level_t *entry_level;
+          int to_remove = rand() % (GROUP_SIZE * group->header.chain_length);
+          entry_group_t *to_shrink
+            = get_group(cache, group_index, to_remove / GROUP_SIZE);
+
+          entry = &to_shrink->entries[to_remove % GROUP_SIZE];
+          entry_level = get_cache_level(cache, entry);
+          for (i = 0; i < GROUP_SIZE; ++i)
+            {
+              /* keep L1 entries whenever possible */
+
+              cache_level_t *level
+                = get_cache_level(cache, &to_shrink->entries[i]);
+              if (   (level != entry_level && entry_level == &cache->l1)
+                  || (entry->hit_count > to_shrink->entries[i].hit_count))
+                {
+                  entry_level = level;
+                  entry = &to_shrink->entries[i];
+                }
+            }
 
           /* for the entries that don't have been removed,
            * reduce their hit counts to put them at a relative
            * disadvantage the next time.
            */
           for (i = 0; i < GROUP_SIZE; ++i)
-            if (entry != &group->entries[i])
+            if (entry != &to_shrink->entries[i])
               let_entry_age(cache, entry);
 
           drop_entry(cache, entry);
@@ -1041,7 +1245,7 @@ find_entry(svn_membuffer_t *cache,
 
       /* initialize entry for the new key
        */
-      entry = &group->entries[group->used];
+      entry = &group->entries[group->header.used];
       entry->key[0] = to_find[0];
       entry->key[1] = to_find[1];
     }
@@ -1097,6 +1301,7 @@ promote_entry(svn_membuffer_t *cache, en
   apr_uint32_t idx = get_index(cache, entry);
   apr_size_t size = ALIGN_VALUE(entry->size);
   assert(get_cache_level(cache, entry) == &cache->l1);
+  assert(idx == cache->l1.next);
 
   /* copy item from the current location in L1 to the start of L2's
    * insertion window */
@@ -1127,8 +1332,7 @@ promote_entry(svn_membuffer_t *cache, en
  */
 static svn_boolean_t
 ensure_data_insertable_l2(svn_membuffer_t *cache,
-                          entry_t *to_fit_in,
-                          apr_uint32_t idx)
+                          entry_t *to_fit_in)
 {
   entry_t *entry;
   apr_uint64_t average_hit_value;
@@ -1147,9 +1351,6 @@ ensure_data_insertable_l2(svn_membuffer_
   /* accumulated "worth" of items dropped so far */
   apr_size_t drop_hits = 0;
 
-  /* verify parameters */
-  assert(idx == get_index(cache, to_fit_in));
-
   /* This loop will eventually terminate because every cache entry
    * would get dropped eventually:
    * - hit counts become 0 after the got kept for 32 full scans
@@ -1221,16 +1422,6 @@ ensure_data_insertable_l2(svn_membuffer_
             {
               keep = TRUE;
             }
-          else if (cache->l2.next / GROUP_SIZE == idx / GROUP_SIZE)
-            {
-              /* Special case: we cannot drop entries that are in the same
-               * group as TO_FIT_IN because that might the latter to become
-               * invalidated it it happens to be the highest used entry in
-               * the group.  So, we must keep ENTRY unconditionally.
-               * (this is a very rare condition)
-               */
-              keep = TRUE;
-            }
           else if (   entry->priority < SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY
                    && to_fit_in->priority > entry->priority)
             {
@@ -1308,8 +1499,6 @@ ensure_data_insertable_l2(svn_membuffer_
 static svn_boolean_t
 ensure_data_insertable_l1(svn_membuffer_t *cache, apr_size_t size)
 {
-  entry_t *entry;
-
   /* Guarantees that the while loop will terminate. */
   if (size > cache->l1.size)
     return FALSE;
@@ -1321,9 +1510,11 @@ ensure_data_insertable_l1(svn_membuffer_
     {
       /* first offset behind the insertion window
        */
+      apr_uint32_t entry_index = cache->l1.next;
+      entry_t *entry = get_entry(cache, entry_index);
       apr_uint64_t end = cache->l1.next == NO_INDEX
                        ? cache->l1.start_offset + cache->l1.size
-                       : get_entry(cache, cache->l1.next)->offset;
+                       : entry->offset;
 
       /* leave function as soon as the insertion window is large enough
        */
@@ -1347,12 +1538,16 @@ ensure_data_insertable_l1(svn_membuffer_
           /* Remove the entry from the end of insertion window and promote
            * it to L2, if it is important enough.
            */
-          entry = get_entry(cache, cache->l1.next);
+          svn_boolean_t keep = ensure_data_insertable_l2(cache, entry);
 
-          if (ensure_data_insertable_l2(cache, entry, cache->l1.next))
-            promote_entry(cache, entry);
-          else
-            drop_entry(cache, entry);
+          /* We might have touched the group that contains ENTRY. Recheck. */
+          if (entry_index == cache->l1.next)
+            {
+              if (keep)
+                promote_entry(cache, entry);
+              else
+                drop_entry(cache, entry);
+            }
         }
     }
 
@@ -1395,6 +1590,8 @@ svn_cache__membuffer_cache_create(svn_me
 
   apr_uint32_t seg;
   apr_uint32_t group_count;
+  apr_uint32_t main_group_count;
+  apr_uint32_t spare_group_count;
   apr_uint32_t group_init_size;
   apr_uint64_t data_size;
   apr_uint64_t max_entry_size;
@@ -1469,8 +1666,8 @@ svn_cache__membuffer_cache_create(svn_me
    */
   if (directory_size > total_size - sizeof(entry_group_t))
     directory_size = total_size - sizeof(entry_group_t);
-  if (directory_size < sizeof(entry_group_t))
-    directory_size = sizeof(entry_group_t);
+  if (directory_size < 2 * sizeof(entry_group_t))
+    directory_size = 2 * sizeof(entry_group_t);
 
   /* limit the data size to what we can address.
    * Note that this cannot overflow since all values are of size_t.
@@ -1498,6 +1695,11 @@ svn_cache__membuffer_cache_create(svn_me
               ? (APR_UINT32_MAX / GROUP_SIZE) - 1
               : (apr_uint32_t)(directory_size / sizeof(entry_group_t));
 
+  /* set some of the index directory aside as over-flow (spare) buffers */
+  spare_group_count = MAX(group_count / 4, 1);
+  main_group_count = group_count - spare_group_count;
+  assert(spare_group_count > 0 && main_group_count > 0);
+
   group_init_size = 1 + group_count / (8 * GROUP_INIT_GRANULARITY);
   for (seg = 0; seg < segment_count; ++seg)
     {
@@ -1505,7 +1707,11 @@ svn_cache__membuffer_cache_create(svn_me
        */
       c[seg].segment_count = (apr_uint32_t)segment_count;
 
-      c[seg].group_count = group_count;
+      c[seg].group_count = main_group_count;
+      c[seg].spare_group_count = spare_group_count;
+      c[seg].first_spare_group = NO_INDEX;
+      c[seg].max_spare_used = 0;
+
       c[seg].directory = apr_pcalloc(pool,
                                      group_count * sizeof(entry_group_t));
 
@@ -1870,7 +2076,24 @@ membuffer_cache_has_key_internal(svn_mem
                                  entry_key_t to_find,
                                  svn_boolean_t *found)
 {
-  *found = find_entry(cache, group_index, to_find, FALSE) != NULL;
+  entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
+  if (entry)
+    {
+      /* This is often happen in "block read" where most data is already
+         in L2 and only a few previously evicted items are added to L1
+         again.  While items in L1 are well protected for a while, L2
+         items may get evicted soon.  Thus, mark all them as "hit" to give
+         them a higher chance for survival. */
+      entry->hit_count++;
+      cache->hit_count++;
+      cache->total_hits++;
+
+      *found = TRUE;
+    }
+  else
+    {
+      *found = FALSE;
+    }
 
   return SVN_NO_ERROR;
 }
@@ -1888,6 +2111,8 @@ membuffer_cache_has_key(svn_membuffer_t 
   /* find the entry group that will hold the key.
    */
   apr_uint32_t group_index = get_group_index(&cache, key);
+  cache->total_reads++;
+
   WITH_READ_LOCK(cache,
                  membuffer_cache_has_key_internal(cache,
                                                   group_index,
@@ -2573,8 +2798,10 @@ svn_membuffer_get_segment_info(svn_membu
   if (include_histogram)
     for (i = 0; i < segment->group_count; ++i)
       {
+        entry_group_t *chain_end
+          = last_group_in_chain(segment, &segment->directory[i]);
         apr_size_t use
-          = MIN(segment->directory[i].used,
+          = MIN(chain_end->header.used,
                 sizeof(info->histogram) / sizeof(info->histogram[0]) - 1);
         info->histogram[use]++;
       }

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache_config.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache_config.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache_config.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cache_config.c Thu Sep 26 13:47:21 2013
@@ -118,7 +118,7 @@ svn_cache__get_global_membuffer_cache(vo
       err = svn_cache__membuffer_cache_create(
           &new_cache,
           (apr_size_t)cache_size,
-          (apr_size_t)(cache_size / 10),
+          (apr_size_t)(cache_size / 5),
           0,
           ! svn_cache_config_get()->single_threaded,
           FALSE,

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cmdline.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cmdline.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cmdline.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/cmdline.c Thu Sep 26 13:47:21 2013
@@ -357,7 +357,7 @@ svn_cmdline_fputs(const char *string, FI
         {
           /* ### Issue #3014: Return a specific error for broken pipes,
            * ### with a single element in the error chain. */
-          if (APR_STATUS_IS_EPIPE(apr_get_os_error()))
+          if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
             return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
           else
             return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
@@ -380,7 +380,7 @@ svn_cmdline_fflush(FILE *stream)
         {
           /* ### Issue #3014: Return a specific error for broken pipes,
            * ### with a single element in the error chain. */
-          if (APR_STATUS_IS_EPIPE(apr_get_os_error()))
+          if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
             return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
           else
             return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/compress.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/dirent_uri.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/dirent_uri.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/dirent_uri.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/dirent_uri.c Thu Sep 26 13:47:21 2013
@@ -1862,6 +1862,9 @@ svn_uri_is_canonical(const char *uri, ap
 #endif /* SVN_USE_DOS_PATHS */
 
   /* Now validate the rest of the URI. */
+  seg = ptr;
+  while (*ptr && (*ptr != '/'))
+    ptr++;
   while(1)
     {
       apr_size_t seglen = ptr - seg;
@@ -1880,9 +1883,8 @@ svn_uri_is_canonical(const char *uri, ap
 
       if (*ptr == '/')
         ptr++;
-      seg = ptr;
-
 
+      seg = ptr;
       while (*ptr && (*ptr != '/'))
         ptr++;
     }

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/io.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/io.c Thu Sep 26 13:47:21 2013
@@ -3067,7 +3067,7 @@ svn_io_create_custom_diff_cmd(const char
           svn_stringbuf_appendcstr(token, token_list[i]);
           len = 0;
 
-          while ( (found = strstr(com->data, token->data) && 
+          while ( (found = strstr(com->data, token->data)) && 
                   (strlen(found) > len) ) 
             {
               len = strlen(found); 
@@ -3570,7 +3570,7 @@ do_io_file_wrapper_cleanup(apr_file_t *f
 
   /* ### Issue #3014: Return a specific error for broken pipes,
    * ### with a single element in the error chain. */
-  if (APR_STATUS_IS_EPIPE(status))
+  if (SVN__APR_STATUS_IS_EPIPE(status))
     return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
 
   if (name)

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/packed_data.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/prefix_string.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/utf8proc.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/utf8proc/utf8proc.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/utf8proc/utf8proc.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/utf8proc/utf8proc_data.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/old-and-busted.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/old-and-busted.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/old-and-busted.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/old-and-busted.c Thu Sep 26 13:47:21 2013
@@ -811,11 +811,15 @@ atts_to_entry(svn_wc_entry_t **new_entry
 
      ### not used by loggy; no need to set MODIFY_FLAGS  */
   entry->url = extract_string(atts, ENTRIES_ATTR_URL, pool);
+  if (entry->url)
+    entry->url = svn_uri_canonicalize(entry->url, pool);
 
   /* Set up repository root.  Make sure it is a prefix of url.
 
      ### not used by loggy; no need to set MODIFY_FLAGS  */
   entry->repos = extract_string(atts, ENTRIES_ATTR_REPOS, pool);
+  if (entry->repos)
+    entry->repos = svn_uri_canonicalize(entry->repos, pool);
 
   if (entry->url && entry->repos
       && !svn_uri__is_ancestor(entry->repos, entry->url))

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/update_editor.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/update_editor.c Thu Sep 26 13:47:21 2013
@@ -3010,18 +3010,55 @@ absent_node(const char *path,
       kind = svn_node_unknown;
     }
 
-  if (status == svn_wc__db_status_normal
-      && kind == svn_node_dir)
+  if (status == svn_wc__db_status_normal)
     {
-      /* We found an obstructing working copy!
+      svn_boolean_t wcroot;
+      /* We found an obstructing working copy or a file external! */
 
-         We can do two things now:
-            1) notify the user, record a skip, etc.
-            2) Just record the absent node in BASE in the parent
-               working copy.
+      SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
+                                   scratch_pool));
 
-         As option 2 happens to be exactly what we do anyway, lets do that.
-      */
+      if (wcroot)
+        {
+          /*
+             We have an obstructing working copy; possibly a directory external
+
+             We can do two things now:
+             1) notify the user, record a skip, etc.
+             2) Just record the absent node in BASE in the parent
+                working copy.
+
+             As option 2 happens to be exactly what we do anyway, fall through.
+           */
+        }
+      else
+        {
+          /* The server asks us to replace a file external
+             (Existing BASE node; not reported by the working copy crawler or
+              there would have been a delete_entry() call.
+
+             There is no way we can store this state in the working copy as
+             the BASE layer is already filled.
+
+             We could error out, but that is not helping anybody; the user is not
+             even seeing with what the file external would be replaced, so let's
+             report a skip and continue the update.
+           */
+
+          if (eb->notify_func)
+            {
+              svn_wc_notify_t *notify;
+              notify = svn_wc_create_notify(
+                                    local_abspath,
+                                    svn_wc_notify_update_skip_obstruction,
+                                    scratch_pool);
+
+              eb->notify_func(eb->notify_baton, notify, scratch_pool);
+            }
+
+          svn_pool_destroy(scratch_pool);
+          return SVN_NO_ERROR;
+        }
     }
   else if (status == svn_wc__db_status_not_present
            || status == svn_wc__db_status_server_excluded

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/wc_db_update_move.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/wc_db_update_move.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/wc_db_update_move.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_wc/wc_db_update_move.c Thu Sep 26 13:47:21 2013
@@ -1297,15 +1297,6 @@ tc_editor_move(void *baton,
 }
 
 static svn_error_t *
-tc_editor_rotate(void *baton,
-                 const apr_array_header_t *relpaths,
-                 const apr_array_header_t *revisions,
-                 apr_pool_t *scratch_pool)
-{
-  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
-}
-
-static svn_error_t *
 tc_editor_complete(void *baton,
                    apr_pool_t *scratch_pool)
 {
@@ -1331,7 +1322,6 @@ static const svn_editor_cb_many_t editor
   tc_editor_delete,
   tc_editor_copy,
   tc_editor_move,
-  tc_editor_rotate,
   tc_editor_complete,
   tc_editor_abort
 };

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/merge.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/merge.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/merge.c Thu Sep 26 13:47:21 2013
@@ -115,6 +115,7 @@ static svn_error_t *
 do_resources(const dav_svn_repos *repos,
              svn_fs_root_t *root,
              svn_revnum_t revision,
+             svn_move_behavior_t move_behavior,
              ap_filter_t *output,
              apr_bucket_brigade *bb,
              apr_pool_t *pool)
@@ -129,7 +130,7 @@ do_resources(const dav_svn_repos *repos,
      and deleted things.  Also, note that deleted things don't merit
      responses of their own -- they are considered modifications to
      their parent.  */
-  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+  SVN_ERR(svn_fs_paths_changed3(&changes, root, move_behavior, pool));
 
   for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
     {
@@ -155,6 +156,8 @@ do_resources(const dav_svn_repos *repos,
 
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
           send_self = TRUE;
           send_parent = TRUE;
           break;
@@ -360,7 +363,10 @@ dav_svn__merge_response(ap_filter_t *out
          ### we can pass back the new version URL */
 
       /* and go make me proud, boy! */
-      serr = do_resources(repos, root, new_rev, output, bb, pool);
+      serr = do_resources(repos, root, new_rev,
+                          /* report changes with no further interpretation */
+                          svn_move_behavior_explicit_moves,
+                          output, bb, pool);
       if (serr != NULL)
         {
           return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/mod_dav_svn.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/mod_dav_svn.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/mod_dav_svn.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/mod_dav_svn.c Thu Sep 26 13:47:21 2013
@@ -217,6 +217,7 @@ create_dir_config(apr_pool_t *p, char *d
   conf->bulk_updates = CONF_BULKUPD_ON;
   conf->v2_protocol = CONF_FLAG_ON;
   conf->hooks_env = NULL;
+  conf->txdelta_cache = CONF_FLAG_ON;
 
   return conf;
 }
@@ -1207,7 +1208,7 @@ static const command_rec cmds[] =
                ACCESS_CONF|RSRC_CONF,
                "speeds up data access to older revisions by caching "
                "delta information if sufficient in-memory cache is "
-               "available (default is Off)."),
+               "available (default is On)."),
 
   /* per directory/location */
   AP_INIT_FLAG("SVNCacheFullTexts", SVNCacheFullTexts_cmd, NULL,

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/reports/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/reports/log.c?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/reports/log.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/mod_dav_svn/reports/log.c Thu Sep 26 13:47:21 2013
@@ -301,6 +301,9 @@ dav_svn__log_report(const dav_resource *
   svn_boolean_t discover_changed_paths = FALSE;      /* off by default */
   svn_boolean_t strict_node_history = FALSE;         /* off by default */
   svn_boolean_t include_merged_revisions = FALSE;    /* off by default */
+  svn_move_behavior_t move_behavior = svn_move_behavior_no_moves;
+                                             /* no moves by default */
+  
   apr_array_header_t *revprops = apr_array_make(resource->pool, 3,
                                                 sizeof(const char *));
   apr_array_header_t *paths
@@ -395,6 +398,24 @@ dav_svn__log_report(const dav_resource *
                                     resource->pool);
           APR_ARRAY_PUSH(paths, const char *) = target;
         }
+      else if (strcmp(child->name, "move-behavior") == 0)
+        {
+          int move_behavior_param;
+          serr = svn_cstring_atoi(&move_behavior_param,
+                                  dav_xml_get_cdata(child, resource->pool, 1));
+          if (serr)
+            return dav_svn__convert_err(serr, HTTP_BAD_REQUEST,
+                                        "Malformed CDATA in element "
+                                        "\"move-behavior\"", resource->pool);
+
+          if (   move_behavior_param < 0
+              || move_behavior_param > svn_move_behavior_auto_moves)
+            return dav_svn__convert_err(serr, HTTP_BAD_REQUEST,
+                                        "Invalid CDATA in element "
+                                        "\"move-behavior\"", resource->pool);
+
+          move_behavior = (svn_move_behavior_t) move_behavior_param;
+        }
       /* else unknown element; skip it */
     }
 
@@ -424,7 +445,7 @@ dav_svn__log_report(const dav_resource *
      flag in our log_receiver_baton structure). */
 
   /* Send zero or more log items. */
-  serr = svn_repos_get_logs4(repos->repos,
+  serr = svn_repos_get_logs5(repos->repos,
                              paths,
                              start,
                              end,
@@ -432,6 +453,7 @@ dav_svn__log_report(const dav_resource *
                              discover_changed_paths,
                              strict_node_history,
                              include_merged_revisions,
+                             move_behavior,
                              revprops,
                              dav_svn__authz_read_func(&arb),
                              &arb,

Modified: subversion/branches/invoke-diff-cmd-feature/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/svn/cl.h?rev=1526487&r1=1526486&r2=1526487&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/svn/cl.h (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/svn/cl.h Thu Sep 26 13:47:21 2013
@@ -225,6 +225,7 @@ typedef struct svn_cl__opt_state_t
                                     (not converted to UTF-8) */
   svn_boolean_t parents;         /* create intermediate directories */
   svn_boolean_t use_merge_history; /* use/display extra merge information */
+  svn_boolean_t auto_moves;      /* interpret unique DEL/ADD pairs as moves */
   svn_cl__accept_t accept_which;   /* how to handle conflicts */
   svn_cl__show_revs_t show_revs;   /* mergeinfo flavor */
   svn_depth_t set_depth;           /* new sticky ambient depth value */