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/15 10:52:18 UTC

svn commit: r1532250 [32/37] - in /subversion/branches/cache-server: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/emacs/ contrib/hook-scripts/ contrib/server-side/fsfsfixer/ contrib/se...

Modified: subversion/branches/cache-server/subversion/svnserve/server.h
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/svnserve/server.h?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/svnserve/server.h (original)
+++ subversion/branches/cache-server/subversion/svnserve/server.h Tue Oct 15 08:52:06 2013
@@ -36,14 +36,18 @@ extern "C" {
 #include "svn_repos.h"
 #include "svn_ra_svn.h"
 
+#include "private/svn_mutex.h"
+
 enum username_case_type { CASE_FORCE_UPPER, CASE_FORCE_LOWER, CASE_ASIS };
 
-typedef struct server_baton_t {
+enum authn_type { UNAUTHENTICATED, AUTHENTICATED };
+enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS };
+
+typedef struct repository_t {
   svn_repos_t *repos;
   const char *repos_name;  /* URI-encoded name of repository (not for authz) */
   svn_fs_t *fs;            /* For convenience; same as svn_repos_fs(repos) */
   const char *base;        /* Base directory for config files */
-  svn_config_t *cfg;       /* Parsed repository svnserve.conf */
   svn_config_t *pwdb;      /* Parsed password database */
   svn_authz_t *authzdb;    /* Parsed authz rules */
   const char *authz_repos_name; /* The name of the repository for authz */
@@ -51,24 +55,35 @@ typedef struct server_baton_t {
   const char *repos_url;   /* URL to base of repository */
   svn_stringbuf_t *fs_path;/* Decoded base in-repos path (w/ leading slash) */
   apr_hash_t *fs_config;   /* Additional FS configuration parameters */
-  const char *user;        /* Authenticated username of the user */
   enum username_case_type username_case; /* Case-normalize the username? */
+  svn_boolean_t use_sasl;  /* Use Cyrus SASL for authentication;
+                              always false if SVN_HAVE_SASL not defined */
+  unsigned min_ssf;        /* min-encryption SASL parameter */
+  unsigned max_ssf;        /* max-encryption SASL parameter */
+
+  enum access_type auth_access; /* access granted to authenticated users */
+  enum access_type anon_access; /* access granted to annonymous users */
+  
+} repository_t;
+
+typedef struct client_info_t {
+  const char *user;        /* Authenticated username of the user */
+  const char *remote_host; /* IP of the client that contacted the server */
   const char *authz_user;  /* Username for authz ('user' + 'username_case') */
   svn_boolean_t tunnel;    /* Tunneled through login agent */
   const char *tunnel_user; /* Allow EXTERNAL to authenticate as this */
+} client_info_t;
+
+typedef struct server_baton_t {
+  repository_t *repository; /* repository-specific data to use */
+  client_info_t *client_info; /* client-specific data to use */
+  struct logger_t *logger; /* Log file data structure.
+                              May be NULL even if log_file is not. */
   svn_boolean_t read_only; /* Disallow write access (global flag) */
-  svn_boolean_t use_sasl;  /* Use Cyrus SASL for authentication;
-                              always false if SVN_HAVE_SASL not defined */
-  apr_file_t *log_file;    /* Log filehandle. */
   svn_boolean_t vhost;     /* Use virtual-host-based path to repo. */
   apr_pool_t *pool;
 } server_baton_t;
 
-enum authn_type { UNAUTHENTICATED, AUTHENTICATED };
-enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS };
-
-enum access_type get_access(server_baton_t *b, enum authn_type auth);
-
 typedef struct serve_params_t {
   /* The virtual root of the repositories to serve.  The client URL
      path is interpreted relative to this root and is not allowed to
@@ -97,8 +112,8 @@ typedef struct serve_params_t {
      per-repository svnserve.conf are not read. */
   svn_config_t *cfg;
 
-  /* A filehandle open for writing logs to; possibly NULL. */
-  apr_file_t *log_file;
+  /* logging data structure; possibly NULL. */
+  struct logger_t *logger;
 
   /* Username case normalization style. */
   enum username_case_type username_case;
@@ -133,29 +148,16 @@ typedef struct serve_params_t {
   svn_boolean_t vhost;
 } serve_params_t;
 
+/* Return a client_info_t structure allocated in POOL and initialize it
+ * with data from CONN. */
+client_info_t * get_client_info(svn_ra_svn_conn_t *conn,
+                                serve_params_t *params,
+                                apr_pool_t *pool);
+
 /* Serve the connection CONN according to the parameters PARAMS. */
 svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
                    apr_pool_t *pool);
 
-/* Load the password database for the listening server based on the
-   entries in the SERVER struct.
-
-   SERVER and CONN must not be NULL. The real errors will be logged with
-   SERVER and CONN but return generic errors to the client. */
-svn_error_t *load_pwdb_config(server_baton_t *server,
-                              svn_ra_svn_conn_t *conn,
-                              apr_pool_t *pool);
-
-/* Load the authz database for the listening server based on the
-   entries in the SERVER struct.
-
-   SERVER and CONN must not be NULL. The real errors will be logged with
-   SERVER and CONN but return generic errors to the client. */
-svn_error_t *load_authz_config(server_baton_t *server,
-                               svn_ra_svn_conn_t *conn,
-                               const char *repos_root,
-                               apr_pool_t *pool);
-
 /* Initialize the Cyrus SASL library. POOL is used for allocations. */
 svn_error_t *cyrus_init(apr_pool_t *pool);
 
@@ -172,13 +174,6 @@ svn_error_t *cyrus_auth_request(svn_ra_s
 apr_size_t escape_errorlog_item(char *dest, const char *source,
                                 apr_size_t buflen);
 
-/* Log ERR to LOG_FILE if LOG_FILE is not NULL.  Include REMOTE_HOST,
-   USER, and REPOS in the log if they are not NULL.  Allocate temporary
-   char buffers in POOL (which caller can then clear or dispose of). */
-void
-log_error(svn_error_t *err, apr_file_t *log_file, const char *remote_host,
-          const char *user, const char *repos, apr_pool_t *pool);
-
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/cache-server/subversion/svnserve/svnserve.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/svnserve/svnserve.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/svnserve/svnserve.c (original)
+++ subversion/branches/cache-server/subversion/svnserve/svnserve.c Tue Oct 15 08:52:06 2013
@@ -53,6 +53,23 @@
 
 #include "private/svn_dep_compat.h"
 #include "private/svn_cmdline_private.h"
+#include "private/svn_atomic.h"
+#include "private/svn_mutex.h"
+#include "private/svn_subr_private.h"
+
+/* Alas! old APR-Utils don't provide thread pools */
+#if APR_HAS_THREADS
+#  if APR_VERSION_AT_LEAST(1,3,0)
+#    include <apr_thread_pool.h>
+#    define HAVE_THREADPOOLS 1
+#    define THREAD_ERROR_MSG _("Can't push task")
+#  else
+#    define HAVE_THREADPOOLS 0
+#    define THREAD_ERROR_MSG _("Can't create thread")
+#  endif
+#else
+#  define HAVE_THREADPOOLS 0
+#endif
 
 #include "winservice.h"
 
@@ -61,6 +78,7 @@
 #endif
 
 #include "server.h"
+#include "logger.h"
 
 /* The strategy for handling incoming connections.  Some of these may be
    unavailable due to platform limitations. */
@@ -101,6 +119,50 @@ enum run_mode {
 
 #endif
 
+/* Parameters for the worker thread pool used in threaded mode. */
+
+/* Have at least this many worker threads (even if there are no requests
+ * to handle).
+ *
+ * A 0 value is legal but increases the latency for the next incoming
+ * request.  Higher values may be useful for servers that experience short
+ * bursts of concurrent requests followed by longer idle periods.
+ */
+#define THREADPOOL_MIN_SIZE 1
+
+/* Maximum number of worker threads.  If there are more concurrent requests
+ * than worker threads, the extra requests get queued.
+ *
+ * Since very slow connections will hog a full thread for a potentially
+ * long time before timing out, be sure to not set this limit too low.
+ * 
+ * On the other hand, keep in mind that every thread will allocate up to
+ * 4MB of unused RAM in the APR allocator of its root pool.  32 bit servers
+ * must hence do with fewer threads.
+ */
+#if (APR_SIZEOF_VOIDP <= 4)
+#define THREADPOOL_MAX_SIZE 64
+#else
+#define THREADPOOL_MAX_SIZE 256
+#endif
+
+/* Number of microseconds that an unused thread remains in the pool before
+ * being terminated.
+ *
+ * Higher values are useful if clients frequently send small requests and
+ * you want to minimize the latency for those.
+ */
+#define THREADPOOL_THREAD_IDLE_LIMIT 1000000
+
+/* Number of client to server connections that may concurrently in the
+ * TCP 3-way handshake state, i.e. are in the process of being created.
+ *
+ * Larger values improve scalability with lots of small requests comming
+ * on over long latency networks.
+ * 
+ * The OS may actually use a lower limit than specified here.
+ */
+#define ACCEPT_BACKLOG 128
 
 #ifdef WIN32
 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
@@ -223,7 +285,7 @@ static const apr_getopt_option_t svnserv
         "                             "
         "revisions.\n"
         "                             "
-        "Default is no.\n"
+        "Default is yes.\n"
         "                             "
         "[used for FSFS repositories only]")},
     {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1,
@@ -290,7 +352,6 @@ static const apr_getopt_option_t svnserv
     {0,                  0,   0, 0}
   };
 
-
 static void usage(const char *progname, apr_pool_t *pool)
 {
   if (!progname)
@@ -309,15 +370,21 @@ static void help(apr_pool_t *pool)
 #ifdef WIN32
   svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
                                       "| --service] [options]\n"
+                                      "Subversion repository server.\n"
+                                      "Type 'svnserve --version' to see the "
+                                      "program version.\n"
                                       "\n"
                                       "Valid options:\n"),
-                                    stdout, pool));
+                                      stdout, pool));
 #else
   svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
                                       "[options]\n"
+                                      "Subversion repository server.\n"
+                                      "Type 'svnserve --version' to see the "
+                                      "program version.\n"
                                       "\n"
                                       "Valid options:\n"),
-                                    stdout, pool));
+                                      stdout, pool));
 #endif
   for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
     {
@@ -378,20 +445,108 @@ static apr_status_t redirect_stdout(void
   return apr_file_dup2(out_file, err_file, pool);
 }
 
+#if APR_HAS_THREADS
+/* The pool passed to apr_thread_create can only be released when both
+
+      A: the call to apr_thread_create has returned to the calling thread
+      B: the new thread has started running and reached apr_thread_start_t
+
+   So we set the atomic counter to 2 then both the calling thread and
+   the new thread decrease it and when it reaches 0 the pool can be
+   released.  */
+typedef struct shared_pool_t {
+  svn_atomic_t count;
+  apr_pool_t *pool;              /* root pool used to allocate the socket */
+  svn_root_pools__t *root_pools; /* put it back into this after use */
+} shared_pool_t;
+
+static shared_pool_t *
+attach_shared_pool(apr_pool_t *pool,
+                   svn_root_pools__t *root_pools)
+{
+  shared_pool_t *shared = apr_palloc(pool, sizeof(*shared));
+
+  shared->pool = pool;
+  shared->root_pools = root_pools;
+  svn_atomic_set(&shared->count, 2);
+
+  return shared;
+}
+
+static void
+release_shared_pool(struct shared_pool_t *shared)
+{
+  if (svn_atomic_dec(&shared->count) == 0)
+    svn_root_pools__release_pool(shared->pool, shared->root_pools);
+}
+#endif
+
+/* Wrapper around serve() that takes a socket instead of a connection.
+ * This is to off-load work from the main thread in threaded and fork modes.
+ */
+static svn_error_t *
+serve_socket(apr_socket_t *usock,
+             serve_params_t *params,
+             apr_pool_t *pool)
+{
+  apr_status_t status;
+  svn_ra_svn_conn_t *conn;
+  svn_error_t *err;
+  
+  /* Enable TCP keep-alives on the socket so we time out when
+   * the connection breaks due to network-layer problems.
+   * If the peer has dropped the connection due to a network partition
+   * or a crash, or if the peer no longer considers the connection
+   * valid because we are behind a NAT and our public IP has changed,
+   * it will respond to the keep-alive probe with a RST instead of an
+   * acknowledgment segment, which will cause svn to abort the session
+   * even while it is currently blocked waiting for data from the peer. */
+  status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1);
+  if (status)
+    {
+      /* It's not a fatal error if we cannot enable keep-alives. */
+    }
+
+  /* create the connection, configure ports etc. */
+  conn = svn_ra_svn_create_conn3(usock, NULL, NULL,
+                                 params->compression_level,
+                                 params->zero_copy_limit,
+                                 params->error_check_interval,
+                                 pool);
+
+  /* process the actual request and log errors */
+  err = serve(conn, params, pool);
+  if (err)
+    logger__log_error(params->logger, err, NULL,
+                      get_client_info(conn, params, pool));
+
+  return svn_error_trace(err);
+}
+
 /* "Arguments" passed from the main thread to the connection thread */
 struct serve_thread_t {
-  svn_ra_svn_conn_t *conn;
+  apr_socket_t *usock;
   serve_params_t *params;
-  apr_pool_t *pool;
+#if APR_HAS_THREADS
+  shared_pool_t *shared_pool;
+#endif
 };
 
 #if APR_HAS_THREADS
+
+/* allocate and recycle root pools for connection objects.
+   There should be at most THREADPOOL_MAX_SIZE such pools. */
+svn_root_pools__t *connection_pools;
+
 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
 {
   struct serve_thread_t *d = data;
 
-  svn_error_clear(serve(d->conn, d->params, d->pool));
-  svn_pool_destroy(d->pool);
+  apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
+  svn_error_clear(serve_socket(d->usock, d->params, pool));
+  svn_root_pools__release_pool(pool, connection_pools);
+
+  release_shared_pool(d->shared_pool);
 
   return NULL;
 }
@@ -405,8 +560,9 @@ static svn_error_t *write_pid_file(const
   const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
                                              getpid());
 
+  SVN_ERR(svn_io_remove_file2(filename, TRUE, pool));
   SVN_ERR(svn_io_file_open(&file, filename,
-                           APR_WRITE | APR_CREATE | APR_TRUNCATE,
+                           APR_WRITE | APR_CREATE | APR_EXCL,
                            APR_OS_DEFAULT, pool));
   SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
                                  pool));
@@ -431,7 +587,7 @@ check_lib_versions(void)
     };
   SVN_VERSION_DEFINE(my_version);
 
-  return svn_ver_check_list(&my_version, checklist);
+  return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
 }
 
 
@@ -443,20 +599,22 @@ int main(int argc, const char *argv[])
   apr_file_t *in_file, *out_file;
   apr_sockaddr_t *sa;
   apr_pool_t *pool;
-  apr_pool_t *connection_pool;
   svn_error_t *err;
   apr_getopt_t *os;
   int opt;
   serve_params_t params;
   const char *arg;
   apr_status_t status;
-  svn_ra_svn_conn_t *conn;
   apr_proc_t proc;
 #if APR_HAS_THREADS
+  shared_pool_t *shared_pool;
+  struct serve_thread_t *thread_data;
+#if HAVE_THREADPOOLS
+  apr_thread_pool_t *threads;
+#else
   apr_threadattr_t *tattr;
   apr_thread_t *tid;
-
-  struct serve_thread_t *thread_data;
+#endif
 #endif
   enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
   apr_uint16_t port = SVN_RA_SVN_PORT;
@@ -474,6 +632,7 @@ int main(int argc, const char *argv[])
   const char *pid_filename = NULL;
   const char *log_filename = NULL;
   svn_node_kind_t kind;
+  svn_root_pools__t *socket_pools;
 
   /* Initialize the app. */
   if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
@@ -507,12 +666,12 @@ int main(int argc, const char *argv[])
   params.base = NULL;
   params.cfg = NULL;
   params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
-  params.log_file = NULL;
+  params.logger = NULL;
   params.vhost = FALSE;
   params.username_case = CASE_ASIS;
   params.memory_cache_size = (apr_uint64_t)-1;
   params.cache_fulltexts = TRUE;
-  params.cache_txdeltas = FALSE;
+  params.cache_txdeltas = TRUE;
   params.cache_revprops = FALSE;
   params.zero_copy_limit = 0;
   params.error_check_interval = 4096;
@@ -763,9 +922,7 @@ int main(int argc, const char *argv[])
     }
 
   if (log_filename)
-    SVN_INT_ERR(svn_io_file_open(&params.log_file, log_filename,
-                                 APR_WRITE | APR_CREATE | APR_APPEND,
-                                 APR_OS_DEFAULT, pool));
+    SVN_INT_ERR(logger__create(&params.logger, log_filename, pool));
 
   if (params.tunnel_user && run_mode != run_mode_tunnel)
     {
@@ -778,6 +935,9 @@ int main(int argc, const char *argv[])
 
   if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
     {
+      apr_pool_t *connection_pool;
+      svn_ra_svn_conn_t *conn;
+
       params.tunnel = (run_mode == run_mode_tunnel);
       apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
                                 redirect_stdout);
@@ -924,7 +1084,7 @@ int main(int argc, const char *argv[])
       return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
     }
 
-  apr_socket_listen(sock, 7);
+  apr_socket_listen(sock, ACCEPT_BACKLOG);
 
 #if APR_HAS_FORK
   if (run_mode != run_mode_listen_once && !foreground)
@@ -986,8 +1146,45 @@ int main(int argc, const char *argv[])
     svn_cache_config_set(&settings);
   }
 
+  /* we use (and recycle) separate pools for sockets (many small ones)
+     and connections (fewer but larger ones) */
+  err = svn_root_pools__create(&socket_pools);
+  if (err)
+    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+
+#if APR_HAS_THREADS
+  err = svn_root_pools__create(&connection_pools);
+  if (err)
+    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+#endif
+
+#if HAVE_THREADPOOLS
+  if (handling_mode == connection_mode_thread)
+    {
+      /* create the thread pool */
+      status = apr_thread_pool_create(&threads,
+                                      THREADPOOL_MIN_SIZE,
+                                      THREADPOOL_MAX_SIZE,
+                                      pool);
+      if (status)
+        {
+          err = svn_error_wrap_apr (status, _("Can't create thread pool"));
+          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+        }
+
+      /* let idle threads linger for a while in case more requests are
+         coming in */
+      apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT);
+
+      /* don't queue requests unless we reached the worker thread limit */
+      apr_thread_pool_threshold_set(threads, 0);
+    }
+#endif
+
   while (1)
     {
+      apr_pool_t *socket_pool;
+
 #ifdef WIN32
       if (winservice_is_stopping())
         return ERROR_SUCCESS;
@@ -997,20 +1194,21 @@ int main(int argc, const char *argv[])
          the connection threads so it cannot clean up after each one.  So
          separate pools that can be cleared at thread exit are used. */
 
-      connection_pool
-          = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+      socket_pool = svn_root_pools__acquire_pool(socket_pools);
 
-      status = apr_socket_accept(&usock, sock, connection_pool);
+      status = apr_socket_accept(&usock, sock, socket_pool);
       if (handling_mode == connection_mode_fork)
         {
           /* Collect any zombie child processes. */
           while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
-                                         connection_pool) == APR_CHILD_DONE)
+                                         socket_pool) == APR_CHILD_DONE)
             ;
         }
-      if (APR_STATUS_IS_EINTR(status))
+      if (APR_STATUS_IS_EINTR(status)
+          || APR_STATUS_IS_ECONNABORTED(status)
+          || APR_STATUS_IS_ECONNRESET(status))
         {
-          svn_pool_destroy(connection_pool);
+          svn_root_pools__release_pool(socket_pool, socket_pools);
           continue;
         }
       if (status)
@@ -1020,29 +1218,9 @@ int main(int argc, const char *argv[])
           return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
         }
 
-      /* Enable TCP keep-alives on the socket so we time out when
-       * the connection breaks due to network-layer problems.
-       * If the peer has dropped the connection due to a network partition
-       * or a crash, or if the peer no longer considers the connection
-       * valid because we are behind a NAT and our public IP has changed,
-       * it will respond to the keep-alive probe with a RST instead of an
-       * acknowledgment segment, which will cause svn to abort the session
-       * even while it is currently blocked waiting for data from the peer. */
-      status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1);
-      if (status)
-        {
-          /* It's not a fatal error if we cannot enable keep-alives. */
-        }
-
-      conn = svn_ra_svn_create_conn3(usock, NULL, NULL,
-                                     params.compression_level,
-                                     params.zero_copy_limit,
-                                     params.error_check_interval,
-                                     connection_pool);
-
       if (run_mode == run_mode_listen_once)
         {
-          err = serve(conn, &params, connection_pool);
+          err = serve_socket(usock, &params, socket_pool);
 
           if (err)
             svn_handle_error2(err, stdout, FALSE, "svnserve: ");
@@ -1057,16 +1235,11 @@ int main(int argc, const char *argv[])
         {
         case connection_mode_fork:
 #if APR_HAS_FORK
-          status = apr_proc_fork(&proc, connection_pool);
+          status = apr_proc_fork(&proc, socket_pool);
           if (status == APR_INCHILD)
             {
               apr_socket_close(sock);
-              err = serve(conn, &params, connection_pool);
-              log_error(err, params.log_file,
-                        svn_ra_svn_conn_remote_host(conn),
-                        NULL, NULL, /* user, repos */
-                        connection_pool);
-              svn_error_clear(err);
+              svn_error_clear(serve_socket(usock, &params, socket_pool));
               apr_socket_close(usock);
               exit(0);
             }
@@ -1077,14 +1250,11 @@ int main(int argc, const char *argv[])
           else
             {
               err = svn_error_wrap_apr(status, "apr_proc_fork");
-              log_error(err, params.log_file,
-                        svn_ra_svn_conn_remote_host(conn),
-                        NULL, NULL, /* user, repos */
-                        connection_pool);
+              logger__log_error(params.logger, err, NULL, NULL);
               svn_error_clear(err);
               apr_socket_close(usock);
             }
-          svn_pool_destroy(connection_pool);
+          svn_root_pools__release_pool(socket_pool, socket_pools);
 #endif
           break;
 
@@ -1093,7 +1263,17 @@ int main(int argc, const char *argv[])
              particularly sophisticated strategy for a threaded server, it's
              little different from forking one process per connection. */
 #if APR_HAS_THREADS
-          status = apr_threadattr_create(&tattr, connection_pool);
+          shared_pool = attach_shared_pool(socket_pool, socket_pools);
+
+          thread_data = apr_palloc(socket_pool, sizeof(*thread_data));
+          thread_data->usock = usock;
+          thread_data->params = &params;
+          thread_data->shared_pool = shared_pool;
+#if HAVE_THREADPOOLS
+          status = apr_thread_pool_push(threads, serve_thread, thread_data,
+                                        0, NULL);
+#else
+          status = apr_threadattr_create(&tattr, socket_pool);
           if (status)
             {
               err = svn_error_wrap_apr(status, _("Can't create threadattr"));
@@ -1109,26 +1289,24 @@ int main(int argc, const char *argv[])
               svn_error_clear(err);
               exit(1);
             }
-          thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
-          thread_data->conn = conn;
-          thread_data->params = &params;
-          thread_data->pool = connection_pool;
           status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
-                                     connection_pool);
+                                     shared_pool->pool);
+#endif
           if (status)
             {
-              err = svn_error_wrap_apr(status, _("Can't create thread"));
+              err = svn_error_wrap_apr(status, THREAD_ERROR_MSG);
               svn_handle_error2(err, stderr, FALSE, "svnserve: ");
               svn_error_clear(err);
               exit(1);
             }
+          release_shared_pool(shared_pool);
 #endif
           break;
 
         case connection_mode_single:
           /* Serve one connection at a time. */
-          svn_error_clear(serve(conn, &params, connection_pool));
-          svn_pool_destroy(connection_pool);
+          svn_error_clear(serve_socket(usock, &params, socket_pool));
+          svn_root_pools__release_pool(socket_pool, socket_pools);
         }
     }
 

Modified: subversion/branches/cache-server/subversion/svnsync/svnsync.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/svnsync/svnsync.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/svnsync/svnsync.c (original)
+++ subversion/branches/cache-server/subversion/svnsync/svnsync.c Tue Oct 15 08:52:06 2013
@@ -19,6 +19,7 @@
  * ====================================================================
  */
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_cmdline.h"
 #include "svn_config.h"
@@ -41,8 +42,6 @@
 
 #include "sync.h"
 
-#include "svn_private_config.h"
-
 #include <apr_signal.h>
 #include <apr_uuid.h>
 
@@ -312,7 +311,7 @@ check_lib_versions(void)
     };
   SVN_VERSION_DEFINE(my_version);
 
-  return svn_ver_check_list(&my_version, checklist);
+  return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
 }
 
 
@@ -1297,7 +1296,7 @@ replay_rev_finished(svn_revnum_t revisio
   if (rb->sb->committed_rev != revision)
     return svn_error_createf
              (APR_EINVAL, NULL,
-              _("Commit created rev %ld but should have created %ld"),
+              _("Commit created r%ld but should have created r%ld"),
               rb->sb->committed_rev, revision);
 
   SVN_ERR(svn_ra_rev_proplist(rb->to_session, revision, &existing_props,
@@ -1847,6 +1846,7 @@ help_cmd(apr_getopt_t *os, void *baton, 
 
   const char *header =
     _("general usage: svnsync SUBCOMMAND DEST_URL  [ARGS & OPTIONS ...]\n"
+      "Subversion repository replication tool.\n"
       "Type 'svnsync help <subcommand>' for help on a specific subcommand.\n"
       "Type 'svnsync --version' to see the program version and RA modules.\n"
       "\n"

Modified: subversion/branches/cache-server/subversion/svnsync/sync.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/svnsync/sync.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/svnsync/sync.c (original)
+++ subversion/branches/cache-server/subversion/svnsync/sync.c Tue Oct 15 08:52:06 2013
@@ -19,6 +19,7 @@
  * ====================================================================
  */
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_cmdline.h"
 #include "svn_config.h"
@@ -36,8 +37,6 @@
 
 #include "sync.h"
 
-#include "svn_private_config.h"
-
 #include <apr_network_io.h>
 #include <apr_signal.h>
 #include <apr_uuid.h>

Modified: subversion/branches/cache-server/subversion/svnversion/svnversion.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/svnversion/svnversion.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/svnversion/svnversion.c (original)
+++ subversion/branches/cache-server/subversion/svnversion/svnversion.c Tue Oct 15 08:52:06 2013
@@ -57,7 +57,10 @@ help(const apr_getopt_option_t *options,
   svn_error_clear
     (svn_cmdline_fprintf
      (stdout, pool,
-      _("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n\n"
+      _("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n"
+        "Subversion working copy identification tool.\n"
+        "Type 'svnversion --version' to see the program version.\n"
+        "\n"
         "  Produce a compact version identifier for the working copy path\n"
         "  WC_PATH.  TRAIL_URL is the trailing portion of the URL used to\n"
         "  determine if WC_PATH itself is switched (detection of switches\n"
@@ -110,7 +113,7 @@ check_lib_versions(void)
     };
   SVN_VERSION_DEFINE(my_version);
 
-  return svn_ver_check_list(&my_version, checklist);
+  return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
 }
 
 /*

Propchange: subversion/branches/cache-server/subversion/tests/cmdline/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Oct 15 08:52:06 2013
@@ -8,3 +8,4 @@ httpd-*
 entries-dump
 atomic-ra-revprop-change
 .libs
+.davautocheck.sh.stop

Modified: subversion/branches/cache-server/subversion/tests/cmdline/README
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/README?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/README (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/README Tue Oct 15 08:52:06 2013
@@ -245,9 +245,25 @@ Directory Contents
 
           /verify.py:       Verifies output from Subversion.
 
-          /entry.py:        Parse an `entries' file (### not used yet)
-
-
+          /testcase.py:     Control of test case execution - contains
+                            decorators for expected failures and conditionally
+                            executed tests.
+
+          /sandbox.py:      Tools for manipulating a test's working area 
+                            ("a sandbox"), those are handy for most simple
+                            actions a test might want to perform on a wc.
+
+          /objects.py:      Objects that keep track of state during a test.
+                            (not directly used by the test scripts.)
+
+          /mergetrees.py:   Routines that create merge scenarios.
+          
+          /factory.py:      Automatically generate a (near-)complete new 
+                            cmdline test from a series of shell commands.
+          
+          /error.py:        Error codes as constants, for convenience.
+                            (auto-generated by tools/dev/gen-py-error.py)
+                            
 
 What the Python Tests are Doing
 ===============================

Modified: subversion/branches/cache-server/subversion/tests/cmdline/authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/authz_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/authz_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/authz_tests.py Tue Oct 15 08:52:06 2013
@@ -576,7 +576,8 @@ def authz_log_and_tracing_test(sbox):
   if sbox.repo_url.startswith('http'):
     expected_err2 = expected_err
   else:
-    expected_err2 = ".*svn: E220001: Item is not readable.*"
+    expected_err2 = ".*svn: E220001: Unreadable path encountered; " \
+                    "access denied.*"
 
   # if we do the same thing directly on the unreadable file, we get:
   # svn: Item is not readable
@@ -1550,6 +1551,44 @@ def log_diff_dontdothat(sbox):
                                       'log', ddt_url,
                                       '-c', 1, '--diff')
 
+@Issue(4422)
+@Skip(svntest.main.is_ra_type_file)
+def authz_file_external_to_authz(sbox):
+  "replace file external with authz node"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  repo_url = sbox.repo_url
+
+  write_authz_file(sbox, {"/": "* = rw"})
+  write_restrictive_svnserve_conf(sbox.repo_dir)
+
+  sbox.simple_propset('svn:externals', 'Z ' + repo_url + '/iota', '')
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('', status=' M')
+  expected_status.add({
+    'Z' : Item(status='  ', wc_rev='1', switched='X'),
+  })
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        None, None, expected_status)
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'cp', repo_url + '/A',
+                                           repo_url + '/Z',
+                                      '-m', 'Add Z')
+
+  write_authz_file(sbox, {"/": "* = rw", "/Z": "* = "})
+
+  expected_status.tweak(wc_rev=2)
+
+  # ### This used to assert with
+  # ### svn: E235000: In file 'update_editor.c' line 3043: assertion failed
+  # ###               (status != svn_wc__db_status_normal)
+
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        None, None, expected_status)
+
 
 ########################################################################
 # Run the tests
@@ -1584,6 +1623,7 @@ test_list = [ None,
               authz_svnserve_groups,
               authz_del_from_subdir,
               log_diff_dontdothat,
+              authz_file_external_to_authz,
              ]
 serial_only = True
 

Modified: subversion/branches/cache-server/subversion/tests/cmdline/autoprop_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/autoprop_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/autoprop_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/autoprop_tests.py Tue Oct 15 08:52:06 2013
@@ -695,8 +695,7 @@ def svn_prop_inheritable_autoprops_unver
                        '*.c=svn:eol-style=CR', sbox.ospath('A/B'))
   svntest.main.run_svn(None, 'ps', SVN_PROP_INHERITABLE_AUTOPROPS,
                        '*.c=svn:eol-style=native', sbox.ospath('A/D'))
-  svntest.main.run_svn(None, 'ci', '-m', 'Add inheritable autoprops',
-                       sbox.wc_dir)
+  sbox.simple_commit(message='Add inheritable autoprops')
 
   # Create two subtrees, each with one new file.
   os.mkdir(Z_path)

Modified: subversion/branches/cache-server/subversion/tests/cmdline/basic_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/basic_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/basic_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/basic_tests.py Tue Oct 15 08:52:06 2013
@@ -3049,6 +3049,34 @@ def peg_rev_on_non_existent_wc_path(sbox
   svntest.actions.run_and_verify_svn(None, ['r2\n'], [],
                                      'cat', '-r2', sbox.ospath('mu3') + '@3')
 
+
+@Issue(4299)
+def basic_youngest(sbox):
+  'basic youngest'
+
+  sbox.build(read_only=True)
+
+  repos_url = sbox.repo_url
+  deep_repos_url = repos_url + '/A/D/G'
+
+  wc_dir = sbox.wc_dir
+  deep_wc_dir = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
+  bad_wc_dir = os.path.join(wc_dir, 'Z')
+
+  svntest.actions.run_and_verify_svn("'svn youngest' on bad WC path",
+                                     None, svntest.verify.AnyOutput,
+                                     'youngest', bad_wc_dir)
+
+  for flag, output in [(False, "1\n"), (True, "1")]:
+    for path in [repos_url, deep_repos_url, wc_dir, deep_wc_dir]:
+      if flag:
+        svntest.actions.run_and_verify_svn("svn youngest", [output], [],
+                                           'youngest', '--no-newline', path)
+      else:
+        svntest.actions.run_and_verify_svn("svn youngest", [output], [],
+                                           'youngest', path)
+
+
 ########################################################################
 # Run the tests
 
@@ -3117,6 +3145,7 @@ test_list = [ None,
               rm_missing_with_case_clashing_ondisk_item,
               delete_conflicts_one_of_many,
               peg_rev_on_non_existent_wc_path,
+              basic_youngest,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/cache-server/subversion/tests/cmdline/blame_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/blame_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/blame_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/blame_tests.py Tue Oct 15 08:52:06 2013
@@ -34,7 +34,7 @@ from svntest.main import server_has_merg
 from prop_tests import binary_mime_type_on_text_file_warning
 
 # For some basic merge setup used by blame -g tests.
-from merge_tests import set_up_branch
+from svntest.mergetrees import set_up_branch
 
 # (abbreviation)
 Skip = svntest.testcase.Skip_deco
@@ -772,7 +772,7 @@ def merge_sensitive_blame_and_empty_merg
 
   # Make an edit to A/D/H/psi in r3.
   svntest.main.file_append(psi_path, "trunk edit in revision three.\n")
-  svntest.main.run_svn(None, 'ci', '-m', 'trunk edit', wc_dir)
+  sbox.simple_commit(message='trunk edit')
 
   # Merge r3 from A to A_COPY, reverse merge r3 from A/D/H/psi
   # to A_COPY/D/H/psi, and commit as r4.  This results in empty
@@ -782,21 +782,18 @@ def merge_sensitive_blame_and_empty_merg
                        sbox.repo_url + '/A', A_COPY_path)
   svntest.main.run_svn(None, 'merge', '-c-3',
                        sbox.repo_url + '/A/D/H/psi', psi_COPY_path)
-  svntest.main.run_svn(None, 'ci', '-m',
-                       'Sync merge A to A_COPY excepting A_COPY/D/H/psi',
-                       wc_dir)
+  sbox.simple_commit(message='Sync merge A to A_COPY excepting A_COPY/D/H/psi')
 
   # Make an edit to A/D/H/psi in r5.
   svntest.main.file_append(psi_path, "trunk edit in revision five.\n")
-  svntest.main.run_svn(None, 'ci', '-m', 'trunk edit', wc_dir)
+  sbox.simple_commit(message='trunk edit')
 
   # Sync merge A/D/H/psi to A_COPY/D/H/psi and commit as r6.  This replaces
   # the empty mergeinfo on A_COPY/D/H/psi with '/A/D/H/psi:2-5'.
   svntest.main.run_svn(None, 'up', wc_dir)
   svntest.main.run_svn(None, 'merge',  sbox.repo_url + '/A/D/H/psi',
                        psi_COPY_path)
-  svntest.main.run_svn(None, 'ci', '-m',
-                       'Sync merge A/D/H/psi to A_COPY/D/H/psi', wc_dir)
+  sbox.simple_commit(message='Sync merge A/D/H/psi to A_COPY/D/H/psi')
 
   # Check the blame -g output:
   # Currently this test fails because the trunk edit done in r3 is
@@ -959,6 +956,34 @@ def blame_eol_handling(sbox):
                                        'blame', f2)
 
 
+@SkipUnless(svntest.main.server_has_reverse_get_file_revs)
+def blame_youngest_to_oldest(sbox):
+  "blame_youngest_to_oldest"
+
+  sbox.build()
+
+  # First, make a new revision of iota.
+  iota = sbox.ospath('iota')
+  orig_line = open(iota).read()
+  line = "New contents for iota\n"
+  svntest.main.file_append(iota, line)
+  sbox.simple_commit()
+  
+  # Move the file, to check that the operation will peg correctly.
+  iota_moved = sbox.ospath('iota_moved')
+  sbox.simple_move('iota', 'iota_moved')
+  sbox.simple_commit()
+  
+  # Delete a line.
+  open(iota_moved, 'w').write(line)
+  sbox.simple_commit()
+
+  expected_output = [
+        '     %d    jrandom %s\n' % (3, orig_line[:-1]),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'blame', '-r4:1', iota_moved)
+
 ########################################################################
 # Run the tests
 
@@ -982,6 +1007,7 @@ test_list = [ None,
               merge_sensitive_blame_and_empty_mergeinfo,
               blame_multiple_targets,
               blame_eol_handling,
+              blame_youngest_to_oldest,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/cache-server/subversion/tests/cmdline/checkout_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/checkout_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/checkout_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/checkout_tests.py Tue Oct 15 08:52:06 2013
@@ -660,8 +660,13 @@ def checkout_peg_rev_date(sbox):
   sbox.build()
   wc_dir = sbox.wc_dir
 
-  # note the current time to use it as peg revision date.
-  current_time = time.strftime("%Y-%m-%dT%H:%M:%S")
+  exit_code, output, errput = svntest.main.run_svn(None, 'propget', 'svn:date',
+                                                   '--revprop', '-r1',
+                                                   '--strict',
+                                                   sbox.repo_url)
+  if exit_code or errput != [] or len(output) != 1:
+    raise svntest.Failure("svn:date propget failed")
+  r1_time = output[0]
 
   # sleep till the next second.
   time.sleep(1.1)
@@ -686,7 +691,7 @@ def checkout_peg_rev_date(sbox):
 
   # use an old date to checkout, that way we're sure we get the first revision
   svntest.actions.run_and_verify_checkout(sbox.repo_url +
-                                          '@{' + current_time + '}',
+                                          '@{' + r1_time + '}',
                                           checkout_target,
                                           expected_output,
                                           expected_wc)
@@ -1052,7 +1057,7 @@ def checkout_wc_from_drive(sbox):
   svntest.main.safe_rmtree(sbox.wc_dir)
   os.mkdir(sbox.wc_dir)
 
-  # create a virtual drive to the working copy folder
+  # create a virtual drive to the repository folder
   drive = find_the_next_available_drive_letter()
   if drive is None:
     raise svntest.Skip
@@ -1088,8 +1093,49 @@ def checkout_wc_from_drive(sbox):
     })
     svntest.actions.run_and_verify_checkout(repo_url, wc_dir,
                                             expected_output, expected_wc,
-                                            None, None, None, None,
-                                            '--force')
+                                            None, None, None, None)
+
+    wc2_dir = sbox.add_wc_path('2')
+    expected_output = wc.State(wc2_dir, {
+      'D'                 : Item(status='A '),
+      'D/H'               : Item(status='A '),
+      'D/H/psi'           : Item(status='A '),
+      'D/H/chi'           : Item(status='A '),
+      'D/H/omega'         : Item(status='A '),
+      'D/G'               : Item(status='A '),
+      'D/G/tau'           : Item(status='A '),
+      'D/G/pi'            : Item(status='A '),
+      'D/G/rho'           : Item(status='A '),
+      'D/gamma'           : Item(status='A '),
+      'C'                 : Item(status='A '),
+      'mu'                : Item(status='A '),
+      'B'                 : Item(status='A '),
+      'B/E'               : Item(status='A '),
+      'B/E/alpha'         : Item(status='A '),
+      'B/E/beta'          : Item(status='A '),
+      'B/F'               : Item(status='A '),
+      'B/lambda'          : Item(status='A '),
+    })
+    svntest.actions.run_and_verify_checkout(repo_url + '/A', wc2_dir,
+                                            expected_output, None,
+                                            None, None, None, None)
+
+    wc3_dir = sbox.add_wc_path('3')
+    expected_output = wc.State(wc3_dir, {
+      'H'                 : Item(status='A '),
+      'H/psi'             : Item(status='A '),
+      'H/chi'             : Item(status='A '),
+      'H/omega'           : Item(status='A '),
+      'G'                 : Item(status='A '),
+      'G/tau'             : Item(status='A '),
+      'G/pi'              : Item(status='A '),
+      'G/rho'             : Item(status='A '),
+      'gamma'             : Item(status='A '),
+    })
+
+    svntest.actions.run_and_verify_checkout(repo_url + '/A/D', wc3_dir,
+                                            expected_output, None,
+                                            None, None, None, None)
 
   finally:
     os.chdir(was_cwd)

Modified: subversion/branches/cache-server/subversion/tests/cmdline/commit_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/commit_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/commit_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/commit_tests.py Tue Oct 15 08:52:06 2013
@@ -1250,7 +1250,7 @@ def commit_add_file_twice(sbox):
   svntest.actions.run_and_verify_commit(wc_dir,
                                         None,
                                         None,
-                                        "already exists",
+                                        "E160020: File.*already exists",
                                         wc_dir)
 
 #----------------------------------------------------------------------
@@ -1797,7 +1797,7 @@ def commit_out_of_date_deletions(sbox):
   I_path = sbox.ospath('A/I')
   os.mkdir(I_path)
   svntest.main.run_svn(None, 'add', I_path)
-  svntest.main.run_svn(None, 'ci', '-m', 'prep', wc_dir)
+  sbox.simple_commit(message='prep')
   svntest.main.run_svn(None, 'up', wc_dir)
 
   # Make a backup copy of the working copy
@@ -2945,6 +2945,77 @@ def last_changed_of_copied_subdir(sbox):
              }
   svntest.actions.run_and_verify_info([expected], E_copied)
 
+@XFail()
+def commit_unversioned(sbox):
+  "verify behavior on unversioned targets"
+  
+  sbox.build(read_only=True)
+  wc_dir = sbox.wc_dir
+  
+  expected_err = 'E200009: .*existing.*\' is not under version control'
+
+  # Unversioned, but existing file
+  svntest.main.file_write(sbox.ospath('existing'), "xxxx")  
+  svntest.actions.run_and_verify_commit(wc_dir, None, None, expected_err,
+                                         sbox.ospath('existing'))
+  
+  # Unversioned, not existing
+  svntest.actions.run_and_verify_commit(wc_dir, None, None, expected_err,
+                                         sbox.ospath('not-existing'))
+
+@Issue(4400)
+def commit_cp_with_deep_delete(sbox):
+  "verify behavior of a copy with a deep (>=3) delete"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Prep by adding a tree deep enough to exercise the issue.
+  sbox.simple_mkdir('A/B/E/I')
+  sbox.simple_commit(message='prep')
+  svntest.main.run_svn(None, 'up', wc_dir)
+
+  # copy the deep tree and then delete a dir 3 deep.
+  sbox.simple_copy('A','A2')
+  sbox.simple_rm('A2/B/E/I')
+
+  # come up with the expected output and status
+  expected_output = svntest.wc.State(wc_dir, {
+    'A2'       : Item(verb='Adding'),
+    'A2/B/E/I' : Item(verb='Deleting'),
+    })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.add({
+    'A/B/E/I'           : Item(status='  ', wc_rev='2'),
+    'A2'                : Item(status='  ', wc_rev='3'),
+    'A2/B'              : Item(status='  ', wc_rev='3'),
+    'A2/B/lambda'       : Item(status='  ', wc_rev='3'),
+    'A2/B/F'            : Item(status='  ', wc_rev='3'),
+    'A2/B/E'            : Item(status='  ', wc_rev='3'),
+    'A2/B/E/alpha'      : Item(status='  ', wc_rev='3'),
+    'A2/B/E/beta'       : Item(status='  ', wc_rev='3'),
+    'A2/D'              : Item(status='  ', wc_rev='3'),
+    'A2/D/gamma'        : Item(status='  ', wc_rev='3'),
+    'A2/D/H'            : Item(status='  ', wc_rev='3'),
+    'A2/D/H/psi'        : Item(status='  ', wc_rev='3'),
+    'A2/D/H/omega'      : Item(status='  ', wc_rev='3'),
+    'A2/D/H/chi'        : Item(status='  ', wc_rev='3'),
+    'A2/D/G'            : Item(status='  ', wc_rev='3'),
+    'A2/D/G/tau'        : Item(status='  ', wc_rev='3'),
+    'A2/D/G/rho'        : Item(status='  ', wc_rev='3'),
+    'A2/D/G/pi'         : Item(status='  ', wc_rev='3'),
+    'A2/C'              : Item(status='  ', wc_rev='3'),
+    'A2/mu'             : Item(status='  ', wc_rev='3'),
+    })
+
+  # Commit the copy without the one dir.
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        wc_dir)
+
+  
 
 ########################################################################
 # Run the tests
@@ -3017,6 +3088,8 @@ test_list = [ None,
               commit_add_subadd,
               commit_danglers,
               last_changed_of_copied_subdir,
+              commit_unversioned,
+              commit_cp_with_deep_delete,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/cache-server/subversion/tests/cmdline/davautocheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/davautocheck.sh?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/davautocheck.sh (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/davautocheck.sh Tue Oct 15 08:52:06 2013
@@ -27,7 +27,7 @@
 # testing are:
 #   - Subversion built using --enable-shared --enable-dso --with-apxs options,
 #   - Working Apache 2 HTTPD Server with the apxs program reachable through
-#     PATH or specified via the APXS environment variable,
+#     PATH or specified via the APXS Makefile variable or environment variable,
 #   - Modules dav_module and log_config_module compiled as DSO or built into
 #     Apache HTTPD Server executable.
 # The basic intension of this script is to be able to perform "make check"
@@ -79,12 +79,14 @@
 # environment.
 #
 # Passing --no-tests as argv[1] will have the script start a server
-# but not run any tests.
+# but not run any tests.  Passing --gdb will do the same, and in addition
+# spawn gdb in the foreground attached to the running server.
 
 PYTHON=${PYTHON:-python}
 
 SCRIPTDIR=$(dirname $0)
 SCRIPT=$(basename $0)
+STOPSCRIPT=$SCRIPTDIR/.$SCRIPT.stop
 
 trap stop_httpd_and_die HUP TERM INT
 
@@ -113,13 +115,17 @@ query() {
     read -n 1 -t 32
   else
     # 
-    prog=$(cat) <<'EOF'
+    prog="
 import select as s
 import sys
+import tty, termios
+tty.setcbreak(sys.stdin.fileno(), termios.TCSANOW)
 if s.select([sys.stdin.fileno()], [], [], 32)[0]:
   sys.stdout.write(sys.stdin.read(1))
-EOF
-    REPLY=`stty cbreak; $PYTHON -c "$prog" "$@"; stty -cbreak`
+"
+    stty_state=`stty -g`
+    REPLY=`$PYTHON -u -c "$prog" "$@"`
+    stty $stty_state
   fi
   echo
   [ "${REPLY:-$2}" = 'y' ]
@@ -157,8 +163,20 @@ get_prog_name() {
 }
 
 # Don't assume sbin is in the PATH.
+# ### Presumably this is used to locate /usr/sbin/apxs or /usr/sbin/apache2    
 PATH="$PATH:/usr/sbin:/usr/local/sbin"
 
+# Find the source and build directories. The build dir can be found if it is
+# the current working dir or the source dir.
+ABS_SRCDIR=$(cd ${SCRIPTDIR}/../../../; pwd)
+if [ -x subversion/svn/svn ]; then
+  ABS_BUILDDIR=$(pwd)
+elif [ -x $ABS_SRCDIR/subversion/svn/svn ]; then
+  ABS_BUILDDIR=$ABS_SRCDIR
+else
+  fail "Run this script from the root of Subversion's build tree!"
+fi
+
 # Remove any proxy environmental variables that affect wget or curl.
 # We don't need a proxy to connect to localhost and having the proxy
 # environmental variables set breaks the Apache configuration file
@@ -169,10 +187,18 @@ unset http_proxy
 unset HTTPS_PROXY
 
 # Pick up value from environment or PATH (also try apxs2 - for Debian)
-[ ${APXS:+set} ] \
- || APXS=$(which apxs) \
- || APXS=$(which apxs2) \
- || fail "neither apxs or apxs2 found - required to run davautocheck"
+if [ ${APXS:+set} ]; then
+  :
+elif APXS=$(grep '^APXS' $ABS_BUILDDIR/Makefile | sed 's/^APXS *= *//') && \
+     [ -n "$APXS" ]; then
+  :
+elif APXS=$(which apxs); then
+  :
+elif APXS=$(which apxs2); then
+  :
+else
+  fail "neither apxs or apxs2 found - required to run davautocheck"
+fi
 
 [ -x $APXS ] || fail "Can't execute apxs executable $APXS"
 
@@ -195,17 +221,6 @@ if [ ${CACHE_REVPROPS:+set} ]; then
   CACHE_REVPROPS_SETTING=on
 fi
 
-# Find the source and build directories. The build dir can be found if it is
-# the current working dir or the source dir.
-ABS_SRCDIR=$(cd ${SCRIPTDIR}/../../../; pwd)
-if [ -x subversion/svn/svn ]; then
-  ABS_BUILDDIR=$(pwd)
-elif [ -x $ABS_SRCDIR/subversion/svn/svn ]; then
-  ABS_BUILDDIR=$ABS_SRCDIR
-else
-  fail "Run this script from the root of Subversion's build tree!"
-fi
-
 if [ ${MODULE_PATH:+set} ]; then
     MOD_DAV_SVN="$MODULE_PATH/mod_dav_svn.so"
     MOD_AUTHZ_SVN="$MODULE_PATH/mod_authz_svn.so"
@@ -300,17 +315,16 @@ if [ ${USE_SSL:+set} ]; then
       || fail "SSL module not found"
 fi
 
-random_port() {
-  if [ -n "$BASH_VERSION" ]; then
-    echo $(($RANDOM+1024))
-  else
-    $PYTHON -c 'import random; print random.randint(1024, 2**16-1)'
-  fi
-}
+# Stop any previous instances, os we can re-use the port.
+if [ -x $STOPSCRIPT ]; then $STOPSCRIPT ; sleep 1; fi
 
-HTTPD_PORT=$(random_port)
-while netstat -an | grep $HTTPD_PORT | grep 'LISTEN'; do
-  HTTPD_PORT=$(random_port)
+HTTPD_PORT=3691
+while netstat -an | grep $HTTPD_PORT | grep 'LISTEN' >/dev/null; do
+  HTTPD_PORT=$(( HTTPD_PORT + 1 ))
+  if [ $HTTPD_PORT -eq 65536 ]; then
+    # Most likely the loop condition is true regardless of $HTTPD_PORT
+    fail "netstat claims you have no free ports for httpd to listen on."
+  fi
 done
 HTTPD_ROOT="$ABS_BUILDDIR/subversion/tests/cmdline/httpd-$(date '+%Y%m%d-%H%M%S')"
 HTTPD_CFG="$HTTPD_ROOT/cfg"
@@ -318,7 +332,13 @@ HTTPD_PID="$HTTPD_ROOT/pid"
 HTTPD_ACCESS_LOG="$HTTPD_ROOT/access_log"
 HTTPD_ERROR_LOG="$HTTPD_ROOT/error_log"
 HTTPD_MIME_TYPES="$HTTPD_ROOT/mime.types"
-BASE_URL="http://localhost:$HTTPD_PORT"
+if [ -z "$BASE_URL" ]; then
+  BASE_URL="http://localhost:$HTTPD_PORT"
+else
+  # Specify the public name of the host when using a proxy on another host, the
+  # port number will be appended.
+  BASE_URL="$BASE_URL:$HTTPD_PORT"
+fi
 HTTPD_USERS="$HTTPD_ROOT/users"
 
 mkdir "$HTTPD_ROOT" \
@@ -430,7 +450,7 @@ PidFile             "$HTTPD_PID"
 LogFormat           "%h %l %u %t \"%r\" %>s %b" common
 CustomLog           "$HTTPD_ACCESS_LOG" common
 ErrorLog            "$HTTPD_ERROR_LOG"
-LogLevel            Debug
+LogLevel            debug
 ServerRoot          "$HTTPD_ROOT"
 DocumentRoot        "$HTTPD_ROOT"
 ScoreBoardFile      "$HTTPD_ROOT/run"
@@ -486,6 +506,20 @@ RedirectMatch           ^/svn-test-work/
 __EOF__
 
 START="$HTTPD -f $HTTPD_CFG"
+printf \
+'#!/bin/sh
+if [ -d "%s" ]; then
+  printf "Stopping previous HTTPD instance..."
+  if %s -k stop; then
+    # httpd had no output; echo a newline.
+    echo ""
+  elif [ -s "%s" ]; then
+    # httpd would have printed an error terminated by a newline.
+    kill -9 "`cat %s`"
+  fi
+fi
+' >$STOPSCRIPT "$HTTPD_ROOT" "$START" "$HTTPD_PID" "$HTTPD_PID"
+chmod +x $STOPSCRIPT
 
 $START -t \
   || fail "Configuration file didn't pass the check, most likely modules couldn't be loaded"
@@ -522,10 +556,17 @@ rm "$HTTPD_CFG-copy"
 say "HTTPD is good"
 
 if [ $# -eq 1 ] && [ "x$1" = 'x--no-tests' ]; then
-  echo "http://localhost:$HTTPD_PORT"
+  echo "http://localhost:$HTTPD_PORT/svn-test-work/repositories"
   exit
 fi
 
+if [ $# -eq 1 ] && [ "x$1" = 'x--gdb' ]; then
+  echo "http://localhost:$HTTPD_PORT/svn-test-work/repositories"
+  $STOPSCRIPT && gdb -silent -ex r -args $START -X
+  exit
+fi
+
+
 if type time > /dev/null; then
   TIME_CMD=time
 else

Modified: subversion/branches/cache-server/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/diff_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/diff_tests.py Tue Oct 15 08:52:06 2013
@@ -32,7 +32,7 @@ logger = logging.getLogger()
 
 # Our testing module
 import svntest
-from svntest import err
+from svntest import err, wc
 
 from prop_tests import binary_mime_type_on_text_file_warning
 
@@ -1899,7 +1899,7 @@ def diff_keywords(sbox):
 
 
 def diff_force(sbox):
-  "show diffs for binary files with --force"
+  "show diffs for binary files"
 
   sbox.build()
   wc_dir = sbox.wc_dir
@@ -1943,34 +1943,20 @@ def diff_force(sbox):
   svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                         expected_status, None, wc_dir)
 
-  # Check that we get diff when the first, the second and both files are
-  # marked as binary.
+  # Check that we get diff when the first, the second and both files
+  # are marked as binary.  First we'll use --force.  Then we'll use
+  # the configuration option 'diff-ignore-content-type'.
 
   re_nodisplay = re.compile('^Cannot display:')
 
-  exit_code, stdout, stderr = svntest.main.run_svn(None,
-                                                   'diff', '-r1:2', iota_path,
-                                                   '--force')
-
-  for line in stdout:
-    if (re_nodisplay.match(line)):
-      raise svntest.Failure
-
-  exit_code, stdout, stderr = svntest.main.run_svn(None,
-                                                   'diff', '-r2:1', iota_path,
-                                                   '--force')
-
-  for line in stdout:
-    if (re_nodisplay.match(line)):
-      raise svntest.Failure
-
-  exit_code, stdout, stderr = svntest.main.run_svn(None,
-                                                   'diff', '-r2:3', iota_path,
-                                                   '--force')
-
-  for line in stdout:
-    if (re_nodisplay.match(line)):
-      raise svntest.Failure
+  for opt in ['--force',
+              '--config-option=config:miscellany:diff-ignore-content-type=yes']:
+    for range in ['-r1:2', '-r2:1', '-r2:3']:
+      exit_code, stdout, stderr = svntest.main.run_svn(None, 'diff', range,
+                                                       iota_path, opt)
+      for line in stdout:
+        if (re_nodisplay.match(line)):
+          raise svntest.Failure
 
 #----------------------------------------------------------------------
 # Regression test for issue #2333: Renaming a directory should produce
@@ -4520,6 +4506,134 @@ def diff_dir_replaced_by_dir(sbox):
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'diff', '--summarize', wc_dir)
 
+
+@Issue(4366)
+def diff_repos_empty_file_addition(sbox):
+  "repos diff of rev which adds empty file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Add and commit an empty file.
+  svntest.main.file_append(sbox.ospath('newfile'), "")
+  svntest.main.run_svn(None, 'add', sbox.ospath('newfile'))
+  expected_output = svntest.wc.State(sbox.wc_dir, {
+    'newfile': Item(verb='Adding'),
+    })
+  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
+  expected_status.add({
+    'newfile' : Item(status='  ', wc_rev=2),
+    })
+  svntest.actions.run_and_verify_commit(sbox.wc_dir, expected_output,
+                                        expected_status, None, sbox.wc_dir)
+
+  # Now diff the revision that added the empty file.
+  expected_output = [
+    'Index: newfile\n',
+    '===================================================================\n',
+    ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'diff', '-c', '2', sbox.repo_url)
+
+def diff_missing_tree_conflict_victim(sbox):
+  "diff with missing tree-conflict victim in wc"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Produce an 'incoming edit vs. local missing' tree conflict:
+  # r2: edit iota and commit the change
+  svntest.main.file_append(sbox.ospath('iota'), "This is a change to iota.\n")
+  sbox.simple_propset('k', 'v', 'A/C')
+  sbox.simple_commit()
+  # now remove iota
+  sbox.simple_rm('iota', 'A/C')
+  sbox.simple_commit()
+  # update to avoid mixed-rev wc warning
+  sbox.simple_update()
+  # merge r2 into wc and verify that a tree conflict is flagged on iota
+  expected_output = wc.State(wc_dir, {
+      'iota' : Item(status='  ', treeconflict='C'),
+      'A/C' : Item(status='  ', treeconflict='C')
+  })
+  expected_mergeinfo_output = wc.State(wc_dir, {})
+  expected_elision_output = wc.State(wc_dir, {})
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('iota','A/C')
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
+  expected_status.tweak('iota', 'A/C',
+                        status='! ', treeconflict='C', wc_rev=None)
+  expected_skip = wc.State('', { })
+  svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
+                                       sbox.repo_url, None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None, None, None,
+                                       False, '--ignore-ancestry', wc_dir)
+
+  # 'svn diff' should show no change for the working copy
+  # This currently fails because svn errors out with a 'node not found' error
+  expected_output = [ ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff', wc_dir)
+
+@Issue(4396)
+def diff_local_missing_obstruction(sbox):
+  "diff local missing and obstructed files"
+
+  sbox.build(read_only=True)
+  wc_dir = sbox.wc_dir
+
+  os.unlink(sbox.ospath('iota'))
+  os.unlink(sbox.ospath('A/mu'))
+  os.mkdir(sbox.ospath('A/mu'))
+
+  # Expect no output for missing and obstructed files
+  expected_output = [
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff', wc_dir)
+
+  sbox.simple_propset('K', 'V', 'iota', 'A/mu')
+  sbox.simple_append('IotA', 'Content')
+
+  # But do expect a proper property diff
+  expected_output = [
+    'Index: %s\n' % (sbox.path('A/mu'),),
+    '===================================================================\n',
+    '--- %s\t(revision 1)\n' % (sbox.path('A/mu'),),
+    '+++ %s\t(working copy)\n' % (sbox.path('A/mu'),),
+    '\n',
+    'Property changes on: %s\n' % (sbox.path('A/mu'),),
+    '___________________________________________________________________\n',
+    'Added: K\n',
+    '## -0,0 +1 ##\n',
+    '+V\n',
+    '\ No newline at end of property\n',
+    'Index: %s\n' % (sbox.path('iota'),),
+    '===================================================================\n',
+    '--- %s\t(revision 1)\n' % (sbox.path('iota'),),
+    '+++ %s\t(working copy)\n' % (sbox.path('iota'),),
+    '\n',
+    'Property changes on: %s\n' % (sbox.path('iota'),),
+    '___________________________________________________________________\n',
+    'Added: K\n',
+    '## -0,0 +1 ##\n',
+    '+V\n',
+    '\ No newline at end of property\n',
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff', wc_dir)
+
+  # Create an external. This produces an error in 1.8.0.
+  sbox.simple_propset('svn:externals', 'AA/BB ' + sbox.repo_url + '/A', '.')
+  sbox.simple_update()
+
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'diff', wc_dir)
+
+
 ########################################################################
 #Run the tests
 
@@ -4598,6 +4712,9 @@ test_list = [ None,
               local_tree_replace,
               diff_dir_replaced_by_file,
               diff_dir_replaced_by_dir,
+              diff_repos_empty_file_addition,
+              diff_missing_tree_conflict_victim,
+              diff_local_missing_obstruction,
               ]
 
 if __name__ == '__main__':

Modified: subversion/branches/cache-server/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/externals_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/externals_tests.py Tue Oct 15 08:52:06 2013
@@ -3226,6 +3226,126 @@ def update_dir_external_shallow(sbox):
                                         '--set-depth=infinity',
                                         sbox.ospath('A/B/E'))
 
+@Issue(4411)
+@XFail()
+def switch_parent_relative_file_external(sbox):
+  "switch parent-relative file external"
+
+  sbox.build()
+
+  # Create a parent-relative file external in r2
+  sbox.simple_propset('svn:externals', '../D/gamma gamma-ext', 'A/B')
+  sbox.simple_commit()
+  sbox.simple_update()
+
+  # Create a branch that contains the file external
+  sbox.simple_copy('A', 'A_copy')
+  sbox.simple_commit()
+  sbox.simple_update()
+
+  # Check out A/B_copy to a new working copy
+  branch_wc = sbox.add_wc_path("branch")
+  branch_url = sbox.repo_url + '/A_copy'
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'checkout', branch_url,
+                                     branch_wc)
+
+  # Rename the branch
+  sbox.simple_move('A_copy', 'A_copy2')
+  sbox.simple_commit()
+
+  # Switch the branch working copy to the new branch URL
+  new_branch_url = sbox.repo_url + '/A_copy2'
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'switch', new_branch_url,
+                                     branch_wc)
+
+  # Bug: The branch working copy can no longer be updated.
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'update', branch_wc)
+
+@Issue(4420)
+def file_external_unversioned_obstruction(sbox):
+  """file externals unversioned obstruction"""
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  expected_output = verify.RegexOutput('r2 committed .*')
+  svntest.actions.run_and_verify_svnmucc(None, expected_output, [],
+                           '-U', sbox.repo_url, '-m', 'r2: set external',
+                           'propset', 'svn:externals', '^/A/mu mu-ext', 'A')
+
+  sbox.simple_append('A/mu-ext', 'unversioned obstruction')
+
+  # Update reports a tree-conflict but status doesn't show any such
+  # conflict.  I'm no sure whether this is correct.
+  expected_output = svntest.wc.State(wc_dir, {
+      'A'        : Item(status=' U'),
+      'A/mu-ext' : Item(status='  ', treeconflict='A'),
+      })
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+      'A/mu-ext' : Item('unversioned obstruction'),
+      })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.add({
+      'A/mu-ext' : Item(status='M ', wc_rev='2', switched='X'),
+      })
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output, expected_disk,
+                                        expected_status)
+
+@Issue(4001)
+@XFail()
+def file_external_versioned_obstruction(sbox):
+  """file externals versioned obstruction"""
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  expected_output = verify.RegexOutput('r2 committed .*')
+  svntest.actions.run_and_verify_svnmucc(None, expected_output, [],
+                           '-U', sbox.repo_url, '-m', 'r2: set external',
+                           'propset', 'svn:externals', '^/A/mu mu-ext', 'A')
+
+  expected_output = svntest.wc.State(wc_dir, {
+      'A'        : Item(status=' U'),
+      'A/mu-ext' : Item(status='A '),
+      })
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+      'A/mu-ext' : Item('This is the file \'mu\'.\n'),
+      })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.add({
+      'A/mu-ext' : Item(status='  ', wc_rev='2', switched='X'),
+      })
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output, expected_disk,
+                                        expected_status)
+
+  # Update skips adding the versioned node because of the file
+  # external obstruction then when the external is deleted the
+  # versioned node is missing from disk and wc.db.  Not really sure
+  # what should happen, perhaps a not-present node?
+  expected_output = verify.RegexOutput('r3 committed .*')
+  svntest.actions.run_and_verify_svnmucc(None, expected_output, [],
+                           '-U', sbox.repo_url, '-m', 'r3: copy file',
+                           'cp', 'head', 'A/mu', 'A/mu-ext',
+                           'propdel', 'svn:externals', 'A')
+
+  expected_output = svntest.wc.State(wc_dir, {
+      'A'        : Item(status=' U'),
+      'A/mu-ext' : Item(verb='Removed external', prev_verb='Skipped'),
+      })
+  expected_disk.tweak('A/mu-ext', content='This is the file \'mu\'.\n')
+  expected_status.tweak(wc_rev=3)
+  expected_status.tweak('A/mu-ext', switched=None)
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output, expected_disk,
+                                        expected_status)
+
 
 ########################################################################
 # Run the tests
@@ -3279,6 +3399,9 @@ test_list = [ None,
               move_with_file_externals,
               pinned_externals,
               update_dir_external_shallow,
+              switch_parent_relative_file_external,
+              file_external_unversioned_obstruction,
+              file_external_versioned_obstruction,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests.py Tue Oct 15 08:52:06 2013
@@ -72,6 +72,7 @@ del_lines_res = [
                  re.compile(r"\* ra_(neon|local|svn|serf) :"),
                  re.compile(r"  - handles '(https?|file|svn)' scheme"),
                  re.compile(r"  - with Cyrus SASL authentication"),
+                 re.compile(r"  - using serf \d+\.\d+\.\d+"),
                  re.compile(r"\* fs_(base|fs) :"),
                 ]
 
@@ -89,13 +90,6 @@ rep_lines_res = [
                  # In 'svn --version --quiet', we print only the version
                  # number in a single line.
                  (re.compile(r'^\d+\.\d+\.\d+(-[a-zA-Z0-9]+)?$'), 'X.Y.Z\n'),
-                 # 'svn --help' has a line with the version number.
-                 # It can vary, for example:
-                 # "Subversion command-line client, version 1.1.0."
-                 # "Subversion command-line client, version 1.1.0-dev."
-                 (re.compile(r'Subversion command-line client, '
-                             'version \d+\.\d+\.\d+(.|-[a-zA-Z0-9]+\.)$'),
-                  'Subversion command-line client, version X.Y.Z.'),
                 ]
 
 # This is a trigger pattern that selects the secondary set of

Modified: subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout Tue Oct 15 08:52:06 2013
@@ -1,5 +1,5 @@
 usage: svn <subcommand> [options] [args]
-Subversion command-line client, version X.Y.Z.
+Subversion command-line client.
 Type 'svn help <subcommand>' for help on a specific subcommand.
 Type 'svn --version' to see the program version and RA modules
   or 'svn --version --quiet' to see just the version number.
@@ -45,6 +45,7 @@ Available subcommands:
    unlock
    update (up)
    upgrade
+   youngest
 
 Subversion is a tool for version control.
 For additional information, see http://subversion.apache.org/

Modified: subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout Tue Oct 15 08:52:06 2013
@@ -95,6 +95,8 @@ Valid options:
   --with-all-revprops      : retrieve all revision properties
   --with-no-revprops       : retrieve no revision properties
   --with-revprop ARG       : retrieve revision property ARG
+  --auto-moves             : attempt to interpret matching unique DEL+ADD
+                             pairs as moves
   --depth ARG              : limit operation by depth ARG ('empty', 'files',
                              'immediates', or 'infinity')
   --diff                   : produce diff output

Modified: subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout Tue Oct 15 08:52:06 2013
@@ -1,5 +1,5 @@
 usage: svn <subcommand> [options] [args]
-Subversion command-line client, version X.Y.Z.
+Subversion command-line client.
 Type 'svn help <subcommand>' for help on a specific subcommand.
 Type 'svn --version' to see the program version and RA modules
   or 'svn --version --quiet' to see just the version number.
@@ -45,6 +45,7 @@ Available subcommands:
    unlock
    update (up)
    upgrade
+   youngest
 
 Subversion is a tool for version control.
 For additional information, see http://subversion.apache.org/

Modified: subversion/branches/cache-server/subversion/tests/cmdline/history_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/history_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/history_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/history_tests.py Tue Oct 15 08:52:06 2013
@@ -189,20 +189,17 @@ def cat_avoids_false_identities(sbox):
   svntest.main.run_svn(None, 'del', iota_path)
   svntest.main.file_append(iota_path, "YOU SHOULD NOT SEE THIS\n")
   svntest.main.run_svn(None, 'add', iota_path)
-  svntest.main.run_svn(None, 'ci', '-m', 'log msg',
-                       wc_dir)
+  sbox.simple_commit(message='log msg')
   svntest.main.run_svn(None, 'up', wc_dir)
 
   # r3
   svntest.main.run_svn(None, 'del', iota_path)
-  svntest.main.run_svn(None, 'ci', '-m', 'log msg',
-                       wc_dir)
+  sbox.simple_commit(message='log msg')
   svntest.main.run_svn(None, 'up', wc_dir)
 
   # r4
   svntest.main.run_svn(None, 'cp', iota_url + '@1', wc_dir)
-  svntest.main.run_svn(None, 'ci', '-m', 'log msg',
-                       wc_dir)
+  sbox.simple_commit(message='log msg')
   svntest.main.run_svn(None, 'up', wc_dir)
 
   # 'svn cat -r2 iota' should error, because the line of history

Modified: subversion/branches/cache-server/subversion/tests/cmdline/info_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/info_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/info_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/info_tests.py Tue Oct 15 08:52:06 2013
@@ -526,6 +526,11 @@ def binary_tree_conflict(sbox):
   }]
   svntest.actions.run_and_verify_info(expected_info, iota)
 
+  expected_info = [{
+      'Path' : '%s' % re.escape(wc_dir),
+  }]
+  svntest.actions.run_and_verify_info(expected_info, wc_dir)
+
 def relpath_escaping(sbox):
   "relpath escaping should be usable as-is"
 

Modified: subversion/branches/cache-server/subversion/tests/cmdline/lock_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/tests/cmdline/lock_tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/tests/cmdline/lock_tests.py (original)
+++ subversion/branches/cache-server/subversion/tests/cmdline/lock_tests.py Tue Oct 15 08:52:06 2013
@@ -1500,7 +1500,7 @@ def verify_path_escaping(sbox):
 
   svntest.main.run_svn(None, 'add', file1, file2, file3)
 
-  svntest.main.run_svn(None, 'ci', '-m', 'commit', wc_dir)
+  sbox.simple_commit(message='commit')
 
   svntest.main.run_svn(None, 'lock', '-m', 'lock 1', file1)
   svntest.main.run_svn(None, 'lock', '-m', 'lock 2', sbox.repo_url + '/file%20%232')
@@ -1815,6 +1815,111 @@ def lock_unlock_deleted(sbox):
   expected_status.tweak('A/mu', writelocked=None)
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
+@Issue(4369)
+def commit_stolen_lock(sbox):
+  "commit with a stolen lock"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  sbox.simple_append('A/mu', 'zig-zag')
+  sbox.simple_lock('A/mu')
+
+  expected_output = '\'mu\' locked by user \'jrandom\'.'
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'lock', '--force',
+                                     sbox.repo_url + '/A/mu')
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/mu', status='M ', writelocked='T')
+  err_re = "(.*E160037: Cannot verify lock on path '/A/mu')|" + \
+           "(.*E160038: '/.*/A/mu': no lock token available)"
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        [],
+                                        expected_status,
+                                        err_re,
+                                        wc_dir)
+
+# When removing directories, the locks of contained files were not 
+# correctly removed from the working copy database, thus they later 
+# magically reappeared when new files or directories with the same
+# pathes were added.
+@Issue(4364)
+def drop_locks_on_parent_deletion(sbox):
+  "drop locks when the parent is deleted"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # lock some files, and remove them.
+  sbox.simple_lock('A/B/lambda')
+  sbox.simple_lock('A/B/E/alpha')
+  sbox.simple_lock('A/B/E/beta')
+  sbox.simple_rm('A/B')
+  
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.remove_subtree('A/B')
+  
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        [],
+                                        expected_status,
+                                        None,
+                                        wc_dir)
+
+  # now re-add entities to the deleted pathes.
+  sbox.simple_mkdir('A/B')
+  sbox.simple_add_text('new file replacing old file', 'A/B/lambda')
+  sbox.simple_add_text('file replacing former dir', 'A/B/F')
+  # The bug also resurrected locks on directories when their path
+  # matched a former file.
+  sbox.simple_mkdir('A/B/E', 'A/B/E/alpha')
+    
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/B',
+						'A/B/E',
+						'A/B/E/alpha',
+						'A/B/F',
+						'A/B/lambda',
+						wc_rev='3')
+  expected_status.remove('A/B/E/beta')
+   
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        [],
+                                        expected_status,
+                                        None,
+                                        wc_dir)
+	
+
+def copy_with_lock(sbox):
+  """copy with lock on source"""
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  lock_url = sbox.repo_url + '/A/B/E/alpha'
+
+  svntest.actions.run_and_validate_lock(lock_url, svntest.main.wc_author)
+  sbox.simple_copy('A/B/E', 'A/B/E2')
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/E2' : Item(verb='Adding'),
+    })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/B/E/alpha', writelocked='O')
+  expected_status.add({
+    'A/B/E2'       : Item(status='  ', wc_rev=2),
+    'A/B/E2/alpha' : Item(status='  ', wc_rev=2),
+    'A/B/E2/beta'  : Item(status='  ', wc_rev=2),
+    })
+
+  # This is really a regression test for httpd: 2.2.25 and 2.4.6 have
+  # a bug that causes mod_dav to check for locks on the copy source
+  # and so the commit fails.
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        wc_dir)
+										
 ########################################################################
 # Run the tests
 
@@ -1866,6 +1971,9 @@ test_list = [ None,
               lock_multi_wc,
               locks_stick_over_switch,
               lock_unlock_deleted,
+              commit_stolen_lock,
+              drop_locks_on_parent_deletion,
+              copy_with_lock,
             ]
 
 if __name__ == '__main__':