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 2019/10/21 11:29:41 UTC

svn commit: r1868698 - in /subversion/branches/swig-py3: ./ build/ notes/ subversion/include/private/ subversion/libsvn_fs_fs/ subversion/libsvn_repos/ subversion/libsvn_subr/ subversion/libsvn_wc/ subversion/svnrdump/ subversion/tests/cmdline/ tools/b...

Author: brane
Date: Mon Oct 21 11:29:41 2019
New Revision: 1868698

URL: http://svn.apache.org/viewvc?rev=1868698&view=rev
Log:
On the swig-py3 branch: Sync with trunk up to r1868691.

Added:
    subversion/branches/swig-py3/tools/dist/templates/STATUS.ezt
      - copied unchanged from r1868691, subversion/trunk/tools/dist/templates/STATUS.ezt
    subversion/branches/swig-py3/tools/dist/templates/release-notes.ezt
      - copied unchanged from r1868691, subversion/trunk/tools/dist/templates/release-notes.ezt
Removed:
    subversion/branches/swig-py3/tools/dist/create-minor-release-branch.py
Modified:
    subversion/branches/swig-py3/   (props changed)
    subversion/branches/swig-py3/INSTALL
    subversion/branches/swig-py3/build/run_tests.py
    subversion/branches/swig-py3/notes/moves
    subversion/branches/swig-py3/notes/repos-dictated-config
    subversion/branches/swig-py3/subversion/include/private/svn_repos_private.h
    subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c
    subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c
    subversion/branches/swig-py3/subversion/libsvn_repos/dump_editor.c
    subversion/branches/swig-py3/subversion/libsvn_repos/fs-wrap.c
    subversion/branches/swig-py3/subversion/libsvn_repos/load-fs-vtable.c
    subversion/branches/swig-py3/subversion/libsvn_subr/sysinfo.c
    subversion/branches/swig-py3/subversion/libsvn_wc/wc_db.c
    subversion/branches/swig-py3/subversion/svnrdump/load_editor.c
    subversion/branches/swig-py3/subversion/svnrdump/util.c
    subversion/branches/swig-py3/subversion/tests/cmdline/diff_tests.py
    subversion/branches/swig-py3/subversion/tests/cmdline/move_tests.py
    subversion/branches/swig-py3/subversion/tests/cmdline/svnadmin_tests.py
    subversion/branches/swig-py3/subversion/tests/cmdline/svnserveautocheck.sh
    subversion/branches/swig-py3/subversion/tests/cmdline/tree_conflict_tests.py
    subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh
    subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh
    subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh
    subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh
    subversion/branches/swig-py3/tools/dist/backport.pl
    subversion/branches/swig-py3/tools/dist/release.py
    subversion/branches/swig-py3/tools/dist/templates/download.ezt

Propchange: subversion/branches/swig-py3/
------------------------------------------------------------------------------
  Merged /subversion/trunk:r1867213-1868691

Modified: subversion/branches/swig-py3/INSTALL
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/INSTALL?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/INSTALL (original)
+++ subversion/branches/swig-py3/INSTALL Mon Oct 21 11:29:41 2019
@@ -293,7 +293,7 @@ I.    INTRODUCTION
       instead.
 
       Apache Serf can be obtained via your system's package distribution
-      system or directly from http://code.google.com/p/serf/.
+      system or directly from https://serf.apache.org/.
 
       For more information on Apache Serf and Subversion's ra_serf, see the
       file subversion/libsvn_ra_serf/README.
@@ -924,7 +924,7 @@ II.   INSTALLATION
       C:>nmake -f Makefile.win
       C:>nmake -f Makefile.win install
 
-      Please refere to the build instructions provided by the library source
+      Please refer to the build instructions provided by the library source
       for actual build instructions.
 
       ZLib
@@ -933,7 +933,7 @@ II.   INSTALLATION
       Building ZLib using Visual Studio should be quite simple. Just open the
       appropriate solution and build the project zlibstat using the IDE.
 
-      Please refere to the build instructions provided by the library source
+      Please refer to the build instructions provided by the library source
       for actual build instructions.
 
       Note that you'd make sure to define ZLIB_WINAPI in the ZLib config

Modified: subversion/branches/swig-py3/build/run_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/build/run_tests.py?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/build/run_tests.py (original)
+++ subversion/branches/swig-py3/build/run_tests.py Mon Oct 21 11:29:41 2019
@@ -713,9 +713,11 @@ class TestHarness:
 
     # Summary.
     if failed or xpassed or failed_list:
-      print("SUMMARY: Some tests failed.\n")
+      summary = "Some tests failed"
     else:
-      print("SUMMARY: All tests successful.\n")
+      summary = "All tests successful"
+    print("Python version: %d.%d.%d." % sys.version_info[:3])
+    print("SUMMARY: %s\n" % summary)
 
     self._close_log()
     return failed

Modified: subversion/branches/swig-py3/notes/moves
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/notes/moves?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/notes/moves (original)
+++ subversion/branches/swig-py3/notes/moves Mon Oct 21 11:29:41 2019
@@ -75,7 +75,7 @@ private libsvn_wc API:
 
 More API changes might be needed (TBD).
 In particular, scan_deletion may need to return a list of moves
-in the multi-layer case (http://wiki.apache.org/subversion/MultiLayerMoves)
+in the multi-layer case (https://cwiki.apache.org/confluence/display/SVN/MultiLayerMoves)
 
 We might require a working copy upgrade when going from 1.7 to 1.8,
 and only allow new move functionality to be used with 1.8 working copies.

Modified: subversion/branches/swig-py3/notes/repos-dictated-config
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/notes/repos-dictated-config?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/notes/repos-dictated-config (original)
+++ subversion/branches/swig-py3/notes/repos-dictated-config Mon Oct 21 11:29:41 2019
@@ -1,2 +1,2 @@
 [  The contents and further evolution of this document have been moved to  ]
-[  http://wiki.apache.org/subversion/ServerDictatedConfiguration           ]
+[  https://cwiki.apache.org/confluence/display/SVN/ServerDictatedConfiguration  ]

Modified: subversion/branches/swig-py3/subversion/include/private/svn_repos_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/include/private/svn_repos_private.h?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/include/private/svn_repos_private.h (original)
+++ subversion/branches/swig-py3/subversion/include/private/svn_repos_private.h Mon Oct 21 11:29:41 2019
@@ -86,9 +86,11 @@ svn_repos__validate_prop(const char *nam
  *
  * NAME is used to check that VALUE should be normalized, and if this
  * is the case, VALUE is then normalized, allocated from RESULT_POOL.
- * If no normalization is required, VALUE will be copied to RESULT_POOL
- * unchanged.  If NORMALIZED_P is not NULL, and the normalization
- * happened, set *NORMALIZED_P to non-zero.  If the property is returned
+ * If no normalization happened, *RESULT_P will be set to VALUE, and
+ * no copying of the value will occur.
+ *
+ * If NORMALIZED_P is not NULL, and the normalization happened,
+ * set *NORMALIZED_P to non-zero.  If the property is returned
  * unchanged and NORMALIZED_P is not NULL, then *NORMALIZED_P will be
  * set to zero.  SCRATCH_POOL will be used for temporary allocations.
  */

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c Mon Oct 21 11:29:41 2019
@@ -277,6 +277,7 @@ fs_ioctl(svn_fs_t *fs, svn_fs_ioctl_code
                                        cancel_func, cancel_baton,
                                        result_pool, scratch_pool));
           *output_p = output;
+          return SVN_NO_ERROR;
         }
       else if (ctlcode.code == SVN_FS_FS__IOCTL_DUMP_INDEX.code)
         {
@@ -288,6 +289,7 @@ fs_ioctl(svn_fs_t *fs, svn_fs_ioctl_code
                                         cancel_func, cancel_baton,
                                         scratch_pool));
           *output_p = NULL;
+          return SVN_NO_ERROR;
         }
       else if (ctlcode.code == SVN_FS_FS__IOCTL_LOAD_INDEX.code)
         {
@@ -296,6 +298,7 @@ fs_ioctl(svn_fs_t *fs, svn_fs_ioctl_code
           SVN_ERR(svn_fs_fs__load_index(fs, input->revision, input->entries,
                                         scratch_pool));
           *output_p = NULL;
+          return SVN_NO_ERROR;
         }
       else if (ctlcode.code == SVN_FS_FS__IOCTL_REVISION_SIZE.code)
         {
@@ -307,14 +310,11 @@ fs_ioctl(svn_fs_t *fs, svn_fs_ioctl_code
                                            fs, input->revision,
                                            scratch_pool));
           *output_p = output;
+          return SVN_NO_ERROR;
         }
-      else
-        return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
     }
-  else
-    return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
 
-  return SVN_NO_ERROR;
+  return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
 }
 
 /* The vtable associated with a specific open filesystem. */

Modified: subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c Mon Oct 21 11:29:41 2019
@@ -569,7 +569,7 @@ parse_rule_path(authz_rule_t *rule,
               || (pattern->len == 2 && pattern->data[1] == '*'))
             {
               /* Process * and **, applying normalization as per
-                 https://wiki.apache.org/subversion/AuthzImprovements. */
+                 https://cwiki.apache.org/confluence/display/SVN/Authz+Improvements. */
 
               authz_rule_segment_t *const prev =
                 (nseg > 1 ? segment - 1 : NULL);

Modified: subversion/branches/swig-py3/subversion/libsvn_repos/dump_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_repos/dump_editor.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_repos/dump_editor.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_repos/dump_editor.c Mon Oct 21 11:29:41 2019
@@ -61,8 +61,8 @@ normalize_props(apr_hash_t **normal_prop
       svn_pool_clear(iterpool);
 
       SVN_ERR(svn_repos__normalize_prop(&value, NULL, key, value,
-                                        result_pool, iterpool));
-      svn_hash_sets(*normal_props, key, value);
+                                        iterpool, iterpool));
+      svn_hash_sets(*normal_props, key, svn_string_dup(value, result_pool));
     }
   svn_pool_destroy(iterpool);
 

Modified: subversion/branches/swig-py3/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_repos/fs-wrap.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_repos/fs-wrap.c Mon Oct 21 11:29:41 2019
@@ -282,7 +282,7 @@ svn_repos__normalize_prop(const svn_stri
     }
   else
     {
-      *result_p = svn_string_dup(value, result_pool);
+      *result_p = value;
       if (normalized_p)
         *normalized_p = FALSE;
     }

Modified: subversion/branches/swig-py3/subversion/libsvn_repos/load-fs-vtable.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_repos/load-fs-vtable.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_repos/load-fs-vtable.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_repos/load-fs-vtable.c Mon Oct 21 11:29:41 2019
@@ -155,9 +155,11 @@ get_revision_mapping(apr_hash_t *rev_map
 }
 
 
-/* Change revision property NAME to VALUE for REVISION in REPOS.  If
-   VALIDATE_PROPS is set, use functions which perform validation of
-   the property value.  Otherwise, bypass those checks. */
+/* Change revision property NAME to VALUE for REVISION in REPOS.
+   If NORMALIZE_PROPS is set, attempt to normalize properties before
+   changing them, if that is needed.  If VALIDATE_PROPS is set, use
+   functions which perform validation of the property value.
+   Otherwise, bypass those checks. */
 static svn_error_t *
 change_rev_prop(svn_repos_t *repos,
                 svn_revnum_t revision,
@@ -179,17 +181,23 @@ change_rev_prop(svn_repos_t *repos,
                                    NULL, value, pool);
 }
 
-/* Change property NAME to VALUE for PATH in TXN_ROOT.  If
-   VALIDATE_PROPS is set, use functions which perform validation of
-   the property value.  Otherwise, bypass those checks. */
+/* Change property NAME to VALUE for PATH in TXN_ROOT.
+   If NORMALIZE_PROPS is set, attempt to normalize properties before
+   changing them, if that is needed.  If VALIDATE_PROPS is set, use
+   functions which perform validation of the property value.
+   Otherwise, bypass those checks. */
 static svn_error_t *
 change_node_prop(svn_fs_root_t *txn_root,
                  const char *path,
                  const char *name,
                  const svn_string_t *value,
                  svn_boolean_t validate_props,
+                 svn_boolean_t normalize_props,
                  apr_pool_t *pool)
 {
+  if (normalize_props)
+    SVN_ERR(svn_repos__normalize_prop(&value, NULL, name, value, pool, pool));
+
   if (validate_props)
     return svn_repos_fs_change_node_prop(txn_root, path, name, value, pool);
   else
@@ -874,7 +882,8 @@ set_node_property(void *baton,
     }
 
   return change_node_prop(rb->txn_root, nb->path, name, value,
-                          pb->validate_props, nb->pool);
+                          pb->validate_props, rb->pb->normalize_props,
+                          nb->pool);
 }
 
 
@@ -890,7 +899,8 @@ delete_node_property(void *baton,
     return SVN_NO_ERROR;
 
   return change_node_prop(rb->txn_root, nb->path, name, NULL,
-                          rb->pb->validate_props, nb->pool);
+                          rb->pb->validate_props, rb->pb->normalize_props,
+                          nb->pool);
 }
 
 
@@ -914,7 +924,8 @@ remove_node_props(void *baton)
       const char *key = apr_hash_this_key(hi);
 
       SVN_ERR(change_node_prop(rb->txn_root, nb->path, key, NULL,
-                               rb->pb->validate_props, nb->pool));
+                               rb->pb->validate_props, rb->pb->normalize_props,
+                               nb->pool));
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/swig-py3/subversion/libsvn_subr/sysinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_subr/sysinfo.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_subr/sysinfo.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_subr/sysinfo.c Mon Oct 21 11:29:41 2019
@@ -1,3 +1,4 @@
+
 /*
  * sysinfo.c :  information about the running system
  *
@@ -1386,6 +1387,7 @@ release_name_from_minor_version(int mino
         case 12: return "Sierra";
         case 13: return "High Sierra";
         case 14: return "Mojave";
+        case 15: return "Catalina";
         }
     }
   return NULL;

Modified: subversion/branches/swig-py3/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_wc/wc_db.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_wc/wc_db.c Mon Oct 21 11:29:41 2019
@@ -7991,7 +7991,7 @@ struct op_delete_baton_t {
  * Note that the following sequence results in the same DB state:
  *   mv A B; mv B/F B/G
  * We do not care about the order the moves were performed in.
- * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
+ * For details, see https://cwiki.apache.org/confluence/display/SVN/MultiLayerMoves
  */
 struct moved_node_t {
   /* The source of the move. */

Modified: subversion/branches/swig-py3/subversion/svnrdump/load_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/svnrdump/load_editor.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/svnrdump/load_editor.c (original)
+++ subversion/branches/swig-py3/subversion/svnrdump/load_editor.c Mon Oct 21 11:29:41 2019
@@ -576,7 +576,8 @@ set_revision_property(void *baton,
     {
       if (! svn_hash_gets(rb->pb->skip_revprops, name))
         svn_hash_sets(rb->revprop_table,
-                      apr_pstrdup(rb->pool, name), value);
+                      apr_pstrdup(rb->pool, name),
+                      svn_string_dup(value, rb->pool));
     }
   else if (rb->head_rev_before_commit == 0
            && ! svn_hash_gets(rb->pb->skip_revprops, name))
@@ -591,9 +592,9 @@ set_revision_property(void *baton,
   /* Remember any datestamp/ author that passes through (see comment
      in close_revision). */
   if (!strcmp(name, SVN_PROP_REVISION_DATE))
-    rb->datestamp = value;
+    rb->datestamp = svn_string_dup(value, rb->pool);
   if (!strcmp(name, SVN_PROP_REVISION_AUTHOR))
-    rb->author = value;
+    rb->author = svn_string_dup(value, rb->pool);
 
   return SVN_NO_ERROR;
 }
@@ -636,7 +637,7 @@ set_node_property(void *baton,
 
   prop = apr_palloc(nb->rb->pool, sizeof (*prop));
   prop->name = apr_pstrdup(pool, name);
-  prop->value = value;
+  prop->value = svn_string_dup(value, pool);
   svn_hash_sets(nb->prop_changes, prop->name, prop);
 
   return SVN_NO_ERROR;

Modified: subversion/branches/swig-py3/subversion/svnrdump/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/svnrdump/util.c?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/svnrdump/util.c (original)
+++ subversion/branches/swig-py3/subversion/svnrdump/util.c Mon Oct 21 11:29:41 2019
@@ -46,8 +46,8 @@ svn_rdump__normalize_props(apr_hash_t **
       svn_pool_clear(iterpool);
 
       SVN_ERR(svn_repos__normalize_prop(&value, NULL, key, value,
-                                        result_pool, iterpool));
-      svn_hash_sets(*normal_props, key, value);
+                                        iterpool, iterpool));
+      svn_hash_sets(*normal_props, key, svn_string_dup(value, result_pool));
     }
   svn_pool_destroy(iterpool);
 

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/diff_tests.py?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/diff_tests.py Mon Oct 21 11:29:41 2019
@@ -5253,6 +5253,83 @@ def diff_git_format_copy(sbox):
   svntest.actions.run_and_verify_svn(expected_output, [], 'diff',
                                      '--git', '.')
 
+#----------------------------------------------------------------------
+# Regression test for issue #1722: 'svn diff' produced a wrong header,
+# indicating one revision as being in the working copy when it should
+# be 'nonexistent'
+@Issue(1722)
+def diff_nonexistent_in_wc(sbox):
+  "nonexistent in working copy"
+
+  sbox.build(empty=True)
+  wc_dir = sbox.wc_dir
+
+  # We mirror the actions of the reproduction script (with one exception:
+  # we 'svn up -r 0' instead of checking out a second working copy)
+
+  sbox.simple_add_text('test\n', 'file')
+  sbox.simple_commit()
+  sbox.simple_update(revision=0)
+
+  # Expected output is empty for these cases:
+  # svn diff -r BASE
+  # svn diff -r 0:BASE
+  # svn diff -r 0
+
+  # Expected output for:
+  # svn diff -r BASE:HEAD
+  # svn diff -r 0:HEAD
+  # svn diff -r 0:1
+  expected_output_base_head = make_diff_header("file", "nonexistent",
+                                               "revision 1") + [
+  "@@ -0,0 +1 @@\n",
+  "+test\n",
+  ]
+
+  # Expected output for:
+  # svn diff -r HEAD:BASE
+  # svn diff -r HEAD
+  # svn diff -r 1:0
+  # svn diff -r 1
+  expected_output_head_base = make_diff_header("file", "revision 1",
+                                               "nonexistent") + [
+  "@@ -1 +0,0 @@\n",
+  "-test\n"
+  ]
+
+  os.chdir(wc_dir)
+
+  svntest.actions.run_and_verify_svn(expected_output_base_head, [],
+                                     'diff', '-r', 'BASE:HEAD')
+
+  svntest.actions.run_and_verify_svn(expected_output_head_base, [],
+                                     'diff', '-r', 'HEAD:BASE')
+
+  svntest.actions.run_and_verify_svn([], [],
+                                     'diff', '-r', 'BASE')
+
+  svntest.actions.run_and_verify_svn(expected_output_head_base, [],
+                                     'diff', '-r', 'HEAD')
+
+  svntest.actions.run_and_verify_svn([], [],
+                                     'diff', '-r', '0:BASE')
+
+  svntest.actions.run_and_verify_svn(expected_output_base_head, [],
+                                     'diff', '-r', '0:HEAD')
+
+  svntest.actions.run_and_verify_svn(expected_output_base_head, [],
+                                     'diff', '-r', '0:1')
+
+  svntest.actions.run_and_verify_svn(expected_output_head_base, [],
+                                     'diff', '-r', '1:0')
+
+  svntest.actions.run_and_verify_svn([], [],
+                                     'diff', '-r', '0')
+
+  svntest.actions.run_and_verify_svn(expected_output_head_base, [],
+                                     'diff', '-r', '1')
+
+
 ########################################################################
 #Run the tests
 
@@ -5353,6 +5430,7 @@ test_list = [ None,
               diff_summary_repo_wc_local_copy_unmodified,
               diff_file_replaced_by_symlink,
               diff_git_format_copy,
+              diff_nonexistent_in_wc,
               ]
 
 if __name__ == '__main__':

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/move_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/move_tests.py?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/move_tests.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/move_tests.py Mon Oct 21 11:29:41 2019
@@ -883,7 +883,7 @@ def build_simple_file_move_func(sbox, so
 #
 #   Each test must return on success or raise on failure.
 #
-# See http://wiki.apache.org/subversion/LocalMoves
+# See https://cwiki.apache.org/confluence/display/SVN/LocalMoves
 
 def lateral_move_file_test(sbox):
   "lateral (rename) move of a file test"

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/svnadmin_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/svnadmin_tests.py?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/svnadmin_tests.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/svnadmin_tests.py Mon Oct 21 11:29:41 2019
@@ -3859,7 +3859,7 @@ def dump_no_canonicalize_svndate(sbox):
                                      sbox.repo_url)
 
   dump_lines = svntest.actions.run_and_verify_dump(sbox.repo_dir)
-  assert propval + '\n' in dump_lines
+  assert propval.encode() + b'\n' in dump_lines
 
 def check_recover_prunes_rep_cache(sbox, enable_rep_sharing):
   """Check 'recover' prunes the rep-cache while enable-rep-sharing is
@@ -3965,6 +3965,78 @@ def dump_include_copied_directory(sbox):
   svntest.actions.run_and_verify_svn(expected_output, [],
                                      'log', '-v', '-q', sbox2.repo_url)
 
+def load_normalize_node_props(sbox):
+  "svnadmin load --normalize node props"
+
+  dump_str = b"""SVN-fs-dump-format-version: 2
+
+UUID: dc40867b-38f6-0310-9f5f-f81aa277e06f
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2005-05-03T19:09:41.129900Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 99
+Content-length: 99
+
+K 7
+svn:log
+V 0
+
+K 10
+svn:author
+V 2
+pl
+K 8
+svn:date
+V 27
+2005-05-03T19:10:19.975578Z
+PROPS-END
+
+Node-path: 
+Node-kind: dir
+Node-action: change
+Prop-content-length: 32
+Content-length: 32
+
+K 10
+svn:ignore
+V 3
+\n\r\n
+PROPS-END
+
+
+"""
+  sbox.build(empty=True)
+
+  # Try to load the dumpstream, expecting a failure (because of mixed
+  # EOLs in the svn:ignore property value).
+  exp_err = svntest.verify.RegexListOutput(['svnadmin: E125005:.*',
+                                            'svnadmin: E125017:.*'],
+                                           match_all=False)
+  load_and_verify_dumpstream(sbox, [], exp_err, dumpfile_revisions,
+                             False, dump_str, '--ignore-uuid')
+
+  # Now try it again with prop normalization.
+  svntest.actions.load_repo(sbox, dump_str=dump_str,
+                            bypass_prop_validation=False,
+                            normalize_props=True)
+  # We should get the normalized property value.
+  exit_code, output, _ = svntest.main.run_svn(None, 'pg', 'svn:ignore',
+                                              '--no-newline',
+                                              sbox.repo_url)
+  svntest.verify.verify_exit_code(None, exit_code, 0)
+  if output != ['\n', '\n']:
+    raise svntest.Failure("Unexpected property value %s" % output)
+
+
 ########################################################################
 # Run the tests
 
@@ -4043,6 +4115,7 @@ test_list = [ None,
               recover_prunes_rep_cache_when_enabled,
               recover_prunes_rep_cache_when_disabled,
               dump_include_copied_directory,
+              load_normalize_node_props,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/svnserveautocheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/svnserveautocheck.sh?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/svnserveautocheck.sh (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/svnserveautocheck.sh Mon Oct 21 11:29:41 2019
@@ -134,7 +134,7 @@ random_port() {
   if [ -n "$BASH_VERSION" ]; then
     echo $(($RANDOM+1024))
   else
-    $PYTHON -c 'import random; print random.randint(1024, 2**16-1)'
+    $PYTHON -c 'import random; print(random.randint(1024, 2**16-1))'
   fi
 }
 

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/tree_conflict_tests.py?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/tree_conflict_tests.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/tree_conflict_tests.py Mon Oct 21 11:29:41 2019
@@ -1518,7 +1518,7 @@ def local_missing_dir_endless_loop(sbox)
   sbox.simple_move('A/B', 'A/B2')
   sbox.simple_commit()
   sbox.simple_update()
-  main.file_append_binary(sbox.ospath("A/B2/lambda"), "This is more content.\n")
+  main.file_append(sbox.ospath("A/B2/lambda"), "This is more content.\n")
   sbox.simple_commit()
   sbox.simple_update()
 
@@ -1541,7 +1541,7 @@ interactive-conflicts = true
   # If everything works as expected the resolver will recommended a
   # resolution option and 'svn' will resolve the conflict automatically.
   # Verify that 'A1/B/lambda' contains the merged content:
-  contents = open(sbox.ospath('A1/B/lambda'), 'rb').readlines()
+  contents = open(sbox.ospath('A1/B/lambda'), 'r').readlines()
   svntest.verify.compare_and_display_lines(
     "A1/B/lambda has unexpectected contents", sbox.ospath("A1/B/lambda"),
     [ "This is the file 'lambda'.\n", "This is more content.\n"], contents)

Modified: subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh (original)
+++ subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh Mon Oct 21 11:29:41 2019
@@ -24,17 +24,51 @@ scripts=$(cd $(dirname "$0") && pwd)
 
 . ${scripts}/setenv.sh
 
+# Parse arguments to find out which bindings we should build
+if [ -z "$1"  ]; then
+    use_python3=false
+    build_swig_py=true
+    build_swig_pl=true
+    build_swig_rb=true
+    build_javahl=true
+else
+    use_python3=false
+    build_swig_py=false
+    build_swig_pl=false
+    build_swig_rb=false
+    build_javahl=false
+
+    while [ ! -z "$1" ]; do
+        case "$1" in
+            python3) use_python3=true;;
+            swig-py) build_swig_py=true;;
+            swig-pl) build_swig_pl=true;;
+            swig-rb) build_swig_rb=true;;
+            javahl)  build_javahl=true;;
+            *)       exit 1;;
+        esac
+        shift
+    done
+fi
+
+${use_python3} \
+    && test -n "${SVNBB_PYTHON3ENV}" \
+    && . ${SVNBB_PYTHON3ENV}/bin/activate \
+    && export PYTHON="$(which python)"
+
 #
-# Step 4: build bindings
+# Step 1: build bindings
 #
 
 build_bindings() {
     echo "============ make $1"
     cd ${absbld}
-    make $1
+    make $1 || exit 1
 }
 
-build_bindings swig-py
-build_bindings swig-pl
-build_bindings swig-rb
-build_bindings javahl
+${build_swig_py} && build_bindings swig-py
+${build_swig_pl} && build_bindings swig-pl
+${build_swig_rb} && build_bindings swig-rb
+${build_javahl} && build_bindings javahl
+
+exit 0

Modified: subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh (original)
+++ subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh Mon Oct 21 11:29:41 2019
@@ -54,6 +54,13 @@ else
     parallel=${SVNBB_PARALLEL}
 fi
 
+# An optional parameter tells us if this build should use Python 3.
+if [ "$3" = "python3" ]; then
+    test -n "${SVNBB_PYTHON3ENV}" \
+        && . ${SVNBB_PYTHON3ENV}/bin/activate \
+        && export PYTHON="$(which python)"
+fi
+
 #
 # Step 0: Create a directory for the test log files
 #

Modified: subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh (original)
+++ subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh Mon Oct 21 11:29:41 2019
@@ -37,6 +37,7 @@ scripts=$(cd $(dirname "$0") && pwd)
 . ${scripts}/setenv.sh
 
 # Parse arguments to find out which tests we should run
+use_python3=false
 check_swig_py=false
 check_swig_pl=false
 check_swig_rb=false
@@ -44,15 +45,21 @@ check_javahl=false
 
 while [ ! -z "$1" ]; do
     case "$1" in
+        python3) use_python3=true;;
         swig-py) check_swig_py=true;;
         swig-pl) check_swig_pl=true;;
         swig-rb) check_swig_rb=true;;
         javahl)  check_javahl=true;;
-        *)     exit 1;;
+        *)       exit 1;;
     esac
     shift
 done
 
+${use_python3} \
+    && test -n "${SVNBB_PYTHON3ENV}" \
+    && . ${SVNBB_PYTHON3ENV}/bin/activate \
+    && export PYTHON="$(which python)"
+
 ${check_swig_py} && run_tests swig-py
 ${check_swig_pl} && run_tests swig-pl
 ${check_swig_rb} && run_tests swig-rb

Modified: subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh (original)
+++ subversion/branches/swig-py3/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh Mon Oct 21 11:29:41 2019
@@ -98,7 +98,10 @@ while [ ! -z "$1" ]; do
     shift
 done
 
-${use_python3} && test -n "${SVNBB_PYTHON3ENV}" && . ${SVNBB_PYTHON3ENV}/bin/activate
+${use_python3} \
+    && test -n "${SVNBB_PYTHON3ENV}" \
+    && . ${SVNBB_PYTHON3ENV}/bin/activate \
+    && export PYTHON="$(which python)"
 
 ${check_local} && check_tests local
 ${check_svn} && check_tests svn

Modified: subversion/branches/swig-py3/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/dist/backport.pl?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/dist/backport.pl (original)
+++ subversion/branches/swig-py3/tools/dist/backport.pl Mon Oct 21 11:29:41 2019
@@ -262,6 +262,9 @@ numbers); it will be ignored.  For examp
     $0 "Committed revision 42." "\$Some_justification"
 will nominate r42.
 
+Revision numbers within the last thousand revisions may be specified using
+the last three digits only.
+
 The justification can be an arbitrarily-long string; if it is wider than the
 available width, this script will wrap it for you (and allow you to review
 the result before committing).
@@ -1238,6 +1241,20 @@ sub nominate_main {
 
   die "Unable to proceed." if warned_cannot_commit "Nominating failed";
 
+  # To save typing, require just the last three digits if they're unambiguous.
+  my $BASE_revision = `$SVN info --show-item=revision` + 0;
+  if ($BASE_revision > 1000) {
+    my $residue = $BASE_revision % 1000;
+    my $thousands = $BASE_revision - $residue;
+    @revnums = map {
+      $_ >= 1000
+        ? $_
+        : $thousands + $_ - 1000 * ($_ > $residue)
+      }
+      @revnums;
+  }
+
+  # Deduplicate and sort
   @revnums = sort { $a <=> $b } keys %{{ map { $_ => 1 } @revnums }};
   die "No revision numbers specified" unless @revnums;
 

Modified: subversion/branches/swig-py3/tools/dist/release.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/dist/release.py?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/dist/release.py (original)
+++ subversion/branches/swig-py3/tools/dist/release.py Mon Oct 21 11:29:41 2019
@@ -68,77 +68,52 @@ except ImportError:
 
 
 # Our required / recommended release tool versions by release branch
-tool_versions = {
-  'trunk' : {
+tool_versions = {}
+tool_versions['1.8'] = {
             'autoconf' : ['2.69',
             '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
-            'libtool'  : ['2.4.6',
-            'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
-            'swig'     : ['3.0.12',
-            '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
-  },
-  '1.13' : {
-            'autoconf' : ['2.69',
-            '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
-            'libtool'  : ['2.4.6',
-            'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
-            'swig'     : ['3.0.12',
-            '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
-  },
-  '1.12' : {
-            'autoconf' : ['2.69',
-            '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
-            'libtool'  : ['2.4.6',
-            'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
-            'swig'     : ['3.0.12',
-            '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
-  },
-  '1.11' : {
+            'libtool'  : ['2.4.3',
+            '36b4881c1843d7585de9c66c4c3d9a067ed3a3f792bc670beba21f5a4960acdf'],
+            'swig'     : ['2.0.9',
+            '586954000d297fafd7e91d1ad31089cc7e249f658889d11a44605d3662569539'],
+  }
+tool_versions['1.9'] = {
             'autoconf' : ['2.69',
             '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
             'libtool'  : ['2.4.6',
             'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
-            'swig'     : ['3.0.12',
-            '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
-  },
-  '1.10' : {
+            'swig'     : ['2.0.12',
+            '65e13f22a60cecd7279c59882ff8ebe1ffe34078e85c602821a541817a4317f7'],
+  }
+tool_versions['1.10'] = {
             'autoconf' : ['2.69',
             '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
             'libtool'  : ['2.4.6',
             'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
             'swig'     : ['3.0.12',
             '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
-  },
-  '1.9' : {
-            'autoconf' : ['2.69',
-            '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
-            'libtool'  : ['2.4.6',
-            'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
-            'swig'     : ['2.0.12',
-            '65e13f22a60cecd7279c59882ff8ebe1ffe34078e85c602821a541817a4317f7'],
-  },
-  '1.8' : {
-            'autoconf' : ['2.69',
-            '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
-            'libtool'  : ['2.4.3',
-            '36b4881c1843d7585de9c66c4c3d9a067ed3a3f792bc670beba21f5a4960acdf'],
-            'swig'     : ['2.0.9',
-            '586954000d297fafd7e91d1ad31089cc7e249f658889d11a44605d3662569539'],
-  },
-}
+  }
+tool_versions['1.11'] = tool_versions['1.10']
+tool_versions['1.12'] = tool_versions['1.10']
+tool_versions['1.13'] = tool_versions['1.10']
+tool_versions['trunk'] = tool_versions['1.10']
 
 # The version that is our current recommended release
 # ### TODO: derive this from svn_version.h; see ../../build/getversion.py
-recommended_release = '1.12'
+recommended_release = '1.13'
 # For clean-dist, a whitelist of artifacts to keep, by version.
 supported_release_lines = frozenset({"1.9", "1.10", "1.12", "1.13"})
 
 # Some constants
-svn_repos = 'https://svn.apache.org/repos/asf/subversion'
-dist_repos = 'https://dist.apache.org/repos/dist'
+svn_repos = os.getenv('SVN_RELEASE_SVN_REPOS',
+                      'https://svn.apache.org/repos/asf/subversion')
+dist_repos = os.getenv('SVN_RELEASE_DIST_REPOS',
+                       'https://dist.apache.org/repos/dist')
 dist_dev_url = dist_repos + '/dev/subversion'
 dist_release_url = dist_repos + '/release/subversion'
 dist_archive_url = 'https://archive.apache.org/dist/subversion'
+buildbot_repos = os.getenv('SVN_RELEASE_BUILDBOT_REPOS',
+                           'https://svn.apache.org/repos/infra/infrastructure/buildbot/aegis/buildmaster')
 KEYS = 'https://people.apache.org/keys/group/subversion.asc'
 extns = ['zip', 'tar.gz', 'tar.bz2']
 
@@ -183,18 +158,6 @@ class Version(object):
     def is_prerelease(self):
         return self.pre != None
 
-    def is_recommended(self):
-        return self.branch == recommended_release
-
-    def get_download_anchor(self):
-        if self.is_prerelease():
-            return 'pre-releases'
-        else:
-            if self.is_recommended():
-                return 'recommended-release'
-            else:
-                return 'supported-releases'
-
     def get_ver_tags(self, revnum):
         # These get substituted into svn_version.h
         ver_tag = ''
@@ -282,15 +245,12 @@ def get_exportdir(base_dir, version, rev
     return os.path.join(get_tempdir(base_dir),
                         'subversion-%s-r%d' % (version, revnum))
 
-def get_deploydir(base_dir):
-    return os.path.join(base_dir, 'deploy')
-
 def get_target(args):
     "Return the location of the artifacts"
     if args.target:
         return args.target
     else:
-        return get_deploydir(args.base_dir)
+        return os.path.join(args.base_dir, 'deploy')
 
 def get_branch_path(args):
     if not args.branch:
@@ -314,7 +274,7 @@ def get_tmplfile(filename):
 def get_nullfile():
     return open(os.path.devnull, 'w')
 
-def run_command(cmd, verbose=True, hide_stderr=False):
+def run_command(cmd, verbose=True, hide_stderr=False, dry_run=False):
     if verbose:
         print("+ " + ' '.join(cmd))
     stderr = None
@@ -325,7 +285,10 @@ def run_command(cmd, verbose=True, hide_
         if hide_stderr:
             stderr = get_nullfile()
 
-    subprocess.check_call(cmd, stdout=stdout, stderr=stderr)
+    if not dry_run:
+        subprocess.check_call(cmd, stdout=stdout, stderr=stderr)
+    else:
+        print('  ## dry-run; not executed')
 
 def run_script(verbose, script, hide_stderr=False):
     for l in script.split('\n'):
@@ -345,15 +308,28 @@ def download_file(url, target, checksum)
                            "downloaded: '%s'; expected: '%s'" % \
                            (target, checksum, checksum2))
 
-def run_svn(cmd, verbose=True, username=None):
+def run_svn(cmd, verbose=True, dry_run=False, username=None):
     if (username):
         cmd[:0] = ['--username', username]
-    run_command(['svn'] + cmd, verbose)
+    run_command(['svn'] + cmd, verbose=verbose, dry_run=dry_run)
 
-def run_svnmucc(cmd, verbose=True, username=None):
+def run_svnmucc(cmd, verbose=True, dry_run=False, username=None):
     if (username):
         cmd[:0] = ['--username', username]
-    run_command(['svnmucc'] + cmd, verbose)
+    run_command(['svnmucc'] + cmd, verbose=verbose, dry_run=dry_run)
+
+#----------------------------------------------------------------------
+def is_recommended(version):
+    return version.branch == recommended_release
+
+def get_download_anchor(version):
+    if version.is_prerelease():
+        return 'pre-releases'
+    else:
+        if is_recommended(version):
+            return 'recommended-release'
+        else:
+            return 'supported-releases'
 
 #----------------------------------------------------------------------
 # ezt helpers
@@ -378,7 +354,7 @@ def cleanup(args):
 
     shutil.rmtree(get_prefix(args.base_dir), True)
     shutil.rmtree(get_tempdir(args.base_dir), True)
-    shutil.rmtree(get_deploydir(args.base_dir), True)
+    shutil.rmtree(get_target(args), True)
 
 
 #----------------------------------------------------------------------
@@ -535,6 +511,217 @@ def build_env(args):
 
 
 #----------------------------------------------------------------------
+# Create a new minor release branch
+
+def get_trunk_wc_path(base_dir, path=None):
+    trunk_wc_path = os.path.join(get_tempdir(base_dir), 'svn-trunk')
+    if path is None: return trunk_wc_path
+    return os.path.join(trunk_wc_path, path)
+
+def get_buildbot_wc_path(base_dir, path=None):
+    buildbot_wc_path = os.path.join(get_tempdir(base_dir), 'svn-buildmaster')
+    if path is None: return buildbot_wc_path
+    return os.path.join(buildbot_wc_path, path)
+
+def get_trunk_url(revnum=None):
+    return svn_repos + '/trunk' + '@' + (str(revnum) if revnum else '')
+
+def get_branch_url(ver):
+    return svn_repos + '/branches/' + ver.branch + '.x'
+
+def get_tag_url(ver):
+    return svn_repos + '/tags/' + ver.base
+
+def edit_file(path, pattern, replacement):
+    print("Editing '%s'" % (path,))
+    print("  pattern='%s'" % (pattern,))
+    print("  replace='%s'" % (replacement,))
+    old_text = open(path, 'r').read()
+    new_text = re.sub(pattern, replacement, old_text)
+    assert new_text != old_text
+    open(path, 'w').write(new_text)
+
+def edit_changes_file(path, newtext):
+    """Insert NEWTEXT in the 'CHANGES' file found at PATH,
+       just before the first line that starts with 'Version '.
+    """
+    print("Prepending to '%s'" % (path,))
+    print("  text='%s'" % (newtext,))
+    lines = open(path, 'r').readlines()
+    for i, line in enumerate(lines):
+      if line.startswith('Version '):
+        with open(path, 'w') as newfile:
+          newfile.writelines(lines[:i])
+          newfile.write(newtext)
+          newfile.writelines(lines[i:])
+        break
+
+#----------------------------------------------------------------------
+def make_release_branch(args):
+    ver = args.version
+    run_svn(['copy',
+             get_trunk_url(args.revnum),
+             get_branch_url(ver),
+             '-m', 'Create the ' + ver.branch + '.x release branch.'],
+            dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def update_minor_ver_in_trunk(args):
+    """Change the minor version in trunk to the next (future) minor version.
+    """
+    ver = args.version
+    trunk_wc = get_trunk_wc_path(args.base_dir)
+    run_svn(['checkout',
+             get_trunk_url(args.revnum),
+             trunk_wc])
+
+    prev_ver = Version('1.%d.0' % (ver.minor - 1,))
+    next_ver = Version('1.%d.0' % (ver.minor + 1,))
+    relpaths = []
+
+    relpath = 'subversion/include/svn_version.h'
+    relpaths.append(relpath)
+    edit_file(get_trunk_wc_path(args.base_dir, relpath),
+              r'(#define SVN_VER_MINOR *)%s' % (ver.minor,),
+              r'\g<1>%s' % (next_ver.minor,))
+
+    relpath = 'subversion/tests/cmdline/svntest/main.py'
+    relpaths.append(relpath)
+    edit_file(get_trunk_wc_path(args.base_dir, relpath),
+              r'(SVN_VER_MINOR = )%s' % (ver.minor,),
+              r'\g<1>%s' % (next_ver.minor,))
+
+    relpath = 'subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java'
+    relpaths.append(relpath)
+    try:
+        # since r1817921 (just after branching 1.10)
+        edit_file(get_trunk_wc_path(args.base_dir, relpath),
+                  r'SVN_VER_MINOR = %s;' % (ver.minor,),
+                  r'SVN_VER_MINOR = %s;' % (next_ver.minor,))
+    except:
+        # before r1817921: two separate places
+        edit_file(get_trunk_wc_path(args.base_dir, relpath),
+                  r'version.isAtLeast\(1, %s, 0\)' % (ver.minor,),
+                  r'version.isAtLeast\(1, %s, 0\)' % (next_ver.minor,))
+        edit_file(get_trunk_wc_path(args.base_dir, relpath),
+                  r'1.%s.0, but' % (ver.minor,),
+                  r'1.%s.0, but' % (next_ver.minor,))
+
+    relpath = 'CHANGES'
+    relpaths.append(relpath)
+    # insert at beginning of CHANGES file
+    edit_changes_file(get_trunk_wc_path(args.base_dir, relpath),
+                 'Version ' + next_ver.base + '\n'
+                 + '(?? ??? 20XX, from /branches/' + next_ver.branch + '.x)\n'
+                 + get_tag_url(next_ver) + '\n'
+                 + '\n')
+
+    log_msg = '''\
+Increment the trunk version number to %s, and introduce a new CHANGES
+section, following the creation of the %s.x release branch.
+
+* subversion/include/svn_version.h,
+  subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java,
+  subversion/tests/cmdline/svntest/main.py
+    (SVN_VER_MINOR): Increment to %s.
+
+* CHANGES: New section for %s.0.
+''' % (next_ver.branch, ver.branch, next_ver.minor, next_ver.branch)
+    commit_paths = [get_trunk_wc_path(args.base_dir, p) for p in relpaths]
+    run_svn(['commit'] + commit_paths + ['-m', log_msg],
+            dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def create_status_file_on_branch(args):
+    ver = args.version
+    branch_wc = get_workdir(args.base_dir)
+    branch_url = get_branch_url(ver)
+    run_svn(['checkout', branch_url, branch_wc, '--depth=immediates'])
+
+    status_local_path = os.path.join(branch_wc, 'STATUS')
+    template_filename = 'STATUS.ezt'
+    data = { 'major-minor'          : ver.branch,
+             'major-minor-patch'    : ver.base,
+           }
+
+    template = ezt.Template(compress_whitespace=False)
+    template.parse(get_tmplfile(template_filename).read())
+
+    with open(status_local_path, 'wx') as g:
+        template.generate(g, data)
+    run_svn(['add', status_local_path])
+    run_svn(['commit', status_local_path,
+             '-m', '* branches/' + ver.branch + '.x/STATUS: New file.'],
+            dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def update_backport_bot(args):
+    ver = args.version
+    print("""\
+
+*** MANUAL STEP REQUIRED ***
+
+  Ask someone with appropriate access to add the %s.x branch
+  to the backport merge bot.  See
+  http://subversion.apache.org/docs/community-guide/releasing.html#backport-merge-bot
+
+***
+
+""" % (ver.branch,))
+
+#----------------------------------------------------------------------
+def update_buildbot_config(args):
+    """Add the new branch to the list of branches monitored by the buildbot
+       master.
+    """
+    ver = args.version
+    buildbot_wc = get_buildbot_wc_path(args.base_dir)
+    run_svn(['checkout', buildbot_repos, buildbot_wc])
+
+    prev_ver = Version('1.%d.0' % (ver.minor - 1,))
+    next_ver = Version('1.%d.0' % (ver.minor + 1,))
+
+    relpath = 'master1/projects/subversion.conf'
+    edit_file(get_buildbot_wc_path(args.base_dir, relpath),
+              r'(MINOR_LINES=\[.*%s)(\])' % (prev_ver.minor,),
+              r'\1, %s\2' % (ver.minor,))
+
+    log_msg = '''\
+Subversion: start monitoring the %s branch.
+''' % (ver.branch)
+    commit_paths = [get_buildbot_wc_path(args.base_dir, relpath)]
+    run_svn(['commit'] + commit_paths + ['-m', log_msg],
+            dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def create_release_branch(args):
+    make_release_branch(args)
+    update_minor_ver_in_trunk(args)
+    create_status_file_on_branch(args)
+    update_backport_bot(args)
+    update_buildbot_config(args)
+
+
+#----------------------------------------------------------------------
+def write_release_notes(args):
+
+    template_filename = 'release-notes.ezt'
+
+    prev_ver = Version('%d.%d.0' % (args.version.major, args.version.minor - 1))
+    data = { 'major-minor'          : args.version.branch,
+             'previous-major-minor' : prev_ver.branch,
+           }
+
+    template = ezt.Template(compress_whitespace=False)
+    template.parse(get_tmplfile(template_filename).read())
+
+    if args.edit_html_file:
+        with open(args.edit_html_file, 'w') as g:
+            template.generate(g, data)
+    else:
+        template.generate(sys.stdout, data)
+
+#----------------------------------------------------------------------
 # Create release artifacts
 
 def compare_changes(repos, branch, revision):
@@ -611,11 +798,11 @@ def roll_tarballs(args):
         compare_changes(svn_repos, branch, args.revnum)
 
     # Ensure the output directory doesn't already exist
-    if os.path.exists(get_deploydir(args.base_dir)):
+    if os.path.exists(get_target(args)):
         raise RuntimeError('output directory \'%s\' already exists'
-                                            % get_deploydir(args.base_dir))
+                                            % get_target(args))
 
-    os.mkdir(get_deploydir(args.base_dir))
+    os.mkdir(get_target(args))
 
     logging.info('Preparing working copy source')
     shutil.rmtree(get_workdir(args.base_dir), True)
@@ -682,9 +869,9 @@ def roll_tarballs(args):
     def export(windows):
         shutil.rmtree(exportdir, True)
         if windows:
-            eol_style = "--native-eol CRLF"
+            eol_style = "--native-eol=CRLF"
         else:
-            eol_style = "--native-eol LF"
+            eol_style = "--native-eol=LF"
         run_svn(['export',
                  eol_style, get_workdir(args.base_dir), exportdir],
                 verbose=args.verbose)
@@ -761,8 +948,8 @@ def roll_tarballs(args):
     for e in extns:
         filename = basename + '.' + e
         filepath = os.path.join(get_tempdir(args.base_dir), filename)
-        shutil.move(filepath, get_deploydir(args.base_dir))
-        filepath = os.path.join(get_deploydir(args.base_dir), filename)
+        shutil.move(filepath, get_target(args))
+        filepath = os.path.join(get_target(args), filename)
         if args.version < Version("1.11.0-alpha1"):
             # 1.10 and earlier generate *.sha1 files for compatibility reasons.
             # They are deprecated, however, so we don't publicly link them in
@@ -778,7 +965,7 @@ def roll_tarballs(args):
     if args.version.pre != 'nightly':
         shutil.copy(os.path.join(get_workdir(args.base_dir),
                                  'subversion', 'include', 'svn_version.h'),
-                    os.path.join(get_deploydir(args.base_dir),
+                    os.path.join(get_target(args),
                                  'svn_version.h.dist-%s' % str(args.version)))
 
     # And we're done!
@@ -1006,8 +1193,8 @@ def write_news(args):
              'major-minor' : args.version.branch,
              'version' : str(args.version),
              'version_base' : args.version.base,
-             'anchor': args.version.get_download_anchor(),
-             'is_recommended': ezt_bool(args.version.is_recommended()),
+             'anchor': get_download_anchor(args.version),
+             'is_recommended': ezt_bool(is_recommended(args.version)),
              'announcement_url': args.announcement_url,
            }
 
@@ -1067,7 +1254,7 @@ def write_announcement(args):
              'siginfo'              : "\n".join(siginfo) + "\n",
              'major-minor'          : args.version.branch,
              'major-minor-patch'    : args.version.base,
-             'anchor'               : args.version.get_download_anchor(),
+             'anchor'               : get_download_anchor(args.version),
            }
 
     if args.version.is_prerelease():
@@ -1468,13 +1655,18 @@ def main():
     parser = argparse.ArgumentParser(
                             description='Create an Apache Subversion release.')
     parser.add_argument('--clean', action='store_true', default=False,
-                   help='Remove any directories previously created by %(prog)s')
+                   help='''Remove any directories previously created by %(prog)s,
+                           including the 'prefix' dir, the 'temp' dir, and the
+                           default or specified target dir.''')
     parser.add_argument('--verbose', action='store_true', default=False,
                    help='Increase output verbosity')
     parser.add_argument('--base-dir', default=os.getcwd(),
                    help='''The directory in which to create needed files and
                            folders.  The default is the current working
                            directory.''')
+    parser.add_argument('--target',
+                   help='''The full path to the directory containing
+                           release artifacts. Default: <BASE_DIR>/deploy''')
     parser.add_argument('--branch',
                    help='''The branch to base the release on,
                            as a path relative to ^/subversion/.
@@ -1499,6 +1691,39 @@ def main():
                     help='''Attempt to use existing build dependencies before
                             downloading and building a private set.''')
 
+    # Setup the parser for the create-release-branch subcommand
+    subparser = subparsers.add_parser('create-release-branch',
+                    help='''Create a minor release branch: branch from trunk,
+                            update version numbers on trunk, create status
+                            file on branch, update backport bot,
+                            update buildbot config.''')
+    subparser.set_defaults(func=create_release_branch)
+    subparser.add_argument('version', type=Version,
+                    help='''A version number to indicate the branch, such as
+                            '1.7.0' (the '.0' is required).''')
+    subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
+                           nargs='?', default=None,
+                    help='''The trunk revision number to base the branch on.
+                            Default is HEAD.''')
+    subparser.add_argument('--dry-run', action='store_true', default=False,
+                   help='Avoid committing any changes to repositories.')
+
+    # Setup the parser for the create-release-branch subcommand
+    subparser = subparsers.add_parser('write-release-notes',
+                    help='''Write a template release-notes file.''')
+    subparser.set_defaults(func=write_release_notes)
+    subparser.add_argument('version', type=Version,
+                    help='''A version number to indicate the branch, such as
+                            '1.7.0' (the '.0' is required).''')
+    subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
+                           nargs='?', default=None,
+                    help='''The trunk revision number to base the branch on.
+                            Default is HEAD.''')
+    subparser.add_argument('--edit-html-file',
+                    help='''Write the template release-notes to this file.''')
+    subparser.add_argument('--dry-run', action='store_true', default=False,
+                   help='Avoid committing any changes to repositories.')
+
     # Setup the parser for the roll subcommand
     subparser = subparsers.add_parser('roll',
                     help='''Create the release artifacts.''')
@@ -1516,9 +1741,6 @@ def main():
     subparser.set_defaults(func=sign_candidates)
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
-    subparser.add_argument('--target',
-                    help='''The full path to the directory containing
-                            release artifacts.''')
     subparser.add_argument('--userid',
                     help='''The (optional) USER-ID specifying the key to be
                             used for signing, such as '110B1C95' (Key-ID). If
@@ -1531,9 +1753,6 @@ def main():
     subparser.set_defaults(func=post_candidates)
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
-    subparser.add_argument('--target',
-                    help='''The full path to the directory containing
-                            release artifacts.''')
 
     # Setup the parser for the create-tag subcommand
     subparser = subparsers.add_parser('create-tag',
@@ -1544,9 +1763,6 @@ def main():
                     help='''The release label, such as '1.7.0-alpha1'.''')
     subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
                     help='''The revision number to base the release on.''')
-    subparser.add_argument('--target',
-                    help='''The full path to the directory containing
-                            release artifacts.''')
 
     # Setup the parser for the bump-versions-on-branch subcommand
     subparser = subparsers.add_parser('bump-versions-on-branch',
@@ -1556,9 +1772,6 @@ def main():
                     help='''The release label, such as '1.7.0-alpha1'.''')
     subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
                     help='''The revision number to base the release on.''')
-    subparser.add_argument('--target',
-                    help='''The full path to the directory containing
-                            release artifacts.''')
 
     # The clean-dist subcommand
     subparser = subparsers.add_parser('clean-dist',
@@ -1597,9 +1810,6 @@ def main():
     subparser.add_argument('--security', action='store_true', default=False,
                     help='''The release being announced includes security
                             fixes.''')
-    subparser.add_argument('--target',
-                    help='''The full path to the directory containing
-                            release artifacts.''')
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
 
@@ -1608,9 +1818,6 @@ def main():
                     help='''Output to stdout template text for the download
                             table for subversion.apache.org''')
     subparser.set_defaults(func=write_downloads)
-    subparser.add_argument('--target',
-                    help='''The full path to the directory containing
-                            release artifacts.''')
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
 
@@ -1621,9 +1828,6 @@ def main():
     subparser.set_defaults(func=check_sigs)
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
-    subparser.add_argument('--target',
-                    help='''The full path to the directory containing
-                            release artifacts.''')
 
     # get-keys
     subparser = subparsers.add_parser('get-keys',

Modified: subversion/branches/swig-py3/tools/dist/templates/download.ezt
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/dist/templates/download.ezt?rev=1868698&r1=1868697&r2=1868698&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/dist/templates/download.ezt (original)
+++ subversion/branches/swig-py3/tools/dist/templates/download.ezt Mon Oct 21 11:29:41 2019
@@ -7,8 +7,6 @@
 </tr>
 [for fileinfo]<tr>
   <td><a href="[[]preferred]subversion/[fileinfo.filename]">[fileinfo.filename]</a></td>
-  <!-- The sha512 line does not have a class="checksum" since the link needn't
-       be rendered in monospace. -->
   <td>[<a href="https://www.apache.org/dist/subversion/[fileinfo.filename].sha512">SHA-512</a>]</td>
   <td>[<a href="https://www.apache.org/dist/subversion/[fileinfo.filename].asc">PGP</a>]</td>
 </tr>[end]