You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2018/11/13 07:49:08 UTC

svn commit: r1846488 [18/23] - in /subversion/branches/mod-dav-svn-expressions: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ build/generator/util/ build/win32/ contrib/client-side/ contrib/client-side/svn...

Modified: subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.c
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.c?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.c (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.c Tue Nov 13 07:49:04 2018
@@ -59,6 +59,7 @@ enum svn_svnrdump__longopt_t
     opt_config_option,
     opt_auth_username,
     opt_auth_password,
+    opt_auth_password_from_stdin,
     opt_auth_nocache,
     opt_non_interactive,
     opt_skip_revprop,
@@ -73,29 +74,38 @@ enum svn_svnrdump__longopt_t
                                    opt_config_option, \
                                    opt_auth_username, \
                                    opt_auth_password, \
+                                   opt_auth_password_from_stdin, \
                                    opt_auth_nocache, \
                                    opt_trust_server_cert, \
                                    opt_trust_server_cert_failures, \
                                    opt_non_interactive, \
                                    opt_force_interactive
 
-static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] =
+static const svn_opt_subcommand_desc3_t svnrdump__cmd_table[] =
 {
-  { "dump", dump_cmd, { 0 },
-    N_("usage: svnrdump dump URL [-r LOWER[:UPPER]]\n\n"
+  { "dump", dump_cmd, { 0 }, {N_(
+       "usage: svnrdump dump URL [-r LOWER[:UPPER]]\n"
+       "\n"), N_(
        "Dump revisions LOWER to UPPER of repository at remote URL to stdout\n"
        "in a 'dumpfile' portable format.  If only LOWER is given, dump that\n"
-       "one revision.\n"),
-    { 'r', 'q', opt_incremental, SVN_SVNRDUMP__BASE_OPTIONS } },
-  { "load", load_cmd, { 0 },
-    N_("usage: svnrdump load URL\n\n"
-       "Load a 'dumpfile' given on stdin to a repository at remote URL.\n"),
-    { 'q', opt_skip_revprop, SVN_SVNRDUMP__BASE_OPTIONS } },
-  { "help", 0, { "?", "h" },
-    N_("usage: svnrdump help [SUBCOMMAND...]\n\n"
-       "Describe the usage of this program or its subcommands.\n"),
+       "one revision.\n"
+    )},
+    { 'r', 'q', opt_incremental, 'F', SVN_SVNRDUMP__BASE_OPTIONS },
+    {{'F', N_("write to file ARG instead of stdout")}} },
+  { "load", load_cmd, { 0 }, {N_(
+       "usage: svnrdump load URL\n"
+       "\n"), N_(
+       "Load a 'dumpfile' given on stdin to a repository at remote URL.\n"
+    )},
+    { 'q', opt_skip_revprop, 'F', SVN_SVNRDUMP__BASE_OPTIONS },
+    {{'F', N_("read from file ARG instead of stdin")}} },
+  { "help", 0, { "?", "h" }, {N_(
+       "usage: svnrdump help [SUBCOMMAND...]\n"
+       "\n"), N_(
+       "Describe the usage of this program or its subcommands.\n"
+    )},
     { 0 } },
-  { NULL, NULL, { 0 }, NULL, { 0 } }
+  { NULL, NULL, { 0 }, {NULL}, { 0 } }
 };
 
 static const apr_getopt_option_t svnrdump__options[] =
@@ -114,6 +124,8 @@ static const apr_getopt_option_t svnrdum
                       N_("specify a username ARG")},
     {"password",      opt_auth_password, 1,
                       N_("specify a password ARG")},
+    {"password-from-stdin",   opt_auth_password_from_stdin, 0,
+                      N_("read password from stdin")},
     {"non-interactive", opt_non_interactive, 0,
                       N_("do no interactive prompting (default is to prompt\n"
                          "                             "
@@ -154,6 +166,8 @@ static const apr_getopt_option_t svnrdum
                        "valid certificate) and 'other' (all other not\n"
                        "                             "
                        "separately classified certificate errors).")},
+    {"file",          'F', 1,
+                      N_("read/write file ARG instead of stdin/stdout")},
     {0, 0, 0, 0}
   };
 
@@ -174,6 +188,7 @@ typedef struct opt_baton_t {
   svn_client_ctx_t *ctx;
   svn_ra_session_t *session;
   const char *url;
+  const char *dumpfile;
   svn_boolean_t help;
   svn_boolean_t version;
   svn_opt_revision_t start_revision;
@@ -332,7 +347,7 @@ init_client_context(svn_client_ctx_t **c
      ### auxiliary GETs/PROPFINDs to happening (well-ordered) on a
      ### single server connection.
      ###
-     ### See http://subversion.tigris.org/issues/show_bug.cgi?id=4116.
+     ### See https://issues.apache.org/jira/browse/SVN-4116.
   */
   cfg_servers = svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_SERVERS);
   svn_config_set_bool(cfg_servers, SVN_CONFIG_SECTION_GLOBAL,
@@ -417,7 +432,7 @@ dump_initial_full_revision(svn_ra_sessio
      our update-driven dump generation work the way a replay-driven
      one would.
 
-     See http://subversion.tigris.org/issues/show_bug.cgi?id=4101
+     See https://issues.apache.org/jira/browse/SVN-4101
   */
   SVN_ERR(svn_ra_get_session_url(session, &session_url, pool));
   SVN_ERR(svn_ra_get_path_relative_to_root(session, &source_relpath,
@@ -463,31 +478,38 @@ replay_revisions(svn_ra_session_t *sessi
                  svn_revnum_t end_revision,
                  svn_boolean_t quiet,
                  svn_boolean_t incremental,
+                 const char *dumpfile,
                  apr_pool_t *pool)
 {
   struct replay_baton *replay_baton;
   const char *uuid;
-  svn_stream_t *stdout_stream;
+  svn_stream_t *output_stream;
 
-  SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
+  if (dumpfile)
+    {
+      SVN_ERR(svn_stream_open_writable(&output_stream, dumpfile, pool, pool));
+    }
+  else
+    {
+      SVN_ERR(svn_stream_for_stdout(&output_stream, pool));
+    }
 
   replay_baton = apr_pcalloc(pool, sizeof(*replay_baton));
-  replay_baton->stdout_stream = stdout_stream;
+  replay_baton->stdout_stream = output_stream;
   replay_baton->extra_ra_session = extra_ra_session;
   replay_baton->quiet = quiet;
 
   /* Write the magic header and UUID */
-  SVN_ERR(svn_stream_printf(stdout_stream, pool,
-                            SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
-                            SVN_REPOS_DUMPFILE_FORMAT_VERSION));
+  SVN_ERR(svn_repos__dump_magic_header_record(output_stream,
+                                              SVN_REPOS_DUMPFILE_FORMAT_VERSION,
+                                              pool));
   SVN_ERR(svn_ra_get_uuid2(session, &uuid, pool));
-  SVN_ERR(svn_stream_printf(stdout_stream, pool,
-                            SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid));
+  SVN_ERR(svn_repos__dump_uuid_header_record(output_stream, uuid, pool));
 
   /* Fake revision 0 if necessary */
   if (start_revision == 0)
     {
-      SVN_ERR(dump_revision_header(session, stdout_stream,
+      SVN_ERR(dump_revision_header(session, output_stream,
                                    start_revision, pool));
 
       /* Revision 0 has no tree changes, so we're done. */
@@ -506,7 +528,7 @@ replay_revisions(svn_ra_session_t *sessi
   if (!incremental)
     {
       SVN_ERR(dump_initial_full_revision(session, extra_ra_session,
-                                         stdout_stream, start_revision,
+                                         output_stream, start_revision,
                                          quiet, pool));
       start_revision++;
     }
@@ -526,6 +548,7 @@ replay_revisions(svn_ra_session_t *sessi
 #endif
     }
 
+  SVN_ERR(svn_stream_close(output_stream));
   return SVN_NO_ERROR;
 }
 
@@ -538,19 +561,27 @@ replay_revisions(svn_ra_session_t *sessi
 static svn_error_t *
 load_revisions(svn_ra_session_t *session,
                svn_ra_session_t *aux_session,
-               const char *url,
+               const char *dumpfile,
                svn_boolean_t quiet,
                apr_hash_t *skip_revprops,
                apr_pool_t *pool)
 {
-  svn_stream_t *stdin_stream;
+  svn_stream_t *output_stream;
 
-  SVN_ERR(svn_stream_for_stdin2(&stdin_stream, TRUE, pool));
+  if (dumpfile)
+    {
+      SVN_ERR(svn_stream_open_readonly(&output_stream, dumpfile, pool, pool));
+    }
+  else
+    {
+      SVN_ERR(svn_stream_for_stdin2(&output_stream, TRUE, pool));
+    }
 
-  SVN_ERR(svn_rdump__load_dumpstream(stdin_stream, session, aux_session,
+  SVN_ERR(svn_rdump__load_dumpstream(output_stream, session, aux_session,
                                      quiet, skip_revprops,
                                      check_cancel, NULL, pool));
 
+  SVN_ERR(svn_stream_close(output_stream));
   return SVN_NO_ERROR;
 }
 
@@ -591,7 +622,7 @@ version(const char *progname,
                          pool);
 
   SVN_ERR(svn_ra_print_modules(version_footer, pool));
-  return svn_opt_print_help4(NULL, ensure_appname(progname, pool),
+  return svn_opt_print_help5(NULL, ensure_appname(progname, pool),
                              TRUE, quiet, FALSE, version_footer->data,
                              NULL, NULL, NULL, NULL, NULL, pool);
 }
@@ -616,7 +647,8 @@ dump_cmd(apr_getopt_t *os,
   return replay_revisions(opt_baton->session, extra_ra_session,
                           opt_baton->start_revision.value.number,
                           opt_baton->end_revision.value.number,
-                          opt_baton->quiet, opt_baton->incremental, pool);
+                          opt_baton->quiet, opt_baton->incremental,
+                          opt_baton->dumpfile, pool);
 }
 
 /* Handle the "load" subcommand.  Implements `svn_opt_subcommand_t'.  */
@@ -630,8 +662,9 @@ load_cmd(apr_getopt_t *os,
 
   SVN_ERR(svn_client_open_ra_session2(&aux_session, opt_baton->url, NULL,
                                       opt_baton->ctx, pool, pool));
-  return load_revisions(opt_baton->session, aux_session, opt_baton->url,
-                        opt_baton->quiet, opt_baton->skip_revprops, pool);
+  return load_revisions(opt_baton->session, aux_session,
+                        opt_baton->dumpfile, opt_baton->quiet,
+                        opt_baton->skip_revprops, pool);
 }
 
 /* Handle the "help" subcommand.  Implements `svn_opt_subcommand_t'.  */
@@ -648,7 +681,7 @@ help_cmd(apr_getopt_t *os,
       "\n"
       "Available subcommands:\n");
 
-  return svn_opt_print_help4(os, "svnrdump", FALSE, FALSE, FALSE, NULL,
+  return svn_opt_print_help5(os, "svnrdump", FALSE, FALSE, FALSE, NULL,
                              header, svnrdump__cmd_table, svnrdump__options,
                              NULL, NULL, pool);
 }
@@ -754,7 +787,7 @@ static svn_error_t *
 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
 {
   svn_error_t *err = SVN_NO_ERROR;
-  const svn_opt_subcommand_desc2_t *subcommand = NULL;
+  const svn_opt_subcommand_desc3_t *subcommand = NULL;
   opt_baton_t *opt_baton;
   svn_revnum_t latest_revision = SVN_INVALID_REVNUM;
   const char *config_dir = NULL;
@@ -772,12 +805,14 @@ sub_main(int *exit_code, int argc, const
   apr_getopt_t *os;
   apr_array_header_t *received_opts;
   int i;
+  svn_boolean_t read_pass_from_stdin = FALSE;
 
   opt_baton = apr_pcalloc(pool, sizeof(*opt_baton));
   opt_baton->start_revision.kind = svn_opt_revision_unspecified;
   opt_baton->end_revision.kind = svn_opt_revision_unspecified;
   opt_baton->url = NULL;
   opt_baton->skip_revprops = apr_hash_make(pool);
+  opt_baton->dumpfile = NULL;
 
   SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
 
@@ -850,6 +885,9 @@ sub_main(int *exit_code, int argc, const
         case opt_auth_password:
           SVN_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool));
           break;
+        case opt_auth_password_from_stdin:
+          read_pass_from_stdin = TRUE;
+          break;
         case opt_auth_nocache:
           no_auth_cache = TRUE;
           break;
@@ -890,6 +928,11 @@ sub_main(int *exit_code, int argc, const
                                                      opt_arg, 
                                                      "svnrdump: ",
                                                      pool));
+          break;
+        case 'F':
+          SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool));
+          opt_baton->dumpfile = opt_arg;
+          break;
         }
     }
 
@@ -904,7 +947,7 @@ sub_main(int *exit_code, int argc, const
 
   if (opt_baton->help)
     {
-      subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table,
+      subcommand = svn_opt_get_canonical_subcommand3(svnrdump__cmd_table,
                                                      "help");
     }
   if (subcommand == NULL)
@@ -914,8 +957,8 @@ sub_main(int *exit_code, int argc, const
           if (opt_baton->version)
             {
               /* Use the "help" subcommand to handle the "--version" option. */
-              static const svn_opt_subcommand_desc2_t pseudo_cmd =
-                { "--version", help_cmd, {0}, "",
+              static const svn_opt_subcommand_desc3_t pseudo_cmd =
+                { "--version", help_cmd, {0}, {""},
                   {opt_version,  /* must accept its own option */
                    'q',  /* --quiet */
                   } };
@@ -935,7 +978,7 @@ sub_main(int *exit_code, int argc, const
 
           SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++],
                                           pool));
-          subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table,
+          subcommand = svn_opt_get_canonical_subcommand3(svnrdump__cmd_table,
                                                          first_arg);
 
           if (subcommand == NULL)
@@ -963,11 +1006,11 @@ sub_main(int *exit_code, int argc, const
       if (opt_id == 'h' || opt_id == '?')
         continue;
 
-      if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
+      if (! svn_opt_subcommand_takes_option4(subcommand, opt_id, NULL))
         {
           const char *optstr;
           const apr_getopt_option_t *badopt =
-            svn_opt_get_option_from_code2(opt_id, svnrdump__options,
+            svn_opt_get_option_from_code3(opt_id, svnrdump__options,
                                           subcommand, pool);
           svn_opt_format_option(&optstr, badopt, FALSE, pool);
           if (subcommand->name[0] == '-')
@@ -1005,6 +1048,24 @@ sub_main(int *exit_code, int argc, const
                                   "--non-interactive"));
     }
 
+  if (read_pass_from_stdin && !non_interactive)
+    {
+      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                              _("--password-from-stdin requires "
+                                "--non-interactive"));
+    }
+
+  if (strcmp(subcommand->name, "load") == 0)
+    {
+      if (read_pass_from_stdin && opt_baton->dumpfile == NULL)
+        {
+          /* error here, since load cannot process a password over stdin */
+          return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                  _("load subcommand with "
+                                    "--password-from-stdin requires -F"));
+        }
+    }
+
   /* Expect one more non-option argument:  the repository URL. */
   if (os->ind != os->argc - 1)
     {
@@ -1039,6 +1100,12 @@ sub_main(int *exit_code, int argc, const
         force_interactive = (username == NULL || password == NULL);
     }
 
+  /* Get password from stdin if necessary */
+  if (read_pass_from_stdin)
+    {
+      SVN_ERR(svn_cmdline__stdin_readline(&password, pool, pool));
+    }
+
   non_interactive = !svn_cmdline__be_interactive(non_interactive,
                                                  force_interactive);
 

Modified: subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.h
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.h?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.h (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/svnrdump/svnrdump.h Tue Nov 13 07:49:04 2018
@@ -77,12 +77,21 @@ svn_rdump__get_dump_editor_v2(svn_editor
 
 /**
  * Load the dumpstream carried in @a stream to the location described
- * by @a session.  Use @a aux_session (which is opened to the same URL
- * as @a session) for any secondary, out-of-band RA communications
- * required.  If @a quiet is set, suppress notifications.  Use @a pool
- * for all memory allocations.  Use @a cancel_func and @a cancel_baton
- * to check for user cancellation of the operation (for
- * timely-but-safe termination).
+ * by @a session.
+ *
+ * Use @a aux_session (which is opened to the same URL as @a session)
+ * for any secondary, out-of-band RA communications required. This is
+ * needed when loading a non-deltas dump, and for Ev2.
+ *
+ * Print feedback to the console for each revision, unless @a quiet is true.
+ *
+ * Ignore (don't set) any revision property whose name is a key in
+ * @a skip_revprops. The values in the hash are unimportant.
+ *
+ * Use @a cancel_func and @a cancel_baton to check for user cancellation
+ * of the operation (for timely-but-safe termination).
+ *
+ * Use @a pool for all memory allocations.
  */
 svn_error_t *
 svn_rdump__load_dumpstream(svn_stream_t *stream,

Modified: subversion/branches/mod-dav-svn-expressions/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/svnserve/serve.c?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/svnserve/serve.c (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/svnserve/serve.c Tue Nov 13 07:49:04 2018
@@ -369,7 +369,7 @@ handle_config_error(svn_error_t *error,
 
       /* Now that we've logged the error, clear it and return a
        * nice, generic error to the user:
-       * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */
+       * https://issues.apache.org/jira/browse/SVN-2271 */
       svn_error_clear(error);
       return svn_error_create(apr_err, NULL, NULL);
     }
@@ -2821,17 +2821,9 @@ static svn_error_t *file_rev_handler(voi
       svn_stream_set_write(stream, svndiff_handler);
       svn_stream_set_close(stream, svndiff_close_handler);
 
-      /* If the connection does not support SVNDIFF1 or if we don't want to use
-       * compression, use the non-compressing "version 0" implementation */
-      /* ### TODO: Check SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED and decide between
-       * ###       svndiff1[at compression_level] and svndiff2 */
-      if (   svn_ra_svn_compression_level(frb->conn) > 0
-          && svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1))
-        svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1,
-                                svn_ra_svn_compression_level(frb->conn), pool);
-      else
-        svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0,
-                                svn_ra_svn_compression_level(frb->conn), pool);
+      svn_txdelta_to_svndiff3(d_handler, d_baton, stream,
+                              svn_ra_svn__svndiff_version(frb->conn),
+                              svn_ra_svn_compression_level(frb->conn), pool);
     }
   else
     SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, ""));

Modified: subversion/branches/mod-dav-svn-expressions/subversion/svnserve/svnserve.c
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/svnserve/svnserve.c?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/svnserve/svnserve.c (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/svnserve/svnserve.c Tue Nov 13 07:49:04 2018
@@ -479,7 +479,7 @@ static svn_error_t * version(svn_boolean
                            _("\nCyrus SASL authentication is available.\n"));
 #endif
 
-  return svn_opt_print_help4(NULL, "svnserve", TRUE, quiet, FALSE,
+  return svn_opt_print_help5(NULL, "svnserve", TRUE, quiet, FALSE,
                              version_footer->data,
                              NULL, NULL, NULL, NULL, NULL, pool);
 }

Modified: subversion/branches/mod-dav-svn-expressions/subversion/svnsync/svnsync.c
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/svnsync/svnsync.c?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/svnsync/svnsync.c (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/svnsync/svnsync.c Tue Nov 13 07:49:04 2018
@@ -89,17 +89,17 @@ enum svnsync__opt {
                              svnsync_opt_config_dir, \
                              svnsync_opt_config_options
 
-static const svn_opt_subcommand_desc2_t svnsync_cmd_table[] =
+static const svn_opt_subcommand_desc3_t svnsync_cmd_table[] =
   {
-    { "initialize", initialize_cmd, { "init" },
-      N_("usage: svnsync initialize DEST_URL SOURCE_URL\n"
-         "\n"
+    { "initialize", initialize_cmd, { "init" }, {N_(
+         "usage: svnsync initialize DEST_URL SOURCE_URL\n"
+         "\n"), N_(
          "Initialize a destination repository for synchronization from\n"
          "another repository.\n"
-         "\n"
+         "\n"), N_(
          "If the source URL is not the root of a repository, only the\n"
          "specified part of the repository will be synchronized.\n"
-         "\n"
+         "\n"), N_(
          "The destination URL must point to the root of a repository which\n"
          "has been configured to allow revision property changes.  In\n"
          "the general case, the destination repository must contain no\n"
@@ -109,65 +109,70 @@ static const svn_opt_subcommand_desc2_t
          "their counterparts in the source repository.  (This is useful\n"
          "when initializing a copy of a repository as a mirror of that same\n"
          "repository, for example.)\n"
-         "\n"
+         "\n"), N_(
          "You should not commit to, or make revision property changes in,\n"
          "the destination repository by any method other than 'svnsync'.\n"
          "In other words, the destination repository should be a read-only\n"
-         "mirror of the source repository.\n"),
+         "mirror of the source repository.\n"
+      )},
       { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_prop_encoding, 'q',
         svnsync_opt_allow_non_empty, svnsync_opt_disable_locking,
         svnsync_opt_steal_lock, 'M' } },
-    { "synchronize", synchronize_cmd, { "sync" },
-      N_("usage: svnsync synchronize DEST_URL [SOURCE_URL]\n"
-         "\n"
+    { "synchronize", synchronize_cmd, { "sync" }, {N_(
+         "usage: svnsync synchronize DEST_URL [SOURCE_URL]\n"
+         "\n"), N_(
          "Transfer all pending revisions to the destination from the source\n"
          "with which it was initialized.\n"
-         "\n"
+         "\n"), N_(
          "If SOURCE_URL is provided, use that as the source repository URL,\n"
          "ignoring what is recorded in the destination repository as the\n"
          "source URL.  Specifying SOURCE_URL is recommended in particular\n"
          "if untrusted users/administrators may have write access to the\n"
-         "DEST_URL repository.\n"),
+         "DEST_URL repository.\n"
+      )},
       { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_prop_encoding, 'q',
         svnsync_opt_disable_locking, svnsync_opt_steal_lock, 'M' } },
-    { "copy-revprops", copy_revprops_cmd, { 0 },
-      N_("usage:\n"
-         "\n"
+    { "copy-revprops", copy_revprops_cmd, { 0 }, {N_(
+         "usage:\n"
+         "\n"), N_(
          "    1. svnsync copy-revprops DEST_URL [SOURCE_URL]\n"
          "    2. svnsync copy-revprops DEST_URL REV[:REV2]\n"
-         "\n"
+         "\n"), N_(
          "Copy the revision properties in a given range of revisions to the\n"
          "destination from the source with which it was initialized.  If the\n"
          "revision range is not specified, it defaults to all revisions in\n"
          "the DEST_URL repository.  Note also that the 'HEAD' revision is the\n"
          "latest in DEST_URL, not necessarily the latest in SOURCE_URL.\n"
-         "\n"
+         "\n"), N_(
          "If SOURCE_URL is provided, use that as the source repository URL,\n"
          "ignoring what is recorded in the destination repository as the\n"
          "source URL.  Specifying SOURCE_URL is recommended in particular\n"
          "if untrusted users/administrators may have write access to the\n"
          "DEST_URL repository.\n"
-         "\n"
+         "\n"), N_(
          "Unless you need to trigger the destination repositoy's revprop\n"
          "change hooks for all revision properties, it is recommended to use\n"
          "the --skip-unchanged option for best performance.\n"
-         "\n"
-         "Form 2 is deprecated syntax, equivalent to specifying \"-rREV[:REV2]\".\n"),
+         "\n"), N_(
+         "Form 2 is deprecated syntax, equivalent to specifying \"-rREV[:REV2]\".\n"
+      )},
       { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_prop_encoding, 'q', 'r',
         svnsync_opt_disable_locking, svnsync_opt_steal_lock,
         svnsync_opt_skip_unchanged, 'M' } },
-    { "info", info_cmd, { 0 },
-      N_("usage: svnsync info DEST_URL\n"
-         "\n"
+    { "info", info_cmd, { 0 }, {N_(
+         "usage: svnsync info DEST_URL\n"
+         "\n"), N_(
          "Print information about the synchronization destination repository\n"
-         "located at DEST_URL.\n"),
+         "located at DEST_URL.\n"
+      )},
       { SVNSYNC_OPTS_DEFAULT } },
-    { "help", help_cmd, { "?", "h" },
-      N_("usage: svnsync help [SUBCOMMAND...]\n"
-         "\n"
-         "Describe the usage of this program or its subcommands.\n"),
+    { "help", help_cmd, { "?", "h" }, {N_(
+         "usage: svnsync help [SUBCOMMAND...]\n"
+         "\n"), N_(
+         "Describe the usage of this program or its subcommands.\n"
+      )},
       { 0 } },
-    { NULL, NULL, { 0 }, NULL, { 0 } }
+    { NULL, NULL, { 0 }, {NULL}, { 0 } }
   };
 
 static const apr_getopt_option_t svnsync_options[] =
@@ -1937,7 +1942,7 @@ help_cmd(apr_getopt_t *os, void *baton,
 
   SVN_ERR(svn_ra_print_modules(version_footer, pool));
 
-  SVN_ERR(svn_opt_print_help4(os, "svnsync",
+  SVN_ERR(svn_opt_print_help5(os, "svnsync",
                               opt_baton ? opt_baton->version : FALSE,
                               opt_baton ? opt_baton->quiet : FALSE,
                               /*###opt_state ? opt_state->verbose :*/ FALSE,
@@ -1960,7 +1965,7 @@ help_cmd(apr_getopt_t *os, void *baton,
 static svn_error_t *
 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
 {
-  const svn_opt_subcommand_desc2_t *subcommand = NULL;
+  const svn_opt_subcommand_desc3_t *subcommand = NULL;
   apr_array_header_t *received_opts;
   opt_baton_t opt_baton;
   svn_config_t *config;
@@ -2189,7 +2194,7 @@ sub_main(int *exit_code, int argc, const
     }
 
   if (opt_baton.help)
-    subcommand = svn_opt_get_canonical_subcommand2(svnsync_cmd_table, "help");
+    subcommand = svn_opt_get_canonical_subcommand3(svnsync_cmd_table, "help");
 
   /* The --non-interactive and --force-interactive options are mutually
    * exclusive. */
@@ -2269,8 +2274,8 @@ sub_main(int *exit_code, int argc, const
           if (opt_baton.version)
             {
               /* Use the "help" subcommand to handle "--version". */
-              static const svn_opt_subcommand_desc2_t pseudo_cmd =
-                { "--version", help_cmd, {0}, "",
+              static const svn_opt_subcommand_desc3_t pseudo_cmd =
+                { "--version", help_cmd, {0}, {""},
                   {svnsync_opt_version,  /* must accept its own option */
                    'q',  /* --quiet */
                   } };
@@ -2290,7 +2295,7 @@ sub_main(int *exit_code, int argc, const
 
           SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++],
                                           pool));
-          subcommand = svn_opt_get_canonical_subcommand2(svnsync_cmd_table,
+          subcommand = svn_opt_get_canonical_subcommand3(svnsync_cmd_table,
                                                          first_arg);
           if (subcommand == NULL)
             {
@@ -2308,11 +2313,11 @@ sub_main(int *exit_code, int argc, const
       if (opt_id == 'h' || opt_id == '?')
         continue;
 
-      if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
+      if (! svn_opt_subcommand_takes_option4(subcommand, opt_id, NULL))
         {
           const char *optstr;
           const apr_getopt_option_t *badopt =
-            svn_opt_get_option_from_code2(opt_id, svnsync_options, subcommand,
+            svn_opt_get_option_from_code3(opt_id, svnsync_options, subcommand,
                                           pool);
           svn_opt_format_option(&optstr, badopt, FALSE, pool);
           if (subcommand->name[0] == '-')

Modified: subversion/branches/mod-dav-svn-expressions/subversion/svnversion/svnversion.c
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/svnversion/svnversion.c?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/svnversion/svnversion.c (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/svnversion/svnversion.c Tue Nov 13 07:49:04 2018
@@ -38,7 +38,7 @@
 static svn_error_t *
 version(svn_boolean_t quiet, apr_pool_t *pool)
 {
-  return svn_opt_print_help4(NULL, "svnversion", TRUE, quiet, FALSE,
+  return svn_opt_print_help5(NULL, "svnversion", TRUE, quiet, FALSE,
                              NULL, NULL, NULL, NULL, NULL, NULL, pool);
 }
 

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/authz_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/authz_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/authz_tests.py Tue Nov 13 07:49:04 2018
@@ -168,7 +168,7 @@ def authz_read_access(sbox):
   fws_url = B_url + '/folder with spaces'
   fws_empty_folder_url = fws_url + '/empty folder'
 
-  if sbox.repo_url.startswith("http"):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: E170001: Authorization failed.*"
@@ -280,7 +280,7 @@ def authz_write_access(sbox):
 
   write_restrictive_svnserve_conf(sbox.repo_dir)
 
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: E220004: Access denied.*"
@@ -367,7 +367,7 @@ def authz_checkout_test(sbox):
   # 1st part: disable all read access, checkout should fail
 
   # write an authz file with *= on /
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: E170001: Authorization failed.*"
@@ -502,7 +502,7 @@ def authz_log_and_tracing_test(sbox):
   write_restrictive_svnserve_conf(sbox.repo_dir)
 
   # write an authz file with *=rw on /
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: E170001: Authorization failed.*"
@@ -533,7 +533,7 @@ def authz_log_and_tracing_test(sbox):
 
   # now disable read access on the first version of rho, keep the copy in
   # /A/D readable.
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: E170001: Authorization failed.*"
@@ -551,7 +551,7 @@ def authz_log_and_tracing_test(sbox):
                                      'log', '-r', '2', '--limit', '1',
                                      wc_dir)
 
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err2 = expected_err
   else:
     expected_err2 = ".*svn: E220001: ((Unreadable path encountered; " \
@@ -593,7 +593,7 @@ def authz_log_and_tracing_test(sbox):
   svntest.actions.run_and_verify_svn(None, expected_err2,
                                      'cat', '-r', '2', D_url+'/rho')
 
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err2 = expected_err
   else:
     expected_err2 = ".*svn: E220001: Unreadable path encountered; access denied.*"
@@ -624,7 +624,7 @@ def authz_aliases(sbox):
 
   write_restrictive_svnserve_conf(sbox.repo_dir)
 
-  if sbox.repo_url.startswith("http"):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: E170001: Authorization failed.*"
@@ -669,9 +669,9 @@ def authz_validate(sbox):
   write_authz_file(sbox, { "/"  : "* = r",
                            "/A/B" : "@undefined_group = rw" })
 
-  if sbox.repo_url.startswith("http"):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
-  elif sbox.repo_url.startswith("svn"):
+  elif svntest.main.is_ra_type_svn():
     expected_err = ".*Invalid authz configuration"
   else:
     expected_err = ".*@undefined_group.*"
@@ -688,9 +688,9 @@ devs1 = @admins, dev1
 devs2 = @admins, dev2
 devs = @devs1, dev3, dev4""" })
 
-  if sbox.repo_url.startswith("http"):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
-  elif sbox.repo_url.startswith("svn"):
+  elif svntest.main.is_ra_type_svn():
     expected_err = ".*Invalid authz configuration"
   else:
     expected_err = ".*Circular dependency.*"
@@ -726,7 +726,7 @@ def authz_locking(sbox):
   write_authz_file(sbox, {"/": "", "/A": "jrandom = rw"})
   write_restrictive_svnserve_conf(sbox.repo_dir)
 
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: warning: W170001: Authorization failed.*"
@@ -781,7 +781,7 @@ def authz_locking(sbox):
   svntest.actions.run_and_verify_info([{'Lock Token' : None}],
                                       sbox.ospath('A/mu'))
 
-  if sbox.repo_url.startswith('http'):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: warning: W160039: .*([Aa]uth.*perf|[Ff]orbidden).*"
   else:
     expected_err = ".*svn: warning: W170001: Authorization failed.*"
@@ -863,7 +863,7 @@ def authz_svnserve_anon_access_read(sbox
   # is not really a branch (it's the same URL), but we only care about
   # authz here, not the semantics of the merge.  (Merges had been
   # failing in authz, for the reasons summarized in
-  # http://subversion.tigris.org/issues/show_bug.cgi?id=2712#desc13.)
+  # https://issues.apache.org/jira/browse/SVN-2712#desc13.)
   svntest.actions.run_and_verify_svn(None, [],
                                      'merge', '-c', '2',
                                      B_url, B_path)
@@ -981,7 +981,7 @@ def multiple_matches(sbox):
   sbox.build(create_wc = False)
   root_url = sbox.repo_url
   write_restrictive_svnserve_conf(sbox.repo_dir)
-  if sbox.repo_url.startswith("http"):
+  if svntest.main.is_ra_type_dav():
     expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
   else:
     expected_err = ".*svn: E170001: Authorization failed.*"
@@ -1137,7 +1137,7 @@ def case_sensitive_authz(sbox):
   # error messages
   expected_error_for_commit = ".*Commit failed.*"
 
-  if sbox.repo_url.startswith("http"):
+  if svntest.main.is_ra_type_dav():
     expected_error_for_cat = ".*[Ff]orbidden.*"
   else:
     expected_error_for_cat = ".*svn: E170001: Authorization failed.*"

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/basic_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/basic_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/basic_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/basic_tests.py Tue Nov 13 07:49:04 2018
@@ -3024,16 +3024,19 @@ def peg_rev_on_non_existent_wc_path(sbox
   # setup some history
   sbox.simple_move('A', 'A2')
   sbox.simple_move('A2/mu', 'A2/mu2')
-  open(sbox.ospath('A2/mu2'), 'w').write('r2\n')
+  with open(sbox.ospath('A2/mu2'), 'w') as f:
+    f.write('r2\n')
   sbox.simple_commit(message='r2')
   #
   sbox.simple_move('A2/mu2', 'A2/mu3')
   sbox.simple_move('A2', 'A3')
-  open(sbox.ospath('A3/mu3'), 'w').write('r3\n')
+  with open(sbox.ospath('A3/mu3'), 'w') as f:
+    f.write('r3\n')
   sbox.simple_commit(message='r3')
   #
   sbox.simple_move('A3/mu3', 'A3/mu4')
-  open(sbox.ospath('A3/mu4'), 'w').write('r4\n')
+  with open(sbox.ospath('A3/mu4'), 'w') as f:
+    f.write('r4\n')
   sbox.simple_move('A3', 'A4')
   sbox.simple_commit(message='r4')
 
@@ -3047,6 +3050,15 @@ def peg_rev_on_non_existent_wc_path(sbox
                                      'cat', '-r2', sbox.ospath('mu3') + '@3')
 
 
+@Issue(4532)
+def diff_previous_revision_of_r0(sbox):
+  """diff -rPREV on WC at revision 0"""
+
+  sbox.build(empty=True)
+  svntest.actions.run_and_verify_svn(None, 'svn: E195002: ',
+                                     'diff', '-rPREV', sbox.ospath(''))
+
+
 # With 'svn mkdir --parents' the target directory may already exist on disk.
 # In that case it was wrongly performing a recursive 'add' on its contents.
 def mkdir_parents_target_exists_on_disk(sbox):
@@ -3114,31 +3126,122 @@ def plaintext_password_storage_disabled(
       f.close()
 
 
-
+@Skip(svntest.main.is_os_windows)
 def filtered_ls(sbox):
   "filtered 'svn ls'"
 
   sbox.build(read_only=True)
   path = sbox.repo_url + "/A/D"
 
-  # check plain info
-  expected = [ "H/omega\n",
-               "gamma\n" ]
-
-  exit_code, output, error = svntest.actions.run_and_verify_svn(
-    expected, [], 'ls', path, '--depth=infinity', '--search=*a')
-
-  # check case-insensitivity
-  exit_code, output, error = svntest.actions.run_and_verify_svn(
-    expected, [], 'ls', path, '--depth=infinity', '--search=*A')
-
-  expected = [ "H/\n" ]
-  exit_code, output, error = svntest.actions.run_and_verify_svn(
-    expected, [], 'ls', path, '--depth=infinity', '--search=h')
-
-  # we don't match full paths
-  exit_code, output, error = svntest.actions.run_and_verify_svn(
-    [], [], 'ls', path, '--depth=infinity', '--search=*/*')
+  # with and without externals, because without externals on a 1.10+ server
+  # a server-side code path is used
+  for extra_opts in [ [], ['--include-externals'] ]:
+
+    expected = [ "H/omega\n",
+                 "gamma\n" ]
+
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      expected, [], 'ls', path, '--depth=infinity', '--search=*a', *extra_opts)
+
+    # check case-insensitivity
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      expected, [], 'ls', path, '--depth=infinity', '--search=*A', *extra_opts)
+
+    expected = [ "H/\n" ]
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      expected, [], 'ls', path, '--depth=infinity', '--search=h', *extra_opts)
+
+    # we don't match full paths
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      [], [], 'ls', path, '--depth=infinity', '--search=*/*', *extra_opts)
+
+@Issue(4700)
+@XFail(svntest.main.is_fs_type_fsx)
+def null_update_last_changed_revision(sbox):
+  "null 'update' updates last changed rev"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # r2: Random text change.
+  old_contents = open(sbox.path("iota")).read()
+  sbox.simple_append("iota", "Line 2.\n")
+  sbox.simple_commit(message='r2')
+  sbox.simple_update()
+
+  # r3: Revert r2.
+  sbox.simple_append("iota", old_contents, truncate=True)
+  sbox.simple_commit(message='r3')
+  sbox.simple_update()
+
+  # Perform a null update.
+  #
+  # This used to say '3'; probably because iota@3 and iota@1 were textually
+  # identical. It seems this problem was introduced in r1760570.
+  sbox.simple_update(revision='1')
+  svntest.actions.run_and_verify_svn(["1\n"], [],
+                                     'info', sbox.path('iota'),
+                                     '--show-item', 'last-changed-revision')
+
+@Issue(4700)
+@XFail(svntest.main.is_fs_type_bdb)
+@XFail(svntest.main.is_fs_type_fsx)
+def null_prop_update_last_changed_revision(sbox):
+  "null 'property update' updates last changed rev"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  sbox.simple_propset("prop", "value", "iota")
+  sbox.simple_commit(message='r2')
+  sbox.simple_update()
+
+  # r3: change the property
+  sbox.simple_propset("prop", "changed", "iota")
+  sbox.simple_commit(message='r3')
+  sbox.simple_update()
+
+  # r4: Revert r3.
+  sbox.simple_propset("prop", "value", "iota")
+  sbox.simple_commit(message='r4')
+  sbox.simple_update()
+
+  # Perform a null update.
+  sbox.simple_update(revision='2')
+  svntest.actions.run_and_verify_svn(["2\n"], [],
+                                     'info', sbox.path('iota'),
+                                     '--show-item', 'last-changed-revision')
+
+@Skip(svntest.main.is_os_windows)
+def filtered_ls_top_level_path(sbox):
+  "filtered 'svn ls' top level path"
+
+  sbox.build(read_only=True, create_wc=False)
+  d_path = sbox.repo_url + "/A/B"
+  f_path = sbox.repo_url + "/A/B/lambda"
+
+  d_expected = svntest.verify.RegexListOutput([
+                 r".* \./",  # expect '*B*' to match its name which is 'B'
+                 r".* E/beta",
+                 r".* lambda" ])
+  f_expected = [ "lambda\n" ]
+
+  # with and without externals, because without externals on a 1.10+ server
+  # a server-side code path is used
+  for extra_opts in [ [], ['--include-externals'] ]:
+
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      d_expected, [], 'ls', '-v', d_path, '-R', '--search=*B*', *extra_opts)
+
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      f_expected, [], 'ls', f_path, '--search=lambda', *extra_opts)
+
+    # we don't match full paths, even for the top level path
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      [], [], 'ls', '-v', d_path, '-R', '--search=*/*', *extra_opts)
+    exit_code, output, error = svntest.actions.run_and_verify_svn(
+      [], [], 'ls', f_path, '--search=*/*', *extra_opts)
+
 
 ########################################################################
 # Run the tests
@@ -3208,9 +3311,13 @@ test_list = [ None,
               rm_missing_with_case_clashing_ondisk_item,
               delete_conflicts_one_of_many,
               peg_rev_on_non_existent_wc_path,
+              diff_previous_revision_of_r0,
               mkdir_parents_target_exists_on_disk,
               plaintext_password_storage_disabled,
               filtered_ls,
+              null_update_last_changed_revision,
+              null_prop_update_last_changed_revision,
+              filtered_ls_top_level_path,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/blame_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/blame_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/blame_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/blame_tests.py Tue Nov 13 07:49:04 2018
@@ -958,7 +958,8 @@ def blame_youngest_to_oldest(sbox):
   sbox.simple_commit() #r3
 
   # Delete a line.
-  open(iota_moved, 'w').write(line)
+  with open(iota_moved, 'w') as f:
+    f.write(line)
   sbox.simple_commit() #r4
 
   expected_output = [

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/commit_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/commit_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/commit_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/commit_tests.py Tue Nov 13 07:49:04 2018
@@ -2823,7 +2823,8 @@ def commit_add_subadd(sbox):
 
   # prepare targets file
   targets = "A/D A/D/H A/D/H/chi A/D/H/omega A/D/H/psi".split()
-  open(targets_file, 'w').write("\n".join(targets))
+  with open(targets_file, 'w') as f:
+    f.write("\n".join(targets))
 
   # r2: rm A/D
   sbox.simple_rm('A/D')
@@ -3113,6 +3114,39 @@ def commit_xml(sbox):
   sbox.simple_append('index.html', '<Q></R>', True)
   sbox.simple_commit()
 
+@Issue(4722)
+def commit_issue4722_checksum(sbox):
+  "commit that triggered checksum failure"
+
+  sbox.build()
+
+  # This bug only ever affected FSFS in 1.9.7.  The test could be
+  # considered a bit "fragile" as any change to the on-disk
+  # representation may well make it pass trivially.  On the other hand
+  # it should still pass irrespective of that representation, and for
+  # all other repository types.
+
+  # Enough data to allow the bug to occur
+  with open(sbox.ospath('f'), 'w') as fp:
+    for i in range(0, 2001):
+      fp.write('abcdefghijklmnopqrstuvwxyz')
+  sbox.simple_add('f')
+  sbox.simple_commit()
+
+  # Just the right data to trigger the bug
+  with open(sbox.ospath('f'), 'w') as fp:
+    for i in range(0, 8713):
+      fp.write(str(i))
+    fp.write("11111")
+  sbox.simple_commit()
+
+  # Trigger deduplication which is when the bug occurred
+  with open(sbox.ospath('f'), 'w') as fp:
+    for i in range(0, 2001):
+      fp.write('abcdefghijklmnopqrstuvwxyz')
+  sbox.simple_commit()
+
+
 ########################################################################
 # Run the tests
 
@@ -3190,6 +3224,7 @@ test_list = [ None,
               commit_mergeinfo_ood,
               mkdir_conflict_proper_error,
               commit_xml,
+              commit_issue4722_checksum,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/copy_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/copy_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/copy_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/copy_tests.py Tue Nov 13 07:49:04 2018
@@ -1021,6 +1021,56 @@ def repos_to_wc(sbox):
                                      os.path.join(D_dir, 'B'))
 
 #----------------------------------------------------------------------
+def foreign_repos_to_wc(sbox):
+  "foreign repository to WC copy"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  def move_url(repo_url, source, dest):
+    svntest.main.run_svn(False, 'move', '-m', svntest.main.make_log_msg(),
+                         repo_url + '/' + source,
+                         repo_url + '/' + dest)
+
+  # We have a standard repository and working copy.  Now we create a
+  # second repository with the same greek tree, but different UUID.
+  repo_dir       = sbox.repo_dir
+  other_repo_dir, other_repo_url = sbox.add_repo_path('other')
+  svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 1)
+  move_url(other_repo_url, 'A', 'A2')
+  move_url(other_repo_url, 'A2', 'A3')
+
+  # URL->wc copy:
+  # copy a file and a directory from a foreign repository.
+  # we should get some scheduled additions *without history*.
+  E_url = other_repo_url + "/A2/B/E"
+  pi_url = other_repo_url + "/A2/D/G/pi"
+  peg_rev = '2'
+  op_rev = '1'
+  E_url_resolved = E_url.replace('/A2/', '/A/')
+  pi_url_resolved = pi_url.replace('/A2/', '/A/')
+
+  expected_output = svntest.verify.UnorderedOutput([
+    '--- Copying from foreign repository URL \'%s\':\n' % E_url_resolved,
+    'A         %s\n' % sbox.ospath('E'),
+    'A         %s\n' % sbox.ospath('E/beta'),
+    'A         %s\n' % sbox.ospath('E/alpha'),
+  ])
+  svntest.actions.run_and_verify_svn(expected_output, [],
+                                     'copy', '-r' + op_rev,
+                                     E_url + '@' + peg_rev,
+                                     wc_dir)
+
+  expected_output = [
+    '--- Copying from foreign repository URL \'%s\':\n' % pi_url_resolved,
+    'A         %s\n' % sbox.ospath('pi'),
+  ]
+  svntest.actions.run_and_verify_svn(expected_output, [],
+                                     'copy', '-r' + op_rev,
+                                     pi_url + '@' + peg_rev,
+                                     wc_dir)
+
+#----------------------------------------------------------------------
 # Issue 1084: ra_svn move/copy bug
 @Issue(1084)
 def copy_to_root(sbox):
@@ -3504,6 +3554,50 @@ def copy_make_parents_wc_wc(sbox):
                                         expected_output,
                                         expected_status)
 
+
+#----------------------------------------------------------------------
+# Test copying and creating parents in the wc with dst directory being
+# precreated and unversioned
+
+def copy_make_parents_wc_wc_existing_unversioned_dst(sbox):
+  "svn cp --parents WC_PATH WC_PATH (ex. unver. dst)"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  iota_path = sbox.ospath('iota')
+  new_iota_path = sbox.ospath('X/Y/Z/iota')
+  os.makedirs(os.path.dirname(new_iota_path))
+
+  # Copy iota
+  svntest.actions.run_and_verify_svn(None, [],
+                                     'cp', '--parents',
+                                     iota_path, new_iota_path)
+
+  # Create expected output
+  expected_output = svntest.wc.State(wc_dir, {
+    'X'          : Item(verb='Adding'),
+    'X/Y'        : Item(verb='Adding'),
+    'X/Y/Z'      : Item(verb='Adding'),
+    'X/Y/Z/iota' : Item(verb='Adding'),
+    })
+
+  # Create expected status tree
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+
+  # Add the moved files
+  expected_status.add({
+    'X'           : Item(status='  ', wc_rev=2),
+    'X/Y'         : Item(status='  ', wc_rev=2),
+    'X/Y/Z'       : Item(status='  ', wc_rev=2),
+    'X/Y/Z/iota'  : Item(status='  ', wc_rev=2),
+    })
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status)
+
+
 #----------------------------------------------------------------------
 # Test copying and creating parents from the repo to the wc
 
@@ -3546,6 +3640,49 @@ def copy_make_parents_repo_wc(sbox):
 
 
 #----------------------------------------------------------------------
+# Test copying and creating parents from the repo to the wc with dst
+# directory being precreated and unversioned
+
+def copy_make_parents_repo_wc_existing_unversioned_dst(sbox):
+  "svn cp --parents URL WC_PATH with (ex. unver. dst)"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  iota_url = sbox.repo_url + '/iota'
+  new_iota_path = sbox.ospath('X/Y/Z/iota')
+  os.makedirs(os.path.dirname(new_iota_path))
+
+  # Copy iota
+  svntest.actions.run_and_verify_svn(None, [],
+                                     'cp', '--parents',
+                                     iota_url, new_iota_path)
+
+  # Create expected output
+  expected_output = svntest.wc.State(wc_dir, {
+    'X'           : Item(verb='Adding'),
+    'X/Y'         : Item(verb='Adding'),
+    'X/Y/Z'       : Item(verb='Adding'),
+    'X/Y/Z/iota'  : Item(verb='Adding'),
+    })
+
+  # Create expected status tree
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+
+  # Add the moved files
+  expected_status.add({
+    'X'           : Item(status='  ', wc_rev=2),
+    'X/Y'         : Item(status='  ', wc_rev=2),
+    'X/Y/Z'       : Item(status='  ', wc_rev=2),
+    'X/Y/Z/iota'  : Item(status='  ', wc_rev=2),
+    })
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status)
+
+
+#----------------------------------------------------------------------
 # Test copying and creating parents from the wc to the repo
 
 def copy_make_parents_wc_repo(sbox):
@@ -4333,7 +4470,7 @@ def copy_added_dir_with_copy(sbox):
 def copy_broken_symlink(sbox):
   """copy broken symlink"""
 
-  ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3303. ##
+  ## See https://issues.apache.org/jira/browse/SVN-3303. ##
 
   sbox.build()
   wc_dir = sbox.wc_dir
@@ -5904,7 +6041,9 @@ test_list = [ None,
               copy_peg_rev_url,
               old_dir_wc_to_wc,
               copy_make_parents_wc_wc,
+              copy_make_parents_wc_wc_existing_unversioned_dst,
               copy_make_parents_repo_wc,
+              copy_make_parents_repo_wc_existing_unversioned_dst,
               copy_make_parents_wc_repo,
               copy_make_parents_repo_repo,
               URI_encoded_repos_to_wc,
@@ -5955,6 +6094,7 @@ test_list = [ None,
               ext_wc_copy_deleted,
               copy_subtree_deleted,
               resurrect_at_root,
+              foreign_repos_to_wc,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/dav-mirror-autocheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/dav-mirror-autocheck.sh?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/dav-mirror-autocheck.sh (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/dav-mirror-autocheck.sh Tue Nov 13 07:49:04 2018
@@ -31,7 +31,7 @@
 #
 # The set of changes sent through the system is currently
 # just the test case for issue 2939, using svnmucc
-# http://subversion.tigris.org/issues/show_bug.cgi?id=2939
+# https://issues.apache.org/jira/browse/SVN-2939
 # But of course, any svn traffic liable to break over
 # mirroring would be a good addition.
 #
@@ -100,6 +100,7 @@ function setup_config() {
 
   say "setting up config: " $1
 cat > "$1" <<__EOF__
+$LOAD_MOD_MPM
 $LOAD_MOD_LOG_CONFIG
 $LOAD_MOD_MIME
 $LOAD_MOD_UNIXD
@@ -114,9 +115,30 @@ $LOAD_MOD_AUTHZ_CORE
 $LOAD_MOD_AUTHZ_USER
 $LOAD_MOD_AUTHZ_HOST
 
+__EOF__
+
+if "$HTTPD" -v | grep '/2\.[012]' >/dev/null; then
+  cat >> "$1" <<__EOF__
 LockFile            lock
 User                $(id -un)
 Group               $(id -gn)
+__EOF__
+else
+HTTPD_LOCK="$HTTPD_ROOT/lock"
+mkdir "$HTTPD_LOCK" \
+  || fail "couldn't create lock directory '$HTTPD_LOCK'"
+  cat >> "$1" <<__EOF__
+# worker and prefork MUST have a mpm-accept lockfile in 2.3.0+
+<IfModule worker.c>
+  Mutex "file:$HTTPD_LOCK" mpm-accept
+</IfModule>
+<IfModule prefork.c>
+  Mutex "file:$HTTPD_LOCK" mpm-accept
+</IfModule>
+__EOF__
+fi
+
+cat >> "$1" <<__EOF__
 Listen              ${TEST_PORT}
 ServerName          localhost
 PidFile             "${HTTPD_ROOT}/pid"
@@ -133,6 +155,9 @@ MaxRequestsPerChild 0
 <IfModule worker.c>
   ThreadsPerChild   8
 </IfModule>
+<IfModule event.c>
+  ThreadsPerChild   8
+</IfModule>
 MaxClients          16
 HostNameLookups     Off
 LogFormat           "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" format
@@ -202,6 +227,12 @@ function usage() {
 
 SCRIPT=$(basename $0)
 
+NO_TESTS=
+if [ "x$1" = 'x--no-tests' ]; then
+  NO_TESTS=1
+  shift
+fi
+
 if [ $# -ne 1 ] ; then
   usage
 fi
@@ -249,7 +280,7 @@ HTPASSWD=$(get_prog_name htpasswd htpass
 SVN=$ABS_BUILDDIR/subversion/svn/svn
 SVNADMIN=$ABS_BUILDDIR/subversion/svnadmin/svnadmin
 SVNSYNC=$ABS_BUILDDIR/subversion/svnsync/svnsync
-SVNMUCC=${SVNMUCC:-$ABS_BUILDDIR/tools/client-side/svnmucc/svnmucc}
+SVNMUCC=$ABS_BUILDDIR/subversion/svnmucc/svnmucc
 SVNLOOK=$ABS_BUILDDIR/subversion/svnlook/svnlook
 
 [ -x $HTTPD ] || fail "HTTPD '$HTTPD' not executable"
@@ -259,9 +290,7 @@ SVNLOOK=$ABS_BUILDDIR/subversion/svnlook
 [ -x $SVNADMIN ] || fail "SVNADMIN $SVNADMIN not built"
 [ -x $SVNSYNC ] || fail "SVNSYNC $SVNSYNC not built"
 [ -x $SVNLOOK ] || fail "SVNLOOK $SVNLOOK not built"
-[ -x $SVNMUCC ] \
- || fail SVNMUCC $SVNMUCC executable not built, needed for test. \
-    \'cd $ABS_BUILDDIR\; make svnmucc\' to fix.
+[ -x $SVNMUCC ] || fail "SVNMUCC $SVNMUCC not built"
 
 say HTTPD: $HTTPD
 say SVN: $SVN
@@ -309,6 +338,10 @@ LOAD_MOD_AUTHN_FILE="$(get_loadmodule_co
 LOAD_MOD_AUTHZ_USER="$(get_loadmodule_config mod_authz_user)" \
     || fail "Authz_User module not found."
 }
+if [ ${APACHE_MPM:+set} ]; then
+    LOAD_MOD_MPM=$(get_loadmodule_config mod_mpm_$APACHE_MPM) \
+      || fail "MPM module not found"
+fi
 
 if [ ${MODULE_PATH:+set} ]; then
     MOD_DAV_SVN="$MODULE_PATH/mod_dav_svn.so"
@@ -365,7 +398,9 @@ $SVNADMIN create "$SLAVE_REPOS" || fail
 $SVNADMIN dump "$MASTER_REPOS" | $SVNADMIN load "$SLAVE_REPOS" \
   || fail "duplicate repositories failed"
 # make sure uuid's match
-[ `cat "$SLAVE_REPOS/db/uuid"` = `cat "$MASTER_REPOS/db/uuid"` ] \
+read MASTER_UUID < "$MASTER_REPOS/db/uuid"
+read SLAVE_UUID < "$SLAVE_REPOS/db/uuid"
+[ "$SLAVE_UUID" = "$MASTER_UUID" ] \
   || fail "master/slave uuid mismatch"
 # setup hooks:
 #  slave allows revprop changes
@@ -397,12 +432,18 @@ $SVNSYNC initialize --non-interactive "$
     --username=svnsync --password=svnsync \
     || fail "svnsync initialize failed"
 
+if [ $NO_TESTS ]; then
+  echo "MASTER_URL=$MASTER_URL"
+  echo "SLAVE_URL=$SLAVE_URL"
+  exit
+fi
+
 # OK, let's start testing! Commit changes to slave, expect
 # them to proxy through to the master, and then
 # svnsync back to the slave
 #
 # reproducible test case from:
-# http://subversion.tigris.org/issues/show_bug.cgi?id=2939
+# https://issues.apache.org/jira/browse/SVN-2939
 #
 BASE_URL="$SLAVE_URL"
 say running svnmucc test to $BASE_URL

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/davautocheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/davautocheck.sh?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/davautocheck.sh (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/davautocheck.sh Tue Nov 13 07:49:04 2018
@@ -52,6 +52,7 @@
 # Run this script with the test suite name and test number to execute just this
 # test:
 #   subversion/tests/cmdline/davautocheck.sh basic 4
+# This script can also be invoked via "make davautocheck".
 #
 # If the temporary directory is not deleted, it can be reused for further
 # manual DAV protocol interoperation testing. HTTPD must be started by
@@ -65,22 +66,23 @@
 #   APXS=/opt/svn/1.4.x/bin/apxs MODULE_PATH=/opt/svn/1.4.x/modules \
 #     subversion/tests/cmdline/davautocheck.sh
 #
-# To prevent the server from advertising httpv2, pass USE_HTTPV1 in
-# the environment.
+# Other environment variables that are interpreted by this script:
 #
-# To enable "SVNCacheRevProps on" set CACHE_REVPROPS in the environment.
+#  make davautocheck CACHE_REVPROPS=1       # sets SVNCacheRevProps on
 #
-# To test over https set USE_SSL in the environment.
+#  make davautocheck BLOCK_READ=1           # sets SVNBlockRead on
 #
-# To use value for "SVNPathAuthz" directive set SVN_PATH_AUTHZ with
-# appropriate value in the environment.
+#  make davautocheck USE_SSL=1              # run over https
 #
-# To load an MPM module for Apache 2.4 use APACHE_MPM=event in the
-# environment.
+#  make davautocheck USE_HTTPV1=1           # sets SVNAdvertiseV2Protocol off
+#
+#  make davautocheck APACHE_MPM=event       # specifies the 2.4 MPM
+#
+#  make davautocheck SVN_PATH_AUTHZ=short_circuit  # SVNPathAuthz short_circuit
 #
 # Passing --no-tests as argv[1] will have the script start a server
-# but not run any tests.  Passing --gdb will do the same, and in addition
-# spawn gdb in the foreground attached to the running server.
+# but not run any tests.  Passing --gdb or --lldb will do the same, and in
+# addition spawn gdb/lldb in the foreground attached to the running server.
 
 PYTHON=${PYTHON:-python}
 
@@ -222,6 +224,11 @@ if [ ${CACHE_REVPROPS:+set} ]; then
   CACHE_REVPROPS_SETTING=on
 fi
 
+BLOCK_READ_SETTING=off
+if [ ${BLOCK_READ:+set} ]; then
+  BLOCK_READ_SETTING=on
+fi
+
 if [ ${MODULE_PATH:+set} ]; then
     MOD_DAV_SVN="$MODULE_PATH/mod_dav_svn.so"
     MOD_AUTHZ_SVN="$MODULE_PATH/mod_authz_svn.so"
@@ -274,6 +281,9 @@ say "Using '$HTPASSWD'..."
 LOAD_MOD_DAV=$(get_loadmodule_config mod_dav) \
   || fail "DAV module not found"
 
+LOAD_MOD_DAV_FS=$(get_loadmodule_config mod_dav_fs) \
+  || fail "Filesystem DAV module not found"
+
 LOAD_MOD_LOG_CONFIG=$(get_loadmodule_config mod_log_config) \
   || fail "log_config module not found"
 
@@ -440,6 +450,7 @@ $LOAD_MOD_MIME
 $LOAD_MOD_ALIAS
 $LOAD_MOD_UNIXD
 $LOAD_MOD_DAV
+$LOAD_MOD_DAV_FS
 LoadModule          dav_svn_module "$MOD_DAV_SVN"
 $LOAD_MOD_AUTH
 $LOAD_MOD_AUTHN_CORE
@@ -475,6 +486,13 @@ mkdir "$HTTPD_LOCK" \
 __EOF__
 fi
 
+HTTPD_DAV="$HTTPD_ROOT/dav"
+mkdir "$HTTPD_DAV" \
+    || fail "couldn't create DAV lock directory '$HTTPD_DAV'"
+cat >> "$HTTPD_CFG" <<__EOF__
+DavLockDB "$HTTPD_DAV/lock.db"
+__EOF__
+
 if [ ${USE_SSL:+set} ]; then
 cat >> "$HTTPD_CFG" <<__EOF__
 SSLEngine on
@@ -518,40 +536,59 @@ CustomLog           "$HTTPD_ROOT/ops" "%
   #Require           all granted
 </Directory>
 
+Alias /nodavroot $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/nodavroot
+<Directory $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/nodavroot>
+</Directory>
+
+Alias /fsdavroot $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/fsdavroot
+<Directory $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/fsdavroot>
+  DAV filesystem
+</Directory>
+
 <Location /svn-test-work/repositories>
+__EOF__
+location_common() {
+cat >> "$HTTPD_CFG" <<__EOF__
   DAV               svn
-  SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/repositories"
   AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
   AuthType          Basic
   AuthName          "Subversion Repository"
   AuthUserFile      $HTTPD_USERS
-  Require           valid-user
   SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
   SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
+  SVNListParentPath On
+  SVNBlockRead      ${BLOCK_READ_SETTING}
+__EOF__
+}
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
+  SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/repositories"
+  Require           valid-user
   ${SVN_PATH_AUTHZ_LINE}
 </Location>
 <Location /ddt-test-work/repositories>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/repositories"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   Require           valid-user
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-  SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
   ${SVN_PATH_AUTHZ_LINE}
   DontDoThatConfigFile "$HTTPD_DONTDOTHAT"
 </Location>
 <Location /svn-test-work/local_tmp/repos>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNPath           "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp/repos"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   Require           valid-user
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
+  ${SVN_PATH_AUTHZ_LINE}
+</Location>
+<Location /svn-test-work/local_tmp/trojan>
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
+  SVNPath           "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp/trojan"
+  Require           valid-user
   ${SVN_PATH_AUTHZ_LINE}
 </Location>
 <Location /authz-test-work/anon>
@@ -574,84 +611,54 @@ CustomLog           "$HTTPD_ROOT/ops" "%
   ${SVN_PATH_AUTHZ_LINE}
 </Location>
 <Location /authz-test-work/mixed>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-  SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-  SVNListParentPath On
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   Require           valid-user
   Satisfy Any
   ${SVN_PATH_AUTHZ_LINE}
 </Location>
 <Location /authz-test-work/mixed-noauthwhenanon>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-  SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-  SVNListParentPath On
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   Require           valid-user
   AuthzSVNNoAuthWhenAnonymousAllowed On
   SVNPathAuthz On
 </Location>
 <Location /authz-test-work/authn>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-  SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-  SVNListParentPath On
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   Require           valid-user
   ${SVN_PATH_AUTHZ_LINE}
 </Location>
 <Location /authz-test-work/authn-anonoff>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-  SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-  SVNListParentPath On
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   Require           valid-user
   AuthzSVNAnonymous Off
   SVNPathAuthz On
 </Location>
 <Location /authz-test-work/authn-lcuser>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-  SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-  SVNListParentPath On
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   Require           valid-user
   AuthzForceUsernameCase Lower
   ${SVN_PATH_AUTHZ_LINE}
 </Location>
 <Location /authz-test-work/authn-group>
-  DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
   SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-  AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-  SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-  SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-  SVNListParentPath On
-  AuthType          Basic
-  AuthName          "Subversion Repository"
-  AuthUserFile      $HTTPD_USERS
   AuthGroupFile     $HTTPD_GROUPS
   Require           group random
   AuthzSVNAuthoritative Off
@@ -659,15 +666,10 @@ CustomLog           "$HTTPD_ROOT/ops" "%
 </Location>
 <IfModule mod_authz_core.c>
   <Location /authz-test-work/sallrany>
-    DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
     SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-    AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-    SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-    SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-    SVNListParentPath On
-    AuthType          Basic
-    AuthName          "Subversion Repository"
-    AuthUserFile      $HTTPD_USERS
     AuthzSendForbiddenOnFailure On
     Satisfy All
     <RequireAny>
@@ -677,15 +679,10 @@ CustomLog           "$HTTPD_ROOT/ops" "%
     ${SVN_PATH_AUTHZ_LINE}
   </Location>
   <Location /authz-test-work/sallrall>
-    DAV               svn
+__EOF__
+location_common
+cat >> "$HTTPD_CFG" <<__EOF__
     SVNParentPath     "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
-    AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
-    SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
-    SVNCacheRevProps  ${CACHE_REVPROPS_SETTING}
-    SVNListParentPath On
-    AuthType          Basic
-    AuthName          "Subversion Repository"
-    AuthUserFile      $HTTPD_USERS
     AuthzSendForbiddenOnFailure On
     Satisfy All
     <RequireAll>
@@ -763,6 +760,12 @@ if [ $# -eq 1 ] && [ "x$1" = 'x--no-test
   exit
 fi
 
+if [ $# -eq 1 ] && [ "x$1" = 'x--lldb' ]; then
+  echo "http://localhost:$HTTPD_PORT/svn-test-work/repositories"
+  $STOPSCRIPT && lldb --one-line=run -- $START -X
+  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

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/depth_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/depth_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/depth_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/depth_tests.py Tue Nov 13 07:49:04 2018
@@ -1073,7 +1073,7 @@ def commit_depth_immediates(sbox):
   #    Message-ID: <46...@collab.net>
   #    Date: Thu, 12 Jul 2007 15:59:45 -0400
   #
-  # See also http://subversion.tigris.org/issues/show_bug.cgi?id=2882.
+  # See also https://issues.apache.org/jira/browse/SVN-2882.
   #
   # Outline of the test:
   # ====================

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/diff_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/diff_tests.py Tue Nov 13 07:49:04 2018
@@ -36,7 +36,6 @@ from svntest import err, wc
 
 from prop_tests import binary_mime_type_on_text_file_warning
 from svntest.verify import make_diff_header, make_no_diff_deleted_header, \
-                           make_diff_header, make_no_diff_deleted_header, \
                            make_git_diff_header, make_diff_prop_header, \
                            make_diff_prop_val, make_diff_prop_deleted, \
                            make_diff_prop_added, make_diff_prop_modified
@@ -2712,7 +2711,7 @@ def diff_ignore_eolstyle(sbox):
     " Bb\n",
     "-Cc\n",
     "+Cc\n",
-    "\ No newline at end of file\n" ]
+    "\\ No newline at end of file\n" ]
 
   svntest.actions.run_and_verify_svn(expected_output, [],
                                      'diff', '-x', '--ignore-eol-style',
@@ -3531,7 +3530,7 @@ def diff_git_empty_files(sbox):
   expected_output = make_git_diff_header(new_path, "new", "nonexistent",
                                          "working copy",
                                          add=True, text_changes=False) + [
-  ] + make_git_diff_header(iota_path, "iota", "revision 2", "working copy",
+  ] + make_git_diff_header(iota_path, "iota", "revision 2", "nonexistent",
                            delete=True, text_changes=False)
 
   # Two files in diff may be in any order.
@@ -3855,7 +3854,7 @@ def diff_arbitrary_files_and_dirs(sbox):
   sbox.build()
   wc_dir = sbox.wc_dir
 
-  # diff iota with A/mu
+  # diff files (iota with A/mu)
   expected_output = make_diff_header("iota", "working copy", "working copy",
                                      "iota", "A/mu") + [
                       "@@ -1 +1 @@\n",
@@ -3866,7 +3865,11 @@ def diff_arbitrary_files_and_dirs(sbox):
                                      'diff', '--old', sbox.ospath('iota'),
                                      '--new', sbox.ospath('A/mu'))
 
-  # diff A/B/E with A/D
+  # diff dirs (A/B/E with A/D)
+  # .../gamma is to show as replaced; .../beta is to show as modified
+  sbox.simple_mkdir('A/B/E/gamma')
+  sbox.simple_propset('p', 'v', 'A/B/E/gamma')
+  sbox.simple_add_text("This is a different beta file.\n", 'A/D/beta')
   expected_output = make_diff_header("G/pi", "nonexistent", "working copy",
                                      "B/E", "D") + [
                       "@@ -0,0 +1 @@\n",
@@ -3896,11 +3899,16 @@ def diff_arbitrary_files_and_dirs(sbox):
                       "@@ -1 +0,0 @@\n",
                       "-This is the file 'alpha'.\n"
                     ] + make_diff_header("beta", "working copy",
-                                         "nonexistent", "B/E", "D") + [
-                      "@@ -1 +0,0 @@\n",
-                      "-This is the file 'beta'.\n"
-                    ] + make_diff_header("gamma", "nonexistent",
                                          "working copy", "B/E", "D") + [
+                      "@@ -1 +1 @@\n",
+                      "-This is the file 'beta'.\n",
+                      "+This is a different beta file.\n"
+                    ] + make_diff_header("gamma", "working copy",
+                                           "nonexistent", "B/E", "D") \
+                      + make_diff_prop_header("gamma") \
+                      + make_diff_prop_deleted("p", "v") \
+                      + make_diff_header("gamma", "nonexistent",
+                                       "working copy", "B/E", "D") + [
                       "@@ -0,0 +1 @@\n",
                       "+This is the file 'gamma'.\n"
                     ]
@@ -5043,7 +5051,7 @@ def diff_symlinks(sbox):
     '===================================================================\n',
     'diff --git a/to-iota b/to-iota\n',
     'new file mode 120644\n',
-    '--- /dev/null\t(nonexistent)\n',
+    '--- a/to-iota\t(nonexistent)\n',
     '+++ b/to-iota\t(working copy)\n',
     '@@ -0,0 +1 @@\n',
     '+iota\n',
@@ -5140,6 +5148,110 @@ def diff_peg_resolve(sbox):
                                      repo_url + '/branches/A2',
                                      wc_dir, '-r1:2')
 
+@XFail()
+@Issue(4706)
+def diff_unversioned_files_git(sbox):
+  "diff unversioned files in git format"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  svntest.main.file_write(sbox.ospath('foo'), "foo\n")
+  svntest.main.file_write(sbox.ospath('A/bar'), "bar\n")
+  expected_output = make_diff_header("foo", "working copy", "working copy",
+                                     "foo", "A/bar") + [
+                      "@@ -1 +1 @@\n",
+                      "-foo\n",
+                      "+bar\n"
+                    ]
+  svntest.actions.run_and_verify_svn(expected_output, [],
+                                     'diff', '--git',
+                                     '--old', sbox.ospath('foo'),
+                                     '--new', sbox.ospath('A/bar'))
+
+# Summary diff with a repository source side and a local copy target side.
+# This particular combination crashed in 1.10.0 and earlier releases.
+def diff_summary_repo_wc_local_copy(sbox):
+  "diff summary repo wc local copy"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  sbox.simple_copy('iota', 'iota2')
+  sbox.simple_append('iota2', 'hello\n')
+  expected_diff = svntest.wc.State(wc_dir, {
+    'iota': Item(status='M '),
+    })
+  svntest.actions.run_and_verify_diff_summarize(
+                    expected_diff,
+                    '--old=' + sbox.ospath('iota') + '@HEAD',
+                    '--new=' + sbox.ospath('iota2'))
+
+# Summary diff with a repository source side and a local copy target side.
+# Svn reported the unmodified copy as modified in 1.10.0 and earlier releases.
+@XFail()
+def diff_summary_repo_wc_local_copy_unmodified(sbox):
+  "diff summary repo wc local copy unmodified"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  sbox.simple_copy('iota', 'iota2')
+  expected_diff = svntest.wc.State(wc_dir, {
+    })
+  svntest.actions.run_and_verify_diff_summarize(
+                    expected_diff,
+                    '--old=' + sbox.ospath('iota') + '@HEAD',
+                    '--new=' + sbox.ospath('iota2'))
+
+# Fails with "Can't open file '.../iota': Too many levels of symbolic links"
+# on Unix.
+@XFail()
+@Skip(svntest.main.is_os_windows)
+def diff_file_replaced_by_symlink(sbox):
+  "diff base vs working: symlink replaces a file"
+  sbox.build(read_only=True)
+  wc_dir = sbox.wc_dir
+
+  iota_path = sbox.ospath('iota')
+  os.remove(iota_path)
+
+  # create a symlink pointing to itself
+  # alternatively it could point to a non-existing path
+  sbox.simple_symlink('iota', 'iota')
+
+  # TODO: add a full expected output
+  expected_output = svntest.verify.AnyOutput
+  svntest.actions.run_and_verify_svn(expected_output, [], 'diff', wc_dir)
+
+# Test 'svn diff --git' with a copy.
+#
+# When this diff is rooted at a path below the repository root directory,
+# it errored out while printing the git diff header, due to confusion of
+# diff-relative and repository-relative copyfrom paths.
+@XFail()
+def diff_git_format_copy(sbox):
+  "diff git format copy"
+  sbox.build(create_wc=False)
+  svntest.actions.run_and_verify_svn(None, [], 'checkout',
+                                     sbox.repo_url + '/A/B',
+                                     sbox.wc_dir)
+  os.chdir(sbox.wc_dir)
+  sbox.wc_dir = ''
+
+  sbox.simple_copy('E/alpha', 'alpha_copied')
+  sbox.simple_append('alpha_copied', "This is a copy of 'alpha'.\n")
+
+  expected_output = \
+    make_git_diff_header('alpha_copied', 'A/B/alpha_copied',
+                         "revision 1", "working copy",
+                         copyfrom_path="A/B/E/alpha",
+                         copyfrom_rev='1', cp=True,
+                         text_changes=True) + [
+    "@@ -1 +1,2 @@\n",
+    " This is the file 'alpha'.\n",
+    "+This is a copy of 'alpha'.\n",
+  ]
+
+  svntest.actions.run_and_verify_svn(expected_output, [], 'diff',
+                                     '--git', '.')
 
 ########################################################################
 #Run the tests
@@ -5236,6 +5348,11 @@ test_list = [ None,
               diff_incomplete_props,
               diff_symlinks,
               diff_peg_resolve,
+              diff_unversioned_files_git,
+              diff_summary_repo_wc_local_copy,
+              diff_summary_repo_wc_local_copy_unmodified,
+              diff_file_replaced_by_symlink,
+              diff_git_format_copy,
               ]
 
 if __name__ == '__main__':

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/entries_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/entries_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/entries_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/entries_tests.py Tue Nov 13 07:49:04 2018
@@ -121,14 +121,17 @@ def basic_entries(sbox):
                                         G_path, iota_path)
 
   # Add a file over the DELETED 'alpha'. It should be schedule-add.
-  open(alpha_path, 'w').write('New alpha contents\n')
+  with open(alpha_path, 'w') as f:
+    f.write('New alpha contents\n')
 
   # Delete 'beta', then add a file over it. Should be schedule-replace.
   svntest.actions.run_and_verify_svn(None, [], 'rm', beta_path)
-  open(beta_path, 'w').write('New beta contents\n')
+  with open(beta_path, 'w') as f:
+    f.write('New beta contents\n')
 
   # Plain old add. Should have revision == 0.
-  open(added_path, 'w').write('Added file contents\n')
+  with open(added_path, 'w') as f:
+    f.write('Added file contents\n')
 
   svntest.actions.run_and_verify_svn(None, [], 'add',
                                      alpha_path, beta_path, added_path)

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/export_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/export_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/export_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/export_tests.py Tue Nov 13 07:49:04 2018
@@ -610,7 +610,8 @@ def export_file_overwrite_fails(sbox):
   os.mkdir(tmpdir)
 
   # Run it for source local
-  open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+  with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+    f.write(not_iota_contents)
   svntest.actions.run_and_verify_svn([], '.*exist.*',
                                      'export', iota_path, tmpdir)
 
@@ -621,7 +622,8 @@ def export_file_overwrite_fails(sbox):
   svntest.actions.verify_disk(tmpdir, expected_disk)
 
   # Run it for source URL
-  open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+  with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+    f.write(not_iota_contents)
   svntest.actions.run_and_verify_svn([], '.*exist.*',
                                      'export', iota_url, tmpdir)
 
@@ -721,7 +723,7 @@ def export_working_copy_ignoring_keyword
 def export_with_url_unsafe_characters(sbox):
   "export file with URL unsafe characters"
 
-  ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3683 ##
+  ## See https://issues.apache.org/jira/browse/SVN-3683 ##
 
   sbox.build()
   wc_dir = sbox.wc_dir
@@ -904,14 +906,16 @@ def export_file_overwrite_with_force(sbo
       })
 
   # Run it for WC export
-  open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+  with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+    f.write(not_iota_contents)
   svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput,
                                      [], 'export', '--force',
                                      iota_path, tmpdir)
   svntest.actions.verify_disk(tmpdir, expected_disk)
 
   # Run it for URL export
-  open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+  with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+    f.write(not_iota_contents)
   svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput,
                                      [], 'export', '--force',
                                      iota_url, tmpdir)

Modified: subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/externals_tests.py?rev=1846488&r1=1846487&r2=1846488&view=diff
==============================================================================
--- subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/branches/mod-dav-svn-expressions/subversion/tests/cmdline/externals_tests.py Tue Nov 13 07:49:04 2018
@@ -1146,7 +1146,7 @@ def external_into_path_with_spaces(sbox)
   repo_url = sbox.repo_url
 
   ext = '^/A/D        "A/copy of D"\n' +\
-        '^/A/D        A/another\ copy\ of\ D'
+        '^/A/D        A/another\\ copy\\ of\\ D'
   change_external(wc_dir, ext)
 
   expected_output = svntest.wc.State(wc_dir, {
@@ -2818,7 +2818,7 @@ def remap_file_external_with_prop_del(sb
 
   # Now update to bring the new external down.
   # This previously segfaulted as described in
-  # http://subversion.tigris.org/issues/show_bug.cgi?id=4093#desc1
+  # https://issues.apache.org/jira/browse/SVN-4093#desc1
   svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
 
 
@@ -3871,12 +3871,14 @@ def copy_pin_externals_whitespace_dir(sb
   extdef = sbox.get_tempname('extdef')
   info = sbox.get_tempname('info')
 
-  open(extdef, 'w').write(
+  with open(extdef, 'w') as f:
+    f.write(
       '"' + ss_path +'/deps/sqlite"  ext/sqlite\n' +
       '"^/deps/A P R" \'ext/A P R\'\n' +
-      '^/deps/B\ D\ B\' ext/B\ D\ B\'\n' +
+      '^/deps/B\\ D\\ B\' ext/B\\ D\\ B\'\n' +
       repo_url + '/deps/wors%23+t ext/wors#+t')
-  open(info, 'w').write('info\n')
+  with open(info, 'w') as f:
+    f.write('info\n')
 
   svntest.actions.run_and_verify_svnmucc(None, [], '-U', repo_url,
                                          'mkdir', 'trunk',
@@ -4398,6 +4400,55 @@ def invalid_uris_in_repo(sbox):
                                             "-r", revision)
     svntest.main.safe_rmtree(sbox.wc_dir)
 
+# Like issue #3741 'externals not removed when working copy is made shallow'
+# but with --set-depth=exclude instead of --set-depth=empty.
+def update_dir_external_exclude(sbox):
+  "exclude update should remove externals"
+
+  sbox.build()
+
+  # Create an external in r2
+  sbox.simple_propset('svn:externals', '^/A/D/H X', 'A/B/E')
+  sbox.simple_commit()
+
+  # Update to fetch externals
+  expected_output = svntest.wc.State(sbox.wc_dir, {
+    'A/B/E/X/chi'       : Item(status='A '),
+    'A/B/E/X/omega'     : Item(status='A '),
+    'A/B/E/X/psi'       : Item(status='A '),
+  })
+  svntest.actions.run_and_verify_update(sbox.wc_dir,
+                                        expected_output, None, None,
+                                        [], False,
+                                        sbox.ospath('A/B/E'))
+
+  # Now make A/B/E shallow by updating with "--set-depth exclude"
+  expected_output = svntest.wc.State(sbox.wc_dir, {
+    'A/B/E'       : Item(status='D '),
+    'A/B/E/X'     : Item(verb='Removed external'),
+  })
+  svntest.actions.run_and_verify_update(sbox.wc_dir,
+                                        expected_output, None, None,
+                                        [], False,
+                                        '--set-depth=exclude',
+                                        sbox.ospath('A/B/E'))
+
+  # And bring the external back by updating with "--set-depth infinity"
+  expected_output = svntest.wc.State(sbox.wc_dir, {
+    'A/B/E'         : Item(status='A '),
+    'A/B/E/alpha'   : Item(status='A '),
+    'A/B/E/beta'    : Item(status='A '),
+    'A/B/E/X/chi'   : Item(status='A '),
+    'A/B/E/X/omega' : Item(status='A '),
+    'A/B/E/X/psi'   : Item(status='A '),
+  })
+  svntest.actions.run_and_verify_update(sbox.wc_dir,
+                                        expected_output, None, None,
+                                        [], False,
+                                        '--set-depth=infinity',
+                                        sbox.ospath('A/B/E'))
+
+
 ########################################################################
 # Run the tests
 
@@ -4474,6 +4525,7 @@ test_list = [ None,
               file_external_recorded_info,
               external_externally_removed,
               invalid_uris_in_repo,
+              update_dir_external_exclude,
              ]
 
 if __name__ == '__main__':