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/03/30 12:44:32 UTC

svn commit: r1899386 - in /subversion/branches/pristines-on-demand-on-mwf: ./ build/generator/ subversion/bindings/swig/ subversion/include/ subversion/libsvn_repos/ subversion/libsvn_wc/ subversion/tests/cmdline/ subversion/tests/cmdline/svntest/ tool...

Author: julianfoad
Date: Wed Mar 30 12:44:32 2022
New Revision: 1899386

URL: http://svn.apache.org/viewvc?rev=1899386&view=rev
Log:
On the 'pristines-on-demand-on-mwf' branch: sync with trunk@1899385.

Modified:
    subversion/branches/pristines-on-demand-on-mwf/   (props changed)
    subversion/branches/pristines-on-demand-on-mwf/build/generator/gen_win_dependencies.py
    subversion/branches/pristines-on-demand-on-mwf/subversion/bindings/swig/INSTALL
    subversion/branches/pristines-on-demand-on-mwf/subversion/include/svn_types.h
    subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_repos/log.c
    subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql
    subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc.h
    subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/authz_tests.py
    subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/svntest/sandbox.py
    subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/upgrade_tests.py
    subversion/branches/pristines-on-demand-on-mwf/tools/dist/backport.pl

Propchange: subversion/branches/pristines-on-demand-on-mwf/
------------------------------------------------------------------------------
  Merged /subversion/trunk:r1899008-1899385

Modified: subversion/branches/pristines-on-demand-on-mwf/build/generator/gen_win_dependencies.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/build/generator/gen_win_dependencies.py?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/build/generator/gen_win_dependencies.py (original)
+++ subversion/branches/pristines-on-demand-on-mwf/build/generator/gen_win_dependencies.py Wed Mar 30 12:44:32 2022
@@ -285,6 +285,11 @@ class GenDependenciesBase(gen_base.Gener
           self.sln_version = '12.00'
           self.vcproj_version = '14.2'
           self.vcproj_extension = '.vcxproj'
+        elif val == '2022' or val == '17':
+          self.vs_version = '2022'
+          self.sln_version = '12.00'
+          self.vcproj_version = '14.3'
+          self.vcproj_extension = '.vcxproj'
         elif re.match('^20\d+$', val):
           print('WARNING: Unknown VS.NET version "%s",'
                 ' assuming VS2012. Your VS can probably upgrade')

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/bindings/swig/INSTALL
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/bindings/swig/INSTALL?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/bindings/swig/INSTALL (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/bindings/swig/INSTALL Wed Mar 30 12:44:32 2022
@@ -84,8 +84,10 @@ Step 1: [Optional] Install a suitable ve
       following notes:
       - SWIG 1.3.24 and later 1.3.x may work, but we do not test these
         versions on our latest source code.
-      - For Python 2 bindings, SWIG 4.0.0 or later is not supported.
-      - For Python 3 bindings, SWIG 3.0.10 or later is required.
+      - For Python 3 bindings, use SWIG 3.0.10 or later. (SWIG 4.0.0
+        or later is supported as well.)
+      - For Python 2 bindings, use SWIG 2.x-3.x. (SWIG 4.0.0 or later
+        is not supported as it lacks the '-classic' option.)
       - Note that SWIG 3.0.9 has some trouble with Python support.
         (See https://sourceforge.net/p/swig/news/2016/06/swig-3010-released/)
       - For Perl 5.16 and later, SWIG 2.0.8 or later is required.
@@ -171,12 +173,11 @@ Step 3:  Install Specific Language Bindi
 
 *  Python
 
-   1.  (Optional) If you want to build Python bindings for a version of
-       Python than other than that the prebuilt bindings C sources target
-       (e.g., if you use the Subversion distribution tarball but want to build
-       Python 2 bindings), run 'make clean-swig-py' from the top of the
-       Subversion build tree, to ensure not to use incompatible version of
-       bindings source files.
+   1.  (Optional) Run 'make clean-swig-py' from the top of the Subversion
+       build tree.  This will clean any prebuilt or previously built bindings
+       to avoid a mixture of incompatible bindings source files (e.g., if you
+       use the Subversion distribution tarball but want to build Python 2
+       bindings).
 
    2.  Run 'make swig-py' from the top of the Subversion build tree,
        to build the bindings.

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/include/svn_types.h
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/include/svn_types.h?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/include/svn_types.h (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/include/svn_types.h Wed Mar 30 12:44:32 2022
@@ -610,7 +610,7 @@ svn_dirent_create(apr_pool_t *result_poo
  * keywords (e.g., $NetBSD$).  See
  *
  * @verbatim
-      http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=8921
+      https://svn.haxx.se/dev/archive-2001-12/0479.shtml
       =====
       From: "Jonathan M. Manning" <jm...@alisa-jon.net>
       To: dev@subversion.tigris.org
@@ -621,7 +621,7 @@ svn_dirent_create(apr_pool_t *result_poo
  * and Eric Gillespie's support of same:
  *
  * @verbatim
-      http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=8757
+      https://svn.haxx.se/dev/archive-2001-12/0315.shtml
       =====
       From: "Eric Gillespie, Jr." <ep...@pretzelnet.org>
       To: dev@subversion.tigris.org
@@ -878,7 +878,7 @@ typedef struct svn_log_entry_t
    * value as changed_paths for compatibility with users assuming an older
    * version.
    *
-   * @note See http://svn.haxx.se/dev/archive-2010-08/0362.shtml for
+   * @note See https://svn.haxx.se/dev/archive-2010-08/0362.shtml for
    * further explanation.
    *
    * @since New in 1.6.

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_repos/log.c?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_repos/log.c Wed Mar 30 12:44:32 2022
@@ -337,42 +337,36 @@ detect_changed(svn_repos_revision_access
       if (   (change->change_kind == svn_fs_path_change_add)
           || (change->change_kind == svn_fs_path_change_replace))
         {
-          const char *copyfrom_path = change->copyfrom_path;
-          svn_revnum_t copyfrom_rev = change->copyfrom_rev;
-
           /* the following is a potentially expensive operation since on FSFS
              we will follow the DAG from ROOT to PATH and that requires
              actually reading the directories along the way. */
           if (!change->copyfrom_known)
             {
-              SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
+              SVN_ERR(svn_fs_copied_from(&change->copyfrom_rev, &change->copyfrom_path,
                                         root, path, iterpool));
               change->copyfrom_known = TRUE;
             }
 
-          if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
+          if (change->copyfrom_path && SVN_IS_VALID_REVNUM(change->copyfrom_rev))
             {
-              svn_boolean_t readable = TRUE;
-
               if (callbacks->authz_read_func)
                 {
                   svn_fs_root_t *copyfrom_root;
+                  svn_boolean_t readable;
 
                   SVN_ERR(svn_fs_revision_root(&copyfrom_root, fs,
-                                               copyfrom_rev, iterpool));
+                                               change->copyfrom_rev, iterpool));
                   SVN_ERR(callbacks->authz_read_func(&readable,
                                                      copyfrom_root,
-                                                     copyfrom_path,
+                                                     change->copyfrom_path,
                                                      callbacks->authz_read_baton,
                                                      iterpool));
                   if (! readable)
-                    found_unreadable = TRUE;
-                }
-
-              if (readable)
-                {
-                  change->copyfrom_path = copyfrom_path;
-                  change->copyfrom_rev = copyfrom_rev;
+                    {
+                      found_unreadable = TRUE;
+                      change->copyfrom_path = NULL;
+                      change->copyfrom_rev = SVN_INVALID_REVNUM;
+                    }
                 }
             }
         }

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql Wed Mar 30 12:44:32 2022
@@ -793,6 +793,7 @@ PRAGMA user_version = 33; */
  *   * The implementation of svn_client_latest_wc_version()
  *   * The implementation of svn_wc__format_from_version()
  *   * The implementation of svn_client_get_wc_formats_supported()
+ *   * subversion/tests/cmdline/svntest/main.py:wc_format()
  *   * The comment above the comment above SVN_WC__VERSION
  *   * The value of SVN_WC__VERSION, if needed
  */

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc.h
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc.h?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc.h (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc.h Wed Mar 30 12:44:32 2022
@@ -182,6 +182,7 @@ extern "C" {
  */
 /* IMPORTANT: Update the implementation of svn_client_default_wc_version()
               and svn_client_get_wc_formats_supported()
+              and svntest.main.wc_format()
               whenever you change this value! */
 #define SVN_WC__SUPPORTED_VERSION 31
 

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/authz_tests.py?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/authz_tests.py (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/authz_tests.py Wed Mar 30 12:44:32 2022
@@ -1763,6 +1763,60 @@ def delete_file_with_starstar_rules(sbox
 
   svntest.main.run_svn(None, 'rm', sbox.repo_url + '/iota', '-m', 'rm by URL')
 
+@Skip(svntest.main.is_ra_type_file)
+def log_inaccessible_copyfrom(sbox):
+  "log doesn't leak inaccessible copyfrom paths"
+
+  sbox.build(empty=True)
+  sbox.simple_add_text('secret', 'private')
+  sbox.simple_commit(message='log message for r1')
+  sbox.simple_copy('private', 'public')
+  sbox.simple_commit(message='log message for r2')
+
+  svntest.actions.enable_revprop_changes(sbox.repo_dir)
+  # Remove svn:date and svn:author for predictable output.
+  svntest.actions.run_and_verify_svn(None, [], 'propdel', '--revprop',
+                                     '-r2', 'svn:date', sbox.repo_url)
+  svntest.actions.run_and_verify_svn(None, [], 'propdel', '--revprop',
+                                     '-r2', 'svn:author', sbox.repo_url)
+
+  write_restrictive_svnserve_conf(sbox.repo_dir)
+
+  # First test with blanket access.
+  write_authz_file(sbox,
+                   {"/" : "* = rw"})
+  expected_output = svntest.verify.ExpectedOutput([
+    "------------------------------------------------------------------------\n",
+    "r2 | (no author) | (no date) | 1 line\n",
+    "Changed paths:\n",
+    "   A /public (from /private:1)\n",
+    "\n",
+    "log message for r2\n",
+    "------------------------------------------------------------------------\n",
+  ])
+  svntest.actions.run_and_verify_svn(expected_output, [],
+                                     'log', '-r2', '-v',
+                                     sbox.repo_url)
+
+  # Now test with an inaccessible copy source (/private).
+  write_authz_file(sbox,
+                   {"/" : "* = rw"},
+                   {"/private" : "* ="})
+  expected_output = svntest.verify.ExpectedOutput([
+    "------------------------------------------------------------------------\n",
+    "r2 | (no author) | (no date) | 1 line\n",
+    "Changed paths:\n",
+    # The copy is shown as a plain add with no copyfrom info.
+    "   A /public\n",
+    "\n",
+    # No log message, as the revision is only partially visible.
+    "\n",
+    "------------------------------------------------------------------------\n",
+  ])
+  svntest.actions.run_and_verify_svn(expected_output, [],
+                                     'log', '-r2', '-v',
+                                     sbox.repo_url)
+
 
 ########################################################################
 # Run the tests
@@ -1804,6 +1858,7 @@ test_list = [ None,
               group_member_empty_string,
               empty_group,
               delete_file_with_starstar_rules,
+              log_inaccessible_copyfrom,
              ]
 serial_only = True
 

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/svntest/sandbox.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/svntest/sandbox.py?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/svntest/sandbox.py (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/svntest/sandbox.py Wed Mar 30 12:44:32 2022
@@ -600,17 +600,37 @@ class Sandbox:
                        self.read_only and "true" or "false"))
     pass
 
-  def read_wc_format(self):
-    dot_svn = svntest.main.get_admin_name()
-    db = svntest.sqlite3.connect(os.path.join(self.wc_dir, dot_svn, 'wc.db'))
+  @staticmethod
+  def _wc_format_of(wc_db_path):
+    """Return the working copy format of the given wc.db file."""
+    db = svntest.sqlite3.connect(wc_db_path)
     c = db.cursor()
     c.execute('pragma user_version;')
     found_format = c.fetchone()[0]
     db.close()
     return found_format
 
-  def pristines_on_demand_enabled(self):
-    return self.read_wc_format() == 32
+  def read_wc_formats(self):
+    """Return a dictionary mapping working copy root relpaths to their
+    format numbers.
+
+    The relpaths are relative to self.wc_dir.
+
+    The return value will always contain an empty string key.
+    """
+    dot_svn = svntest.main.get_admin_name()
+    ret = dict()
+    for root, dirs, files in os.walk(self.wc_dir):
+      if dot_svn in dirs:
+        wc_db_path = os.path.join(root, dot_svn, 'wc.db')
+        # If we didn't check existence, wc.db would be auto-created if .svn
+        # exists and .svn/wc.db doesn't.
+        if os.path.exists(wc_db_path):
+          ret[root[len(self.wc_dir)+1:]] = self._wc_format_of(wc_db_path)
+    return { k.replace(os.sep, '/') : ret[k] for k in ret }
+
+  def pristines_on_demand_enabled(self, relpath=''):
+    return self.read_wc_formats().get(relpath, 0) == 32
 
 def is_url(target):
   return (target.startswith('^/')

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/upgrade_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/upgrade_tests.py?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/upgrade_tests.py (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/tests/cmdline/upgrade_tests.py Wed Mar 30 12:44:32 2022
@@ -102,13 +102,19 @@ def replace_sbox_repo_with_tarfile(sbox,
   shutil.move(os.path.join(extract_dir, dir), sbox.repo_dir)
 
 def check_format(sbox, expected_format):
-  found_format = sbox.read_wc_format()
-  if found_format != expected_format:
+  assert isinstance(expected_format, int)
+  formats = sbox.read_wc_formats()
+  if formats[''] != expected_format:
     raise svntest.Failure("found format '%d'; expected '%d'; in wc '%s'" %
-                          (found_format, expected_format, sbox.wc_dir))
+                          (formats[''], expected_format, sbox.wc_dir))
 
-def expect_pristines_all_present(sbox):
-  return sbox.read_wc_format() <= 31
+def check_formats(sbox, expected_formats):
+  assert isinstance(expected_formats, dict)
+  formats = sbox.read_wc_formats()
+  ### If we ever need better error messages here, reuse run_and_verify_info().
+  if formats != expected_formats:
+    raise svntest.Failure("found format '%s'; expected '%s'; in wc '%s'" %
+                          (formats, expected_formats, sbox.wc_dir))
 
 def check_pristine(sbox, files):
   for file in files:
@@ -117,10 +123,10 @@ def check_pristine(sbox, files):
     try:
       file_pristine = open(svntest.wc.text_base_path(file_path), 'r').read()
     except (FileNotFoundError, svntest.Failure): # FileNotFoundError
-      if expect_pristines_all_present(sbox):
-        raise
-      # Pristine missing; pristines optional so ignore it
-      continue
+      if sbox.pristines_on_demand_enabled(''):
+        # Pristine missing; pristines optional so ignore it
+        continue
+      raise
     if (file_text != file_pristine):
       raise svntest.Failure("pristine mismatch for '%s'" % (file))
 
@@ -342,7 +348,18 @@ def upgrade_with_externals(sbox):
                                      'upgrade', sbox.wc_dir)
 
   # Actually check the format number of the upgraded working copy
-  check_format(sbox, get_current_format())
+  check_formats(sbox,
+      {relpath: get_current_format()
+       for relpath in (
+         '',
+         'A/D/exdir_A',
+         'A/D/exdir_A/G',
+         'A/D/exdir_A/H',
+         'A/D/x',
+         'A/C/exdir_G',
+         'A/C/exdir_H',
+       )})
+
   check_pristine(sbox, ['iota', 'A/mu',
                         'A/D/x/lambda', 'A/D/x/E/alpha'])
 
@@ -477,13 +494,30 @@ def basic_upgrade_1_0(sbox):
   # Now upgrade the working copy
   svntest.actions.run_and_verify_svn(None, [],
                                      'upgrade', sbox.wc_dir)
-  # And the separate working copy below COPIED or check_format() fails
+
+  # Actually check the format number of the upgraded working copy, including
+  # the external, and of the separate working copy (implicitly)
+  current_format = get_current_format()
+  check_formats(sbox, {'': current_format})
+
+  # And the separate working copy below COPIED
+  #
+  # ### This was originally added in r919021, during 1.7 development, because
+  # ### check_format() recursed into the separate working copy.
+  # ### 
+  # ### The remainder of the test passes if this call is removed.
+  # ### 
+  # ### So, for now, this call serves only as a smoke test, to confirm that the
+  # ### upgrade returns 0.  However:
+  # ###
+  # ### TODO: Verify the results of this upgrade
   svntest.actions.run_and_verify_svn(None, [],
                                      'upgrade',
                                      os.path.join(sbox.wc_dir, 'COPIED', 'G'))
 
-  # Actually check the format number of the upgraded working copy
-  check_format(sbox, get_current_format())
+  # Actually check the format number of the upgraded working copy and of
+  # the separate working copy
+  check_formats(sbox, {k: current_format for k in ('', 'COPIED/G')})
 
   # Now check the contents of the working copy
   # #### This working copy is not just a basic tree,
@@ -1484,13 +1518,31 @@ def upgrade_1_0_with_externals(sbox):
   # Now upgrade the working copy
   svntest.actions.run_and_verify_svn(None, [],
                                      'upgrade', sbox.wc_dir)
-  # And the separate working copy below COPIED or check_format() fails
+
+  # Actually check the format number of the upgraded working copy, including
+  # the external, and of the separate working copy (implicitly)
+  current_format = get_current_format()
+  check_formats(sbox, {'': current_format, 'exdir_G': current_format})
+
+  # And the separate working copy below COPIED
+  #
+  # ### This was originally added in r1702474, during 1.10 development, because
+  # ### check_format() recursed into the separate working copy.  It was copied
+  # ### from basic_upgrade_1_0() above.
+  # ### 
+  # ### The remainder of the test passes if this call is removed.
+  # ### 
+  # ### So, for now, this call serves only as a smoke test, to confirm that the
+  # ### upgrade returns 0.  However:
+  # ###
+  # ### TODO: Verify the results of this upgrade
   svntest.actions.run_and_verify_svn(None, [],
                                      'upgrade',
                                      os.path.join(sbox.wc_dir, 'COPIED', 'G'))
 
-  # Actually check the format number of the upgraded working copy
-  check_format(sbox, get_current_format())
+  # Actually check the format number of the upgraded working copy, including
+  # the external, and of the separate working copy
+  check_formats(sbox, {k: current_format for k in ('', 'exdir_G', 'COPIED/G')})
 
   # Now check the contents of the working copy
   # #### This working copy is not just a basic tree,

Modified: subversion/branches/pristines-on-demand-on-mwf/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/tools/dist/backport.pl?rev=1899386&r1=1899385&r2=1899386&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/tools/dist/backport.pl (original)
+++ subversion/branches/pristines-on-demand-on-mwf/tools/dist/backport.pl Wed Mar 30 12:44:32 2022
@@ -9,11 +9,11 @@ use v5.10.0; # needed for $^V
 # experimental and "subject to change" in v5.18 (see perl5180delta).  Every
 # use of it now triggers a warning.
 #
-# As of Perl v5.30.0, the semantics of given/when provided by Perl are
+# As of Perl v5.34.0, the semantics of given/when provided by Perl are
 # compatible with those expected by the script, so disable the warning for
 # those Perls.  But don't try to disable the the warning category on Perls
 # that don't know that category, since that breaks compilation.
-no if (v5.17.0 le $^V and $^V le v5.30.0),
+no if (v5.17.0 le $^V and $^V le v5.34.0),
    warnings => 'experimental::smartmatch';
 
 # Licensed to the Apache Software Foundation (ASF) under one
@@ -591,10 +591,10 @@ sub parse_entry {
   # summary
   do {
     push @logsummary, shift
-  } until $_[0] =~ /^\s*[A-Z][][\w]*:/ or not defined $_[0];
+  } until not defined $_[0] or $_[0] =~ /^\s*[A-Z][][\w]*:/;
 
   # votes
-  unshift @votes, pop until $_[-1] =~ /^\s*Votes:/ or not defined $_[-1];
+  unshift @votes, pop until not defined $_[-1] or $_[-1] =~ /^\s*Votes:/;
   pop;
 
   # depends, branch, notes