You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/01/14 14:01:51 UTC

svn commit: r1897034 [29/37] - in /subversion/branches/multi-wc-format: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/s...

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c Fri Jan 14 14:01:45 2022
@@ -23,6 +23,7 @@
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <assert.h>
 
 #include <apr_pools.h>
 #include <apr_general.h>
@@ -34,6 +35,7 @@
 #include "svn_pools.h"
 #include "svn_wc.h"
 #include "svn_dirent_uri.h"
+#include "svn_xml.h"
 
 #include "private/svn_wc_private.h"
 
@@ -41,14 +43,61 @@
 #include "../../libsvn_wc/lock.h"
 
 static void
-str_value(const char *name, const char *value)
+print_prefix(void)
+{
+  puts("if b'' == '':\n"
+       "  def _to_str(s):\n"
+       "    return s\n"
+       "else:\n"
+       "  def _to_str(s):\n"
+       "    return s.decode('utf-8', 'surrogateescape')\n"
+       "\n"
+       "class Entry(object):\n"
+       "  \"\"\"An Entry object represents one node's entry in a pre-1.6"
+       " .svn/entries file.\n\n"
+       "Similar to #svn_wc_entry_t, but not all fields are populated.\n\n"
+       "Entry objects are generated by the 'entries-dump'"
+       " test helper tool.\"\"\"\n\n"
+       "  if b'' == '':\n"
+       "    def set_strval(self, name, val):\n"
+       "      self.__setattr__(name, val)\n"
+       "  else:\n"
+       "    def set_strval(self, name, val):\n"
+       "      global _to_str\n"
+       "      self.__setattr__(name, _to_str(val))\n");
+}
+
+static void
+print_as_bytes(const char *val)
+{
+  printf("b'");
+  while(*val)
+    {
+      printf("\\x%02x", (unsigned int)(unsigned char)*val++);
+    }
+  printf("'");
+}
+
+static void
+str_value(const char *name, const char *value, apr_pool_t *pool)
 {
   if (value == NULL)
     printf("e.%s = None\n", name);
   else
-    printf("e.%s = '%s'\n", name, value);
-}
+    {
+      svn_stringbuf_t *escaped_value = NULL;
+      svn_xml_escape_attr_cstring(&escaped_value, value, pool);
 
+      /* Print the human-readable value. */
+      assert(NULL == strchr(escaped_value->data, '\n'));
+      printf("# e.%s = '%s'\n", name, escaped_value->data);
+
+      /* Print the machine-readable value. */
+      printf("e.set_strval('%s', ", name);
+      print_as_bytes(value);
+      printf(")\n");
+    }
+}
 
 static void
 int_value(const char *name, long int value)
@@ -76,6 +125,7 @@ entries_dump(const char *dir_path, svn_w
   svn_error_t *err;
   svn_wc_context_t *wc_ctx = NULL;
   const char *dir_abspath;
+  apr_pool_t *iterpool = svn_pool_create(pool); 
 
   SVN_ERR(svn_dirent_get_absolute(&dir_abspath, dir_path, pool));
 
@@ -124,44 +174,45 @@ entries_dump(const char *dir_path, svn_w
 
   for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
     {
+      svn_stringbuf_t *escaped_key;
       const char *key = apr_hash_this_key(hi);
       const svn_wc_entry_t *entry = apr_hash_this_val(hi);
 
+      svn_pool_clear(iterpool); 
       SVN_ERR_ASSERT(strcmp(key, entry->name) == 0);
-
       printf("e = Entry()\n");
-      str_value("name", entry->name);
+      str_value("name", entry->name, iterpool);
       int_value("revision", entry->revision);
-      str_value("url", entry->url);
-      str_value("repos", entry->repos);
-      str_value("uuid", entry->uuid);
+      str_value("url", entry->url, iterpool);
+      str_value("repos", entry->repos, iterpool);
+      str_value("uuid", entry->uuid, iterpool);
       int_value("kind", entry->kind);
       int_value("schedule", entry->schedule);
       bool_value("copied", entry->copied);
       bool_value("deleted", entry->deleted);
       bool_value("absent", entry->absent);
       bool_value("incomplete", entry->incomplete);
-      str_value("copyfrom_url", entry->copyfrom_url);
+      str_value("copyfrom_url", entry->copyfrom_url, iterpool);
       int_value("copyfrom_rev", entry->copyfrom_rev);
-      str_value("conflict_old", entry->conflict_old);
-      str_value("conflict_new", entry->conflict_new);
-      str_value("conflict_wrk", entry->conflict_wrk);
-      str_value("prejfile", entry->prejfile);
+      str_value("conflict_old", entry->conflict_old, iterpool);
+      str_value("conflict_new", entry->conflict_new, iterpool);
+      str_value("conflict_wrk", entry->conflict_wrk, iterpool);
+      str_value("prejfile", entry->prejfile, iterpool);
       /* skip: text_time */
       /* skip: prop_time */
       /* skip: checksum */
       int_value("cmt_rev", entry->cmt_rev);
       /* skip: cmt_date */
-      str_value("cmt_author", entry->cmt_author);
-      str_value("lock_token", entry->lock_token);
-      str_value("lock_owner", entry->lock_owner);
-      str_value("lock_comment", entry->lock_comment);
+      str_value("cmt_author", entry->cmt_author, iterpool);
+      str_value("lock_token", entry->lock_token, iterpool);
+      str_value("lock_owner", entry->lock_owner, iterpool);
+      str_value("lock_comment", entry->lock_comment, iterpool);
       /* skip: lock_creation_date */
       /* skip: has_props */
       /* skip: has_prop_mods */
       /* skip: cachable_props */
       /* skip: present_props */
-      str_value("changelist", entry->changelist);
+      str_value("changelist", entry->changelist, iterpool);
       /* skip: working_size */
       /* skip: keep_local */
       int_value("depth", entry->depth);
@@ -170,8 +221,17 @@ entries_dump(const char *dir_path, svn_w
       /* skip: file_external_peg_rev */
       /* skip: file_external_rev */
       bool_value("locked", locked && *entry->name == '\0');
-      printf("entries['%s'] = e\n", (const char *)key);
+      /* Print the human-readable value. */
+      escaped_key = NULL;
+      svn_xml_escape_attr_cstring(&escaped_key, key, iterpool);
+      assert(NULL == strchr(escaped_key->data, '\n'));
+      printf("# entries['%s'] = e\n", escaped_key->data);
+      /* Print the machine-readable value. */
+      printf("entries[_to_str(");
+      print_as_bytes(key);
+      printf(")] = e\n");
     }
+  svn_pool_destroy(iterpool);
 
   if (wc_ctx)
     SVN_ERR(svn_wc_context_destroy(wc_ctx));
@@ -282,6 +342,7 @@ tree_dump_dir(const char *local_abspath,
 {
   struct directory_walk_baton *bt = walk_baton;
   const char *path;
+  svn_stringbuf_t *escaped_path;
 
   if (kind != svn_node_dir)
     return SVN_NO_ERROR;
@@ -307,7 +368,15 @@ tree_dump_dir(const char *local_abspath,
   printf("entries = {}\n");
   SVN_ERR(entries_dump(path, bt->adm_access, scratch_pool));
 
-  printf("dirs['%s'] = entries\n", path);
+  /* Print the human-readable value. */
+  escaped_path = NULL;
+  svn_xml_escape_attr_cstring(&escaped_path, path, scratch_pool);
+  assert(NULL == strchr(escaped_path->data, '\n'));
+  printf("# dirs['%s'] = entries\n", escaped_path->data);
+  /* Print the machine-readable value. */
+  printf("dirs[_to_str(");
+  print_as_bytes(path);
+  printf(")] = entries\n");
   return SVN_NO_ERROR;
 
 }
@@ -384,15 +453,25 @@ main(int argc, const char *argv[])
     cmd = NULL;
 
   if (!cmd || !strcmp(cmd, "--entries"))
-    err = entries_dump(path, NULL, pool);
+    {
+      print_prefix();
+      err = entries_dump(path, NULL, pool);
+    }
   else if (!strcmp(cmd, "--subdirs"))
-    err = directory_dump(path, pool);
+    {
+      err = directory_dump(path, pool);
+    }
   else if (!strcmp(cmd, "--tree-dump"))
-    err = tree_dump(path, pool);
+    {
+      print_prefix();
+      err = tree_dump(path, pool);
+    }
   else
-    err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                            "Invalid command '%s'",
-                            cmd);
+    {
+      err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+                              "Invalid command '%s'",
+                              cmd);
+    }
   if (err)
     {
       svn_handle_error2(err, stderr, FALSE, "entries-dump: ");

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py Fri Jan 14 14:01:45 2022
@@ -1124,6 +1124,81 @@ def export_revision_with_root_relative_e
                                         expected_disk,
                                         '-r', 2)
 
+def export_keyword_translation_inconsistent_eol(sbox):
+  "export keyword translation with inconsistent EOLs"
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  # Create a file with keywords and inconsistent EOLs, don't set svn:eol-style.
+  sbox.simple_add_text('$LastChangedRevision$\n\r\n', 'dir/file')
+  sbox.simple_propset('svn:keywords', 'LastChangedRevision', 'dir/file')
+  sbox.simple_commit()
+
+  export_target = sbox.add_wc_path('export')
+
+  expected_disk = svntest.wc.State('', {
+    'dir'      : Item(),
+    'dir/file' : Item("$LastChangedRevision: 1 $\n\r\n"),
+  })
+
+  expected_output = svntest.wc.State(export_target, {
+    ''         : Item(status='A '),
+    'dir'      : Item(status='A '),
+    'dir/file' : Item(status='A ')
+  })
+
+  # We should be able to export without any unexpected errors.
+  svntest.actions.run_and_verify_export2(sbox.repo_url,
+                                         export_target,
+                                         expected_output,
+                                         expected_disk,
+                                         keep_eol_style=True)
+
+def export_working_copy_eol_translation(sbox):
+  "export working copy with EOL translation"
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('test\n', 'dir/file')
+  sbox.simple_propset('svn:eol-style', 'CRLF', 'dir/file')
+  sbox.simple_commit()
+
+  export_target = sbox.add_wc_path('export')
+
+  expected_disk = svntest.wc.State('', {
+    'dir'      : Item(),
+    'dir/file' : Item("test\r\n"),
+  })
+
+  expected_output = svntest.wc.State(export_target, {
+    'dir'      : Item(status='A '),
+    'dir/file' : Item(status='A ')
+  })
+
+  svntest.actions.run_and_verify_export2(sbox.wc_dir,
+                                         export_target,
+                                         expected_output,
+                                         expected_disk,
+                                         keep_eol_style=True)
+
+def export_working_copy_inconsistent_eol(sbox):
+  "export working copy with inconsistent EOLs"
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('test\n', 'dir/file')
+  sbox.simple_propset('svn:eol-style', 'CRLF', 'dir/file')
+  sbox.simple_commit()
+
+  # Edit the file so that it would have inconsistent EOLs.
+  sbox.simple_append('dir/file', 'test\n\r\n', truncate=True)
+
+  # Attempt to export the working copy, expect an error.
+  export_target = sbox.add_wc_path('export')
+  svntest.actions.run_and_verify_svn(
+    None,
+    "svn: E135000: Inconsistent line ending style\n",
+    'export',
+    sbox.wc_dir,
+    export_target)
+
 
 ########################################################################
 # Run the tests
@@ -1162,6 +1237,9 @@ test_list = [ None,
               export_file_external,
               export_file_externals2,
               export_revision_with_root_relative_external,
+              export_keyword_translation_inconsistent_eol,
+              export_working_copy_eol_translation,
+              export_working_copy_inconsistent_eol,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py Fri Jan 14 14:01:45 2022
@@ -2946,7 +2946,7 @@ def url_to_wc_copy_of_externals(sbox):
   external_tau_path = os.path.join(wc_dir, "External-WC-to-URL-Copy",
                                    "external", "tau")
   expected_stdout = verify.UnorderedOutput([
-    " U   " + external_root_path + "\n",
+    "A         " + external_root_path + "\n",
     "\n",
     "Fetching external item into '" + external_ex_path + "':\n",
     "A    " + external_pi_path + "\n",
@@ -2954,8 +2954,6 @@ def url_to_wc_copy_of_externals(sbox):
     "A    " + external_tau_path + "\n",
     "Checked out external at revision 2.\n",
     "\n",
-    "Checked out revision 2.\n",
-    "A         " + external_root_path + "\n"
   ])
   exit_code, stdout, stderr = svntest.actions.run_and_verify_svn2(
     expected_stdout, [], 0, 'copy', repo_url + '/A/C',
@@ -3913,7 +3911,7 @@ def copy_pin_externals_whitespace_dir(sb
   branches_url = repo_url + '/branches'
   trunk_wc = sbox.ospath('trunk')
 
-  # Create a new revision to creat interesting pinning revisions
+  # Create a new revision to create interesting pinning revisions
   sbox.simple_propset('A', 'B', 'trunk')
   sbox.simple_commit('trunk')
 

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py Fri Jan 14 14:01:45 2022
@@ -38,10 +38,6 @@ import svntest
 
 #----------------------------------------------------------------------
 
-# This directory contains all the expected output from svn.
-getopt_output_dir = os.path.join(os.path.dirname(sys.argv[0]),
-                                 'getopt_tests_data')
-
 # Naming convention for golden files: take the svn command line as a
 # single string and apply the following sed transformations:
 #   echo svn option1 option2 ... | sed -e 's/ /_/g' -e 's/_--/--/g'
@@ -51,6 +47,10 @@ getopt_output_dir = os.path.join(os.path
 def load_expected_output(basename):
   "load the expected standard output and standard error"
 
+  # This directory contains all the expected output from svn.
+  getopt_output_dir = os.path.join(os.path.dirname(sys.argv[0]),
+                                   'getopt_tests_data')
+
   stdout_filename = os.path.join(getopt_output_dir, basename + '_stdout')
   stderr_filename = os.path.join(getopt_output_dir, basename + '_stderr')
 
@@ -233,7 +233,7 @@ def getopt_config_option(sbox):
   expected_stderr = '.*W205000.*did you mean.*'
   expected_stdout = svntest.verify.AnyOutput
   svntest.actions.run_and_verify_svn2(expected_stdout, expected_stderr, 0,
-                                      'info', 
+                                      'info',
                                       '--config-option',
                                       'config:miscellanous:diff-extensions=' +
                                         '-u -p',

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout Fri Jan 14 14:01:45 2022
@@ -47,14 +47,6 @@ Available subcommands:
    unlock
    update (up)
    upgrade
-   x-shelf-diff
-   x-shelf-drop
-   x-shelf-list (x-shelves)
-   x-shelf-list-by-paths
-   x-shelf-log
-   x-shelf-save
-   x-shelve
-   x-unshelve
 
 Subversion is a tool for version control.
 For additional information, see http://subversion.apache.org/

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout Fri Jan 14 14:01:45 2022
@@ -6,7 +6,7 @@ This software consists of contributions
 see the NOTICE file for more information.
 Subversion is open source software, see http://subversion.apache.org/
 
-Supported working copy (WC) versions: from 1.8 to 1.12
+Supported working copy (WC) versions: from 1.8 to 1.15
 
 The following repository access (RA) modules are available:
 

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout Fri Jan 14 14:01:45 2022
@@ -6,7 +6,7 @@ This software consists of contributions
 see the NOTICE file for more information.
 Subversion is open source software, see http://subversion.apache.org/
 
-Supported working copy (WC) versions: from 1.8 to 1.12
+Supported working copy (WC) versions: from 1.8 to 1.15
 
 The following repository access (RA) modules are available:
 

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout Fri Jan 14 14:01:45 2022
@@ -135,32 +135,10 @@ Valid options:
                              to prevent shell expansion)
   --search-and ARG         : combine ARG with the previous search pattern
 
-Global options:
-  --username ARG           : specify a username ARG
-  --password ARG           : specify a password ARG (caution: on many operating
-                             systems, other users will be able to see this)
-  --password-from-stdin    : read password from stdin
-  --no-auth-cache          : do not cache authentication tokens
-  --non-interactive        : do no interactive prompting (default is to prompt
-                             only if standard input is a terminal device)
-  --force-interactive      : do interactive prompting even if standard input
-                             is not a terminal device
-  --trust-server-cert      : deprecated; same as
-                             --trust-server-cert-failures=unknown-ca
-  --trust-server-cert-failures ARG : with --non-interactive, accept SSL server
-                             certificates with failures; ARG is comma-separated
-                             list of 'unknown-ca' (Unknown Authority),
-                             'cn-mismatch' (Hostname mismatch), 'expired'
-                             (Expired certificate), 'not-yet-valid' (Not yet
-                             valid certificate) and 'other' (all other not
-                             separately classified certificate errors).
-  --config-dir ARG         : read user configuration files from directory ARG
-  --config-option ARG      : set user configuration option in the format:
-                                 FILE:SECTION:OPTION=[VALUE]
-                             For example:
-                                 servers:global:http-library=serf
+(Use '-v' to show global and experimental options.)
 
-switch (sw): Update the working copy to a different URL within the same repository.
+switch (sw): Update the working copy to a different URL within the same
+repository.
 usage: 1. switch URL[@PEGREV] [PATH]
        2. switch --relocate FROM-PREFIX TO-PREFIX [PATH...]
 
@@ -196,9 +174,6 @@ usage: 1. switch URL[@PEGREV] [PATH]
 
   Examples:
     svn switch ^/branches/1.x-release
-    svn switch --relocate http:// svn://
-    svn switch --relocate http://www.example.com/repo/project \
-                          svn://svn.example.com/repo/project
 
 Valid options:
   -r [--revision] ARG      : ARG (some commands also take ARG1:ARG2 range)
@@ -226,28 +201,5 @@ Valid options:
                              'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l', 'r')
   --relocate               : deprecated; use 'svn relocate'
 
-Global options:
-  --username ARG           : specify a username ARG
-  --password ARG           : specify a password ARG (caution: on many operating
-                             systems, other users will be able to see this)
-  --password-from-stdin    : read password from stdin
-  --no-auth-cache          : do not cache authentication tokens
-  --non-interactive        : do no interactive prompting (default is to prompt
-                             only if standard input is a terminal device)
-  --force-interactive      : do interactive prompting even if standard input
-                             is not a terminal device
-  --trust-server-cert      : deprecated; same as
-                             --trust-server-cert-failures=unknown-ca
-  --trust-server-cert-failures ARG : with --non-interactive, accept SSL server
-                             certificates with failures; ARG is comma-separated
-                             list of 'unknown-ca' (Unknown Authority),
-                             'cn-mismatch' (Hostname mismatch), 'expired'
-                             (Expired certificate), 'not-yet-valid' (Not yet
-                             valid certificate) and 'other' (all other not
-                             separately classified certificate errors).
-  --config-dir ARG         : read user configuration files from directory ARG
-  --config-option ARG      : set user configuration option in the format:
-                                 FILE:SECTION:OPTION=[VALUE]
-                             For example:
-                                 servers:global:http-library=serf
+(Use '-v' to show global and experimental options.)
 

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout Fri Jan 14 14:01:45 2022
@@ -47,14 +47,6 @@ Available subcommands:
    unlock
    update (up)
    upgrade
-   x-shelf-diff
-   x-shelf-drop
-   x-shelf-list (x-shelves)
-   x-shelf-list-by-paths
-   x-shelf-log
-   x-shelf-save
-   x-shelve
-   x-unshelve
 
 Subversion is a tool for version control.
 For additional information, see http://subversion.apache.org/

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py Fri Jan 14 14:01:45 2022
@@ -716,6 +716,72 @@ def info_item_uncommmitted(sbox):
     sbox.ospath('newfile'), sbox.ospath('newdir'))
 
 
+def info_item_size_wc_recursive(sbox):
+  "recursive '--show-item=repos-size' on local path"
+
+  sbox.build(read_only=True)
+
+  svntest.actions.run_and_verify_svn(
+    [], [],
+    'info', '--show-item=repos-size', '--recursive',
+    sbox.ospath(''))
+
+
+def info_item_size_repos(sbox):
+  "non-recursive '--show-item=repos-size' on URL"
+
+  sbox.build(read_only=True)
+
+  svntest.actions.run_and_verify_svn(
+    "25\n", [],
+    'info', '--show-item=repos-size',
+    sbox.repo_url + "/iota")
+
+  # Same, but without the newline.
+  svntest.actions.run_and_verify_svn(
+    "25", [],
+    'info', '--show-item=repos-size', '--no-newline',
+    sbox.repo_url + "/iota")
+
+  # Same, but with "human-readable" output.
+  svntest.actions.run_and_verify_svn(
+    "25 B", [],
+    'info', '--show-item=repos-size', '--human-readable',
+    sbox.repo_url + "/iota")
+
+  # No output when the URL is a directory.
+  svntest.actions.run_and_verify_svn(
+    [], [],
+    'info', '--show-item=repos-size',
+    sbox.repo_url)
+
+
+def info_item_size_repos_recursive(sbox):
+  "recursive '--show-item=repos-size' on dir URL"
+
+  sbox.build(read_only=True)
+
+  expected_output = svntest.verify.UnorderedOutput([
+    "25         " + sbox.repo_url + "/iota\n",
+    "27         " + sbox.repo_url + "/A/B/lambda\n",
+    "25         " + sbox.repo_url + "/A/B/E/beta\n",
+    "26         " + sbox.repo_url + "/A/B/E/alpha\n",
+    "23         " + sbox.repo_url + "/A/mu\n",
+    "26         " + sbox.repo_url + "/A/D/gamma\n",
+    "23         " + sbox.repo_url + "/A/D/G/pi\n",
+    "24         " + sbox.repo_url + "/A/D/G/rho\n",
+    "24         " + sbox.repo_url + "/A/D/G/tau\n",
+    "26         " + sbox.repo_url + "/A/D/H/omega\n",
+    "24         " + sbox.repo_url + "/A/D/H/psi\n",
+    "24         " + sbox.repo_url + "/A/D/H/chi\n",
+  ])
+
+  svntest.actions.run_and_verify_svn(
+    expected_output, [],
+    'info', '--show-item=repos-size', '--recursive',
+    sbox.repo_url)
+
+
 def info_item_failures(sbox):
   "failure modes of 'svn info --show-item'"
 
@@ -746,6 +812,158 @@ def info_item_failures(sbox):
     'info', '--show-item=revision', '--no-newline',
     sbox.ospath('A'), sbox.ospath('iota'))
 
+  svntest.actions.run_and_verify_svn(
+    None, (r".*E200007: can't show in-repository size.*"),
+    'info', '--show-item=repos-size',
+    sbox.ospath('iota'))
+
+
+@Issue(4837)
+def info_file_in_file_replaced_dir(sbox):
+  "info, file in file-replaced dir"
+
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('text\n', 'dir/file')
+  sbox.simple_commit(message='Add file')
+
+  sbox.simple_copy('dir/file', 'file-moved')
+  sbox.simple_rm('dir')
+  sbox.simple_add_text('replaced\n', 'dir')
+  sbox.simple_commit(message='Replace dir with file')
+
+  sbox.simple_update()
+
+  expected = {'Relative URL' : r'\^/dir/file',
+              'Node Kind' : 'file',
+              'Revision': '1',
+              'Last Changed Rev': '1',
+             }
+
+  svntest.actions.run_and_verify_info([expected],
+                                      sbox.repo_url + '/dir/file@1')
+
+@Issue(4869)
+def info_tree_conflict_source(sbox):
+  "info --xml: verify source-left, source-right"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  B_path = os.path.join(wc_dir, 'A', 'B')
+  lambda_path = os.path.join(B_path, 'lambda')
+  alpha_path = os.path.join(B_path, 'E', 'alpha')
+  F_path = os.path.join(B_path, 'F')
+
+  B2_url = sbox.repo_url + '/A/B2'
+  B2_path = os.path.join(wc_dir, 'A', 'B2')
+  lambda2_path = os.path.join(B2_path, 'lambda')
+
+  # Rev 2 copy B to B2
+  sbox.simple_repo_copy('A/B', 'A/B2')
+  sbox.simple_update()
+
+  # Rev 3:
+  # edit A/B/lambda to test text conflict case
+  # add property to A/B/E/alpha to test property conflict case
+  # rename A/B/F to A/B/Z to test tree conflict case
+
+  svntest.main.file_write(lambda_path, 'B/lambda side of conflict')
+  sbox.simple_propset('blue', 'azul', 'A/B/E/alpha')
+  sbox.simple_move('A/B/F', 'A/B/Z');
+  sbox.simple_commit()
+
+  # Rev 4:
+  # edit A/B2/lambda
+  # add property to A/B2/E/alpha
+  # rename A/B2/F to A/B2/Y
+
+  svntest.main.file_write(lambda2_path, 'B2/lambda side of conflict')
+  sbox.simple_propset('blue', 'bleue', 'A/B2/E/alpha')
+  sbox.simple_move('A/B2/F', 'A/B2/Y');
+  sbox.simple_commit()
+
+  # Now merge B2 into B to cause a text conflict, property conflict, and
+  # tree conflict
+  sbox.simple_update()
+
+  svntest.actions.run_and_verify_svn2(svntest.verify.AnyOutput, [],
+                                      0, 'merge', B2_url, B_path)
+
+  # Verify 'svn info --xml' on the text conflicted case
+
+  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
+                                                                [], 'info',
+                                                                lambda_path,
+                                                                '--xml')
+
+  verify_xml_elements(output,
+                      [('version', {'revision'     : '1',
+                                    'side'         : 'source-left',
+                                    'kind'         : 'file',
+                                    'path-in-repos': 'A/B/lambda',
+                                    'repos-url'    : sbox.repo_url,
+                                   },
+                       )])
+
+  verify_xml_elements(output,
+                      [('version', {'revision'     : '4',
+                                    'side'         : 'source-right',
+                                    'kind'         : 'file',
+                                    'path-in-repos': 'A/B2/lambda',
+                                    'repos-url'    : sbox.repo_url,
+                                   },
+                       )])
+
+  # Verify 'svn info --xml' on the property conflicted case
+
+  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
+                                                                [], 'info',
+                                                                alpha_path,
+                                                                '--xml')
+
+  verify_xml_elements(output,
+                      [('version', {'revision'     : '1',
+                                    'side'         : 'source-left',
+                                    'kind'         : 'file',
+                                    'path-in-repos': 'A/B/E/alpha',
+                                    'repos-url'    : sbox.repo_url,
+                                   },
+                       )])
+
+  verify_xml_elements(output,
+                      [('version', {'revision'     : '4',
+                                    'side'         : 'source-right',
+                                    'kind'         : 'file',
+                                    'path-in-repos': 'A/B2/E/alpha',
+                                    'repos-url'    : sbox.repo_url,
+                                   },
+                       )])
+
+  # Verify 'svn info --xml' on the tree conflicted case
+
+  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
+                                                                [], 'info',
+                                                                F_path,
+                                                                '--xml')
+
+  verify_xml_elements(output,
+                      [('version', {'revision'     : '1',
+                                    'side'         : 'source-left',
+                                    'kind'         : 'dir',
+                                    'path-in-repos': 'A/B/F',
+                                    'repos-url'    : sbox.repo_url,
+                                   },
+                       )])
+
+  verify_xml_elements(output,
+                      [('version', {'revision'     : '4',
+                                    'side'         : 'source-right',
+                                    'kind'         : 'none',
+                                    'path-in-repos': 'A/B2/F',
+                                    'repos-url'    : sbox.repo_url,
+                                   },
+                       )])
 
 ########################################################################
 # Run the tests
@@ -767,7 +985,12 @@ test_list = [ None,
               info_item_simple_multiple,
               info_item_url,
               info_item_uncommmitted,
+              info_item_size_wc_recursive,
+              info_item_size_repos,
+              info_item_size_repos_recursive,
               info_item_failures,
+              info_file_in_file_replaced_dir,
+              info_tree_conflict_source,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py Fri Jan 14 14:01:45 2022
@@ -1001,7 +1001,7 @@ def lock_and_exebit1(sbox):
   if (not gamma_stat & mode_r
       or gamma_stat & mode_w
       or not gamma_stat & mode_x):
-    logger.warn("Commiting a file with 'svn:needs-lock, svn:executable'")
+    logger.warn("Committing a file with 'svn:needs-lock, svn:executable'")
     logger.warn("after unlocking modified file's permissions")
     raise svntest.Failure
 
@@ -1065,7 +1065,7 @@ def lock_and_exebit2(sbox):
   if (not gamma_stat & mode_r
       or gamma_stat & mode_w
       or not gamma_stat & mode_x):
-    logger.warn("Commiting a file with 'svn:needs-lock, svn:executable'")
+    logger.warn("Committing a file with 'svn:needs-lock, svn:executable'")
     logger.warn("did not set the file to read-only, executable")
     raise svntest.Failure
 
@@ -1454,7 +1454,7 @@ def lock_path_not_in_head(sbox):
   svntest.actions.run_and_verify_svn(None, [], 'up', '-r1', wc_dir)
   expected_lock_fail_err_re = "svn: warning: W160042: " \
   "(Path .* doesn't exist in HEAD revision)"
-  # Issue #3524 These lock attemtps were triggering an assert over ra_serf:
+  # Issue #3524 These lock attempts were triggering an assert over ra_serf:
   #
   # working_copies\lock_tests-37>svn lock A\D
   # ..\..\..\subversion\libsvn_client\ra.c:275: (apr_err=235000)
@@ -1835,7 +1835,7 @@ def commit_stolen_lock(sbox):
 # 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.
+# paths were added.
 @Issue(4364)
 def drop_locks_on_parent_deletion(sbox):
   "drop locks when the parent is deleted"
@@ -1856,7 +1856,7 @@ def drop_locks_on_parent_deletion(sbox):
                                         [],
                                         expected_status)
 
-  # now re-add entities to the deleted pathes.
+  # now re-add entities to the deleted paths.
   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')
@@ -2479,6 +2479,94 @@ def replace_dir_with_lots_of_locked_file
   # This problem was introduced on the 1.8.x branch in r1606976.
   sbox.simple_commit()
 
+def update_add_file_needs_lock(sbox):
+  "update adding a file with svn:needs-lock"
+
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('test\n', 'dir/file')
+  sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file')
+  sbox.simple_commit()
+
+  sbox.simple_update(revision=0)
+  sbox.simple_update(revision=1)
+  is_readonly(sbox.ospath('dir/file'))
+
+def update_edit_file_needs_lock(sbox):
+  "update editing a file with svn:needs-lock"
+
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('test\n', 'dir/file')
+  sbox.simple_commit()
+
+  sbox.simple_append('dir/file', 'edited\n', truncate=True)
+  sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file')
+  sbox.simple_commit()
+
+  sbox.simple_update(revision=1)
+  is_writable(sbox.ospath('dir/file'))
+  sbox.simple_update(revision=2)
+  is_readonly(sbox.ospath('dir/file'))
+
+def update_add_file_has_lock(sbox):
+  "update adding svn:needs-lock file with lock"
+
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('test\n', 'dir/file')
+  sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file')
+  sbox.simple_commit()
+
+  # Acquire the lock for a file.
+  svntest.actions.run_and_verify_svn(".*locked by user", [], 'lock',
+                                     '-m', '', sbox.ospath('dir/file'))
+
+  sbox.simple_update(revision=0)
+  sbox.simple_update(revision=1)
+  # We have a lock for that file, so it should be writable.
+  is_writable(sbox.ospath('dir/file'))
+
+def update_edit_file_has_lock(sbox):
+  "update editing svn:needs-lock file with lock"
+
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('test\n', 'dir/file')
+  sbox.simple_commit()
+
+  sbox.simple_append('dir/file', 'edited\n', truncate=True)
+  sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file')
+  sbox.simple_commit()
+
+  # Acquire the lock for a file.
+  svntest.actions.run_and_verify_svn(".*locked by user", [], 'lock',
+                                     '-m', '', sbox.ospath('dir/file'))
+
+  sbox.simple_update(revision=1)
+  # No svn:needs-lock on the file, so it should be writable.
+  is_writable(sbox.ospath('dir/file'))
+  sbox.simple_update(revision=2)
+  # We have a lock for that file, so it should be writable.
+  is_writable(sbox.ospath('dir/file'))
+
+def update_remove_needs_lock(sbox):
+  "update removing svn:needs-lock on a file"
+
+  sbox.build(empty=True)
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('test\n', 'dir/file')
+  sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file')
+  sbox.simple_commit()
+
+  sbox.simple_propdel('svn:needs-lock', 'dir/file')
+  sbox.simple_commit()
+
+  sbox.simple_update(revision=1)
+  is_readonly(sbox.ospath('dir/file'))
+  sbox.simple_update(revision=2)
+  is_writable(sbox.ospath('dir/file'))
+
 ########################################################################
 # Run the tests
 
@@ -2547,6 +2635,11 @@ test_list = [ None,
               delete_dir_with_lots_of_locked_files,
               delete_locks_on_depth_commit,
               replace_dir_with_lots_of_locked_files,
+              update_add_file_needs_lock,
+              update_edit_file_needs_lock,
+              update_add_file_has_lock,
+              update_edit_file_has_lock,
+              update_remove_needs_lock,
             ]
 
 if __name__ == '__main__':

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py Fri Jan 14 14:01:45 2022
@@ -2094,7 +2094,7 @@ def merge_sensitive_log_copied_path_inhe
   svntest.main.run_svn(None, 'move', old_gamma_path, new_gamma_path)
   sbox.simple_commit(message='Move file')
 
-  # 'svn log -g --stop-on-copy ^/A/C/gamma' hould return *only* r5
+  # 'svn log -g --stop-on-copy ^/A/C/gamma' should return *only* r5
   # Previously this test failed because the change in gamma's inherited
   # mergeinfo between r4 and r5, due to the move, was understood as a merge:
   #
@@ -2779,6 +2779,42 @@ def log_on_deleted_deep(sbox):
                                      '',
                                      '-q', '-c', '1-2')
 
+@XFail()
+@Issue(4711)
+def log_with_merge_history_and_search(sbox):
+  "log --use-merge-history --search"
+  
+  sbox.build()
+
+  # r2: create branch
+  sbox.simple_repo_copy('A', 'A2') # r2
+
+  # r3: mod in trunk
+  sbox.simple_append('A/mu', 'line 2')
+  sbox.simple_commit(message='r3: mod')
+  sbox.simple_update()
+
+  # r4: merge
+  svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', sbox.ospath('A2'))
+  sbox.simple_commit(message='r4: merge')
+  sbox.simple_update()
+
+  # Helper function
+  def count(haystack, needle):
+    """Return the number of times the string NEEDLE occurs in the string
+    HAYSTACK."""
+    return len(haystack.split(needle)) - 1
+
+  # Check the output is valid
+  # ### Since the test is currently XFail, we only smoke test the output.
+  # ### When fixing this test to PASS, extend this validation.
+  _, output, _ = svntest.main.run_svn(None, 'log', '--xml', '-g',
+                                      '--search', "this will have no matches",
+                                      sbox.ospath('A2'))
+
+  output = '\n'.join(output)
+  if count(output, "<logentry") != count(output, "</logentry"):
+    raise svntest.Failure("Apparently invalid XML in " + repr(output))
 
 ########################################################################
 # Run the tests
@@ -2830,6 +2866,7 @@ test_list = [ None,
               merge_sensitive_log_xml_reverse_merges,
               log_revision_move_copy,
               log_on_deleted_deep,
+              log_with_merge_history_and_search,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py Fri Jan 14 14:01:45 2022
@@ -97,7 +97,7 @@ def mergeinfo_and_skipped_paths(sbox):
   wc_disk, wc_status = set_up_branch(sbox, False, 3)
 
   # Create a restrictive authz where part of the merge source and part
-  # of the target are inaccesible.
+  # of the target are inaccessible.
   write_restrictive_svnserve_conf(sbox.repo_dir)
   write_authz_file(sbox, {"/"               : svntest.main.wc_author +"=rw",
                           # Make a directory in the merge source inaccessible.
@@ -342,7 +342,7 @@ def mergeinfo_and_skipped_paths(sbox):
   # this.
   #
   # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H.  r5 gets merged first
-  # but is a no-op, r8 get's merged next and is operative so the mergeinfo
+  # but is a no-op, r8 gets merged next and is operative so the mergeinfo
   # should be updated on the merge target to reflect both merges.
   expected_output = wc.State(A_COPY_2_H_path, {
     'omega' : Item(status='U '),

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py Fri Jan 14 14:01:45 2022
@@ -1508,7 +1508,7 @@ def reintegrate_with_subtree_mergeinfo(s
     # is all quite ugly as the intersection or multiple known issues
     # is likely to be.  However, given that none of this mergeinfo is
     # particularly harmful and that this test is *not* about issues #3669
-    # or #4309, we are tolerting it.
+    # or #4309, we are tolerating it.
     'D/gamma_moved' : Item(
       "Even newer content", props={SVN_PROP_MERGEINFO :
                                    '/A/D/gamma_moved:2-7,9-12\n'

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py Fri Jan 14 14:01:45 2022
@@ -3323,15 +3323,11 @@ def merge_conflict_markers_matching_eol(
 
   mu_path = sbox.ospath('A/mu')
 
-  # CRLF is a string that will match a CRLF sequence read from a text file.
-  # ### On Windows, we assume CRLF will be read as LF, so it's a poor test.
   if os.name == 'nt':
-    crlf = '\n'
+    native_nl = '\r\n'
   else:
-    crlf = '\r\n'
-
-  # Strict EOL style matching breaks Windows tests at least with Python 2
-  keep_eol_style = not svntest.main.is_os_windows()
+    native_nl = '\n'
+  crlf = '\r\n'
 
   # Checkout a second working copy
   wc_backup = sbox.add_wc_path('backup')
@@ -3349,8 +3345,8 @@ def merge_conflict_markers_matching_eol(
   path_backup = os.path.join(wc_backup, 'A', 'mu')
 
   # do the test for each eol-style
-  for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
-                          [crlf, '\015', '\n', '\012']):
+  for eol, eolchar in zip(['CRLF', 'CR','native', 'LF'],
+                          [crlf, '\015', native_nl, '\012']):
     # rewrite file mu and set the eol-style property.
     svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb')
     svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
@@ -3375,8 +3371,8 @@ def merge_conflict_markers_matching_eol(
     svntest.main.run_svn(None, 'update', wc_backup)
 
     # Make a local mod to mu
-    svntest.main.file_append(mu_path,
-                             'Original appended text for mu' + eolchar)
+    svntest.main.file_append_binary(mu_path,
+                                    'Original appended text for mu' + eolchar)
 
     # Commit the original change and note the 'theirs' revision number
     svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir)
@@ -3384,8 +3380,9 @@ def merge_conflict_markers_matching_eol(
     theirs_rev = cur_rev
 
     # Make a local mod to mu, will conflict with the previous change
-    svntest.main.file_append(path_backup,
-                             'Conflicting appended text for mu' + eolchar)
+    svntest.main.file_append_binary(path_backup,
+                                    'Conflicting appended text for mu'
+                                    + eolchar)
 
     # Create expected output tree for an update of the wc_backup.
     expected_backup_output = svntest.wc.State(wc_backup, {
@@ -3445,7 +3442,7 @@ def merge_conflict_markers_matching_eol(
                                           expected_backup_disk,
                                           expected_backup_status,
                                           expected_backup_skip,
-                                          keep_eol_style=keep_eol_style)
+                                          keep_eol_style=True)
 
     # cleanup for next run
     svntest.main.run_svn(None, 'revert', '-R', wc_backup)
@@ -3468,15 +3465,7 @@ def merge_eolstyle_handling(sbox):
 
   mu_path = sbox.ospath('A/mu')
 
-  # CRLF is a string that will match a CRLF sequence read from a text file.
-  # ### On Windows, we assume CRLF will be read as LF, so it's a poor test.
-  if os.name == 'nt':
-    crlf = '\n'
-  else:
-    crlf = '\r\n'
-
-  # Strict EOL style matching breaks Windows tests at least with Python 2
-  keep_eol_style = not svntest.main.is_os_windows()
+  crlf = '\r\n'
 
   # Checkout a second working copy
   wc_backup = sbox.add_wc_path('backup')
@@ -3518,7 +3507,7 @@ def merge_eolstyle_handling(sbox):
                                         expected_backup_disk,
                                         expected_backup_status,
                                         expected_backup_skip,
-                                        keep_eol_style=keep_eol_style)
+                                        keep_eol_style=True)
 
   # Test 2: now change the eol-style property to another value and commit,
   # merge this revision in the still changed mu in the second working copy;
@@ -3549,7 +3538,7 @@ def merge_eolstyle_handling(sbox):
                                         expected_backup_disk,
                                         expected_backup_status,
                                         expected_backup_skip,
-                                        keep_eol_style=keep_eol_style)
+                                        keep_eol_style=True)
 
   # Test 3: now delete the eol-style property and commit, merge this revision
   # in the still changed mu in the second working copy; there should be no
@@ -3578,7 +3567,7 @@ def merge_eolstyle_handling(sbox):
                                         expected_backup_disk,
                                         expected_backup_status,
                                         expected_backup_skip,
-                                        keep_eol_style=keep_eol_style)
+                                        keep_eol_style=True)
 
 #----------------------------------------------------------------------
 def create_deep_trees(wc_dir):
@@ -4794,7 +4783,7 @@ def mergeinfo_inheritance_and_discontinu
   # Merge r2:6 into A_COPY/D
   #
   # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY
-  # combine it with the discontinous merges performed directly on
+  # combine it with the discontinuous merges performed directly on
   # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'.
   expected_output = wc.State(D_COPY_path, {
     'H/psi'   : Item(status='U '),
@@ -4966,11 +4955,11 @@ def merge_to_switched_path(sbox):
   A_COPY_D_G_rho_path = sbox.ospath('A_COPY/D/G/rho')
 
   expected = svntest.verify.UnorderedOutput(
-         ["A    " + os.path.join(G_COPY_path, "pi") + "\n",
-          "A    " + os.path.join(G_COPY_path, "rho") + "\n",
-          "A    " + os.path.join(G_COPY_path, "tau") + "\n",
-          "Checked out revision 6.\n",
-          "A         " + G_COPY_path + "\n"])
+         ["A         " + G_COPY_path + "\n",
+          "A         " + os.path.join(G_COPY_path, "pi") + "\n",
+          "A         " + os.path.join(G_COPY_path, "rho") + "\n",
+          "A         " + os.path.join(G_COPY_path, "tau") + "\n",
+          ])
 
   # r7 - Copy A/D/G to A/D/G_COPY and commit.
   svntest.actions.run_and_verify_svn(expected, [], 'copy',
@@ -12848,6 +12837,39 @@ def natural_history_filtering(sbox):
   #      the revisions on 'trunk' which occurred after 'branch2' was copied as
   #      these are not part of 'branch2's natural history.
 
+  def path_join(head, tail):
+    if not head: return tail
+    if not tail: return head
+    return head + '/' + tail
+
+  def greek_file_item(path):
+    if path[-1:].islower():
+      basename = re.sub('.*/', '', path)
+      return Item("This is the file '" + basename + "'.\n")
+    return Item()
+
+  A_paths = [
+    "",
+    "B",
+    "B/lambda",
+    "B/E",
+    "B/E/alpha",
+    "B/E/beta",
+    "B/F",
+    "mu",
+    "C",
+    "D",
+    "D/gamma",
+    "D/G",
+    "D/G/pi",
+    "D/G/rho",
+    "D/G/tau",
+    "D/H",
+    "D/H/chi",
+    "D/H/omega",
+    "D/H/psi",
+    ]
+
   sbox.build()
   wc_dir = sbox.wc_dir
 
@@ -12861,68 +12883,16 @@ def natural_history_filtering(sbox):
 
   # r7: Make a second 'branch': Copy A to A_COPY_2
   expected = svntest.verify.UnorderedOutput(
-    ["A    " + os.path.join(A_COPY_2_path, "B") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "B", "lambda") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "B", "E") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "B", "E", "alpha") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "B", "E", "beta") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "B", "F") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "mu") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "C") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "gamma") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "G") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "G", "pi") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "G", "rho") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "G", "tau") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "H") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "H", "chi") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "H", "omega") + "\n",
-     "A    " + os.path.join(A_COPY_2_path, "D", "H", "psi") + "\n",
-     "Checked out revision 6.\n",
-     "A         " + A_COPY_2_path + "\n"])
-  wc_status.add({
-    "A_COPY_2" + "/B"         : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/B/lambda"  : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/B/E"       : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/B/E/alpha" : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/B/E/beta"  : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/B/F"       : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/mu"        : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/C"         : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D"         : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/gamma"   : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/G"       : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/G/pi"    : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/G/rho"   : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/G/tau"   : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/H"       : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/H/chi"   : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/H/omega" : Item(status='  ', wc_rev=7),
-    "A_COPY_2" + "/D/H/psi"   : Item(status='  ', wc_rev=7),
-    "A_COPY_2"                : Item(status='  ', wc_rev=7),
-    })
-  wc_disk.add({
-    "A_COPY_2"                : Item(),
-    "A_COPY_2" + '/B'         : Item(),
-    "A_COPY_2" + '/B/lambda'  : Item("This is the file 'lambda'.\n"),
-    "A_COPY_2" + '/B/E'       : Item(),
-    "A_COPY_2" + '/B/E/alpha' : Item("This is the file 'alpha'.\n"),
-    "A_COPY_2" + '/B/E/beta'  : Item("New content"),
-    "A_COPY_2" + '/B/F'       : Item(),
-    "A_COPY_2" + '/mu'        : Item("This is the file 'mu'.\n"),
-    "A_COPY_2" + '/C'         : Item(),
-    "A_COPY_2" + '/D'         : Item(),
-    "A_COPY_2" + '/D/gamma'   : Item("This is the file 'gamma'.\n"),
-    "A_COPY_2" + '/D/G'       : Item(),
-    "A_COPY_2" + '/D/G/pi'    : Item("This is the file 'pi'.\n"),
-    "A_COPY_2" + '/D/G/rho'   : Item("New content"),
-    "A_COPY_2" + '/D/G/tau'   : Item("This is the file 'tau'.\n"),
-    "A_COPY_2" + '/D/H'       : Item(),
-    "A_COPY_2" + '/D/H/chi'   : Item("New content"),
-    "A_COPY_2" + '/D/H/omega' : Item("This is the file 'omega'.\n"),
-    "A_COPY_2" + '/D/H/psi'   : Item("New content"),
-    })
+    [ "A         " + sbox.ospath(path_join("A_COPY_2", p)) + "\n"
+      for p in A_paths ])
+  wc_status.add(
+    { path_join("A_COPY_2", p) : Item(status='  ', wc_rev=7)
+      for p in A_paths })
+  wc_disk.add(
+    { path_join("A_COPY_2", p) :
+        Item("New content") if p in ['B/E/beta', 'D/G/rho', 'D/H/chi', 'D/H/psi']
+                            else greek_file_item(p)
+      for p in A_paths })
   svntest.actions.run_and_verify_svn(expected, [], 'copy',
                                      sbox.repo_url + "/A",
                                      A_COPY_2_path)
@@ -13380,7 +13350,7 @@ def no_self_referential_filtering_on_add
 def merge_range_prior_to_rename_source_existence(sbox):
   "merge prior to rename src existence still dels src"
 
-  # Replicate a merge bug found while synching up a feature branch on the
+  # Replicate a merge bug found while syncing up a feature branch on the
   # Subversion repository with trunk.  See r874121 of
   # http://svn.apache.org/repos/asf/subversion/branches/ignore-mergeinfo, in which
   # a move was merged to the target, but the delete half of the move
@@ -18530,6 +18500,276 @@ def merge_dir_delete_force(sbox):
                                      'merge', '-c2', '^/', sbox.wc_dir,
                                      '--ignore-ancestry', '--force')
 
+# Issue #4859: Merge removing a folder with non-inheritable mergeinfo ->
+# E155023: can't set properties: invalid status for updating properties
+@Issue(4859)
+def merge_deleted_folder_with_mergeinfo(sbox):
+  "merge deleted folder with mergeinfo"
+
+  sbox.build()
+
+  was_cwd = os.getcwd()
+  os.chdir(sbox.wc_dir)
+  sbox.wc_dir = ''
+
+  # Some non-inheritable mergeinfo
+  sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D')
+  sbox.simple_commit() # r2
+
+  # Branching
+  sbox.simple_repo_copy('A', 'branch_A')  # r3
+  sbox.simple_update()
+
+  # On branch, remove a folder that has non-inheritable mergeinfo
+  sbox.simple_rm('branch_A/D')
+  sbox.simple_commit() # r4
+
+  sbox.simple_update()
+
+  # A merge that removes that folder
+  # (merge revision 4 only from 'branch_A' to 'A')
+  expected_output = wc.State(sbox.ospath(''), {
+    'A/D'    : Item(status='D '),
+    })
+  expected_mergeinfo_output = wc.State(sbox.ospath(''), {
+    'A'      : Item(status=' U'),
+    })
+  expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 4).subtree('A')
+  expected_status.add({ '': Item(status=' M', wc_rev=4) })
+  expected_status.tweak_some(
+    lambda path, item: [True] if path.split('/')[0] == 'D' else [],
+    status='D ')
+  svntest.actions.run_and_verify_merge(sbox.ospath('A'), 3, 4,
+                                       '^/branch_A', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       None,
+                                       None,
+                                       expected_status,
+                                       wc.State('', {}),
+                                       [],
+                                       check_props=False,
+                                       dry_run=False  # as dry run is broken
+                                       )
+
+  os.chdir(was_cwd)
+
+# Issue #4859: Merge removing a folder with non-inheritable mergeinfo ->
+# E155023: can't set properties: invalid status for updating properties
+#
+# In this test we split the merge into two separate operable parts, a
+# delete followed later by an add, to check it will set the mergeinfo on the
+# subtree paths if the deleted folder is later replaced within the same
+# overall merge.
+@Issue(4859)
+def merge_deleted_folder_with_mergeinfo_2(sbox):
+  "merge deleted folder with mergeinfo 2"
+
+  sbox.build()
+
+  was_cwd = os.getcwd()
+  os.chdir(sbox.wc_dir)
+  sbox.wc_dir = ''
+
+  # Some non-inheritable mergeinfo
+  sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D')
+  sbox.simple_commit() # r2
+
+  # Branching
+  sbox.simple_repo_copy('A', 'branch_A')  # r3
+  sbox.simple_update()
+
+  # On branch, remove a folder that has non-inheritable mergeinfo
+  sbox.simple_rm('branch_A/D')
+  sbox.simple_commit() # r4
+
+  # A commit that we don't want to merge from the branch, to split the merge
+  # into two separate operable parts.
+  sbox.simple_mkdir('branch_A/IgnoreThis')
+  sbox.simple_commit() # r5
+
+  # On branch, replace the deleted folder with a new one, with mergeinfo,
+  # to check we don't omit setting mergeinfo on this.
+  sbox.simple_mkdir('branch_A/D')
+  sbox.simple_propset('svn:mergeinfo', '/branch_B/C:1*', 'branch_A/D')
+  sbox.simple_mkdir('branch_A/D/G', 'branch_A/D/G2')
+  sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G:1*', 'branch_A/D/G')
+  sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G2:1*', 'branch_A/D/G2')
+  sbox.simple_commit() # r6
+
+  sbox.simple_propset('svn:mergeinfo', '/branch_A:5', 'A')
+  sbox.simple_commit() # r7
+
+  sbox.simple_update()
+
+  # A merge that removes that folder
+  expected_output = wc.State(sbox.ospath(''), {
+    'A/D'    : Item(status='A ', prev_status='D '),
+    'A/D/G'  : Item(status='A '),
+    'A/D/G2' : Item(status='A '),
+    })
+  # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2.
+  expected_mergeinfo_output = wc.State(sbox.ospath(''), {
+    'A'      : Item(status=' U'),
+    'A/D'    : Item(status=' G'),
+    'A/D/G'  : Item(status=' G'),
+    'A/D/G2' : Item(status=' G'),
+    })
+  expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 7).subtree('A')
+  expected_status.tweak_some(
+    lambda path, item: [True] if path.split('/')[0] == 'D' else [],
+    status='D ')
+  expected_status.add({
+    ''     : Item(status=' M', wc_rev=7),
+    'D'    : Item(status='RM', copied='+', wc_rev='-'),
+    'D/G'  : Item(status=' M', copied='+', wc_rev='-'),
+    'D/G2' : Item(status=' M', copied='+', wc_rev='-'),
+    })
+  svntest.actions.run_and_verify_merge(sbox.ospath('A'), None, None,
+                                       '^/branch_A', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       None,
+                                       None,
+                                       expected_status,
+                                       wc.State('', {}),
+                                       [],
+                                       check_props=False,
+                                       dry_run=False  # as dry run is broken
+                                       )
+
+  # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2.
+
+  # NOTE: When writing out multi-line prop values in svn:* props, the
+  # client converts to local encoding and local eol style.
+  # Therefore, the expected output must contain the right kind of eoln
+  # strings. That's why we use os.linesep in the tests below, not just
+  # plain '\n'.
+
+  expected_mergeinfo = [
+    ('A',       ['/branch_A:3-7']),
+    ('A/D',     ['/branch_A/D:5-7'+os.linesep, '/branch_B/C:1*']),
+    ('A/D/G',   ['/branch_A/D/G:5-7'+os.linesep, '/branch_B/C/G:1*']),
+    ('A/D/G2',  ['/branch_A/D/G2:5-7'+os.linesep, '/branch_B/C/G2:1*']),
+    ]
+  for path, mergeinfo in expected_mergeinfo:
+    svntest.actions.check_prop('svn:mergeinfo', sbox.ospath(path),
+                               [m.encode() for m in mergeinfo])
+
+  os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+# Test that mismatched source repository root URLs in a two-URL merge
+# throws an error E170000 SVN_ERR_RA_ILLEGAL_URL.
+#
+# (Since issue 4874 (Subversion 1.15) that error is also wrapped in
+# E195012 SVN_ERR_CLIENT_UNRELATED_RESOURCES for consistency with
+# the source-target mismatch errors.)
+#
+# For mismatched URLs we use two repositories with the same UUID.
+@Issue(4874)
+def merge_error_if_source_urls_differ(sbox):
+  "merge error if source urls differ"
+
+  sbox.build()
+  expected_disk, expected_status = set_up_branch(sbox)
+  wc_dir = sbox.wc_dir
+  repo_dir = sbox.repo_dir
+
+  # Create a second repository with the same content, same UUID
+  other_repo_dir, other_repo_url = sbox.add_repo_path("other")
+  other_wc_dir = sbox.add_wc_path("other")
+  svntest.main.copy_repos(repo_dir, other_repo_dir, 6, ignore_uuid=False)
+
+  G_COPY_path = sbox.ospath('A_COPY/D/G')
+  svntest.actions.run_and_verify_svn2(None, '.*: E170000: .*', 1,
+                                      'merge',
+                                      sbox.repo_url + '/A/D/G@3',
+                                      other_repo_url + '/A/D/G@4',
+                                      G_COPY_path)
+
+#----------------------------------------------------------------------
+# Test that a merge with mismatched source and target repository root URLs
+# but identical repository UUIDs throws a warning error, for two-URL and
+# pegged merges.
+#
+# Issue #4874 makes this a warning in Subversion 1.15 and an error in 1.16.
+# Previously it was treated as a foreign repository merge.
+#
+# For mismatched URLs we use two repositories with the same UUID.
+@Issue(4874)
+def merge_error_if_ambiguous_foreign_merge(sbox):
+  "merge error if ambiguous foreign merge"
+
+  sbox.build()
+  expected_disk, expected_status = set_up_branch(sbox)
+  wc_dir = sbox.wc_dir
+  repo_dir = sbox.repo_dir
+
+  # Create a second repository with the same content, same UUID
+  other_repo_dir, other_repo_url = sbox.add_repo_path("other")
+  other_wc_dir = sbox.add_wc_path("other")
+  svntest.main.copy_repos(repo_dir, other_repo_dir, 6, ignore_uuid=False)
+
+  # With Issue #4874 implemented, the attempted merges should error out with
+  # SVN_ERR_CLIENT_UNRELATED_RESOURCES, because of mismatched source and
+  # target URLs.
+
+  # expect warning or error (E195012 SVN_ERR_CLIENT_UNRELATED_RESOURCES)?
+  expected_stdout = None if svntest.main.SVN_VER_MINOR < 16 else []
+  expected_stderr = '.*: E195012: .*' if svntest.main.SVN_VER_MINOR >= 15 else []
+  expected_exit = 0 if svntest.main.SVN_VER_MINOR < 16 else 1
+
+  # two-URL merge
+  svntest.actions.run_and_verify_svn2(expected_stdout, expected_stderr,
+                                      expected_exit,
+                                      'merge',
+                                      other_repo_url + '/A/D/G@3',
+                                      other_repo_url + '/A/D/G@4',
+                                      sbox.ospath('A_COPY/D/G'))
+  svntest.main.run_svn(False, 'revert', '-qR', sbox.wc_dir)
+  # pegged merge
+  svntest.actions.run_and_verify_svn2(expected_stdout, expected_stderr,
+                                      expected_exit,
+                                      'merge', '-c4',
+                                      other_repo_url + '/A/D/G',
+                                      sbox.ospath('A_COPY/D/G'))
+  svntest.main.run_svn(False, 'revert', '-qR', sbox.wc_dir)
+
+#----------------------------------------------------------------------
+# Test that a merge with mismatched source and target repository root URLs
+# throws an error, for automatic and reintegrate merges.
+#
+# This behaviour is unchanged by issue #4874, just reinforced and with more
+# informative error messages.
+#
+# For mismatched URLs we use two repositories with the same UUID.
+@Issue(4874)
+def merge_error_if_source_target_url_mismatch(sbox):
+  "merge error if source target url mismatch"
+
+  sbox.build()
+  expected_disk, expected_status = set_up_branch(sbox)
+  wc_dir = sbox.wc_dir
+  repo_dir = sbox.repo_dir
+
+  # Create a second repository with the same content, same UUID
+  other_repo_dir, other_repo_url = sbox.add_repo_path("other")
+  other_wc_dir = sbox.add_wc_path("other")
+  svntest.main.copy_repos(repo_dir, other_repo_dir, 6, ignore_uuid=False)
+
+  expected_stderr = '.*: E195012: .*'  # SVN_ERR_CLIENT_UNRELATED_RESOURCES
+  # automatic merge
+  svntest.actions.run_and_verify_svn2([], expected_stderr, 1,
+                                      'merge',
+                                      other_repo_url + '/A/D/G',
+                                      sbox.ospath('A_COPY/D/G'))
+  # reintegrate merge
+  svntest.actions.run_and_verify_svn2([], expected_stderr, 1,
+                                      'merge', '--reintegrate',
+                                      other_repo_url + '/A/D/G',
+                                      sbox.ospath('A_COPY/D/G'))
+
 ########################################################################
 # Run the tests
 
@@ -18677,6 +18917,11 @@ test_list = [ None,
               merge_to_empty_target_merge_to_infinite_target,
               conflict_naming,
               merge_dir_delete_force,
+              merge_deleted_folder_with_mergeinfo,
+              merge_deleted_folder_with_mergeinfo_2,
+              merge_error_if_source_urls_differ,
+              merge_error_if_ambiguous_foreign_merge,
+              merge_error_if_source_target_url_mismatch,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py Fri Jan 14 14:01:45 2022
@@ -2364,6 +2364,233 @@ def spurios_tree_conflict_with_added_fil
                                        [], False, True, '--reintegrate',
                                        sbox.ospath('A_branch'))
 
+def merge_local_missing_node_kind_none(sbox):
+  "crash in resolver triggered by none-type node"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  sbox.simple_mkdir('branches')
+  sbox.simple_commit() # r2
+  sbox.simple_update()
+
+  # Create a feature branch of A
+  sbox.simple_copy('A', 'branches/feature1')
+  sbox.simple_commit() #r3
+  sbox.simple_update()
+
+  # On the branch, move file alpha to another directory
+  sbox.simple_move('branches/feature1/B/E/alpha',
+                   'branches/feature1/D/H/alpha-from-B-E')
+  sbox.simple_commit() # r4
+  sbox.simple_update()
+
+  # Cherry-pick the delete-half of the above move into A (the trunk)
+  expected_output = svntest.wc.State(sbox.ospath('A/B/E'), {
+    'alpha'          : Item(status='D '),
+  })
+  expected_mergeinfo_output = wc.State(sbox.ospath('A/B/E'), {
+    ''                  : Item(status=' U')
+  })
+  expected_elision_output = wc.State(wc_dir, {
+  })
+  expected_disk = wc.State('', {
+    'beta'              : Item(contents="This is the file 'beta'.\n"),
+    '.'                 : Item(props={u'svn:mergeinfo':
+                                      u'/branches/feature1/B/E:4'}),
+  })
+  expected_status = wc.State(sbox.ospath('A/B/E'), {
+    ''                  : Item(status=' M', wc_rev='4'),
+    'alpha'             : Item(status='D ', wc_rev='4'),
+    'beta'              : Item(status='  ', wc_rev='4'),
+  })
+  expected_skip = wc.State('', {
+  })
+  svntest.actions.run_and_verify_merge(sbox.ospath('A/B/E'), 3, 4,
+                                       sbox.repo_url + '/branches/feature1/B/E',
+                                       None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       check_props=True)
+  sbox.simple_commit() # r5
+  sbox.simple_update()
+
+  # Create a new file on the feature branch
+  sbox.simple_add_text("This is the file 'pi'\n", 'branches/feature1/B/E/pi')
+  sbox.simple_commit() #r6
+  sbox.simple_update()
+
+  # Create a second branch based on the feature branch.
+  sbox.simple_copy('branches/feature1', 'branches/feature2')
+  sbox.simple_commit() #r7
+  sbox.simple_update()
+
+  # Create a new file kappa on this second branch
+  sbox.simple_add_text("This is the file 'kappa'\n",
+                       'branches/feature2/B/E/kappa')
+  sbox.simple_commit() #r8
+  sbox.simple_update()
+
+  # An unrelated additional change on the second branch.
+  sbox.simple_append('branches/feature2/B/E/beta',
+                     "This is a change to file 'beta'.\n")
+  sbox.simple_commit() #r9
+  sbox.simple_update()
+
+  # Merge the second branch back into the feature branch
+  expected_output = svntest.wc.State(sbox.ospath('branches/feature1'), {
+    'B/E/kappa'         : Item(status='A '),
+    'B/E/beta'          : Item(status='U '),
+    })
+  expected_mergeinfo_output = wc.State(sbox.ospath('branches/feature1'), {
+    '.'                 : Item(status=' U'),
+  })
+  expected_elision_output = wc.State(wc_dir, {
+  })
+  expected_disk = wc.State('', {
+    'C'                 : Item(),
+    'B/E/kappa'         : Item(contents="This is the file 'kappa'\n"),
+    'B/E/pi'            : Item(contents="This is the file 'pi'\n"),
+    'B/E/beta'          : Item(contents="This is the file 'beta'.\n" +
+                                        "This is a change to file 'beta'.\n"),
+    'B/lambda'          : Item(contents="This is the file 'lambda'.\n"),
+    'B/F'               : Item(),
+    'D/H/omega'         : Item(contents="This is the file 'omega'.\n"),
+    'D/H/alpha-from-B-E': Item(contents="This is the file 'alpha'.\n"),
+    'D/H/psi'           : Item(contents="This is the file 'psi'.\n"),
+    'D/H/chi'           : Item(contents="This is the file 'chi'.\n"),
+    'D/G/pi'            : Item(contents="This is the file 'pi'.\n"),
+    'D/G/rho'           : Item(contents="This is the file 'rho'.\n"),
+    'D/G/tau'           : Item(contents="This is the file 'tau'.\n"),
+    'D/gamma'           : Item(contents="This is the file 'gamma'.\n"),
+    'mu'                : Item(contents="This is the file 'mu'.\n"),
+    '.'                 : Item(props={u'svn:mergeinfo':
+                                      u'/branches/feature2:7-9'}),
+  })
+  expected_status = wc.State(sbox.ospath('branches/feature1'), {
+    ''                  : Item(status=' M', wc_rev='9'),
+    'mu'                : Item(status='  ', wc_rev='9'),
+    'D'                 : Item(status='  ', wc_rev='9'),
+    'D/H'               : Item(status='  ', wc_rev='9'),
+    'D/H/alpha-from-B-E': Item(status='  ', wc_rev='9'),
+    'D/H/psi'           : Item(status='  ', wc_rev='9'),
+    'D/H/chi'           : Item(status='  ', wc_rev='9'),
+    'D/H/omega'         : Item(status='  ', wc_rev='9'),
+    'D/G'               : Item(status='  ', wc_rev='9'),
+    'D/G/pi'            : Item(status='  ', wc_rev='9'),
+    'D/G/tau'           : Item(status='  ', wc_rev='9'),
+    'D/G/rho'           : Item(status='  ', wc_rev='9'),
+    'D/gamma'           : Item(status='  ', wc_rev='9'),
+    'B'                 : Item(status='  ', wc_rev='9'),
+    'B/E'               : Item(status='  ', wc_rev='9'),
+    'B/E/beta'          : Item(status='M ', wc_rev='9'),
+    'B/E/kappa'         : Item(status='A ', copied='+', wc_rev='-'),
+    'B/E/pi'            : Item(status='  ', wc_rev='9'),
+    'B/lambda'          : Item(status='  ', wc_rev='9'),
+    'B/F'               : Item(status='  ', wc_rev='9'),
+    'C'                 : Item(status='  ', wc_rev='9'),
+  })
+  expected_skip = wc.State('', {
+  })
+  svntest.actions.run_and_verify_merge(sbox.ospath('branches/feature1'),
+                                       None, None,
+                                       sbox.repo_url + '/branches/feature2',
+                                       None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       check_props=True)
+  sbox.simple_commit() # r10
+  sbox.simple_update()
+
+  # On the feature branch, rename the file kappa
+  sbox.simple_move('branches/feature1/B/E/kappa',
+                   'branches/feature1/B/E/kappanew')
+  sbox.simple_commit() # r11
+  sbox.simple_update()
+
+  # On the feature branch, move file kappanew to another directory
+  sbox.simple_move('branches/feature1/B/E/kappanew',
+                   'branches/feature1/D/H/kappanew')
+  sbox.simple_commit() # r12
+  sbox.simple_update()
+
+  # Cherry-pick r12 into A (the trunk)
+  # This triggers an assertion failure in Subversion 1.14.1 because of
+  # a node type lookup which uses the wrong token map:
+  # W: subversion/libsvn_subr/token.c:40: (apr_err=SVN_ERR_ASSERTION_FAIL)
+  # svn: E235000: In file 'subversion/libsvn_subr/token.c' line 40: internal malfunction
+  expected_output = svntest.wc.State(sbox.ospath('A'), {
+    'B/E/kappanew'      : Item(status='  ', treeconflict='C'),
+    'D/H/kappanew'      : Item(status='A '),
+  })
+  expected_mergeinfo_output = wc.State(sbox.ospath('A'), {
+    ''                 : Item(status=' U'),
+    'B/E'              : Item(status=' U'),
+  })
+  expected_elision_output = wc.State(wc_dir, {
+  })
+  expected_disk = wc.State('', {
+    'C'                 : Item(),
+    'B/E'               : Item(props={u'svn:mergeinfo':
+                                      u'/branches/feature1/B/E:4,12'}),
+    'B/E/beta'          : Item(contents="This is the file 'beta'.\n"),
+    'B/lambda'          : Item(contents="This is the file 'lambda'.\n"),
+    'B/F'               : Item(),
+    'D/H/omega'         : Item(contents="This is the file 'omega'.\n"),
+    'D/H/kappanew'      : Item(contents="This is the file 'kappa'\n"),
+    'D/H/psi'           : Item(contents="This is the file 'psi'.\n"),
+    'D/H/chi'           : Item(contents="This is the file 'chi'.\n"),
+    'D/G/pi'            : Item(contents="This is the file 'pi'.\n"),
+    'D/G/rho'           : Item(contents="This is the file 'rho'.\n"),
+    'D/G/tau'           : Item(contents="This is the file 'tau'.\n"),
+    'D/gamma'           : Item(contents="This is the file 'gamma'.\n"),
+    'mu'                : Item(contents="This is the file 'mu'.\n"),
+    '.'                 : Item(props={u'svn:mergeinfo':
+                                      u'/branches/feature1:12'}),
+  })
+  expected_status = wc.State(sbox.ospath('A'), {
+    ''                  : Item(status=' M', wc_rev='12'),
+    'D'                 : Item(status='  ', wc_rev='12'),
+    'D/H'               : Item(status='  ', wc_rev='12'),
+    'D/H/chi'           : Item(status='  ', wc_rev='12'),
+    'D/H/psi'           : Item(status='  ', wc_rev='12'),
+    'D/H/kappanew'      : Item(status='A ', copied='+', wc_rev='-'),
+    'D/H/omega'         : Item(status='  ', wc_rev='12'),
+    'D/G'               : Item(status='  ', wc_rev='12'),
+    'D/G/pi'            : Item(status='  ', wc_rev='12'),
+    'D/G/tau'           : Item(status='  ', wc_rev='12'),
+    'D/G/rho'           : Item(status='  ', wc_rev='12'),
+    'D/gamma'           : Item(status='  ', wc_rev='12'),
+    'B'                 : Item(status='  ', wc_rev='12'),
+    'B/E'               : Item(status=' M', wc_rev='12'),
+    'B/E/beta'          : Item(status='  ', wc_rev='12'),
+    'B/E/kappanew'      : Item(status='! ', treeconflict='C'),
+    'B/F'               : Item(status='  ', wc_rev='12'),
+    'B/lambda'          : Item(status='  ', wc_rev='12'),
+    'mu'                : Item(status='  ', wc_rev='12'),
+    'C'                 : Item(status='  ', wc_rev='12'),
+  })
+  expected_skip = wc.State('', {
+  })
+  svntest.actions.run_and_verify_merge(sbox.ospath('A'), 11, 12,
+                                       sbox.repo_url + '/branches/feature1',
+                                       None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       check_props=True)
+
 
 ########################################################################
 # Run the tests
@@ -2399,6 +2626,7 @@ test_list = [ None,
               merge_obstruction_recording,
               added_revision_recording_in_tree_conflict,
               spurios_tree_conflict_with_added_file,
+              merge_local_missing_node_kind_none,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py Fri Jan 14 14:01:45 2022
@@ -25,7 +25,7 @@
 ######################################################################
 
 # General modules
-import os, re, logging
+import os, re, logging, shutil
 
 logger = logging.getLogger()
 
@@ -167,7 +167,7 @@ def anon(sbox):
 
   write_authz_file(sbox)
 
-  anon_tests = ( 
+  anon_tests = (
                  { 'path': '', 'status': 301 },
                  { 'path': '/', 'status': 200 },
                  { 'path': '/repos', 'status': 301 },
@@ -1043,6 +1043,73 @@ def authn_sallrall(sbox):
   verify_gets(test_area_url, sallrall_tests)
 
 
+@SkipUnless(svntest.main.is_ra_type_dav)
+def repos_relative_access_file(sbox):
+  "repos-relative access file"
+
+  sbox.build()
+
+  test_area_url = sbox.repo_url.replace('/svn-test-work/repositories/',
+                                        '/authz-test-work/in-repos-authz/')
+
+  svntest.main.write_authz_file(sbox, {"/": "", "/A": "%s = rw" % user1})
+  shutil.move(sbox.authz_file, os.path.join(sbox.wc_dir, 'authz'))
+  sbox.simple_add('authz')
+  svntest.actions.run_and_verify_svn(None, [], 'relocate',
+      sbox.file_protocol_repo_url(), sbox.wc_dir)
+  sbox.simple_commit(message="adding in-repository authz rules file")
+
+  in_repos_authz_tests = (
+                 { 'path': '', 'status': 401, },
+                 { 'path': '/authz', 'status': 401, },
+                 { 'path': '/authz', 'user' : user1, 'pw' : user1_pass,
+                   'status': 403, },
+                 { 'path': '/A', 'user' : user1, 'pw' : user1_pass,
+                   'status': 301, },
+                 { 'path': '/A/', 'user' : user1, 'pw' : user1_pass,
+                   'status': 200, },
+  )
+
+  verify_gets(test_area_url, in_repos_authz_tests)
+
+# test for the bug also known as CVE-2020-17525
+@SkipUnless(svntest.main.is_ra_type_dav)
+def nonexistent_repos_relative_access_file(sbox):
+  "repos-relative access file with bad repository URL"
+
+  sbox.build()
+
+  test_area_url = sbox.repo_url.replace('/svn-test-work/repositories/',
+                                        '/authz-test-work/in-repos-authz/')
+
+  # Construct a bad test-area URL to see what happens if we attempt to access
+  # a repository in a subdirectory which does not exist in SVNParentPath.
+  # This used to crash the server with a NULL-pointer dereference upon
+  # unauthenticated access.
+  test_area_url += '-this/does/not/exist'
+
+  svntest.main.write_authz_file(sbox, {"/": "", "/A": "%s = rw" % user1})
+  shutil.move(sbox.authz_file, os.path.join(sbox.wc_dir, 'authz'))
+  sbox.simple_add('authz')
+  svntest.actions.run_and_verify_svn(None, [], 'relocate',
+      sbox.file_protocol_repo_url(), sbox.wc_dir)
+  sbox.simple_commit(message="adding in-repository authz rules file")
+
+  # access is denied across the board since this repository does not exist
+  in_repos_authz_tests = (
+                 { 'path': '', 'status': 401, },
+                 { 'path': '/authz', 'status': 401, },
+                 { 'path': '/authz', 'user' : user1, 'pw' : user1_pass,
+                   'status': 403, },
+                 { 'path': '/A', 'user' : user1, 'pw' : user1_pass,
+                   'status': 403, },
+                 { 'path': '/A/', 'user' : user1, 'pw' : user1_pass,
+                   'status': 403, },
+  )
+
+  verify_gets(test_area_url, in_repos_authz_tests)
+
+
 ########################################################################
 # Run the tests
 
@@ -1058,6 +1125,8 @@ test_list = [ None,
               authn_group,
               authn_sallrany,
               authn_sallrall,
+              repos_relative_access_file,
+              nonexistent_repos_relative_access_file,
              ]
 serial_only = True
 

Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py (original)
+++ subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py Fri Jan 14 14:01:45 2022
@@ -102,7 +102,7 @@ def compare_xml_elem(a, b):
   # iteration.
   def sortcmp(x, y):
     return compare_xml_elem(x, y)[0]
- 
+
   a_children = sorted(list(a), key=functools.cmp_to_key(sortcmp))
   b_children = sorted(list(b), key=functools.cmp_to_key(sortcmp))
 
@@ -640,6 +640,53 @@ def propfind_propname(sbox):
   actual_response = r.read()
   verify_xml_response(expected_response, actual_response)
 
+@SkipUnless(svntest.main.is_ra_type_dav)
+def last_modified_header(sbox):
+  "verify 'Last-Modified' header on 'external' GETs"
+
+  sbox.build(create_wc=False, read_only=True)
+
+  headers = {
+    'Authorization': 'Basic ' + base64.b64encode(b'jconstant:rayjandom').decode(),
+  }
+
+  h = svntest.main.create_http_connection(sbox.repo_url)
+
+  # GET /repos/iota
+  # Expect to see a Last-Modified header.
+  h.request('GET', sbox.repo_url + '/iota', None, headers)
+  r = h.getresponse()
+  if r.status != httplib.OK:
+    raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason))
+  svntest.verify.compare_and_display_lines(None, 'Last-Modified',
+                                           svntest.verify.RegexOutput('.+'),
+                                           r.getheader('Last-Modified'))
+  r.read()
+
+  # HEAD /repos/iota
+  # Expect to see a Last-Modified header.
+  h.request('HEAD', sbox.repo_url + '/iota', None, headers)
+  r = h.getresponse()
+  if r.status != httplib.OK:
+    raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason))
+  svntest.verify.compare_and_display_lines(None, 'Last-Modified',
+                                           svntest.verify.RegexOutput('.+'),
+                                           r.getheader('Last-Modified'))
+  r.read()
+
+  # GET /repos/!svn/rvr/1/iota
+  # There should not be a Last-Modified header (it's costly and not useful,
+  # see r1724790)
+  h.request('GET', sbox.repo_url + '/!svn/rvr/1/iota', None, headers)
+  r = h.getresponse()
+  if r.status != httplib.OK:
+    raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason))
+  last_modified = r.getheader('Last-Modified')
+  if last_modified:
+    raise svntest.Failure('Unexpected Last-Modified header: %s' % last_modified)
+  r.read()
+
+
 ########################################################################
 # Run the tests
 
@@ -652,6 +699,7 @@ test_list = [ None,
               propfind_404,
               propfind_allprop,
               propfind_propname,
+              last_modified_header,
              ]
 serial_only = True